| 1 | – | /* Copyright (c) 1997 Silicon Graphics, Inc. */ | 
| 2 | – |  | 
| 1 |  | #ifndef lint | 
| 2 | < | static char SCCSid[] = "$SunId$ SGI"; | 
| 2 | > | static const char       RCSid[] = "$Id$"; | 
| 3 |  | #endif | 
| 6 | – |  | 
| 4 |  | /* | 
| 5 |  | * X11 driver for holodeck display. | 
| 6 |  | * Based on rview driver. | 
| 7 |  | */ | 
| 8 |  |  | 
| 9 |  | #include "standard.h" | 
| 13 | – | #include "rhd_qtree.h" | 
| 14 | – |  | 
| 10 |  | #include  <X11/Xlib.h> | 
| 11 |  | #include  <X11/cursorfont.h> | 
| 12 |  | #include  <X11/Xutil.h> | 
| 13 | < |  | 
| 13 | > | #include  <time.h> | 
| 14 | > | #include "rhd_qtree.h" | 
| 15 |  | #include  "x11icon.h" | 
| 16 |  |  | 
| 17 | + | #ifndef RAYQLEN | 
| 18 | + | #define RAYQLEN         50000           /* max. rays to queue before flush */ | 
| 19 | + | #endif | 
| 20 | + |  | 
| 21 |  | #ifndef FEQ | 
| 22 |  | #define FEQ(a,b)        ((a)-(b) <= FTINY && (a)-(b) >= -FTINY) | 
| 23 |  | #endif | 
| 24 |  |  | 
| 25 |  | #define GAMMA           2.2             /* default gamma correction */ | 
| 26 |  |  | 
| 27 | + | #define FRAMESTATE(s)   (((s)&(ShiftMask|ControlMask))==(ShiftMask|ControlMask)) | 
| 28 | + |  | 
| 29 |  | #define MOVPCT          7               /* percent distance to move /frame */ | 
| 30 |  | #define MOVDIR(b)       ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1) | 
| 31 |  | #define MOVDEG          (-5)            /* degrees to orbit CW/down /frame */ | 
| 47 |  |  | 
| 48 |  | struct driver   odev;                   /* global device driver structure */ | 
| 49 |  |  | 
| 50 | + | char odev_args[64];                     /* command arguments */ | 
| 51 | + |  | 
| 52 |  | static XEvent  currentevent;            /* current event */ | 
| 53 |  |  | 
| 54 |  | static int  ncolors = 0;                /* color table size */ | 
| 68 |  |  | 
| 69 |  | static int      headlocked = 0;         /* lock vertical motion */ | 
| 70 |  |  | 
| 71 | < | static int  getpixels(), xnewcolr(), freepixels(), resizewindow(), | 
| 71 | > | static int  getpixels(), xnewcolr(), freepixels(), resizewindow(), getframe(), | 
| 72 |  | getevent(), getkey(), moveview(), getmove(), fixwindow(); | 
| 73 |  | static unsigned long  true_pixel(); | 
| 74 |  |  | 
| 85 |  | for (cp = tail; *cp && *cp != '.'; cp++) | 
| 86 |  | ; | 
| 87 |  | if (cp-tail == 3 && !strncmp(tail, "x11", 3)) | 
| 88 | < | return(TM_F_CAMERA); | 
| 88 | > | return(TM_F_CAMERA|TM_F_NOSTDERR); | 
| 89 |  | if (cp-tail == 4 && !strncmp(tail, "x11h", 4)) | 
| 90 | < | return(TM_F_HUMAN); | 
| 90 | > | return(TM_F_HUMAN|TM_F_NOSTDERR); | 
| 91 |  | error(USER, "illegal driver name"); | 
| 92 |  | } | 
| 93 |  |  | 
| 96 |  | char  *id; | 
| 97 |  | { | 
| 98 |  | extern char  *getenv(); | 
| 99 | < | char  *gv; | 
| 99 | > | static RGBPRIMS myprims = STDPRIMS; | 
| 100 | > | char  *ev; | 
| 101 |  | double  gamval = GAMMA; | 
| 102 | + | RGBPRIMP        dpri = stdprims; | 
| 103 |  | int  nplanes; | 
| 104 |  | XSetWindowAttributes    ourwinattr; | 
| 105 |  | XWMHints  ourxwmhints; | 
| 132 |  | ourwhite = WhitePixel(ourdisplay,ourscreen); | 
| 133 |  | } | 
| 134 |  | /* set gamma and tone mapping */ | 
| 135 | < | if ((gv = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL | 
| 136 | < | || (gv = getenv("DISPLAY_GAMMA")) != NULL) | 
| 137 | < | gamval = atof(gv); | 
| 138 | < | if (tmInit(mytmflags(), stdprims, gamval) == NULL) | 
| 135 | > | if ((ev = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL | 
| 136 | > | || (ev = getenv("DISPLAY_GAMMA")) != NULL) | 
| 137 | > | gamval = atof(ev); | 
| 138 | > | if ((ev = getenv("DISPLAY_PRIMARIES")) != NULL && | 
| 139 | > | sscanf(ev, "%f %f %f %f %f %f %f %f", | 
| 140 | > | &myprims[RED][CIEX],&myprims[RED][CIEY], | 
| 141 | > | &myprims[GRN][CIEX],&myprims[GRN][CIEY], | 
| 142 | > | &myprims[BLU][CIEX],&myprims[BLU][CIEY], | 
| 143 | > | &myprims[WHT][CIEX],&myprims[WHT][CIEY]) >= 6) | 
| 144 | > | dpri = myprims; | 
| 145 | > | if (tmInit(mytmflags(), dpri, gamval) == NULL) | 
| 146 |  | error(SYSTEM, "not enough memory in dev_open"); | 
| 147 |  | /* open window */ | 
| 148 |  | ourwinattr.background_pixel = ourblack; | 
| 171 |  | oursizhints.min_height = MINHEIGHT; | 
| 172 |  | oursizhints.flags = PMinSize; | 
| 173 |  | XSetNormalHints(ourdisplay, gwind, &oursizhints); | 
| 161 | – | /* map the window and get its size */ | 
| 162 | – | XMapWindow(ourdisplay, gwind); | 
| 163 | – | dev_input(); | 
| 164 | – | /* allocate our leaf pile */ | 
| 165 | – | if (!qtAllocLeaves(DisplayWidth(ourdisplay,ourscreen) * | 
| 166 | – | DisplayHeight(ourdisplay,ourscreen) / | 
| 167 | – | (qtMinNodesiz*qtMinNodesiz))) | 
| 168 | – | error(SYSTEM, "insufficient memory for leaf storage"); | 
| 169 | – |  | 
| 174 |  | /* figure out sensible view */ | 
| 175 |  | pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) / | 
| 176 |  | DisplayWidth(ourdisplay, ourscreen); | 
| 177 |  | pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) / | 
| 178 |  | DisplayHeight(ourdisplay, ourscreen); | 
| 179 |  | copystruct(&odev.v, &stdview); | 
| 176 | – | odev.name = id; | 
| 180 |  | odev.v.type = VT_PER; | 
| 181 | < | odev.v.horiz = 2.*180./PI * atan(0.5/VIEWDIST*pwidth*odev.hres); | 
| 182 | < | odev.v.vert = 2.*180./PI * atan(0.5/VIEWDIST*pheight*odev.vres); | 
| 181 | > | /* map the window and get its size */ | 
| 182 | > | XMapWindow(ourdisplay, gwind); | 
| 183 | > | dev_input();                    /* sets size and view angles */ | 
| 184 | > | /* allocate our leaf pile */ | 
| 185 | > | if (!qtAllocLeaves(DisplayWidth(ourdisplay,ourscreen) * | 
| 186 | > | DisplayHeight(ourdisplay,ourscreen) * 3 / | 
| 187 | > | (qtMinNodesiz*qtMinNodesiz*2))) | 
| 188 | > | error(SYSTEM, "insufficient memory for leaf storage"); | 
| 189 | > | odev.name = id; | 
| 190 |  | odev.ifd = ConnectionNumber(ourdisplay); | 
| 191 |  | } | 
| 192 |  |  | 
| 208 |  | } | 
| 209 |  |  | 
| 210 |  |  | 
| 211 | + |  | 
| 212 | + | dev_clear()                     /* clear our quadtree */ | 
| 213 | + | { | 
| 214 | + | qtCompost(100); | 
| 215 | + | if (ncolors > 0) | 
| 216 | + | new_ctab(ncolors); | 
| 217 | + | rayqleft = 0;                   /* hold off update */ | 
| 218 | + | } | 
| 219 | + |  | 
| 220 | + |  | 
| 221 |  | int | 
| 222 |  | dev_view(nv)                    /* assign new driver view */ | 
| 223 |  | VIEW    *nv; | 
| 224 |  | { | 
| 225 |  | if (nv->type == VT_PAR ||               /* check view legality */ | 
| 226 | < | nv->horiz > 160. || nv->vert > 160.) { | 
| 226 | > | nv->horiz > 160. || nv->vert > 160.) { | 
| 227 |  | error(COMMAND, "illegal view type/angle"); | 
| 228 |  | nv->type = VT_PER; | 
| 229 |  | nv->horiz = odev.v.horiz; | 
| 242 |  | int     dh = DisplayHeight(ourdisplay,ourscreen); | 
| 243 |  |  | 
| 244 |  | dw -= 25;       /* for window frame */ | 
| 245 | < | dh -= 100; | 
| 245 | > | dh -= 50; | 
| 246 |  | odev.hres = 2.*VIEWDIST/pwidth * | 
| 247 |  | tan(PI/180./2.*nv->horiz); | 
| 248 |  | odev.vres = 2.*VIEWDIST/pheight * | 
| 256 |  | odev.vres = dh; | 
| 257 |  | } | 
| 258 |  | XResizeWindow(ourdisplay, gwind, odev.hres, odev.vres); | 
| 259 | + | dev_input();    /* wait for resize event */ | 
| 260 |  | } | 
| 261 |  | copystruct(&odev.v, nv); | 
| 262 |  | } | 
| 265 |  | } | 
| 266 |  |  | 
| 267 |  |  | 
| 268 | + | dev_section(ofn)                /* add octree for geometry rendering */ | 
| 269 | + | char    *ofn; | 
| 270 | + | { | 
| 271 | + | /* unimplemented */ | 
| 272 | + | } | 
| 273 | + |  | 
| 274 | + |  | 
| 275 | + | dev_auxcom(cmd, args)           /* process an auxiliary command */ | 
| 276 | + | char    *cmd, *args; | 
| 277 | + | { | 
| 278 | + | sprintf(errmsg, "%s: unknown command", cmd); | 
| 279 | + | error(COMMAND, errmsg); | 
| 280 | + | } | 
| 281 | + |  | 
| 282 | + |  | 
| 283 | + | VIEW * | 
| 284 | + | dev_auxview(n, hvres)           /* return nth auxiliary view */ | 
| 285 | + | int     n; | 
| 286 | + | int     hvres[2]; | 
| 287 | + | { | 
| 288 | + | if (n) | 
| 289 | + | return(NULL); | 
| 290 | + | hvres[0] = odev.hres; hvres[1] = odev.vres; | 
| 291 | + | return(&odev.v); | 
| 292 | + | } | 
| 293 | + |  | 
| 294 | + |  | 
| 295 |  | int | 
| 296 |  | dev_input()                     /* get X11 input */ | 
| 297 |  | { | 
| 300 |  | do | 
| 301 |  | getevent(); | 
| 302 |  |  | 
| 303 | < | while (XQLength(ourdisplay) > 0); | 
| 303 | > | while (XPending(ourdisplay) > 0); | 
| 304 |  |  | 
| 305 | + | odev.inpready = 0; | 
| 306 | + |  | 
| 307 |  | return(inpresflags); | 
| 308 |  | } | 
| 309 |  |  | 
| 330 |  | dev_flush()                     /* flush output */ | 
| 331 |  | { | 
| 332 |  | qtUpdate(); | 
| 333 | < | return(XPending(ourdisplay)); | 
| 333 | > | rayqleft = RAYQLEN; | 
| 334 | > | return(odev.inpready = XPending(ourdisplay)); | 
| 335 |  | } | 
| 336 |  |  | 
| 337 |  |  | 
| 375 |  | return(ncolors = 0); | 
| 376 |  | if (XAllocColorCells(ourdisplay,ourmap,0,NULL,0,pixval,ncolors)) | 
| 377 |  | break; | 
| 378 | < | free((char *)pixval); | 
| 378 | > | free((void *)pixval); | 
| 379 |  | pixval = NULL; | 
| 380 |  | } | 
| 381 |  | if (pixval == NULL) { | 
| 410 |  | if (ncolors == 0) | 
| 411 |  | return; | 
| 412 |  | XFreeColors(ourdisplay,ourmap,pixval,ncolors,0L); | 
| 413 | < | free((char *)pixval); | 
| 413 | > | free((void *)pixval); | 
| 414 |  | pixval = NULL; | 
| 415 |  | ncolors = 0; | 
| 416 |  | if (ourmap != DefaultColormap(ourdisplay,ourscreen)) | 
| 460 |  | getkey(levptr(XKeyPressedEvent)); | 
| 461 |  | break; | 
| 462 |  | case ButtonPress: | 
| 463 | < | getmove(levptr(XButtonPressedEvent)); | 
| 463 | > | if (FRAMESTATE(levptr(XButtonPressedEvent)->state)) | 
| 464 | > | getframe(levptr(XButtonPressedEvent)); | 
| 465 | > | else | 
| 466 | > | getmove(levptr(XButtonPressedEvent)); | 
| 467 |  | break; | 
| 468 |  | } | 
| 469 |  | } | 
| 474 |  | int     dp[2][2]; | 
| 475 |  | FVECT   wp[2]; | 
| 476 |  | { | 
| 477 | < | static FVECT    vmin = {0.,0.,0.}, vmax = {1.,1.,FHUGE}; | 
| 478 | < | FVECT   ip[2]; | 
| 479 | < | /* not exactly right, but who cares? */ | 
| 480 | < | viewloc(ip[0], &odev.v, wp[0]); | 
| 481 | < | viewloc(ip[1], &odev.v, wp[1]); | 
| 477 | > | static FVECT    vmin = {0.,0.,0.}, vmax = {1.-FTINY,1.-FTINY,FHUGE}; | 
| 478 | > | FVECT   wpc[2], ip[2]; | 
| 479 | > | double  d, d0, d1; | 
| 480 | > | /* check for points behind view */ | 
| 481 | > | d = DOT(odev.v.vp, odev.v.vdir); | 
| 482 | > | d0 = DOT(wp[0], odev.v.vdir) - d; | 
| 483 | > | d1 = DOT(wp[1], odev.v.vdir) - d; | 
| 484 | > | /* work on copy of world points */ | 
| 485 | > | if (d0 <= d1) { | 
| 486 | > | VCOPY(wpc[0], wp[0]); VCOPY(wpc[1], wp[1]); | 
| 487 | > | } else { | 
| 488 | > | d = d0; d0 = d1; d1 = d; | 
| 489 | > | VCOPY(wpc[1], wp[0]); VCOPY(wpc[0], wp[1]); | 
| 490 | > | } | 
| 491 | > | if (d0 <= FTINY) { | 
| 492 | > | if (d1 <= FTINY) return(0); | 
| 493 | > | VSUB(wpc[0], wpc[0], wpc[1]); | 
| 494 | > | d = .99*d1/(d1-d0); | 
| 495 | > | VSUM(wpc[0], wpc[1], wpc[0], d); | 
| 496 | > | } | 
| 497 | > | /* get view coordinates and clip to window */ | 
| 498 | > | viewloc(ip[0], &odev.v, wpc[0]); | 
| 499 | > | viewloc(ip[1], &odev.v, wpc[1]); | 
| 500 |  | if (!clip(ip[0], ip[1], vmin, vmax)) | 
| 501 |  | return(0); | 
| 502 |  | dp[0][0] = ip[0][0]*odev.hres; | 
| 527 |  | static BYTE     gridrgb[3] = {0x0, 0xff, 0xff}; | 
| 528 |  | unsigned long  pixel; | 
| 529 |  |  | 
| 458 | – | if (!mapped || odev.v.type != VT_PER) | 
| 459 | – | return; | 
| 530 |  | if (ncolors > 0) | 
| 531 |  | pixel = pixval[get_pixel(gridrgb, xnewcolr)]; | 
| 532 |  | else | 
| 542 |  | int     dx, dy, mov, orb; | 
| 543 |  | { | 
| 544 |  | VIEW    nv; | 
| 545 | < | FVECT   v1; | 
| 545 | > | FVECT   odir, v1; | 
| 546 |  | double  d; | 
| 547 |  | register int    li; | 
| 548 |  | /* start with old view */ | 
| 551 |  | if (mov | orb) { | 
| 552 |  | if ((li = qtFindLeaf(dx, dy)) < 0) | 
| 553 |  | return(0);      /* not on window */ | 
| 554 | < | VSUM(nv.vdir, qtL.wp[li], nv.vp, -1.); | 
| 554 | > | VSUM(odir, qtL.wp[li], nv.vp, -1.); | 
| 555 |  | } else { | 
| 556 |  | if (viewray(nv.vp, nv.vdir, &odev.v, | 
| 557 |  | (dx+.5)/odev.hres, (dy+.5)/odev.vres) < -FTINY) | 
| 558 |  | return(0);      /* outside view */ | 
| 559 |  | } | 
| 560 |  | if (orb && mov) {               /* orbit left/right */ | 
| 561 | < | spinvector(nv.vdir, nv.vdir, nv.vup, MOVDEG*PI/180.*mov); | 
| 562 | < | VSUM(nv.vp, qtL.wp[li], nv.vdir, -1.); | 
| 561 | > | spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov); | 
| 562 | > | VSUM(nv.vp, qtL.wp[li], odir, -1.); | 
| 563 | > | spinvector(nv.vdir, nv.vdir, nv.vup, d); | 
| 564 |  | } else if (orb) {               /* orbit up/down */ | 
| 565 | < | fcross(v1, nv.vdir, nv.vup); | 
| 565 | > | fcross(v1, odir, nv.vup); | 
| 566 |  | if (normalize(v1) == 0.) | 
| 567 |  | return(0); | 
| 568 | < | spinvector(nv.vdir, nv.vdir, v1, MOVDEG*PI/180.*orb); | 
| 569 | < | VSUM(nv.vp, qtL.wp[li], nv.vdir, -1.); | 
| 568 | > | spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb); | 
| 569 | > | VSUM(nv.vp, qtL.wp[li], odir, -1.); | 
| 570 | > | spinvector(nv.vdir, nv.vdir, v1, d); | 
| 571 |  | } else if (mov) {               /* move forward/backward */ | 
| 572 |  | d = MOVPCT/100. * mov; | 
| 573 | < | VSUM(nv.vp, nv.vp, nv.vdir, d); | 
| 573 | > | VSUM(nv.vp, nv.vp, odir, d); | 
| 574 |  | } | 
| 575 |  | if (!mov ^ !orb && headlocked) {        /* restore head height */ | 
| 576 |  | VSUM(v1, odev.v.vp, nv.vp, -1.); | 
| 586 |  |  | 
| 587 |  |  | 
| 588 |  | static | 
| 589 | + | getframe(ebut)                          /* get focus frame */ | 
| 590 | + | XButtonPressedEvent     *ebut; | 
| 591 | + | { | 
| 592 | + | int     startx = ebut->x, starty = ebut->y; | 
| 593 | + | int     endx, endy; | 
| 594 | + |  | 
| 595 | + | XMaskEvent(ourdisplay, ButtonReleaseMask, levptr(XEvent)); | 
| 596 | + | endx = levptr(XButtonReleasedEvent)->x; | 
| 597 | + | endy = levptr(XButtonReleasedEvent)->y; | 
| 598 | + | if (endx == startx | endy == starty) { | 
| 599 | + | XBell(ourdisplay, 0); | 
| 600 | + | return; | 
| 601 | + | } | 
| 602 | + | if (endx < startx) {register int c = endx; endx = startx; startx = c;} | 
| 603 | + | if (endy < starty) {register int c = endy; endy = starty; starty = c;} | 
| 604 | + | sprintf(odev_args, "%.3f %.3f %.3f %.3f", | 
| 605 | + | (startx+.5)/odev.hres, 1.-(endy+.5)/odev.vres, | 
| 606 | + | (endx+.5)/odev.hres, 1.-(starty+.5)/odev.vres); | 
| 607 | + | inpresflags |= DFL(DC_FOCUS); | 
| 608 | + | } | 
| 609 | + |  | 
| 610 | + |  | 
| 611 | + | static | 
| 612 | + | waitabit()                              /* pause a moment */ | 
| 613 | + | { | 
| 614 | + | struct timespec ts; | 
| 615 | + | ts.tv_sec = 0; | 
| 616 | + | ts.tv_nsec = 5000000; | 
| 617 | + | nanosleep(&ts, NULL); | 
| 618 | + | } | 
| 619 | + |  | 
| 620 | + |  | 
| 621 | + | static | 
| 622 |  | getmove(ebut)                           /* get view change */ | 
| 623 |  | XButtonPressedEvent     *ebut; | 
| 624 |  | { | 
| 634 |  |  | 
| 635 |  | while (!XCheckMaskEvent(ourdisplay, | 
| 636 |  | ButtonReleaseMask, levptr(XEvent))) { | 
| 637 | < |  | 
| 637 | > | waitabit(); | 
| 638 |  | if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw, | 
| 639 |  | &rootx, &rooty, &wx, &wy, &statemask)) | 
| 640 |  | break;          /* on another screen */ | 
| 663 |  | getkey(ekey)                            /* get input key */ | 
| 664 |  | register XKeyPressedEvent  *ekey; | 
| 665 |  | { | 
| 666 | + | Window  rootw, childw; | 
| 667 | + | int     rootx, rooty, wx, wy; | 
| 668 | + | unsigned int    statemask; | 
| 669 |  | int  n; | 
| 670 |  | char    buf[8]; | 
| 671 |  |  | 
| 682 |  | case 'l':                       /* retrieve last view */ | 
| 683 |  | inpresflags |= DFL(DC_LASTVIEW); | 
| 684 |  | return; | 
| 685 | + | case 'f':                       /* frame view position */ | 
| 686 | + | if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw, | 
| 687 | + | &rootx, &rooty, &wx, &wy, &statemask)) | 
| 688 | + | return;         /* on another screen */ | 
| 689 | + | sprintf(odev_args, "%.4f %.4f", (wx+.5)/odev.hres, | 
| 690 | + | 1.-(wy+.5)/odev.vres); | 
| 691 | + | inpresflags |= DFL(DC_FOCUS); | 
| 692 | + | return; | 
| 693 | + | case 'F':                       /* unfocus */ | 
| 694 | + | odev_args[0] = '\0'; | 
| 695 | + | inpresflags |= DFL(DC_FOCUS); | 
| 696 | + | return; | 
| 697 |  | case 'p':                       /* pause computation */ | 
| 698 |  | inpresflags |= DFL(DC_PAUSE); | 
| 699 |  | return; | 
| 719 |  | if (ncolors > 0) | 
| 720 |  | new_ctab(ncolors); | 
| 721 |  | inpresflags |= DFL(DC_REDRAW);  /* resend values from server */ | 
| 722 | + | rayqleft = 0;                   /* hold off update */ | 
| 723 |  | return; | 
| 724 | + | case 'K':                       /* kill rtrace process(es) */ | 
| 725 | + | inpresflags |= DFL(DC_KILL); | 
| 726 | + | break; | 
| 727 | + | case 'R':                       /* restart rtrace */ | 
| 728 | + | inpresflags |= DFL(DC_RESTART); | 
| 729 | + | break; | 
| 730 | + | case 'C':                       /* clobber holodeck */ | 
| 731 | + | inpresflags |= DFL(DC_CLOBBER); | 
| 732 | + | break; | 
| 733 |  | case 'q':                       /* quit the program */ | 
| 734 |  | inpresflags |= DFL(DC_QUIT); | 
| 735 |  | return; |