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