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