Back to index

radiance  4R0+20100331
rhd_x11.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: rhd_x11.c,v 3.42 2006/06/07 17:52:04 schorsch Exp $";
00003 #endif
00004 /*
00005  * X11 driver for holodeck display.
00006  * Based on rview driver.
00007  */
00008 
00009 #include  <stdlib.h>
00010 #include  <stdio.h>
00011 #include  <X11/Xlib.h>
00012 #include  <X11/cursorfont.h>
00013 #include  <X11/Xutil.h>
00014 #include  <time.h>
00015 
00016 #include "platform.h"
00017 #include "rtmath.h"
00018 #include "rterror.h"
00019 #include "plocate.h"
00020 #include "rhdisp.h"
00021 #include "rhd_qtree.h"
00022 #include "x11icon.h"
00023 
00024 #ifndef RAYQLEN
00025 #define RAYQLEN             50000         /* max. rays to queue before flush */
00026 #endif
00027 
00028 #ifndef FEQ
00029 #define FEQ(a,b)     ((a)-(b) <= FTINY && (a)-(b) >= -FTINY)
00030 #endif
00031 
00032 #define GAMMA        2.2           /* default gamma correction */
00033 
00034 #define FRAMESTATE(s)       (((s)&(ShiftMask|ControlMask))==(ShiftMask|ControlMask))
00035 
00036 #define MOVPCT              7             /* percent distance to move /frame */
00037 #define MOVDIR(b)    ((b)==Button1 ? 1 : (b)==Button2 ? 0 : -1)
00038 #define MOVDEG              (-5)          /* degrees to orbit CW/down /frame */
00039 #define MOVORB(s)    ((s)&ShiftMask ? 1 : (s)&ControlMask ? -1 : 0)
00040 
00041 #define MINWIDTH     480           /* minimum graphics window width */
00042 #define MINHEIGHT    400           /* minimum graphics window height */
00043 
00044 #define VIEWDIST     356           /* assumed viewing distance (mm) */
00045 
00046 #define BORWIDTH     5             /* border width */
00047 
00048 #define  ourscreen   DefaultScreen(ourdisplay)
00049 #define  ourroot     RootWindow(ourdisplay,ourscreen)
00050 #define  ourmask     (StructureNotifyMask|ExposureMask|KeyPressMask|\
00051                      ButtonPressMask|ButtonReleaseMask)
00052 
00053 #define  levptr(etype)      ((etype *)&currentevent)
00054 
00055 struct driver odev;                /* global device driver structure */
00056 
00057 TMstruct      *tmGlobal;           /* global tone-mapping structure */
00058 
00059 char odev_args[64];                /* command arguments */
00060 
00061 static XEvent  currentevent;              /* current event */
00062 
00063 static int  ncolors = 0;           /* color table size */
00064 static int  mapped = 0;                   /* window is mapped? */
00065 static unsigned long  *pixval = NULL;     /* allocated pixels */
00066 static unsigned long  ourblack=0, ourwhite=1;
00067 
00068 static Display  *ourdisplay = NULL;       /* our display */
00069 static XVisualInfo  ourvinfo;             /* our visual information */
00070 static Window  gwind = 0;          /* our graphics window */
00071 static GC  ourgc = 0;                     /* our graphics context for drawing */
00072 static Colormap ourmap = 0;        /* our color map */
00073 
00074 static double pwidth, pheight;     /* pixel dimensions (mm) */
00075 
00076 static int    inpresflags;         /* input result flags */
00077 
00078 static int    headlocked = 0;             /* lock vertical motion */
00079 
00080 
00081 static int mytmflags(void);
00082 static void xnewcolr(int  ndx, int r, int g, int b);
00083 static int getpixels(void);
00084 static void freepixels(void);
00085 static unsigned long true_pixel(register BYTE rgb[3]);
00086 static void getevent(void);
00087 static int ilclip(int dp[2][2], FVECT wp[2]);
00088 static void draw3dline(FVECT wp[2]);
00089 static void draw_grids(void);
00090 static int moveview(int dx, int dy, int mov, int orb);
00091 static void getframe(XButtonPressedEvent *ebut);
00092 static void waitabit(void);
00093 static void getmove(XButtonPressedEvent *ebut);
00094 static void getkey(register XKeyPressedEvent *ekey);
00095 static void fixwindow(register XExposeEvent *eexp);
00096 static void resizewindow(register XConfigureEvent *ersz);
00097 
00098 
00099 static int
00100 mytmflags(void)                    /* figure out tone mapping flags */
00101 {
00102        extern char   *progname;
00103        register char *cp, *tail;
00104                                    /* find basic name */
00105        for (cp = tail = progname; *cp; cp++)
00106               if (*cp == '/')
00107                      tail = cp+1;
00108        for (cp = tail; *cp && *cp != '.'; cp++)
00109               ;
00110        if (cp-tail == 3 && !strncmp(tail, "x11", 3))
00111               return(TM_F_CAMERA|TM_F_NOSTDERR);
00112        if (cp-tail == 4 && !strncmp(tail, "x11h", 4))
00113               return(TM_F_HUMAN|TM_F_NOSTDERR);
00114        error(USER, "illegal driver name");
00115        return 0; /* pro forma return */
00116 }
00117 
00118 
00119 extern void
00120 dev_open(                   /* initialize X11 driver */
00121        char  *id
00122 )
00123 {
00124        static RGBPRIMS      myprims = STDPRIMS;
00125        char  *ev;
00126        double gamval = GAMMA;
00127        RGBPRIMP      dpri = stdprims;
00128        int  nplanes;
00129        XSetWindowAttributes ourwinattr;
00130        XWMHints  ourxwmhints;
00131        XSizeHints    oursizhints;
00132                                    /* set quadtree globals */
00133        qtMinNodesiz = 2;
00134                                    /* open display server */
00135        ourdisplay = XOpenDisplay(NULL);
00136        if (ourdisplay == NULL)
00137               error(USER, "cannot open X-windows; DISPLAY variable set?\n");
00138                                    /* find a usable visual */
00139        nplanes = DisplayPlanes(ourdisplay, ourscreen);
00140        if (XMatchVisualInfo(ourdisplay,ourscreen,
00141                             nplanes>12?nplanes:24,TrueColor,&ourvinfo) ||
00142                      XMatchVisualInfo(ourdisplay,ourscreen,
00143                             nplanes>12?nplanes:24,DirectColor,&ourvinfo)) {
00144               ourblack = 0;
00145               ourwhite = ourvinfo.red_mask |
00146                             ourvinfo.green_mask |
00147                             ourvinfo.blue_mask ;
00148        } else {
00149               if (nplanes < 4)
00150                      error(INTERNAL, "not enough colors\n");
00151               if (!XMatchVisualInfo(ourdisplay,ourscreen,
00152                                    nplanes,PseudoColor,&ourvinfo) &&
00153                             !XMatchVisualInfo(ourdisplay,ourscreen,
00154                                    nplanes,GrayScale,&ourvinfo))
00155                      error(INTERNAL, "unsupported visual type\n");
00156               ourblack = BlackPixel(ourdisplay,ourscreen);
00157               ourwhite = WhitePixel(ourdisplay,ourscreen);
00158        }
00159                                    /* set gamma and tone mapping */
00160        if ((ev = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL
00161                      || (ev = getenv("DISPLAY_GAMMA")) != NULL)
00162               gamval = atof(ev);
00163        if ((ev = getenv("DISPLAY_PRIMARIES")) != NULL &&
00164                      sscanf(ev, "%f %f %f %f %f %f %f %f",
00165                             &myprims[RED][CIEX],&myprims[RED][CIEY],
00166                             &myprims[GRN][CIEX],&myprims[GRN][CIEY],
00167                             &myprims[BLU][CIEX],&myprims[BLU][CIEY],
00168                             &myprims[WHT][CIEX],&myprims[WHT][CIEY]) >= 6)
00169               dpri = myprims;
00170        tmGlobal = tmInit(mytmflags(), dpri, gamval);
00171        if (tmGlobal == NULL)
00172               error(SYSTEM, "not enough memory in dev_open");
00173                                    /* open window */
00174        ourwinattr.background_pixel = ourblack;
00175        ourwinattr.border_pixel = ourblack;
00176        ourwinattr.event_mask = ourmask;
00177                                    /* this is stupid */
00178        ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot,
00179                             ourvinfo.visual, AllocNone);
00180        gwind = XCreateWindow(ourdisplay, ourroot, 0, 0,
00181               DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH,
00182               DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH,
00183               BORWIDTH, ourvinfo.depth, InputOutput, ourvinfo.visual,
00184               CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &ourwinattr);
00185        if (gwind == 0)
00186               error(SYSTEM, "cannot create window\n");
00187        XStoreName(ourdisplay, gwind, id);
00188                                    /* get graphics context */
00189        ourgc = XCreateGC(ourdisplay, gwind, 0, NULL);
00190                                    /* set window manager hints */
00191        ourxwmhints.flags = InputHint|IconPixmapHint;
00192        ourxwmhints.input = True;
00193        ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay, gwind,
00194                      (char *)x11icon_bits, x11icon_width, x11icon_height);
00195        XSetWMHints(ourdisplay, gwind, &ourxwmhints);
00196        oursizhints.min_width = MINWIDTH;
00197        oursizhints.min_height = MINHEIGHT;
00198        oursizhints.flags = PMinSize;
00199        XSetNormalHints(ourdisplay, gwind, &oursizhints);
00200                                    /* figure out sensible view */
00201        pwidth = (double)DisplayWidthMM(ourdisplay, ourscreen) /
00202                      DisplayWidth(ourdisplay, ourscreen);
00203        pheight = (double)DisplayHeightMM(ourdisplay, ourscreen) /
00204                      DisplayHeight(ourdisplay, ourscreen);
00205        odev.v = stdview;
00206        odev.v.type = VT_PER;
00207                                    /* map the window and get its size */
00208        XMapWindow(ourdisplay, gwind);
00209        dev_input();                /* sets size and view angles */
00210                                    /* allocate our leaf pile */
00211        if (!qtAllocLeaves(DisplayWidth(ourdisplay,ourscreen) *
00212                      DisplayHeight(ourdisplay,ourscreen) * 3 /
00213                      (qtMinNodesiz*qtMinNodesiz*2)))
00214               error(SYSTEM, "insufficient memory for leaf storage");
00215        odev.name = id;
00216        odev.ifd = ConnectionNumber(ourdisplay);
00217 }
00218 
00219 
00220 extern void
00221 dev_close(void)                    /* close our display */
00222 {
00223        freepixels();
00224        XFreeGC(ourdisplay, ourgc);
00225        XDestroyWindow(ourdisplay, gwind);
00226        gwind = 0;
00227        ourgc = 0;
00228        XCloseDisplay(ourdisplay);
00229        ourdisplay = NULL;
00230        qtFreeLeaves();
00231        tmDone(tmGlobal);
00232        odev.v.type = 0;
00233        odev.hres = odev.vres = 0;
00234        odev.ifd = -1;
00235 }
00236 
00237 
00238 extern void
00239 dev_clear(void)                    /* clear our quadtree */
00240 {
00241        qtCompost(100);
00242        if (ncolors > 0)
00243               new_ctab(ncolors);
00244        rayqleft = 0;               /* hold off update */
00245 }
00246 
00247 
00248 extern int
00249 dev_view(                   /* assign new driver view */
00250        VIEW   *nv
00251 )
00252 {
00253        if (nv->type == VT_PAR ||          /* check view legality */
00254                      nv->horiz > 160. || nv->vert > 160.) {
00255               error(COMMAND, "illegal view type/angle");
00256               nv->type = VT_PER;
00257               nv->horiz = odev.v.horiz;
00258               nv->vert = odev.v.vert;
00259               return(0);
00260        }
00261        if (nv->vfore > FTINY) {
00262               error(COMMAND, "cannot handle fore clipping");
00263               nv->vfore = 0.;
00264               return(0);
00265        }
00266        if (nv != &odev.v) {
00267               if (!FEQ(nv->horiz,odev.v.horiz) ||       /* resize window? */
00268                             !FEQ(nv->vert,odev.v.vert)) {
00269                      int    dw = DisplayWidth(ourdisplay,ourscreen);
00270                      int    dh = DisplayHeight(ourdisplay,ourscreen);
00271 
00272                      dw -= 25;     /* for window frame */
00273                      dh -= 50;
00274                      odev.hres = 2.*VIEWDIST/pwidth *
00275                                    tan(PI/180./2.*nv->horiz);
00276                      odev.vres = 2.*VIEWDIST/pheight *
00277                                    tan(PI/180./2.*nv->vert);
00278                      if (odev.hres > dw) {
00279                             odev.vres = dw * odev.vres / odev.hres;
00280                             odev.hres = dw;
00281                      }
00282                      if (odev.vres > dh) {
00283                             odev.hres = dh * odev.hres / odev.vres;
00284                             odev.vres = dh;
00285                      }
00286                      XResizeWindow(ourdisplay, gwind, odev.hres, odev.vres);
00287                      dev_input();  /* wait for resize event */
00288               }
00289               odev.v = *nv;
00290        }
00291        qtReplant();
00292        return(1);
00293 }
00294 
00295 
00296 extern void
00297 dev_section(         /* add octree for geometry rendering */
00298        char   *gfn,
00299        char   *pfn
00300 )
00301 {
00302        /* unimplemented */
00303 }
00304 
00305 
00306 extern void
00307 dev_auxcom(          /* process an auxiliary command */
00308        char   *cmd,
00309        char   *args
00310 )
00311 {
00312        sprintf(errmsg, "%s: unknown command", cmd);
00313        error(COMMAND, errmsg);
00314 }
00315 
00316 
00317 extern VIEW *
00318 dev_auxview(         /* return nth auxiliary view */
00319        int    n,
00320        int    hvres[2]
00321 )
00322 {
00323        if (n)
00324               return(NULL);
00325        hvres[0] = odev.hres; hvres[1] = odev.vres;
00326        return(&odev.v);
00327 }
00328 
00329 
00330 extern int
00331 dev_input(void)                    /* get X11 input */
00332 {
00333        inpresflags = 0;
00334 
00335        do
00336               getevent();
00337 
00338        while (XPending(ourdisplay) > 0);
00339 
00340        odev.inpready = 0;
00341 
00342        return(inpresflags);
00343 }
00344 
00345 
00346 extern void
00347 dev_paintr(          /* fill a rectangle */
00348        BYTE   rgb[3],
00349        int  xmin,
00350        int  ymin,
00351        int  xmax,
00352        int  ymax
00353 )
00354 {
00355        unsigned long  pixel;
00356 
00357        if (!mapped)
00358               return;
00359        if (ncolors > 0)
00360               pixel = pixval[get_pixel(rgb, xnewcolr)];
00361        else
00362               pixel = true_pixel(rgb);
00363        XSetForeground(ourdisplay, ourgc, pixel);
00364        XFillRectangle(ourdisplay, gwind, 
00365               ourgc, xmin, odev.vres-ymax, xmax-xmin, ymax-ymin);
00366 }
00367 
00368 
00369 extern int
00370 dev_flush(void)                    /* flush output */
00371 {
00372        qtUpdate();
00373        rayqleft = RAYQLEN;
00374        return(odev.inpready = XPending(ourdisplay));
00375 }
00376 
00377 
00378 static void
00379 xnewcolr(            /* enter a color into hardware table */
00380        int  ndx,
00381        int r,
00382        int g,
00383        int b
00384 )
00385 {
00386        XColor  xcolor;
00387 
00388        xcolor.pixel = pixval[ndx];
00389        xcolor.red = r << 8;
00390        xcolor.green = g << 8;
00391        xcolor.blue = b << 8;
00392        xcolor.flags = DoRed|DoGreen|DoBlue;
00393 
00394        XStoreColor(ourdisplay, ourmap, &xcolor);
00395 }
00396 
00397 
00398 static int
00399 getpixels(void)                           /* get the color map */
00400 {
00401        XColor  thiscolor;
00402        register int  i, j;
00403 
00404        if (ncolors > 0)
00405               return(ncolors);
00406        if (ourvinfo.visual == DefaultVisual(ourdisplay,ourscreen)) {
00407               ourmap = DefaultColormap(ourdisplay,ourscreen);
00408               goto loop;
00409        }
00410 newmap:
00411        ourmap = XCreateColormap(ourdisplay,gwind,ourvinfo.visual,AllocNone);
00412 loop:
00413        for (ncolors = ourvinfo.colormap_size;
00414                      ncolors > ourvinfo.colormap_size/3;
00415                      ncolors = ncolors*.937) {
00416               pixval = (unsigned long *)malloc(ncolors*sizeof(unsigned long));
00417               if (pixval == NULL)
00418                      return(ncolors = 0);
00419               if (XAllocColorCells(ourdisplay,ourmap,0,NULL,0,pixval,ncolors))
00420                      break;
00421               free((void *)pixval);
00422               pixval = NULL;
00423        }
00424        if (pixval == NULL) {
00425               if (ourmap == DefaultColormap(ourdisplay,ourscreen))
00426                      goto newmap;         /* try it with our map */
00427               else
00428                      return(ncolors = 0); /* failed */
00429        }
00430        if (ourmap != DefaultColormap(ourdisplay,ourscreen))
00431               for (i = 0; i < ncolors; i++) {    /* reset black and white */
00432                      if (pixval[i] != ourblack && pixval[i] != ourwhite)
00433                             continue;
00434                      thiscolor.pixel = pixval[i];
00435                      thiscolor.flags = DoRed|DoGreen|DoBlue;
00436                      XQueryColor(ourdisplay,
00437                                    DefaultColormap(ourdisplay,ourscreen),
00438                                    &thiscolor);
00439                      XStoreColor(ourdisplay, ourmap, &thiscolor);
00440                      for (j = i; j+1 < ncolors; j++)
00441                             pixval[j] = pixval[j+1];
00442                      ncolors--;
00443                      i--;
00444               }
00445        XSetWindowColormap(ourdisplay, gwind, ourmap);
00446        return(ncolors);
00447 }
00448 
00449 
00450 static void
00451 freepixels(void)                          /* free our pixels */
00452 {
00453        if (ncolors == 0)
00454               return;
00455        XFreeColors(ourdisplay,ourmap,pixval,ncolors,0L);
00456        free((void *)pixval);
00457        pixval = NULL;
00458        ncolors = 0;
00459        if (ourmap != DefaultColormap(ourdisplay,ourscreen))
00460               XFreeColormap(ourdisplay, ourmap);
00461        ourmap = 0;
00462 }
00463 
00464 
00465 static unsigned long
00466 true_pixel(                 /* return true pixel value for color */
00467        register BYTE rgb[3]
00468 )
00469 {
00470        register unsigned long  rval;
00471 
00472        rval = ourvinfo.red_mask*rgb[RED]/255 & ourvinfo.red_mask;
00473        rval |= ourvinfo.green_mask*rgb[GRN]/255 & ourvinfo.green_mask;
00474        rval |= ourvinfo.blue_mask*rgb[BLU]/255 & ourvinfo.blue_mask;
00475        return(rval);
00476 }
00477 
00478 
00479 static void
00480 getevent(void)                     /* get next event */
00481 {
00482        XNextEvent(ourdisplay, levptr(XEvent));
00483        switch (levptr(XEvent)->type) {
00484        case ConfigureNotify:
00485               resizewindow(levptr(XConfigureEvent));
00486               break;
00487        case UnmapNotify:
00488               mapped = 0;
00489               freepixels();
00490               break;
00491        case MapNotify:
00492               if (ourvinfo.class == PseudoColor ||
00493                             ourvinfo.class == GrayScale) {
00494                      if (getpixels() == 0)
00495                             error(SYSTEM, "cannot allocate colors\n");
00496                      new_ctab(ncolors);
00497               }
00498               mapped = 1;
00499               break;
00500        case Expose:
00501               fixwindow(levptr(XExposeEvent));
00502               break;
00503        case KeyPress:
00504               getkey(levptr(XKeyPressedEvent));
00505               break;
00506        case ButtonPress:
00507               if (FRAMESTATE(levptr(XButtonPressedEvent)->state))
00508                      getframe(levptr(XButtonPressedEvent));
00509               else
00510                      getmove(levptr(XButtonPressedEvent));
00511               break;
00512        }
00513 }
00514 
00515 
00516 static int
00517 ilclip(                     /* clip world coordinates to device */
00518        int    dp[2][2],
00519        FVECT  wp[2]
00520 )
00521 {
00522        static FVECT  vmin = {0.,0.,0.}, vmax = {1.-FTINY,1.-FTINY,FHUGE};
00523        FVECT  wpc[2], ip[2];
00524        double d, d0, d1;
00525                             /* check for points behind view */
00526        d = DOT(odev.v.vp, odev.v.vdir);
00527        d0 = DOT(wp[0], odev.v.vdir) - d;
00528        d1 = DOT(wp[1], odev.v.vdir) - d;
00529                             /* work on copy of world points */
00530        if (d0 <= d1) {
00531               VCOPY(wpc[0], wp[0]); VCOPY(wpc[1], wp[1]);
00532        } else {
00533               d = d0; d0 = d1; d1 = d;
00534               VCOPY(wpc[1], wp[0]); VCOPY(wpc[0], wp[1]);
00535        }
00536        if (d0 <= FTINY) {
00537               if (d1 <= FTINY) return(0);
00538               VSUB(wpc[0], wpc[0], wpc[1]);
00539               d = .99*d1/(d1-d0);
00540               VSUM(wpc[0], wpc[1], wpc[0], d);
00541        }
00542                             /* get view coordinates and clip to window */
00543        viewloc(ip[0], &odev.v, wpc[0]);
00544        viewloc(ip[1], &odev.v, wpc[1]);
00545        if (!clip(ip[0], ip[1], vmin, vmax))
00546               return(0);
00547        dp[0][0] = ip[0][0]*odev.hres;
00548        dp[0][1] = ip[0][1]*odev.vres;
00549        dp[1][0] = ip[1][0]*odev.hres;
00550        dp[1][1] = ip[1][1]*odev.vres;
00551        return(1);
00552 }
00553 
00554 
00555 static void
00556 draw3dline(                 /* draw 3d line in world coordinates */
00557        FVECT  wp[2]
00558 )
00559 {
00560        int    dp[2][2];
00561 
00562        if (!ilclip(dp, wp))
00563               return;
00564        XDrawLine(ourdisplay, gwind, ourgc,
00565                      dp[0][0], odev.vres-1 - dp[0][1],
00566                      dp[1][0], odev.vres-1 - dp[1][1]);
00567 }
00568 
00569 
00570 static void
00571 draw_grids(void)                   /* draw holodeck section grids */
00572 {
00573        static BYTE   gridrgb[3] = {0x0, 0xff, 0xff};
00574        unsigned long  pixel;
00575 
00576        if (ncolors > 0)
00577               pixel = pixval[get_pixel(gridrgb, xnewcolr)];
00578        else
00579               pixel = true_pixel(gridrgb);
00580        XSetForeground(ourdisplay, ourgc, pixel);
00581                                    /* draw each grid line */
00582        gridlines(draw3dline);
00583 }
00584 
00585 
00586 static int
00587 moveview(     /* move our view */
00588        int    dx,
00589        int    dy,
00590        int    mov,
00591        int    orb
00592 )
00593 {
00594        VIEW   nv;
00595        FVECT  odir, v1;
00596        double d;
00597        register int  li;
00598                             /* start with old view */
00599        nv = odev.v;
00600                             /* change view direction */
00601        if (mov | orb) {
00602               if ((li = qtFindLeaf(dx, dy)) < 0)
00603                      return(0);    /* not on window */
00604               VSUM(odir, qtL.wp[li], nv.vp, -1.);
00605        } else {
00606               if (viewray(nv.vp, nv.vdir, &odev.v,
00607                             (dx+.5)/odev.hres, (dy+.5)/odev.vres) < -FTINY)
00608                      return(0);    /* outside view */
00609        }
00610        if (orb && mov) {           /* orbit left/right */
00611               spinvector(odir, odir, nv.vup, d=MOVDEG*PI/180.*mov);
00612               VSUM(nv.vp, qtL.wp[li], odir, -1.);
00613               spinvector(nv.vdir, nv.vdir, nv.vup, d);
00614        } else if (orb) {           /* orbit up/down */
00615               fcross(v1, odir, nv.vup);
00616               if (normalize(v1) == 0.)
00617                      return(0);
00618               spinvector(odir, odir, v1, d=MOVDEG*PI/180.*orb);
00619               VSUM(nv.vp, qtL.wp[li], odir, -1.);
00620               spinvector(nv.vdir, nv.vdir, v1, d);
00621        } else if (mov) {           /* move forward/backward */
00622               d = MOVPCT/100. * mov;
00623               VSUM(nv.vp, nv.vp, odir, d);
00624        }
00625        if (!mov ^ !orb && headlocked) {   /* restore head height */
00626               VSUM(v1, odev.v.vp, nv.vp, -1.);
00627               d = DOT(v1, odev.v.vup);
00628               VSUM(nv.vp, nv.vp, odev.v.vup, d);
00629        }
00630        if (setview(&nv) != NULL)
00631               return(0);    /* illegal view */
00632        dev_view(&nv);
00633        inpresflags |= DFL(DC_SETVIEW);
00634        return(1);
00635 }
00636 
00637 
00638 static void
00639 getframe(                          /* get focus frame */
00640        XButtonPressedEvent  *ebut
00641 )
00642 {
00643        int    startx = ebut->x, starty = ebut->y;
00644        int    endx, endy, midx, midy;
00645        FVECT  v1;
00646        int    li;
00647                                    /* get mouse drag */
00648        XMaskEvent(ourdisplay, ButtonReleaseMask, levptr(XEvent));
00649        endx = levptr(XButtonReleasedEvent)->x;
00650        endy = levptr(XButtonReleasedEvent)->y;
00651        midx = (startx + endx) >> 1;
00652        midy = (starty + endy) >> 1;
00653                                    /* set focus distance */
00654        if ((li = qtFindLeaf(midx, midy)) < 0)
00655               return;                     /* not on window */
00656        VCOPY(v1, qtL.wp[li]);
00657        odev.v.vdist = sqrt(dist2(odev.v.vp, v1));
00658                                    /* set frame for rendering */
00659        if ((endx == startx) | (endy == starty))
00660               return;
00661        if (endx < startx) {register int c = endx; endx = startx; startx = c;}
00662        if (endy < starty) {register int c = endy; endy = starty; starty = c;}
00663        sprintf(odev_args, "%.3f %.3f %.3f %.3f",
00664                      (startx+.5)/odev.hres, 1.-(endy+.5)/odev.vres,
00665                      (endx+.5)/odev.hres, 1.-(starty+.5)/odev.vres);
00666        inpresflags |= DFL(DC_FOCUS);
00667 }
00668 
00669 
00670 static void
00671 waitabit(void)                            /* pause a moment */
00672 {
00673        struct timespec      ts;
00674        ts.tv_sec = 0;
00675        ts.tv_nsec = 100000000;
00676        nanosleep(&ts, NULL);
00677 }
00678 
00679 
00680 static void
00681 getmove(                           /* get view change */
00682        XButtonPressedEvent  *ebut
00683 )
00684 {
00685        int    movdir = MOVDIR(ebut->button);
00686        int    movorb = MOVORB(ebut->state);
00687        int    oldnodesiz = qtMinNodesiz;
00688        Window rootw, childw;
00689        int    rootx, rooty, wx, wy;
00690        unsigned int  statemask;
00691 
00692        qtMinNodesiz = 16;          /* for quicker update */
00693        XNoOp(ourdisplay);
00694 
00695        while (!XCheckMaskEvent(ourdisplay,
00696                      ButtonReleaseMask, levptr(XEvent))) {
00697               waitabit();
00698               if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
00699                             &rootx, &rooty, &wx, &wy, &statemask))
00700                      break;        /* on another screen */
00701 
00702               if (!moveview(wx, odev.vres-1-wy, movdir, movorb)) {
00703                      sleep(1);
00704                      continue;
00705               }
00706               XClearWindow(ourdisplay, gwind);
00707               qtUpdate();
00708               draw_grids();
00709        }
00710        if (!(inpresflags & DFL(DC_SETVIEW))) {   /* do final motion */
00711               movdir = MOVDIR(levptr(XButtonReleasedEvent)->button);
00712               wx = levptr(XButtonReleasedEvent)->x;
00713               wy = levptr(XButtonReleasedEvent)->y;
00714               moveview(wx, odev.vres-1-wy, movdir, movorb);
00715        }
00716        dev_flush();
00717 
00718        qtMinNodesiz = oldnodesiz;  /* restore quadtree resolution */
00719 }
00720 
00721 
00722 static void
00723 getkey(                            /* get input key */
00724        register XKeyPressedEvent  *ekey
00725 )
00726 {
00727        Window rootw, childw;
00728        int    rootx, rooty, wx, wy;
00729        unsigned int  statemask;
00730        int  n;
00731        char   buf[8];
00732 
00733        n = XLookupString(ekey, buf, sizeof(buf), NULL, NULL);
00734        if (n != 1)
00735               return;
00736        switch (buf[0]) {
00737        case 'h':                   /* turn on height motion lock */
00738               headlocked = 1;
00739               return;
00740        case 'H':                   /* turn off height motion lock */
00741               headlocked = 0;
00742               return;
00743        case 'l':                   /* retrieve last view */
00744               inpresflags |= DFL(DC_LASTVIEW);
00745               return;
00746        case 'f':                   /* frame view position */
00747               if (!XQueryPointer(ourdisplay, gwind, &rootw, &childw,
00748                             &rootx, &rooty, &wx, &wy, &statemask))
00749                      return;              /* on another screen */
00750               sprintf(odev_args, "%.4f %.4f", (wx+.5)/odev.hres,
00751                             1.-(wy+.5)/odev.vres);
00752               inpresflags |= DFL(DC_FOCUS);
00753               return;
00754        case 'F':                   /* unfocus */
00755               odev_args[0] = '\0';
00756               inpresflags |= DFL(DC_FOCUS);
00757               return;
00758        case 'p':                   /* pause computation */
00759               inpresflags |= DFL(DC_PAUSE);
00760               return;
00761        case 'v':                   /* spit out view */
00762               inpresflags |= DFL(DC_GETVIEW);
00763               return;
00764        case '\n':
00765        case '\r':                  /* resume computation */
00766               inpresflags |= DFL(DC_RESUME);
00767               return;
00768        case CTRL('R'):                    /* redraw screen */
00769               if (ncolors > 0)
00770                      new_ctab(ncolors);
00771               qtRedraw(0, 0, odev.hres, odev.vres);
00772               return;
00773        case CTRL('L'):                    /* refresh from server */
00774               if (inpresflags & DFL(DC_REDRAW))
00775                      return;
00776               XClearWindow(ourdisplay, gwind);
00777               draw_grids();
00778               XFlush(ourdisplay);
00779               qtCompost(100);                    /* unload the old tree */
00780               if (ncolors > 0)
00781                      new_ctab(ncolors);
00782               inpresflags |= DFL(DC_REDRAW);     /* resend values from server */
00783               rayqleft = 0;               /* hold off update */
00784               return;
00785        case 'K':                   /* kill rtrace process(es) */
00786               inpresflags |= DFL(DC_KILL);
00787               break;
00788        case 'R':                   /* restart rtrace */
00789               inpresflags |= DFL(DC_RESTART);
00790               break;
00791        case 'C':                   /* clobber holodeck */
00792               inpresflags |= DFL(DC_CLOBBER);
00793               break;
00794        case 'q':                   /* quit the program */
00795               inpresflags |= DFL(DC_QUIT);
00796               return;
00797        default:
00798               XBell(ourdisplay, 0);
00799               return;
00800        }
00801 }
00802 
00803 
00804 static void
00805 fixwindow(                         /* repair damage to window */
00806        register XExposeEvent  *eexp
00807 )
00808 {
00809        if (odev.hres == 0 || odev.vres == 0) {   /* first exposure */
00810               odev.hres = eexp->width;
00811               odev.vres = eexp->height;
00812        }
00813        qtRedraw(eexp->x, odev.vres - eexp->y - eexp->height,
00814                      eexp->x + eexp->width, odev.vres - eexp->y);
00815 }
00816 
00817 
00818 static void
00819 resizewindow(               /* resize window */
00820        register XConfigureEvent  *ersz
00821 )
00822 {
00823        if (ersz->width == odev.hres && ersz->height == odev.vres)
00824               return;
00825 
00826        odev.hres = ersz->width;
00827        odev.vres = ersz->height;
00828 
00829        odev.v.horiz = 2.*180./PI * atan(0.5/VIEWDIST*pwidth*odev.hres);
00830        odev.v.vert = 2.*180./PI * atan(0.5/VIEWDIST*pheight*odev.vres);
00831 
00832        inpresflags |= DFL(DC_SETVIEW);
00833 }