earlybrowserreborn - Blame information for rev 1

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