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