Back to index

radiance  4R0+20100331
x11.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static const char    RCSid[] = "$Id: x11.c,v 2.34 2008/08/21 07:05:59 greg Exp $";
00003 #endif
00004 /*
00005  *  x11.c - driver for X-windows version 11
00006  */
00007 
00008 #include "copyright.h"
00009 
00010 #include  "standard.h"
00011 #include  <sys/ioctl.h>
00012 #if  !defined(FNDELAY) && defined(O_NONBLOCK)
00013 #define  FNDELAY  O_NONBLOCK
00014 #endif
00015 
00016 #include  <X11/Xlib.h>
00017 #include  <X11/cursorfont.h>
00018 #include  <X11/Xutil.h>
00019 
00020 #include  "platform.h"
00021 #include  "color.h"
00022 #include  "driver.h"
00023 #include  "x11twind.h"
00024 #include  "x11icon.h"
00025 
00026 #define GAMMA        2.2           /* default exponent correction */
00027 
00028 #define MINWIDTH     (32*COMCW)    /* minimum graphics window width */
00029 #define MINHEIGHT    (MINWIDTH/2)  /* minimum graphics window height */
00030 
00031 #define BORWIDTH     5             /* border width */
00032 #define COMHEIGHT    (COMLH*COMCH) /* command line height (pixels) */
00033 
00034 #define COMFN        "8x13"        /* command line font name */
00035 #define COMLH        3             /* number of command lines */
00036 #define COMCW        8             /* approx. character width (pixels) */
00037 #define COMCH        14            /* approx. character height (pixels) */
00038 
00039 #define  ourscreen   DefaultScreen(ourdisplay)
00040 #define  ourroot     RootWindow(ourdisplay,ourscreen)
00041 
00042 #define  levptr(etype)      ((etype *)&currentevent)
00043 
00044 static XEvent  currentevent;              /* current event */
00045 
00046 static int  ncolors = 0;           /* color table size */
00047 static int  mapped = 0;                   /* window is mapped? */
00048 static unsigned long  *pixval = NULL;     /* allocated pixels */
00049 static unsigned long  ourblack=0, ourwhite=1;
00050 
00051 static Display  *ourdisplay = NULL;       /* our display */
00052 
00053 static XVisualInfo  ourvinfo;             /* our visual information */
00054 
00055 static Window  gwind = 0;          /* our graphics window */
00056 
00057 static Cursor  pickcursor = 0;            /* cursor used for picking */
00058 
00059 static int  gwidth, gheight;              /* graphics window size */
00060 
00061 static int  comheight;                    /* desired comline height */
00062 static TEXTWIND  *comline = NULL;  /* our command line */
00063 
00064 static char  c_queue[64];          /* input queue */
00065 static int  c_first = 0;           /* first character in queue */
00066 static int  c_last = 0;                   /* last character in queue */
00067 
00068 static GC  ourgc = 0;                     /* our graphics context for drawing */
00069 
00070 static Colormap ourmap = 0;        /* our color map */
00071 
00072 #define IC_X11              0
00073 #define IC_IOCTL     1
00074 #define IC_READ             2
00075 
00076 static int  inpcheck;                     /* whence to check input */
00077 
00078 static void x11_errout(char  *msg);
00079 
00080 static dr_closef_t x11_close;
00081 static dr_clearf_t x11_clear;
00082 static dr_paintrf_t x11_paintr;
00083 static dr_getcurf_t x11_getcur;
00084 static dr_comoutf_t x11_comout;
00085 static dr_cominf_t x11_comin;
00086 static dr_flushf_t x11_flush;
00087 
00088 static dr_cominf_t std_comin;
00089 static dr_comoutf_t std_comout;
00090 
00091 static struct driver  x11_driver = {
00092        x11_close, x11_clear, x11_paintr, x11_getcur,
00093        NULL, NULL, x11_flush, 1.0
00094 };
00095 
00096 static dr_getchf_t x11_getc;
00097 
00098 static void freepixels(void);
00099 static int getpixels(void);
00100 static dr_newcolrf_t xnewcolr;
00101 static unsigned long true_pixel(COLOR  col);
00102 static void getevent(void);
00103 static void getkey(XKeyPressedEvent  *ekey);
00104 static void fixwindow(XExposeEvent  *eexp);
00105 static void resizewindow(XConfigureEvent  *ersz);
00106 
00107 extern dr_initf_t x11_init; /* XXX this should be in a seperate header file */
00108 
00109 
00110 extern struct driver *
00111 x11_init(            /* initialize driver */
00112        char  *name,
00113        char  *id
00114 )
00115 {
00116        char  *gv;
00117        int  nplanes;
00118        XSetWindowAttributes ourwinattr;
00119        XWMHints  ourxwmhints;
00120        XSizeHints    oursizhints;
00121                                    /* open display server */
00122        ourdisplay = XOpenDisplay(NULL);
00123        if (ourdisplay == NULL) {
00124               eputs("cannot open X-windows; DISPLAY variable set?\n");
00125               return(NULL);
00126        }
00127                                    /* find a usable visual */
00128        nplanes = DisplayPlanes(ourdisplay, ourscreen);
00129        if (XMatchVisualInfo(ourdisplay,ourscreen,
00130                             nplanes>12?nplanes:24,TrueColor,&ourvinfo) ||
00131                      XMatchVisualInfo(ourdisplay,ourscreen,
00132                             nplanes>12?nplanes:24,DirectColor,&ourvinfo)) {
00133               ourblack = 0;
00134               ourwhite = ourvinfo.red_mask |
00135                             ourvinfo.green_mask |
00136                             ourvinfo.blue_mask ;
00137        } else {
00138               if (nplanes < 4) {
00139                      eputs("not enough colors\n");
00140                      return(NULL);
00141               }
00142               if (!XMatchVisualInfo(ourdisplay,ourscreen,
00143                                    nplanes,PseudoColor,&ourvinfo) &&
00144                             !XMatchVisualInfo(ourdisplay,ourscreen,
00145                                    nplanes,GrayScale,&ourvinfo)) {
00146                      eputs("unsupported visual type\n");
00147                      return(NULL);
00148               }
00149               ourblack = BlackPixel(ourdisplay,ourscreen);
00150               ourwhite = WhitePixel(ourdisplay,ourscreen);
00151        }
00152                                    /* set gamma */
00153        if ((gv = XGetDefault(ourdisplay, "radiance", "gamma")) != NULL
00154                      || (gv = getenv("DISPLAY_GAMMA")) != NULL)
00155               make_gmap(atof(gv));
00156        else
00157               make_gmap(GAMMA);
00158                                    /* X11 command line or no? */
00159        if (!strcmp(name, "x11"))
00160               comheight = COMHEIGHT;
00161        else /* "x11d" */ {
00162               comheight = 0;
00163 #ifndef  FNDELAY
00164               eputs("warning: x11d driver not fully functional on this machine\n");
00165 #endif
00166        }
00167                                    /* open window */
00168        ourwinattr.background_pixel = ourblack;
00169        ourwinattr.border_pixel = ourblack;
00170                                    /* this is stupid */
00171        ourwinattr.colormap = XCreateColormap(ourdisplay, ourroot,
00172                             ourvinfo.visual, AllocNone);
00173        gwind = XCreateWindow(ourdisplay, ourroot, 0, 0,
00174               DisplayWidth(ourdisplay,ourscreen)-2*BORWIDTH,
00175               DisplayHeight(ourdisplay,ourscreen)-2*BORWIDTH,
00176               BORWIDTH, ourvinfo.depth, InputOutput, ourvinfo.visual,
00177               CWBackPixel|CWBorderPixel|CWColormap, &ourwinattr);
00178        if (gwind == 0) {
00179               eputs("cannot create window\n");
00180               return(NULL);
00181        }
00182        XStoreName(ourdisplay, gwind, id);
00183        /* create a cursor */
00184        pickcursor = XCreateFontCursor(ourdisplay, XC_diamond_cross);
00185        ourgc = XCreateGC(ourdisplay, gwind, 0, NULL);
00186        ourxwmhints.flags = InputHint|IconPixmapHint;
00187        ourxwmhints.input = True;
00188        ourxwmhints.icon_pixmap = XCreateBitmapFromData(ourdisplay,
00189                      gwind, x11icon_bits, x11icon_width, x11icon_height);
00190        XSetWMHints(ourdisplay, gwind, &ourxwmhints);
00191        oursizhints.min_width = MINWIDTH;
00192        oursizhints.min_height = MINHEIGHT+comheight;
00193        oursizhints.flags = PMinSize;
00194        XSetNormalHints(ourdisplay, gwind, &oursizhints);
00195        XSelectInput(ourdisplay, gwind, ExposureMask);
00196        XMapWindow(ourdisplay, gwind);
00197        XWindowEvent(ourdisplay, gwind, ExposureMask, levptr(XEvent));
00198        gwidth = levptr(XExposeEvent)->width;
00199        gheight = levptr(XExposeEvent)->height - comheight;
00200        x11_driver.xsiz = gwidth < MINWIDTH ? MINWIDTH : gwidth;
00201        x11_driver.ysiz = gheight < MINHEIGHT ? MINHEIGHT : gheight;
00202        x11_driver.inpready = 0;
00203        mapped = 1;
00204                                    /* set i/o vectors */
00205        if (comheight) {
00206               x11_driver.comin = x11_comin;
00207               x11_driver.comout = x11_comout;
00208               erract[COMMAND].pf = x11_comout;
00209               /*                   doesn't work with raypcalls.c
00210               if (erract[WARNING].pf != NULL)
00211                      erract[WARNING].pf = x11_errout;
00212               */
00213               inpcheck = IC_X11;
00214        } else {
00215               x11_driver.comin = std_comin;
00216               x11_driver.comout = std_comout;
00217               erract[COMMAND].pf = std_comout;
00218               inpcheck = IC_IOCTL;
00219        }
00220        return(&x11_driver);
00221 }
00222 
00223 
00224 static void
00225 x11_close(void)                    /* close our display */
00226 {
00227        erract[COMMAND].pf = NULL;         /* reset error vectors */
00228        if (erract[WARNING].pf != NULL)
00229               erract[WARNING].pf = wputs;
00230        if (ourdisplay == NULL)
00231               return;
00232        if (comline != NULL) {
00233               xt_close(comline);
00234               comline = NULL;
00235        }
00236        freepixels();
00237        XFreeGC(ourdisplay, ourgc);
00238        XDestroyWindow(ourdisplay, gwind);
00239        gwind = 0;
00240        ourgc = 0;
00241        XFreeCursor(ourdisplay, pickcursor);
00242        XCloseDisplay(ourdisplay);
00243        ourdisplay = NULL;
00244 }
00245 
00246 
00247 static void
00248 x11_clear(                  /* clear our display */
00249        int  xres,
00250        int  yres
00251 )
00252 {
00253                                           /* check limits */
00254        if (xres < MINWIDTH)
00255               xres = MINWIDTH;
00256        if (yres < MINHEIGHT)
00257               yres = MINHEIGHT;
00258                                           /* resize window */
00259        if (xres != gwidth || yres != gheight) {
00260               XSelectInput(ourdisplay, gwind, 0);
00261               XResizeWindow(ourdisplay, gwind, xres, yres+comheight);
00262               gwidth = xres;
00263               gheight = yres;
00264               XFlush(ourdisplay);
00265               sleep(2);                   /* wait for window manager */
00266               XSync(ourdisplay, 1);              /* discard input */
00267        }
00268        XClearWindow(ourdisplay, gwind);
00269                                           /* reinitialize color table */
00270        if (ourvinfo.class == PseudoColor || ourvinfo.class == GrayScale) {
00271               if (getpixels() == 0)
00272                      eputs("cannot allocate colors\n");
00273               else
00274                      new_ctab(ncolors);
00275        }
00276                                           /* get new command line */
00277        if (comline != NULL)
00278               xt_close(comline);
00279        if (comheight) {
00280               comline = xt_open(ourdisplay, gwind, 0, gheight, gwidth,
00281                             comheight, 0, ourblack, ourwhite, COMFN);
00282               if (comline == NULL) {
00283                      eputs("cannot open command line window\n");
00284                      quit(1);
00285               }
00286               XSelectInput(ourdisplay, comline->w, ExposureMask);
00287                                           /* remove earmuffs */
00288               XSelectInput(ourdisplay, gwind,
00289               StructureNotifyMask|ExposureMask|KeyPressMask|ButtonPressMask);
00290        } else                             /* remove earmuffs */
00291               XSelectInput(ourdisplay, gwind,
00292                      StructureNotifyMask|ExposureMask|ButtonPressMask);
00293 }
00294 
00295 
00296 static void
00297 x11_paintr(          /* fill a rectangle */
00298        COLOR  col,
00299        int  xmin,
00300        int  ymin,
00301        int  xmax,
00302        int  ymax
00303 )
00304 {
00305        unsigned long  pixel;
00306 
00307        if (!mapped)
00308               return;
00309        if (ncolors > 0)
00310               pixel = pixval[get_pixel(col, xnewcolr)];
00311        else
00312               pixel = true_pixel(col);
00313        XSetForeground(ourdisplay, ourgc, pixel);
00314        XFillRectangle(ourdisplay, gwind, 
00315               ourgc, xmin, gheight-ymax, xmax-xmin, ymax-ymin);
00316 }
00317 
00318 
00319 static void
00320 x11_flush(void)                    /* flush output */
00321 {
00322        char   buf[256];
00323        int    n;
00324                                           /* check for input */
00325        XNoOp(ourdisplay);
00326        n = XPending(ourdisplay);                 /* from X server */
00327        while (n-- > 0)
00328               getevent();
00329 #ifdef FNDELAY
00330        if (inpcheck == IC_IOCTL) {               /* from stdin */
00331 #ifdef FIONREAD
00332               if (ioctl(fileno(stdin), FIONREAD, &n) < 0) {
00333 #else
00334               if (1) {
00335 #endif
00336                      if (fcntl(fileno(stdin), F_SETFL, FNDELAY) < 0) {
00337                             eputs("cannot change input mode\n");
00338                             quit(1);
00339                      }
00340                      inpcheck = IC_READ;
00341               } else
00342                      x11_driver.inpready += n;
00343        }
00344        if (inpcheck == IC_READ) {
00345               n = read(fileno(stdin), buf, sizeof(buf)-1);
00346               if (n > 0) {
00347                      buf[n] = '\0';
00348                      tocombuf(buf, &x11_driver);
00349               }
00350        }
00351 #endif
00352 }
00353 
00354 
00355 static void
00356 x11_comin(           /* read in a command line */
00357        char  *inp,
00358        char  *prompt
00359 )
00360 {
00361        if (prompt != NULL) {
00362               x11_flush();         /* make sure we get everything */
00363               if (fromcombuf(inp, &x11_driver))
00364                      return;
00365               xt_puts(prompt, comline);
00366        }
00367        xt_cursor(comline, TBLKCURS);
00368        editline(inp, x11_getc, x11_comout);
00369        xt_cursor(comline, TNOCURS);
00370 }
00371 
00372 
00373 static void
00374 x11_comout(          /* output a string to command line */
00375        char  *outp
00376 )
00377 {
00378        if (comline == NULL || outp == NULL || !outp[0])
00379               return;
00380        xt_puts(outp, comline);
00381        if (outp[strlen(outp)-1] == '\n')
00382               XFlush(ourdisplay);
00383 }
00384 
00385 
00386 static void
00387 x11_errout(                 /* output an error message */
00388        char  *msg
00389 )
00390 {
00391        eputs(msg);          /* send to stderr also! */
00392        x11_comout(msg);
00393 }
00394 
00395 
00396 static void
00397 std_comin(           /* read in command line from stdin */
00398        char  *inp,
00399        char  *prompt
00400 )
00401 {
00402        if (prompt != NULL) {
00403               if (fromcombuf(inp, &x11_driver))
00404                      return;
00405               if (!x11_driver.inpready)
00406                      std_comout(prompt);
00407        }
00408 #ifdef FNDELAY
00409        if (inpcheck == IC_READ) {  /* turn off FNDELAY */
00410               if (fcntl(fileno(stdin), F_SETFL, 0) < 0) {
00411                      eputs("cannot change input mode\n");
00412                      quit(1);
00413               }
00414               inpcheck = IC_IOCTL;
00415        }
00416 #endif
00417        if (gets(inp) == NULL) {
00418               strcpy(inp, "quit");
00419               return;
00420        }
00421        x11_driver.inpready -= strlen(inp) + 1;
00422        if (x11_driver.inpready < 0)
00423               x11_driver.inpready = 0;
00424 }
00425 
00426 
00427 static void
00428 std_comout(          /* write out string to stdout */
00429        char   *outp
00430 )
00431 {
00432        fputs(outp, stdout);
00433        fflush(stdout);
00434 }
00435 
00436 
00437 static int
00438 x11_getcur(          /* get cursor position */
00439        int  *xp,
00440        int  *yp
00441 )
00442 {
00443        while (XGrabPointer(ourdisplay, gwind, True, ButtonPressMask,
00444                      GrabModeAsync, GrabModeAsync, None, pickcursor,
00445                      CurrentTime) != GrabSuccess)
00446               sleep(2);
00447 
00448        do
00449               getevent();
00450        while (c_last <= c_first && levptr(XEvent)->type != ButtonPress);
00451        *xp = levptr(XButtonPressedEvent)->x;
00452        *yp = gheight-1 - levptr(XButtonPressedEvent)->y;
00453        XUngrabPointer(ourdisplay, CurrentTime);
00454        XFlush(ourdisplay);                       /* insure release */
00455        if (c_last > c_first)                     /* key pressed */
00456               return(x11_getc());
00457                                           /* button pressed */
00458        if (levptr(XButtonPressedEvent)->button == Button1)
00459               return(MB1);
00460        if (levptr(XButtonPressedEvent)->button == Button2)
00461               return(MB2);
00462        if (levptr(XButtonPressedEvent)->button == Button3)
00463               return(MB3);
00464        return(ABORT);
00465 }
00466 
00467 
00468 static void
00469 xnewcolr(            /* enter a color into hardware table */
00470        int  ndx,
00471        int  r,
00472        int  g,
00473        int  b
00474 )
00475 {
00476        XColor  xcolor;
00477 
00478        xcolor.pixel = pixval[ndx];
00479        xcolor.red = r << 8;
00480        xcolor.green = g << 8;
00481        xcolor.blue = b << 8;
00482        xcolor.flags = DoRed|DoGreen|DoBlue;
00483 
00484        XStoreColor(ourdisplay, ourmap, &xcolor);
00485 }
00486 
00487 
00488 static int
00489 getpixels(void)                           /* get the color map */
00490 {
00491        XColor  thiscolor;
00492        register int  i, j;
00493 
00494        if (ncolors > 0)
00495               return(ncolors);
00496        if (ourvinfo.visual == DefaultVisual(ourdisplay,ourscreen)) {
00497               ourmap = DefaultColormap(ourdisplay,ourscreen);
00498               goto loop;
00499        }
00500 newmap:
00501        ourmap = XCreateColormap(ourdisplay,gwind,ourvinfo.visual,AllocNone);
00502 loop:
00503        for (ncolors = ourvinfo.colormap_size;
00504                      ncolors > ourvinfo.colormap_size/3;
00505                      ncolors = ncolors*.937) {
00506               pixval = (unsigned long *)malloc(ncolors*sizeof(unsigned long));
00507               if (pixval == NULL)
00508                      return(ncolors = 0);
00509               if (XAllocColorCells(ourdisplay,ourmap,0,NULL,0,pixval,ncolors))
00510                      break;
00511               free((void *)pixval);
00512               pixval = NULL;
00513        }
00514        if (pixval == NULL) {
00515               if (ourmap == DefaultColormap(ourdisplay,ourscreen))
00516                      goto newmap;         /* try it with our map */
00517               else
00518                      return(ncolors = 0); /* failed */
00519        }
00520        if (ourmap != DefaultColormap(ourdisplay,ourscreen))
00521               for (i = 0; i < ncolors; i++) {    /* reset black and white */
00522                      if (pixval[i] != ourblack && pixval[i] != ourwhite)
00523                             continue;
00524                      thiscolor.pixel = pixval[i];
00525                      thiscolor.flags = DoRed|DoGreen|DoBlue;
00526                      XQueryColor(ourdisplay,
00527                                    DefaultColormap(ourdisplay,ourscreen),
00528                                    &thiscolor);
00529                      XStoreColor(ourdisplay, ourmap, &thiscolor);
00530                      for (j = i; j+1 < ncolors; j++)
00531                             pixval[j] = pixval[j+1];
00532                      ncolors--;
00533                      i--;
00534               }
00535        XSetWindowColormap(ourdisplay, gwind, ourmap);
00536        return(ncolors);
00537 }
00538 
00539 
00540 static void
00541 freepixels(void)                          /* free our pixels */
00542 {
00543        if (ncolors == 0)
00544               return;
00545        XFreeColors(ourdisplay,ourmap,pixval,ncolors,0L);
00546        free((void *)pixval);
00547        pixval = NULL;
00548        ncolors = 0;
00549        if (ourmap != DefaultColormap(ourdisplay,ourscreen))
00550               XFreeColormap(ourdisplay, ourmap);
00551        ourmap = 0;
00552 }
00553 
00554 
00555 static unsigned long
00556 true_pixel(                 /* return true pixel value for color */
00557        COLOR  col
00558 )
00559 {
00560        unsigned long  rval;
00561        BYTE  rgb[3];
00562 
00563        map_color(rgb, col);
00564        rval = ourvinfo.red_mask*rgb[RED]/255 & ourvinfo.red_mask;
00565        rval |= ourvinfo.green_mask*rgb[GRN]/255 & ourvinfo.green_mask;
00566        rval |= ourvinfo.blue_mask*rgb[BLU]/255 & ourvinfo.blue_mask;
00567        return(rval);
00568 }
00569 
00570 
00571 static int
00572 x11_getc(void)                     /* get a command character */
00573 {
00574        while (c_last <= c_first) {
00575               c_first = c_last = 0;              /* reset */
00576               getevent();                 /* wait for key */
00577        }
00578        x11_driver.inpready--;
00579        return(c_queue[c_first++]);
00580 }
00581 
00582 
00583 static void
00584 getevent(void)                     /* get next event */
00585 {
00586        XNextEvent(ourdisplay, levptr(XEvent));
00587        switch (levptr(XEvent)->type) {
00588        case ConfigureNotify:
00589               resizewindow(levptr(XConfigureEvent));
00590               break;
00591        case UnmapNotify:
00592               mapped = 0;
00593               freepixels();
00594               break;
00595        case MapNotify:
00596               if (ourvinfo.class == PseudoColor ||
00597                             ourvinfo.class == GrayScale) {
00598                      if (getpixels() == 0)
00599                             eputs("cannot allocate colors\n");
00600                      else
00601                             new_ctab(ncolors);
00602               }
00603               mapped = 1;
00604               break;
00605        case Expose:
00606               fixwindow(levptr(XExposeEvent));
00607               break;
00608        case KeyPress:
00609               getkey(levptr(XKeyPressedEvent));
00610               break;
00611        case ButtonPress:
00612               break;
00613        }
00614 }
00615 
00616 
00617 static void
00618 getkey(                            /* get input key */
00619        register XKeyPressedEvent  *ekey
00620 )
00621 {
00622        register int  n;
00623 
00624        n = XLookupString(ekey, c_queue+c_last, sizeof(c_queue)-c_last,
00625                             NULL, NULL);
00626        c_last += n;
00627        x11_driver.inpready += n;
00628 }
00629 
00630 
00631 static void
00632 fixwindow(                         /* repair damage to window */
00633        register XExposeEvent  *eexp
00634 )
00635 {
00636        char  buf[80];
00637 
00638        if (eexp->window == gwind) {
00639               sprintf(buf, "repaint %d %d %d %d\n",
00640                      eexp->x, gheight - eexp->y - eexp->height,
00641                      eexp->x + eexp->width, gheight - eexp->y);
00642               tocombuf(buf, &x11_driver);
00643        } else if (eexp->window == comline->w) {
00644               if (eexp->count == 0)
00645                      xt_redraw(comline);
00646        }
00647 }
00648 
00649 
00650 static void
00651 resizewindow(               /* resize window */
00652        register XConfigureEvent  *ersz
00653 )
00654 {
00655        if (ersz->width == gwidth && ersz->height-comheight == gheight)
00656               return;
00657 
00658        gwidth = ersz->width;
00659        gheight = ersz->height-comheight;
00660        x11_driver.xsiz = gwidth < MINWIDTH ? MINWIDTH : gwidth;
00661        x11_driver.ysiz = gheight < MINHEIGHT ? MINHEIGHT : gheight;
00662 
00663        tocombuf("new\n", &x11_driver);
00664 }