1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 5 Display *display; 6 Font *font; 7 Image *screen; 8 int _drawdebug = 0; 9 10 static char deffontname[] = "*default*"; 11 Screen *_screen; 12 13 int debuglockdisplay = 0; 14 15 static void _closedisplay(Display*, int); 16 17 /* note handler */ 18 static void 19 drawshutdown(void) 20 { 21 Display *d; 22 23 d = display; 24 if(d){ 25 display = nil; 26 _closedisplay(d, 1); 27 } 28 } 29 30 int 31 geninitdraw(char *devdir, void(*error)(Display*, char*), char *fontname, char *label, char *windir, int ref) 32 { 33 int fd, n; 34 Subfont *df; 35 char buf[128]; 36 37 display = initdisplay(devdir, windir, error); 38 if(display == nil) 39 return -1; 40 41 /* 42 * Set up default font 43 */ 44 df = getdefont(display); 45 display->defaultsubfont = df; 46 if(df == nil){ 47 fprint(2, "imageinit: can't open default subfont: %r\n"); 48 Error: 49 closedisplay(display); 50 display = nil; 51 return -1; 52 } 53 if(fontname == nil){ 54 fd = open("/env/font", OREAD); 55 if(fd >= 0){ 56 n = read(fd, buf, sizeof(buf)); 57 if(n>0 && n<sizeof buf-1){ 58 buf[n] = 0; 59 fontname = buf; 60 } 61 close(fd); 62 } 63 } 64 /* 65 * Build fonts with caches==depth of screen, for speed. 66 * If conversion were faster, we'd use 0 and save memory. 67 */ 68 if(fontname == nil){ 69 snprint(buf, sizeof buf, "%d %d\n0 %d\t%s\n", df->height, df->ascent, 70 df->n-1, deffontname); 71 //BUG: Need something better for this installsubfont("*default*", df); 72 font = buildfont(display, buf, deffontname); 73 if(font == nil){ 74 fprint(2, "imageinit: can't open default font: %r\n"); 75 goto Error; 76 } 77 }else{ 78 font = openfont(display, fontname); /* BUG: grey fonts */ 79 if(font == nil){ 80 fprint(2, "imageinit: can't open font %s: %r\n", fontname); 81 goto Error; 82 } 83 } 84 display->defaultfont = font; 85 86 /* 87 * Write label; ignore errors (we might not be running under rio) 88 */ 89 if(label){ 90 snprint(buf, sizeof buf, "%s/label", display->windir); 91 fd = open(buf, OREAD); 92 if(fd >= 0){ 93 read(fd, display->oldlabel, (sizeof display->oldlabel)-1); 94 close(fd); 95 fd = create(buf, OWRITE, 0666); 96 if(fd >= 0){ 97 write(fd, label, strlen(label)); 98 close(fd); 99 } 100 } 101 } 102 103 snprint(buf, sizeof buf, "%s/winname", display->windir); 104 if(gengetwindow(display, buf, &screen, &_screen, ref) < 0) 105 goto Error; 106 107 atexit(drawshutdown); 108 109 return 1; 110 } 111 112 int 113 initdraw(void(*error)(Display*, char*), char *fontname , char *label) 114 { 115 char *dev = "/dev"; 116 117 if(access("/dev/draw/new", AEXIST)<0 && bind("#i", "/dev", MAFTER)<0){ 118 fprint(2, "imageinit: can't bind /dev/draw: %r"); 119 return -1; 120 } 121 return geninitdraw(dev, error, fontname, label, dev, Refnone); 122 } 123 124 /* 125 * Attach, or possibly reattach, to window. 126 * If reattaching, maintain value of screen pointer. 127 */ 128 int 129 gengetwindow(Display *d, char *winname, Image **winp, Screen **scrp, int ref) 130 { 131 int n, fd; 132 char buf[64+1]; 133 Image *image; 134 Rectangle r; 135 136 fd = open(winname, OREAD); 137 if(fd<0 || (n=read(fd, buf, sizeof buf-1))<=0){ 138 if((image=d->image) == nil){ 139 fprint(2, "gengetwindow: %r\n"); 140 *winp = nil; 141 d->screenimage = nil; 142 return -1; 143 } 144 strcpy(buf, "noborder"); 145 }else{ 146 close(fd); 147 buf[n] = '\0'; 148 if(*winp != nil){ 149 _freeimage1(*winp); 150 freeimage((*scrp)->image); 151 freescreen(*scrp); 152 *scrp = nil; 153 } 154 image = namedimage(d, buf); 155 if(image == 0){ 156 fprint(2, "namedimage %s failed: %r\n", buf); 157 *winp = nil; 158 d->screenimage = nil; 159 return -1; 160 } 161 assert(image->chan != 0); 162 } 163 164 d->screenimage = image; 165 *scrp = allocscreen(image, d->white, 0); 166 if(*scrp == nil){ 167 freeimage(d->screenimage); 168 *winp = nil; 169 d->screenimage = nil; 170 return -1; 171 } 172 173 r = image->r; 174 if(strncmp(buf, "noborder", 8) != 0) 175 r = insetrect(image->r, Borderwidth); 176 *winp = _allocwindow(*winp, *scrp, r, ref, DWhite); 177 if(*winp == nil){ 178 freescreen(*scrp); 179 *scrp = nil; 180 freeimage(image); 181 d->screenimage = nil; 182 return -1; 183 } 184 d->screenimage = *winp; 185 assert((*winp)->chan != 0); 186 return 1; 187 } 188 189 int 190 getwindow(Display *d, int ref) 191 { 192 char winname[128]; 193 194 snprint(winname, sizeof winname, "%s/winname", d->windir); 195 return gengetwindow(d, winname, &screen, &_screen, ref); 196 } 197 198 #define NINFO 12*12 199 200 Display* 201 initdisplay(char *dev, char *win, void(*error)(Display*, char*)) 202 { 203 char buf[128], info[NINFO+1], *t, isnew; 204 int n, datafd, ctlfd, reffd; 205 Display *disp; 206 Dir *dir; 207 Image *image; 208 209 fmtinstall('P', Pfmt); 210 fmtinstall('R', Rfmt); 211 if(dev == 0) 212 dev = "/dev"; 213 if(win == 0) 214 win = "/dev"; 215 if(strlen(dev)>sizeof buf-25 || strlen(win)>sizeof buf-25){ 216 werrstr("initdisplay: directory name too long"); 217 return nil; 218 } 219 t = strdup(win); 220 if(t == nil) 221 return nil; 222 223 sprint(buf, "%s/draw/new", dev); 224 ctlfd = open(buf, ORDWR|OCEXEC); 225 if(ctlfd < 0){ 226 if(bind("#i", dev, MAFTER) < 0){ 227 Error1: 228 free(t); 229 werrstr("initdisplay: %s: %r", buf); 230 return 0; 231 } 232 ctlfd = open(buf, ORDWR|OCEXEC); 233 } 234 if(ctlfd < 0) 235 goto Error1; 236 if((n=read(ctlfd, info, sizeof info)) < 12){ 237 Error2: 238 close(ctlfd); 239 goto Error1; 240 } 241 if(n==NINFO+1) 242 n = NINFO; 243 buf[n] = '\0'; 244 isnew = 0; 245 if(n < NINFO) /* this will do for now, we need something better here */ 246 isnew = 1; 247 sprint(buf, "%s/draw/%d/data", dev, atoi(info+0*12)); 248 datafd = open(buf, ORDWR|OCEXEC); 249 if(datafd < 0) 250 goto Error2; 251 sprint(buf, "%s/draw/%d/refresh", dev, atoi(info+0*12)); 252 reffd = open(buf, OREAD|OCEXEC); 253 if(reffd < 0){ 254 Error3: 255 close(datafd); 256 goto Error2; 257 } 258 disp = mallocz(sizeof(Display), 1); 259 if(disp == 0){ 260 Error4: 261 close(reffd); 262 goto Error3; 263 } 264 image = nil; 265 if(0){ 266 Error5: 267 free(image); 268 free(disp); 269 goto Error4; 270 } 271 if(n >= NINFO){ 272 image = mallocz(sizeof(Image), 1); 273 if(image == nil) 274 goto Error5; 275 image->display = disp; 276 image->id = 0; 277 image->chan = strtochan(info+2*12); 278 image->depth = chantodepth(image->chan); 279 image->repl = atoi(info+3*12); 280 image->r.min.x = atoi(info+4*12); 281 image->r.min.y = atoi(info+5*12); 282 image->r.max.x = atoi(info+6*12); 283 image->r.max.y = atoi(info+7*12); 284 image->clipr.min.x = atoi(info+8*12); 285 image->clipr.min.y = atoi(info+9*12); 286 image->clipr.max.x = atoi(info+10*12); 287 image->clipr.max.y = atoi(info+11*12); 288 } 289 290 disp->_isnewdisplay = isnew; 291 disp->bufsize = iounit(datafd); 292 if(disp->bufsize <= 0) 293 disp->bufsize = 8000; 294 if(disp->bufsize < 512){ 295 werrstr("iounit %d too small", disp->bufsize); 296 goto Error5; 297 } 298 disp->buf = malloc(disp->bufsize+5); /* +5 for flush message */ 299 if(disp->buf == nil) 300 goto Error5; 301 302 disp->image = image; 303 disp->dirno = atoi(info+0*12); 304 disp->fd = datafd; 305 disp->ctlfd = ctlfd; 306 disp->reffd = reffd; 307 disp->bufp = disp->buf; 308 disp->error = error; 309 disp->windir = t; 310 disp->devdir = strdup(dev); 311 qlock(&disp->qlock); 312 disp->white = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DWhite); 313 disp->black = allocimage(disp, Rect(0, 0, 1, 1), GREY1, 1, DBlack); 314 if(disp->white == nil || disp->black == nil){ 315 free(disp->devdir); 316 free(disp->white); 317 free(disp->black); 318 goto Error5; 319 } 320 disp->opaque = disp->white; 321 disp->transparent = disp->black; 322 dir = dirfstat(ctlfd); 323 if(dir!=nil && dir->type=='i'){ 324 disp->local = 1; 325 disp->dataqid = dir->qid.path; 326 } 327 if(dir!=nil && dir->qid.vers==1) /* other way to tell */ 328 disp->_isnewdisplay = 1; 329 free(dir); 330 331 return disp; 332 } 333 334 /* 335 * Call with d unlocked. 336 * Note that disp->defaultfont and defaultsubfont are not freed here. 337 */ 338 void 339 closedisplay(Display *disp) 340 { 341 _closedisplay(disp, 0); 342 } 343 344 static void 345 _closedisplay(Display *disp, int isshutdown) 346 { 347 int fd; 348 char buf[128]; 349 350 if(disp == nil) 351 return; 352 if(disp == display) 353 display = nil; 354 if(disp->oldlabel[0]){ 355 snprint(buf, sizeof buf, "%s/label", disp->windir); 356 fd = open(buf, OWRITE); 357 if(fd >= 0){ 358 write(fd, disp->oldlabel, strlen(disp->oldlabel)); 359 close(fd); 360 } 361 } 362 363 /* 364 * if we're shutting down, don't free all the resources. 365 * if other procs are getting shot down by notes too, 366 * one might get shot down while holding the malloc lock. 367 * just let the kernel clean things up when we exit. 368 */ 369 if(isshutdown) 370 return; 371 372 free(disp->devdir); 373 free(disp->windir); 374 freeimage(disp->white); 375 freeimage(disp->black); 376 close(disp->fd); 377 close(disp->ctlfd); 378 /* should cause refresh slave to shut down */ 379 close(disp->reffd); 380 qunlock(&disp->qlock); 381 free(disp); 382 } 383 384 void 385 lockdisplay(Display *disp) 386 { 387 if(debuglockdisplay){ 388 /* avoid busy looping; it's rare we collide anyway */ 389 while(!canqlock(&disp->qlock)){ 390 fprint(1, "proc %d waiting for display lock...\n", getpid()); 391 sleep(1000); 392 } 393 }else 394 qlock(&disp->qlock); 395 } 396 397 void 398 unlockdisplay(Display *disp) 399 { 400 qunlock(&disp->qlock); 401 } 402 403 void 404 drawerror(Display *d, char *s) 405 { 406 char err[ERRMAX]; 407 408 if(d && d->error) 409 d->error(d, s); 410 else{ 411 errstr(err, sizeof err); 412 fprint(2, "draw: %s: %s\n", s, err); 413 exits(s); 414 } 415 } 416 417 static 418 int 419 doflush(Display *d) 420 { 421 int n, nn; 422 423 n = d->bufp-d->buf; 424 if(n <= 0) 425 return 1; 426 427 if((nn=write(d->fd, d->buf, n)) != n){ 428 if(_drawdebug) 429 fprint(2, "flushimage fail: d=%p: n=%d nn=%d %r\n", d, n, nn); /**/ 430 d->bufp = d->buf; /* might as well; chance of continuing */ 431 return -1; 432 } 433 d->bufp = d->buf; 434 return 1; 435 } 436 437 int 438 flushimage(Display *d, int visible) 439 { 440 if(d == nil) 441 return 0; 442 if(visible){ 443 *d->bufp++ = 'v'; /* five bytes always reserved for this */ 444 if(d->_isnewdisplay){ 445 BPLONG(d->bufp, d->screenimage->id); 446 d->bufp += 4; 447 } 448 } 449 return doflush(d); 450 } 451 452 uchar* 453 bufimage(Display *d, int n) 454 { 455 uchar *p; 456 457 if(n<0 || n>d->bufsize){ 458 werrstr("bad count in bufimage"); 459 return 0; 460 } 461 if(d->bufp+n > d->buf+d->bufsize) 462 if(doflush(d) < 0) 463 return 0; 464 p = d->bufp; 465 d->bufp += n; 466 return p; 467 } 468 469