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