home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / xloadimg.zip / xloadimage.4.1 / window.c < prev    next >
C/C++ Source or Header  |  1993-11-08  |  28KB  |  1,020 lines

  1. /* window.c:
  2.  *
  3.  * display an image in a window
  4.  *
  5.  * jim frost 10.03.89
  6.  *
  7.  * Copyright 1989, 1990, 1991 Jim Frost.
  8.  * See included file "copyright.h" for complete copyright information.
  9.  */
  10.  
  11. #include "copyright.h"
  12. #include <ctype.h>
  13. #include "xloadimage.h"
  14. #include <X11/cursorfont.h>
  15. #include <X11/Xatom.h>
  16. #include <signal.h>
  17. #include <errno.h>
  18. #include <sys/types.h>
  19. #ifdef HAS_POLL
  20. #include <poll.h>
  21. #else /* !HAS_POLL */
  22. #ifdef HAS_SELECT_INCLUDE
  23. #include <sys/select.h>
  24. #endif /* HAS_SELECT_INCLUDE */
  25. #endif /* !HAS_POLL */
  26.  
  27. /* we can use timeouts if either select() or poll() are available.
  28.  */
  29. #if IS_BSD
  30. #define ENABLE_TIMEOUT
  31. #endif
  32. #if defined(HAS_SELECT_INCLUDE) || defined(HAS_POLL)
  33. #define ENABLE_TIMEOUT
  34. #endif
  35.  
  36. /* SUPPRESS 560 */
  37.  
  38. extern int errno; /* not defined in errno.h on some systems */
  39.  
  40. static Window    ImageWindow= 0;
  41. static Window    ViewportWin= 0;
  42. static Colormap  ImageColormap;
  43.  
  44. #ifdef ENABLE_TIMEOUT
  45. static int AlarmWentOff = 0;
  46.  
  47. static void delayAlarmHandler()
  48. {
  49.     AlarmWentOff = 1;
  50. }
  51.  
  52. /* this is a bogus function whose only purpose is to interrupt
  53.  * the XNextEvent signal call in imageInWindow().
  54.  * This is added to allow automatic cycling through the specified list
  55.  * of pictures. The amount of wait time is specified using the -delay
  56.  * option, which is the number of seconds to pause between pictures.
  57.  * - mfc 90/10/08
  58.  */
  59.  
  60. static int getNextEventWithTimeout(disp, event)
  61.      Display      *disp;
  62.      XEvent       *event;
  63. {
  64. #ifdef HAS_POLL
  65.   struct pollfd fds[1];
  66. #else
  67.   fd_set rmask;
  68. #endif
  69.  
  70.   /* force any output to occur before we set & spin
  71.    */
  72.  
  73.   XFlush(disp);
  74.  
  75.   /* wait for alarm
  76.    */
  77.  
  78.   while (AlarmWentOff == 0) {
  79.     if (XPending(disp)) {
  80.       XNextEvent(disp, event);
  81.       return(1);
  82.     }
  83. #ifdef HAS_POLL
  84.     fds[0].fd = ConnectionNumber(disp);
  85.     fds[0].events = POLLIN;
  86.  
  87.     if ((poll(fds, 1, 0) == -1) && (errno != EINTR))
  88.     perror("poll");
  89. #else /* !HAS_POLL */
  90.     FD_ZERO(&rmask);
  91.     FD_SET(ConnectionNumber(disp), &rmask);
  92.     if ((select(ConnectionNumber(disp)+1, &rmask,
  93.            (fd_set *) 0, (fd_set *) 0, /*(struct timeval *)*/0) == -1) &&
  94.     (errno != EINTR))
  95.       perror("select");
  96. #endif
  97.   }
  98.   return(0);
  99. }
  100. #endif /* ENABLE_TIMEOUT */
  101.  
  102. static void setCursor(disp, window, iw, ih, ww, wh, cursor)
  103.      Display      *disp;
  104.      Window        window;
  105.      unsigned int  iw, ih;
  106.      unsigned int  ww, wh;
  107.      Cursor       *cursor;
  108. { XSetWindowAttributes swa;
  109.  
  110.   if ((ww >= iw) && (wh >= ih))
  111.     swa.cursor= XCreateFontCursor(disp, XC_icon);
  112.   else if ((ww < iw) && (wh >= ih))
  113.     swa.cursor= XCreateFontCursor(disp, XC_sb_h_double_arrow);
  114.   else if ((ww >= iw) && (wh < ih))
  115.     swa.cursor= XCreateFontCursor(disp, XC_sb_v_double_arrow);
  116.   else
  117.     swa.cursor= XCreateFontCursor(disp, XC_fleur);
  118.   XChangeWindowAttributes(disp, window, CWCursor, &swa);
  119.   XFreeCursor(disp, *cursor);
  120.   *cursor= swa.cursor;
  121. }
  122.  
  123. /* place an image
  124.  */
  125.  
  126. static void placeImage(disp, width, height, winwidth, winheight, rx, ry)
  127.      Display *disp;
  128.      int width, height, winwidth, winheight;
  129.      int *rx, *ry; /* supplied and returned */
  130. { int pixx, pixy;
  131.  
  132.   pixx= *rx;
  133.   pixy= *ry;
  134.  
  135.   if (winwidth > width)
  136.     pixx= (winwidth - width) / 2;
  137.   else {
  138.     if ((pixx < 0) && (pixx + width < winwidth))
  139.       pixx= winwidth - width;
  140.     if (pixx > 0)
  141.       pixx= 0;
  142.   }
  143.   if (winheight > height)
  144.     pixy= (winheight - height) / 2;
  145.   else {
  146.     if ((pixy < 0) && (pixy + height < winheight))
  147.       pixy= winheight - height;
  148.     if (pixy > 0)
  149.       pixy= 0;
  150.   }
  151.   *rx= pixx;
  152.   *ry= pixy;
  153.   XMoveWindow(disp, ImageWindow, pixx, pixy);
  154. }
  155.  
  156. /* blit an image
  157.  */
  158.  
  159. static void blitImage(ximageinfo, width, height,
  160.               x, y, w, h)
  161.      XImageInfo      *ximageinfo;
  162.      unsigned int  width, height;
  163.      int       x, y, w, h;
  164. {
  165.   if (w > width)
  166.     w= width;
  167.   if (h > height)
  168.     h= height;
  169.   if (x < 0) {
  170.     XClearArea(ximageinfo->disp, ximageinfo->drawable, x, y, -x, h, False);
  171.     w -= (0 - x);
  172.     x= 0;
  173.   }
  174.   if (y < 0) {
  175.     XClearArea(ximageinfo->disp, ximageinfo->drawable, x, y, w, -y, False);
  176.     h -= (0 - y);
  177.     y= 0;
  178.   }
  179.   if (x + w > width) {
  180.     XClearArea(ximageinfo->disp, ximageinfo->drawable,
  181.            x + width, y, x + w - width, h, False);
  182.     w -= x + w - width;
  183.   }
  184.   if (y + h > height) {
  185.     XClearArea(ximageinfo->disp, ximageinfo->drawable,
  186.            x, y + height, w, y + h - height, False);
  187.     h -= y + h - height;
  188.   }
  189.   sendXImage(ximageinfo, x, y, x, y, w, h);
  190. }
  191.  
  192. /* clean up static window if we're through with it
  193.  */
  194.  
  195. void cleanUpWindow(disp)
  196.      Display *disp;
  197. {
  198.   if (ImageWindow)
  199.     XDestroyWindow(disp, ImageWindow);
  200.   ImageWindow= 0;
  201.   if (ViewportWin)
  202.     XDestroyWindow(disp, ViewportWin);
  203.   ViewportWin= 0;
  204. }
  205.  
  206. /* clean up after displaying an image
  207.  */
  208.  
  209. static void cleanUpImage(disp, scrn, cursor, pixmap, image, ximageinfo)
  210.      Display      *disp;
  211.      int           scrn;
  212.      Cursor        cursor;
  213.      Pixmap        pixmap;
  214.      Image        *image;
  215.      XImageInfo      *ximageinfo;
  216. {
  217.   XFreeCursor(disp, cursor);
  218.   if (pixmap != None)
  219.       XFreePixmap(disp, pixmap);
  220.   freeXImage(image, ximageinfo);
  221. }
  222.  
  223. /* this sets the colormap and WM_COLORMAP_WINDOWS properly for the
  224.  * viewport.
  225.  */
  226.  
  227. void setViewportColormap(disp, scrn, visual)
  228.      Display *disp;
  229.      int scrn;
  230.      Visual *visual;
  231. { XSetWindowAttributes swa;
  232.   static cmap_atom= None;
  233.   Window cmap_windows[2];
  234.  
  235.   if (cmap_atom == None)
  236.     cmap_atom = XInternAtom(disp, "WM_COLORMAP_WINDOWS", False);
  237.  
  238.   /* if the visual we're using is the same as the default visual (used by
  239.    * the viewport window) then we can set the viewport window to use the
  240.    * image's colormap.  this keeps most window managers happy.
  241.    */
  242.  
  243.   if (visual == DefaultVisual(disp, scrn)) {
  244.     swa.colormap= ImageColormap;
  245.     XChangeWindowAttributes(disp, ViewportWin, CWColormap, &swa);
  246.     XDeleteProperty(disp, ViewportWin, cmap_atom);
  247.   }
  248.  
  249.   /* smart window managers can handle it when we use a different colormap
  250.    * in our subwindow so long as we set the WM_COLORMAP_WINDOWS property
  251.    * ala ICCCM.
  252.    */
  253.  
  254.   else {
  255.     cmap_windows[0]= ImageWindow;
  256.     cmap_windows[1]= ViewportWin;
  257.     XChangeProperty(disp, ViewportWin, cmap_atom, XA_WINDOW, 32,
  258.             PropModeReplace, (unsigned char *)cmap_windows, 2);
  259.   }
  260.  
  261. }
  262.  
  263. /* this attempts to convert an image title into a reasonable icon name
  264.  */
  265.  
  266. static char *iconName(s)
  267.      char *s;
  268. { static char buf[BUFSIZ];
  269.   char *t;
  270.  
  271.   if (!s)
  272.     return("Unnamed");
  273.   buf[BUFSIZ - 1]= '\0';
  274.   strncpy(buf, s, BUFSIZ - 1);
  275.   t= index(buf, ' '); /* strip off stuff following 1st word.  this strips */
  276.   if (t)              /* info added by processing functions too. */
  277.     *t= '\0';
  278.  
  279.   /* strip off leading path.  if you don't use unix-style paths, you might
  280.    * want to change this.
  281.    */
  282.  
  283.   if (t= rindex(buf, '/')) {
  284.     for (s= buf, t++; *t; s++, t++)
  285.       *s= *t;
  286.     *s= '\0';
  287.   }
  288.   t= index(buf, '.'); /* look for an extension and strip it off */
  289.   if (t)
  290.     *t= '\0';
  291.   return(buf);
  292. }
  293.  
  294. /* visual class to name table
  295.  */
  296.  
  297. static struct visual_class_name {
  298.   int   class; /* numerical value of class */
  299.   char *name;  /* actual name of class */
  300. } VisualClassName[] = {
  301.   TrueColor,   "TrueColor",
  302.   DirectColor, "DirectColor",
  303.   PseudoColor, "PseudoColor",
  304.   StaticColor, "StaticColor",
  305.   GrayScale,   "GrayScale",
  306.   StaticGray,  "StaticGray",
  307.   StaticGray,  "StaticGrey",
  308.   -1,          NULL
  309. };
  310.  
  311. int visualClassFromName(name)
  312.      char *name;
  313. { int a;
  314.   char *s1, *s2;
  315.   int class= -1;
  316.  
  317.   for (a= 0; VisualClassName[a].name; a++) {
  318.     for (s1= VisualClassName[a].name, s2= name; *s1 && *s2; s1++, s2++)
  319.       if ((isupper(*s1) ? tolower(*s1) : *s1) !=
  320.       (isupper(*s2) ? tolower(*s2) : *s2))
  321.     break;
  322.  
  323.     if ((*s1 == '\0') || (*s2 == '\0')) {
  324.  
  325.       /* check for uniqueness.  we special-case StaticGray because we have two
  326.        * spellings but they are unique if either is found
  327.        */
  328.     
  329.       if ((class != -1) && (class != StaticGray)) {
  330.     fprintf(stderr, "%s does not uniquely describe a visual class (ignored)\n", name);
  331.     return(-1);
  332.       }
  333.       class= VisualClassName[a].class;
  334.     }
  335.   }
  336.   if (class == -1)
  337.     fprintf(stderr, "%s is not a visual class (ignored)\n", name);
  338.   return(class);
  339. }
  340.  
  341. char *nameOfVisualClass(class)
  342.      int class;
  343. { int a;
  344.  
  345.   for (a= 0; VisualClassName[a].name; a++)
  346.     if (VisualClassName[a].class == class)
  347.       return(VisualClassName[a].name);
  348.   return("[Unknown Visual Class]");
  349. }
  350.  
  351. /* find the best visual of a particular class with a particular depth
  352.  */
  353.  
  354. static Visual *bestVisualOfClassAndDepth(disp, scrn, class, depth)
  355.      Display      *disp;
  356.      int           scrn;
  357.      int           class;
  358.      unsigned int  depth;
  359. { Visual *best= NULL;
  360.   XVisualInfo template, *info;
  361.   int nvisuals;
  362.  
  363.   template.screen= scrn;
  364.   template.class= class;
  365.   template.depth= depth;
  366.   if (! (info= XGetVisualInfo(disp, VisualScreenMask | VisualClassMask |
  367.                   VisualDepthMask, &template, &nvisuals)))
  368.     return(NULL); /* no visuals of this depth */
  369.  
  370.   /* not sure what to do if this gives more than one visual of a particular
  371.    * class and depth, so just return the first one.
  372.    */
  373.  
  374.   best= info->visual;
  375.   XFree((char *)info);
  376.   return(best);
  377. }
  378.  
  379. /* this tries to determine the best available visual to use for a particular
  380.  * image
  381.  */
  382.  
  383. static void bestVisual(disp, scrn, image, rvisual, rdepth)
  384.      Display       *disp;
  385.      int            scrn;
  386.      Image         *image;
  387.      Visual       **rvisual;
  388.      unsigned int  *rdepth;
  389. { unsigned int  depth, a;
  390.   Screen       *screen;
  391.   Visual       *visual, *default_visual;
  392.  
  393.   /* figure out the best depth the server supports.  note that some servers
  394.    * (such as the HP 11.3 server) actually say they support some depths but
  395.    * have no visuals that support that depth.  seems silly to me....
  396.    */
  397.  
  398.   depth= 0;
  399.   screen= ScreenOfDisplay(disp, scrn);
  400.   for (a= 0; a < screen->ndepths; a++) {
  401.     if (screen->depths[a].nvisuals &&
  402.     ((!depth ||
  403.       ((depth < image->depth) && (screen->depths[a].depth > depth)) ||
  404.       ((screen->depths[a].depth >= image->depth) &&
  405.        (screen->depths[a].depth < depth)))))
  406.       depth= screen->depths[a].depth;
  407.   }
  408.   if (!depth) { /* this shouldn't happen */
  409.     printf("bestVisual: didn't find any depths?!?\n");
  410.     depth= DefaultDepth(disp, scrn);
  411.   }
  412.  
  413.   /* given this depth, find the best possible visual
  414.    */
  415.  
  416.   default_visual= DefaultVisual(disp, scrn);
  417.   switch (image->type) {
  418.   case ITRUE:
  419.  
  420.     /* if the default visual is DirectColor or TrueColor prioritize such
  421.      * that we use the default type if it exists at this depth
  422.      */
  423.  
  424.     if (default_visual->class == TrueColor) {
  425.       visual= bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth);
  426.       if (!visual)
  427.     visual= bestVisualOfClassAndDepth(disp, scrn, DirectColor, depth);
  428.     }
  429.     else {
  430.       visual= bestVisualOfClassAndDepth(disp, scrn, DirectColor, depth);
  431.       if (!visual)
  432.     visual= bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth);
  433.     }
  434.  
  435.     if (!visual || ((depth <= 8) &&
  436.             bestVisualOfClassAndDepth(disp, scrn, PseudoColor, depth)))
  437.       visual= bestVisualOfClassAndDepth(disp, scrn, PseudoColor, depth);
  438.     if (!visual)
  439.       visual= bestVisualOfClassAndDepth(disp, scrn, StaticColor, depth);
  440.     if (!visual)
  441.       visual= bestVisualOfClassAndDepth(disp, scrn, GrayScale, depth);
  442.     if (!visual)
  443.       visual= bestVisualOfClassAndDepth(disp, scrn, StaticGray, depth);
  444.     break;
  445.  
  446.   case IRGB:
  447.  
  448.     /* if it's an RGB image, we want PseudoColor if we can get it
  449.      */
  450.  
  451.     visual= bestVisualOfClassAndDepth(disp, scrn, PseudoColor, depth);
  452.     if (!visual)
  453.       visual= bestVisualOfClassAndDepth(disp, scrn, DirectColor, depth);
  454.     if (!visual)
  455.       visual= bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth);
  456.     if (!visual)
  457.       visual= bestVisualOfClassAndDepth(disp, scrn, StaticColor, depth);
  458.     if (!visual)
  459.       visual= bestVisualOfClassAndDepth(disp, scrn, GrayScale, depth);
  460.     if (!visual)
  461.       visual= bestVisualOfClassAndDepth(disp, scrn, StaticGray, depth);
  462.     break;
  463.  
  464.   case IBITMAP:
  465.     visual= bestVisualOfClassAndDepth(disp, scrn, PseudoColor, depth);
  466.     if (!visual)
  467.       visual= bestVisualOfClassAndDepth(disp, scrn, StaticColor, depth);
  468.     if (!visual)
  469.       visual= bestVisualOfClassAndDepth(disp, scrn, GrayScale, depth);
  470.     if (!visual)
  471.       visual= bestVisualOfClassAndDepth(disp, scrn, StaticGray, depth);
  472.  
  473.     /* it seems pretty wasteful to use a TrueColor or DirectColor visual
  474.      * to display a bitmap (2-color) image, so we look for those last
  475.      */
  476.  
  477.     if (!visual)
  478.       visual= bestVisualOfClassAndDepth(disp, scrn, DirectColor, depth);
  479.     if (!visual)
  480.       visual= bestVisualOfClassAndDepth(disp, scrn, TrueColor, depth);
  481.     break;
  482.   }
  483.  
  484.   if (!visual) { /* this shouldn't happen */
  485.     fprintf(stderr, "bestVisual: couldn't find one?!?\n");
  486.     depth= DefaultDepth(disp, scrn);
  487.     visual= DefaultVisual(disp, scrn);
  488.   }
  489.   *rvisual= visual;
  490.   *rdepth= depth;
  491. }
  492.  
  493. /* given a visual class, try to find the best visual of that class at
  494.  * the best depth.  returns a null visual and depth if it couldn't find
  495.  * any visual of that type at any depth
  496.  */
  497.  
  498. void bestVisualOfClass(disp, scrn, image, visual_class, rvisual, rdepth)
  499.      Display      *disp;
  500.      int           scrn;
  501.      Image        *image;
  502.      int           visual_class;
  503.      Visual      **rvisual;
  504.      unsigned int *rdepth;
  505. {
  506.   Visual       *visual;
  507.   Screen       *screen;
  508.   unsigned int  a, b, depth;
  509.  
  510.   /* loop through depths looking for a visual of a good depth which matches
  511.    * our visual class.
  512.    */
  513.  
  514.   screen= ScreenOfDisplay(disp, scrn);
  515.   visual= (Visual *)NULL;
  516.   depth= 0;
  517.   for (a= 0; a < screen->ndepths; a++) {
  518.     for (b= 0; b < screen->depths[a].nvisuals; b++) {
  519.       if ((screen->depths[a].visuals[b].class == visual_class) &&
  520.       (!depth ||
  521.        ((depth < image->depth) && (screen->depths[a].depth > depth)) ||
  522.        ((screen->depths[a].depth >= image->depth) &&
  523.         (screen->depths[a].depth < depth)))) {
  524.     depth= screen->depths[a].depth;
  525.     visual= &(screen->depths[a].visuals[b]);
  526.       }
  527.     }
  528.   }
  529.   *rvisual= visual;
  530.   *rdepth= depth;
  531. }
  532.  
  533. char imageInWindow(disp, scrn, image, global_options, image_options,
  534.            argc, argv, verbose)
  535.      Display      *disp;
  536.      int           scrn;
  537.      Image        *image;
  538.      OptionSet    *global_options;
  539.      OptionSet    *image_options;
  540.      int           argc;
  541.      char         *argv[];
  542.      unsigned int  verbose;
  543. { Pixmap                pixmap = None;
  544.   XImageInfo           *ximageinfo;
  545.   Visual               *visual;
  546.   unsigned int          depth;
  547.   Window                oldimagewindow;
  548.   Colormap              oldcmap;
  549.   XSetWindowAttributes  swa_img;
  550.   XSetWindowAttributes  swa_view;
  551.   XClassHint            classhint;
  552.   unsigned int          wa_mask_img;
  553.   XSizeHints            sh;
  554.   XWMHints              wmh;
  555.   int                   pixx= -1, pixy= -1;
  556.   int                   lastx, lasty, mousex, mousey;
  557.   int                   paint;
  558.   static int            old_width= -1, old_height= -1;
  559.   static Atom           proto_atom= None, delete_atom= None;
  560.   union {
  561.     XEvent              event;
  562.     XAnyEvent           any;
  563.     XButtonEvent        button;
  564.     XKeyEvent           key;
  565.     XConfigureEvent     configure;
  566.     XExposeEvent        expose;
  567.     XMotionEvent        motion;
  568.     XResizeRequestEvent resize;
  569.     XClientMessageEvent message;
  570.   } event;
  571.   Option               *opt;
  572.   unsigned int          winx, winy, winwidth, winheight;
  573.   unsigned int          delay;
  574.   unsigned int          fullscreen;
  575.   unsigned int          fit;
  576.   unsigned int          install;
  577.   unsigned int          private_cmap;
  578.   int                   visual_class;
  579.  
  580.   /* get values from options that we'll use a lot
  581.    */
  582.   fit= (getOption(global_options, FIT) != NULL);
  583.   fullscreen= (getOption(global_options, FULLSCREEN) != NULL);
  584.   install= (getOption(global_options, INSTALL) != NULL);
  585.   private_cmap= (getOption(global_options, PRIVATE) != NULL);
  586.   if (opt= getOption(image_options, DELAY))
  587.     delay= opt->info.delay;
  588.   else if (opt= getOption(global_options, DELAY))
  589.     delay= opt->info.delay;
  590.   else
  591.     delay= 0;
  592.   if (opt= getOption(image_options, VISUAL))
  593.     visual_class= opt->info.visual;
  594.   else
  595.     visual_class= -1;
  596.  
  597.   /* figure out the window size.  unless specifically requested to do so,
  598.    * we will not exceed 90% of display real estate.
  599.    */
  600.   opt= getOption(global_options, GEOMETRY);
  601.   if (opt == NULL)
  602.     winx= winy= winwidth= winheight= 0;
  603.   else {
  604.     char def_geom[30];
  605.  
  606.     sprintf(def_geom, "%ux%u+0+0", image->width, image->height);
  607.     XGeometry(disp, scrn, opt->info.geometry.string, def_geom, 0, 1, 1, 0, 0,
  608.           (int *)&winx, (int *)&winy, (int *)&winwidth, (int *)&winheight);
  609.   }
  610.  
  611.   if (fullscreen) {
  612.     winwidth= DisplayWidth(disp, scrn);
  613.     winheight= DisplayHeight(disp, scrn);
  614.   }
  615.   else {
  616.     lastx= (winwidth || winheight); /* user set size flag */
  617.     if (!winwidth) {
  618.       winwidth= image->width;
  619.       if (winwidth > DisplayWidth(disp, scrn) * 0.9)
  620.     winwidth= DisplayWidth(disp, scrn) * 0.9;
  621.     }
  622.     if (!winheight) {
  623.       winheight= image->height;
  624.       if (winheight > DisplayHeight(disp, scrn) * 0.9)
  625.     winheight= DisplayHeight(disp, scrn) * 0.9;
  626.     }
  627.   }
  628.  
  629.   /* if the user told us to fit the colormap, we must use the default
  630.    * visual.
  631.    */
  632.  
  633.   if (fit) {
  634.     visual= DefaultVisual(disp, scrn);
  635.     depth= DefaultDepth(disp, scrn);
  636.   }
  637.   else {
  638.  
  639.     visual= (Visual *)NULL;
  640.     if (visual_class == -1) {
  641.  
  642.       /* try to pick the best visual for the image.
  643.        */
  644.  
  645.       bestVisual(disp, scrn, image, &visual, &depth);
  646.       if (verbose && (visual != DefaultVisual(disp, scrn)))
  647.     printf("  Using %s visual\n", nameOfVisualClass(visual->class));
  648.     }
  649.     else {
  650.  
  651.       /* try to find a visual of the specified class
  652.        */
  653.  
  654.       bestVisualOfClass(disp, scrn, image, visual_class, &visual, &depth);
  655.       if (!visual) {
  656.     bestVisual(disp, scrn, image, &visual, &depth);
  657.     fprintf(stderr, "Server does not support %s visual, using %s\n",
  658.         nameOfVisualClass(visual_class),
  659.         nameOfVisualClass(visual->class));
  660.       }
  661.     }
  662.   }
  663.  
  664.   /* if we're in slideshow mode and the user told us to fit the colormap,
  665.    * free it here.
  666.    */
  667.  
  668.   if (ViewportWin) {
  669.     if (fit) {
  670.       XDestroyWindow(disp, ImageWindow);
  671.       ImageWindow= 0;
  672.       ImageColormap= 0;
  673.     }
  674.  
  675.     /* for the 1st image we display we can use the default cmap.  subsequent
  676.      * images use a private colormap (unless they're bitmaps) so we don't get
  677.      * color erosion when switching images.
  678.      */
  679.  
  680.     else if (!BITMAPP(image))
  681.       private_cmap= 1;
  682.   }
  683.  
  684.   if (! (ximageinfo= imageToXImage(disp, scrn, visual, depth, image,
  685.                    private_cmap, fit, verbose))) {
  686.     fprintf(stderr, "Cannot convert Image to XImage\n");
  687.     exit(1);
  688.   }
  689.  
  690.   swa_view.background_pixel= WhitePixel(disp,scrn);
  691.   swa_view.backing_store= NotUseful;
  692.   swa_view.cursor= XCreateFontCursor(disp, XC_watch);
  693.   swa_view.event_mask= ButtonPressMask | Button1MotionMask | KeyPressMask |
  694.     StructureNotifyMask | EnterWindowMask | LeaveWindowMask;
  695.   swa_view.save_under= False;
  696.  
  697.   classhint.res_class = "Xloadimage";
  698.   classhint.res_name = "xloadimage";
  699.   if (!ViewportWin) {
  700.     ViewportWin= XCreateWindow(disp, RootWindow(disp, scrn), winx, winy,
  701.                    winwidth, winheight, 0,
  702.                    DefaultDepth(disp, scrn), InputOutput,
  703.                    DefaultVisual(disp, scrn),
  704.                    CWBackingStore | CWBackPixel | CWCursor |
  705.                    CWEventMask | CWSaveUnder,
  706.                    &swa_view);
  707.     oldimagewindow= 0;
  708.     XSetCommand(disp, ViewportWin, argv, argc);
  709.     XSetClassHint(disp,ViewportWin,&classhint);
  710.     proto_atom = XInternAtom(disp, "WM_PROTOCOLS", False);
  711.     delete_atom = XInternAtom(disp, "WM_DELETE_WINDOW", False);
  712.     if ((proto_atom != None) && (delete_atom != None))
  713.       XChangeProperty(disp, ViewportWin, proto_atom, XA_ATOM, 32,
  714.               PropModeReplace, (unsigned char *)&delete_atom, 1);
  715.    paint= 0;
  716.   }
  717.   else {
  718.     oldimagewindow= ImageWindow;
  719.     oldcmap= ImageColormap;
  720.     paint= 1;
  721.   }
  722.  
  723.   /* create image window
  724.    */
  725.  
  726.   swa_img.bit_gravity= NorthWestGravity;
  727.   swa_img.save_under= False;
  728.   swa_img.colormap= ximageinfo->cmap;
  729.   swa_img.border_pixel= 0;
  730.   ImageWindow= XCreateWindow(disp, ViewportWin, winx, winy,
  731.                  image->width, image->height, 0,
  732.                  ximageinfo->depth, InputOutput, visual,
  733.                  CWBitGravity | CWColormap | CWSaveUnder |
  734.                  CWBorderPixel, &swa_img);
  735.   ImageColormap= ximageinfo->cmap;
  736.   XSetCommand(disp, ImageWindow, argv, argc);
  737.   XSetClassHint(disp,ImageWindow,&classhint);
  738.  
  739.   /* decide how we're going to handle repaints.  we have three modes:
  740.    * use backing-store, use background pixmap, and use exposures.
  741.    * if the server supports backing-store, we enable it and use it.
  742.    * this really helps servers which are memory constrained.  if the
  743.    * server does not have backing-store, we try to send the image to
  744.    * a pixmap and use that as backing-store.  if that fails, we use
  745.    * exposures to blit the image (which is ugly but it works).
  746.    *
  747.    * the "use_pixmap" flag forces background pixmap mode, which may
  748.    * improve performance.
  749.    */
  750.  
  751.   ximageinfo->drawable= ImageWindow;
  752.   if ((DoesBackingStore(ScreenOfDisplay(disp,scrn)) == NotUseful) ||
  753.       getOption(global_options, PIXMAP)) {
  754.     if (((pixmap= ximageToPixmap(disp, ImageWindow, ximageinfo)) ==
  755.      None) && verbose)
  756.       printf("  Cannot create image in server, repaints will be ugly!\n");
  757.   }
  758.  
  759.   /* build window attributes for the image window
  760.    */
  761.  
  762.   wa_mask_img= 0;
  763.   if (pixmap == None) {
  764.  
  765.     /* No pixmap.  Must paint over the wire.  Ask for BackingStore
  766.      * to cut down on the painting.  But, ask for Exposures so we can
  767.      * paint both viewables and backingstore.
  768.      */
  769.  
  770.     swa_img.background_pixel= WhitePixel(disp,scrn);
  771.     wa_mask_img |= CWBackPixel;
  772.     swa_img.event_mask= ExposureMask;
  773.     wa_mask_img |= CWEventMask;
  774.     swa_img.backing_store= WhenMapped;
  775.     wa_mask_img |= CWBackingStore;
  776.   }
  777.   else {
  778.  
  779.     /* we have a pixmap so tile the window.  to move the image we only
  780.      * have to move the window and the server should do the rest.
  781.      */
  782.  
  783.     swa_img.background_pixmap= pixmap;
  784.     wa_mask_img |= CWBackPixmap;
  785.     swa_img.event_mask= 0;    /* no exposures please */
  786.     wa_mask_img |= CWEventMask;
  787.     swa_img.backing_store= NotUseful;
  788.     wa_mask_img |= CWBackingStore;
  789.   }
  790.   XChangeWindowAttributes(disp, ImageWindow, wa_mask_img, &swa_img);
  791.  
  792.   if (image->title)
  793.     XStoreName(disp, ViewportWin, image->title);
  794.   else
  795.     XStoreName(disp, ViewportWin, "Unnamed");
  796.   XSetIconName(disp, ViewportWin, iconName(image->title));
  797.  
  798.   sh.width= winwidth;
  799.   sh.height= winheight;
  800.   if (fullscreen) {
  801.     sh.min_width= sh.max_width= winwidth;
  802.     sh.min_height= sh.max_height= winheight;
  803.   }
  804.   else {
  805.     sh.min_width= 1;
  806.     sh.min_height= 1;
  807.     sh.max_width= image->width;
  808.     sh.max_height= image->height;
  809.   }
  810.   sh.width_inc= 1;
  811.   sh.height_inc= 1;
  812.   sh.flags= PMinSize | PMaxSize | PResizeInc;
  813.   if (lastx || fullscreen)
  814.     sh.flags |= USSize;
  815.   else
  816.     sh.flags |= PSize;
  817.   if (fullscreen) {
  818.     sh.x= sh.y= 0;
  819.     sh.flags |= USPosition;
  820.   }
  821.   else if (winx || winy) {
  822.     sh.x= winx;
  823.     sh.y= winy;
  824.     sh.flags |= USPosition;
  825.   }
  826.   XSetNormalHints(disp, ViewportWin, &sh);
  827.   sh.min_width= sh.max_width;
  828.   sh.min_height= sh.max_height;
  829.   XSetNormalHints(disp, ImageWindow, &sh);    /* Image doesn't shrink */
  830.  
  831.   wmh.input= True;
  832.   wmh.flags= InputHint;
  833.   XSetWMHints(disp, ViewportWin, &wmh);
  834.  
  835.   setViewportColormap(disp, scrn, visual);
  836.  
  837.   /* map windows and clean up old window if there was one.
  838.    */
  839.  
  840.   XMapWindow(disp, ImageWindow);
  841.   XMapWindow(disp, ViewportWin);
  842.   if (oldimagewindow) {
  843.     if (oldcmap && (oldcmap != DefaultColormap(disp, scrn)))
  844.       XFreeColormap(disp, oldcmap);
  845.     XDestroyWindow(disp, oldimagewindow);
  846.   }
  847.  
  848.   /* start displaying image
  849.    */
  850.  
  851.   placeImage(disp, image->width, image->height, winwidth, winheight, &pixx, &pixy);
  852.   if (paint) {
  853.     if ((winwidth != old_width) || (winheight != old_height)) {
  854.     XResizeWindow(disp, ViewportWin, winwidth, winheight);
  855.     }
  856.     XResizeWindow(disp, ImageWindow, image->width, image->height);
  857.     /* Clear the image window.  Ask for exposure if there is no tile. */
  858.     XClearArea(disp, ImageWindow, 0, 0, 0, 0, (pixmap == None));
  859.   }
  860.   old_width= winwidth;
  861.   old_height= winheight;
  862.  
  863.   /* flush output. this is so that -delay will be close to what was
  864.    * asked for (i.e., do the flushing of output outside of the loop).
  865.    */
  866.   XSync(disp,False);
  867.  
  868.   setCursor(disp, ViewportWin, image->width, image->height,
  869.         winwidth, winheight, &(swa_view.cursor));
  870.   lastx= lasty= -1;
  871.   if (delay) {
  872.       /* reset alarm to -delay seconds after every event */
  873. #ifdef ENABLE_TIMEOUT
  874.       AlarmWentOff = 0;
  875.       signal(SIGALRM, delayAlarmHandler);
  876.       alarm(delay);
  877. #endif /* ENABLE_TIMEOUT */
  878.   }
  879.  
  880.   for (;;) {
  881.  
  882. #ifdef ENABLE_TIMEOUT
  883.     if (delay) {
  884.       if (!getNextEventWithTimeout(disp, &event.event)) {
  885.     Cursor cursor= swa_view.cursor;
  886.  
  887.     /* timeout expired.  clean up and exit.
  888.      */
  889.  
  890.     swa_view.cursor= XCreateFontCursor(disp, XC_watch);
  891.     XChangeWindowAttributes(disp, ImageWindow, CWCursor, &swa_view);
  892.     XFreeCursor(disp, cursor);
  893.     XFlush(disp);
  894.     cleanUpImage(disp, scrn, swa_view.cursor, pixmap,
  895.              image, ximageinfo);
  896.     return('n');
  897.       }
  898.     }
  899.     else
  900. #endif /* ENABLE_TIMEOUT */
  901.       XNextEvent(disp, &event.event);
  902.  
  903.     switch (event.any.type) {
  904.     case ButtonPress:
  905.       if (event.button.button == 1) {
  906.     lastx= event.button.x;
  907.     lasty= event.button.y;
  908.     break;
  909.       }
  910.       break;
  911.  
  912.     case KeyPress: {
  913.       char buf[128];
  914.       KeySym ks;
  915.       XComposeStatus status;
  916.       char ret;
  917.       Cursor cursor;
  918.  
  919.       if (XLookupString(&event.key,buf,128,&ks,&status) != 1)
  920.     break;
  921.       ret= buf[0];
  922.       if (isupper(ret))
  923.     ret= tolower(ret);
  924.       switch (ret) {
  925.       case ' ':
  926.       case 'n':
  927.       case 'p':
  928.       case '<':
  929.       case '>':
  930.     if (delay)
  931.       alarm(0);
  932.     cursor= swa_view.cursor;
  933.     swa_view.cursor= XCreateFontCursor(disp, XC_watch);
  934.     XChangeWindowAttributes(disp, ViewportWin, CWCursor, &swa_view);
  935.     XFreeCursor(disp, cursor);
  936.     XFlush(disp);
  937.     cleanUpImage(disp, scrn, swa_view.cursor, pixmap,
  938.              image, ximageinfo);
  939.     return(ret);
  940.       case '\003': /* ^C */
  941.       case 'q':
  942.     if (delay)
  943.       alarm(0);
  944.     cleanUpImage(disp, scrn, swa_view.cursor, pixmap,
  945.              image, ximageinfo);
  946.     return(ret);
  947.       }
  948.       break;
  949.     }
  950.  
  951.     case MotionNotify:
  952.       if ((image->width <= winwidth) && (image->height <= winheight))
  953.     break; /* we're AT&T */
  954.       mousex= event.button.x;
  955.       mousey= event.button.y;
  956.       /*XSync(disp, False); */
  957.       while (XCheckTypedEvent(disp, MotionNotify, (XEvent*)&event) == True) {
  958.     mousex= event.button.x;
  959.     mousey= event.button.y;
  960.       }
  961.       pixx -= (lastx - mousex);
  962.       pixy -= (lasty - mousey);
  963.       lastx= mousex;
  964.       lasty= mousey;
  965.       placeImage(disp, image->width, image->height, winwidth, winheight,
  966.          &pixx, &pixy);
  967.       break;
  968.  
  969.     case ConfigureNotify:
  970.       winwidth= old_width= event.configure.width;
  971.       winheight= old_height= event.configure.height;
  972.  
  973.       placeImage(disp, image->width, image->height, winwidth, winheight,
  974.          &pixx, &pixy);
  975.  
  976.       /* configure the cursor to indicate which directions we can drag
  977.        */
  978.  
  979.       setCursor(disp, ViewportWin, image->width, image->height,
  980.         winwidth, winheight, &(swa_view.cursor));
  981.       break;
  982.  
  983.     case DestroyNotify:
  984.       cleanUpImage(disp, scrn, swa_view.cursor, pixmap,
  985.            image, ximageinfo);
  986.       return('\0');
  987.  
  988.     case Expose:
  989.       blitImage(ximageinfo, image->width, image->height,
  990.         event.expose.x, event.expose.y,
  991.         event.expose.width, event.expose.height);
  992.       break;
  993.  
  994.     case EnterNotify:
  995.       if (install)
  996.     XInstallColormap(disp, ximageinfo->cmap);
  997.       break;
  998.  
  999.     case LeaveNotify:
  1000.       if (install)
  1001.     XUninstallColormap(disp, ximageinfo->cmap);
  1002.       break;
  1003.  
  1004.     case ClientMessage:
  1005.       /* if we get a client message for the viewport window which has the
  1006.        * value of the delete atom, it means the window manager wants us to
  1007.        * die.
  1008.        */
  1009.  
  1010.       if ((event.message.window == ViewportWin) &&
  1011.       (event.message.data.l[0] == delete_atom)) {
  1012.     cleanUpImage(disp, scrn, swa_view.cursor, pixmap,
  1013.               image, ximageinfo);
  1014.     return('q');
  1015.       }
  1016.       break;
  1017.     }
  1018.   }
  1019. }
  1020.