"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "viewfax-2.6/viewfax.c" of archive viewfax-2.6.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /* Program to view fax files on an X-window screen
    2    Copyright (C) 1990, 1995, 2004  Frank D. Cringle.
    3 
    4 This file is part of viewfax - g3/g4 fax processing software.
    5 
    6 viewfax is free software; you can redistribute it and/or modify it
    7 under the terms of the GNU General Public License as published by the
    8 Free Software Foundation; either version 2 of the License, or (at your
    9 option) any later version.
   10 
   11 This program is distributed in the hope that it will be useful, but
   12 WITHOUT ANY WARRANTY; without even the implied warranty of
   13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14 General Public License for more details.
   15 
   16 You should have received a copy of the GNU General Public License
   17 along with this program; if not, write to the Free Software
   18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
   19 
   20 #include <stdlib.h>
   21 #include <stdio.h>
   22 #include <string.h>
   23 #include <sys/time.h>
   24 /* NewImage() needs to fiddle with the Display structure */
   25 #define XLIB_ILLEGAL_ACCESS
   26 #include <X11/Xlib.h>
   27 #include <X11/Xutil.h>
   28 #include <X11/Xatom.h>
   29 #include <X11/keysym.h>
   30 #include <X11/keysymdef.h>
   31 #include <X11/cursorfont.h>
   32 #include "faxexpand.h"
   33 
   34 #define VERSION "2.6"
   35 
   36 /* If moving the image around with the middle mouse button is jerky or
   37    slow, try defining USE_MOTIONHINT.  It may help (it may also make
   38    things worse - it depends on the server implementation) */
   39 #undef USE_MOTIONHINT
   40 
   41 struct pagenode *firstpage, *lastpage, *thispage, *helppage;
   42 struct pagenode defaultpage;
   43 
   44 /* access the 'extra' field in a pagenode */
   45 #define Pimage(p)	((XImage *)(p)->extra)
   46 
   47 /* getopt declarations */
   48 extern int getopt();
   49 extern char *optarg;
   50 extern int optind, opterr, optopt;
   51 
   52 /* forward declarations */
   53 static XImage *FlipImage(XImage *xi);
   54 static XImage *MirrorImage(XImage *xi);
   55 static XImage *NewImage(int w, int h, char *data, int bit_order);
   56 static XImage *RotImage(XImage *Image);
   57 static XImage *ZoomImage(XImage *Big);
   58 static void FreeImage(XImage *Image);
   59 static int GetImage(struct pagenode *pn);
   60 static void SetupDisplay(int argc, char **argv);
   61 static void ShowLoop(void);
   62 static int release(int quit);
   63 static char *suffix(char *opt, const char *str);
   64 
   65 /* X variables */
   66 static char *DispName = NULL;
   67 static char *PrintCmd = NULL;
   68 static char *EditCmd = NULL;
   69 static char *Geometry = NULL;
   70 static Display *Disp;
   71 static Window Root;
   72 static Window Win;
   73 static int Default_Screen;
   74 static GC PaintGC;
   75 static Cursor WorkCursor;
   76 static Cursor ReadyCursor;
   77 static Cursor MoveCursor;
   78 static Cursor LRCursor;
   79 static Cursor UDCursor;
   80 
   81 char *ProgName;
   82 int verbose = 0;
   83 
   84 static int abell = 0;			/* audio bell */
   85 static int vbell = 1;			/* visual bell */
   86 static int Wheelinv = 0;		/* mouse scrolls by page */
   87 static int zfactor = 0;			/* zoom factor */
   88 
   89 static size_t Memused = 0;		/* image memory usage */
   90 static size_t Memlimit = 4*1024*1024;	/* try not to exceed */
   91 
   92 #undef min
   93 #undef max
   94 #define min(a,b)	((a)<(b)?(a):(b))
   95 #define max(a,b)	((a)>(b)?(a):(b))
   96 
   97 #ifndef EXIT_FAILURE
   98 #define EXIT_FAILURE 1
   99 #endif
  100 
  101 /* OK, OK - this is a dreadful hack.  But it adequately distinguishes
  102    modern big- and little- endian hosts.  We only need this to set the
  103    byte order in XImage structures */
  104 static union { t32bits i; unsigned char b[4]; } bo;
  105 #define ByteOrder	bo.b[3]
  106 
  107 static char Banner[] =
  108 "\nviewfax version " VERSION ",\n"
  109 "Copyright (C) 1990, 1995, 2004 Frank D. Cringle.\n"
  110 "viewfax comes with ABSOLUTELY NO WARRANTY; for details see the\n"
  111 "file \"COPYING\" in the distribution directory.\n\n";
  112 
  113 static char Usage[] =
  114 "usage: %s <flags> file ...\n"
  115 "\t-f\tfine resolution (default unless filename begins with 'fn')\n"
  116 "\t-n\tnormal resolution\n"
  117 "\t-h\theight (number of fax lines)\n"
  118 "\t-w\twidth (dots per fax line)\n"
  119 "\t-l\tturn image 90 degrees (landscape mode)\n"
  120 "\t-u\tturn image upside down\n"
  121 "\t-i\tinvert (black/white)\n"
  122 "\t-d\t(or -display) use an alternate X display\n"
  123 "\t-g\t(or -geometry) size and position of window\n"
  124 "\t-b\tuse audio (-ba) or visual (-bv) warning bell\n"
  125 "\t-m\tmemory usage limit\n"
  126 "\t-r\tfax data is packed ls-bit first in input bytes\n"
  127 "\t-v\tverbose messages\n"
  128 "\t-W\tmousewheel scrolls by page\n"
  129 "\t-z\tinitial zoom factor\n"
  130 "\t-2\traw files are g3-2d\n"
  131 "\t-4\traw files are g4\n";
  132 
  133 int
  134 main(int argc, char **argv)
  135 {
  136     int c;
  137     int err = 0;
  138 
  139     bo.i = 1;
  140     defaultpage.vres = 2;
  141     defaultpage.expander = g31expand;
  142     PrintCmd = getenv("VIEWFAX_PRINT");
  143     EditCmd = getenv("VIEWFAX_EDIT");
  144     opterr = 0;			/* suppress getopt error message */
  145 
  146     if ((ProgName = strrchr(argv[0], '/')) == NULL)
  147 	ProgName = argv[0];
  148     else
  149 	ProgName++;
  150     while ((c = getopt(argc, argv, "b:d:fg:h:ilm:nruvWw:z:24")) != -1)
  151 	switch(c) {
  152 	case 'b':
  153 	    abell = vbell = 0;
  154 	    while (*optarg) {
  155 		abell |= (*optarg == 'a');
  156 		vbell |= (*optarg == 'v');
  157 		optarg++;
  158 	    }
  159 	    break;
  160 	case 'd':		/* display name */
  161 	    if (*(DispName = suffix(optarg, "isplay")) == 0)
  162 		DispName = argv[optind++];
  163 	    break;
  164 	case 'f':		/* fine resolution */
  165 	    defaultpage.vres = 1;
  166 	    break;
  167 	case 'g':		/* geometry */
  168 	    if (*(Geometry = suffix(optarg, "eometry")) == 0)
  169 		Geometry = argv[optind++];
  170 	    break;
  171 	case 'h':		/* user thinks this is the height */
  172 	    defaultpage.height = atoi(optarg);
  173 	    break;
  174 	case 'i':		/* invert black/white */
  175 	    defaultpage.inverse = 1;
  176 	    break;
  177 	case 'l':		/* landscape */
  178 	    defaultpage.orient |= TURN_L;
  179 	    break;
  180 	case 'm':		/* memory usage limit */
  181 	    Memlimit = atoi(optarg);
  182 	    switch(optarg[strlen(optarg)-1]) {
  183 	    case 'M':
  184 	    case 'm':
  185 		Memlimit *= 1024;
  186 	    case 'K':
  187 	    case 'k':
  188 		Memlimit *= 1024;
  189 	    }
  190 	    break;
  191 	case 'n':		/* normal resolution */
  192 	    defaultpage.vres = 0;
  193 	    break;
  194 	case 'r':		/* reverse input bits */
  195 	    defaultpage.lsbfirst = 1;
  196 	    break;
  197 	case 'u':		/* upside down */
  198 	    defaultpage.orient |= TURN_U;
  199 	    break;
  200 	case 'v':		/* verbose messages */
  201 	    verbose = 1;
  202 	    break;
  203 	case 'w':		/* user thinks this is the width */
  204 	    defaultpage.width = atoi(optarg);
  205 	    break;
  206 	case 'W':		/* mouse scroll by page */
  207 	    Wheelinv = 1;
  208 	    break;
  209 	case 'z':		/* zoom factor */
  210 	    c = atoi(optarg);
  211 	    if (c <= 0)
  212 		c = 1;
  213 	    for (zfactor = 1; c > 1; c >>= 1)
  214 		zfactor <<= 1;	/* constrain to a power of 2 */
  215 	    break;
  216 	case '2':
  217 	    defaultpage.expander = g32expand;
  218 	    break;
  219 	case '4':
  220 	    defaultpage.expander = g4expand;
  221 	    break;
  222 	default:
  223 	    err++;
  224 	    break;
  225 	}
  226 
  227     if (defaultpage.expander == g4expand && defaultpage.height == 0) {
  228 	fputs("-h value is required to interpret raw g4 faxes\n", stderr);
  229 	err++;
  230     }
  231 
  232     if (err) {
  233 	fprintf(stderr, Usage, ProgName);
  234 	exit(EXIT_FAILURE);
  235     }
  236 
  237     if (optind == argc) {
  238 	fputs(Banner, stdout);
  239 	exit(0);
  240     }
  241 
  242     if (verbose)
  243 	fputs(Banner, stdout);
  244 
  245     firstpage = lastpage = thispage = helppage = NULL;
  246     for (; optind < argc; optind++)
  247 	(void) notetiff(argv[optind]);
  248 
  249     if (firstpage == NULL)
  250 	exit(EXIT_FAILURE);
  251 
  252     if ((Disp = XOpenDisplay(DispName)) == NULL) {
  253 	fprintf(stderr, "%s: can't open display %s\n", ProgName,
  254 		DispName ? DispName : XDisplayName((char *) NULL));
  255 	exit(EXIT_FAILURE);
  256     }
  257     Default_Screen = XDefaultScreen(Disp);
  258     faxinit();
  259     thispage = firstpage;
  260     while (!GetImage(firstpage))
  261 	/* try again */;
  262     SetupDisplay(argc, argv);
  263     ShowLoop();
  264     exit(0);
  265 }
  266 
  267 /* return mismatching suffix of option name */
  268 static char *
  269 suffix(char *opt, const char *prefix)
  270 {
  271     while (*opt && *opt == *prefix) {
  272 	opt++; prefix++;
  273     }
  274     return opt;
  275 }
  276 
  277 /* Change orientation of all following pages */
  278 static void
  279 TurnFollowing(int How, struct pagenode *pn)
  280 {
  281     while (pn) {
  282 	if (Pimage(pn)) {
  283 	    FreeImage(Pimage(pn));
  284 	    pn->extra = NULL;
  285 	}
  286 	pn->orient ^= How;
  287 	pn = pn->next;
  288     }
  289 }
  290 
  291 static void
  292 drawline(pixnum *run, int LineNum, struct pagenode *pn)
  293 {
  294     t32bits *p, *p1;		/* p - current line, p1 - low-res duplicate */
  295     pixnum *r;			/* pointer to run-lengths */
  296     t32bits pix;		/* current pixel value */
  297     t32bits acc;		/* pixel accumulator */
  298     int nacc;			/* number of valid bits in acc */
  299     int tot;			/* total pixels in line */
  300     int n;
  301 
  302     LineNum += pn->stripnum * pn->rowsperstrip;
  303     if (LineNum >= pn->height) {
  304 	if (verbose && LineNum == pn->height)
  305 	    fputs("Height exceeded\n", stderr);
  306 	return;
  307     }
  308     p = (t32bits *) (Pimage(pn)->data + LineNum*(2-pn->vres)*Pimage(pn)->bytes_per_line);
  309     p1 = pn->vres ? NULL : p + Pimage(pn)->bytes_per_line/sizeof(*p);
  310     r = run;
  311     acc = 0;
  312     nacc = 0;
  313     pix = pn->inverse ? ~0 : 0;
  314     tot = 0;
  315     while (tot < pn->width) {
  316 	n = *r++;
  317 	tot += n;
  318 	/* Watch out for buffer overruns, e.g. when n == 65535.  */
  319 	if (tot > pn->width)
  320 	    break;
  321 	if (pix)
  322 	    acc |= (~(t32bits)0 >> nacc);
  323 	else if (nacc)
  324 	    acc &= (~(t32bits)0 << (32 - nacc));
  325 	else
  326 	    acc = 0;
  327 	if (nacc + n < 32) {
  328 	    nacc += n;
  329 	    pix = ~pix;
  330 	    continue;
  331 	}
  332 	*p++ = acc;
  333 	if (p1)
  334 	    *p1++ = acc;
  335 	n -= 32 - nacc;
  336 	while (n >= 32) {
  337 	    n -= 32;
  338 	    *p++ = pix;
  339 	    if (p1)
  340 		*p1++ = pix;
  341 	}
  342 	acc = pix;
  343 	nacc = n;
  344 	pix = ~pix;
  345     }
  346     if (nacc) {
  347 	*p++ = acc;
  348 	if (p1)
  349 	    *p1++ = acc;
  350     }
  351 }
  352 
  353 static int
  354 GetPartImage(struct pagenode *pn, int n)
  355 {
  356     unsigned char *Data = getstrip(pn, n);
  357 
  358     if (Data == NULL)
  359 	return 0;
  360     pn->stripnum = n;
  361     (*pn->expander)(pn, drawline);
  362     free(Data);
  363     return 1;
  364 }
  365 
  366 static int
  367 GetImage(struct pagenode *pn)
  368 {
  369     int i;
  370     XImage *tp;
  371 
  372     if (pn->strips == NULL) {
  373 	/* raw file; maybe we don't have the height yet */
  374 	unsigned char *Data = getstrip(pn, 0);
  375 	if (Data == NULL)
  376 	    return 0;
  377 	pn->extra = NewImage(pn->width, pn->vres ?
  378 			     pn->height : 2*pn->height, NULL, 1);
  379 	(*pn->expander)(pn, drawline);
  380     }
  381     else {
  382 	/* multi-strip tiff */
  383 	pn->extra = NewImage(pn->width, pn->vres ?
  384 			     pn->height : 2*pn->height, NULL, 1);
  385 	tp = Pimage(pn);
  386 	pn->stripnum = 0;
  387 	for (i = 0; i < pn->nstrips; i++) {
  388 	    if (verbose) printf("\texpanding strip #%d\n", i);
  389 	    if (GetPartImage(pn, i) == 0) {
  390 		/* pn may no longer exist */
  391 		FreeImage(tp);
  392 		return 0;
  393 	    }
  394 	}
  395     }
  396     if (pn->orient & TURN_U)
  397 	pn->extra = FlipImage(Pimage(pn));
  398     if (pn->orient & TURN_M)
  399 	pn->extra = MirrorImage(Pimage(pn));
  400     if (pn->orient & TURN_L)
  401 	pn->extra = RotImage(Pimage(pn));
  402     if (verbose) printf("\tmemused = %lu\n", (unsigned long) Memused);
  403     return 1;
  404 }
  405 
  406 static void
  407 DoExtCmd(char *cmd, int shift)
  408 {
  409     char *syscmd;
  410 
  411     if (cmd == NULL) return;
  412     if (shift == 0) {
  413 	syscmd = xmalloc(strlen(cmd) + strlen(thispage->pathname) + 16);
  414 	sprintf(syscmd, "%s -p %d %s", cmd, thispage->pageno,
  415 		thispage->pathname);
  416     }
  417     else {
  418 	struct pagenode *pn = firstpage;
  419 	char *prev = NULL;
  420 	int size = strlen(cmd) + 1;
  421 
  422 	do {
  423 	    if (prev != pn->pathname)
  424 		size += strlen(pn->pathname) + 1;
  425 	    prev = pn->pathname;
  426 	} while ((pn = pn->next) != NULL);
  427 	syscmd = xmalloc(size);
  428 	pn = firstpage;
  429 	prev = NULL;
  430 	strcpy(syscmd, cmd);
  431 	do {
  432 	    if (prev != pn->pathname) {
  433 		strcat(syscmd, " ");
  434 		strcat(syscmd, pn->pathname);
  435 	    }
  436 	    prev = pn->pathname;
  437 	} while ((pn = pn->next) != NULL);
  438     }
  439     system(syscmd);
  440     free(syscmd);
  441 }
  442 
  443 #ifndef _HAVE_USLEEP
  444 static int
  445 usleep(unsigned usecs)
  446 {
  447     struct timeval t;
  448 
  449     t.tv_sec = usecs/10000000;
  450     t.tv_usec = usecs%1000000;
  451     (void) select(1, NULL, NULL, NULL, &t);
  452     return 0;
  453 }
  454 #endif
  455 
  456 #ifndef REAL_ROOT
  457 /* Function Name: GetVRoot
  458  * Description: Gets the root window, even if it's a virtual root
  459  * Arguments: the display and the screen
  460  * Returns: the root window for the client
  461  *
  462  * by David Elliott, taken from the x-faq
  463  */
  464 static Window
  465 GetVRoot(Display *dpy, int scr)
  466 {
  467     Window rootReturn, parentReturn, *children;
  468     unsigned int numChildren;
  469     Window root = RootWindow(dpy, scr);
  470     Atom __SWM_VROOT = None;
  471     int i;
  472 
  473     __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
  474     XQueryTree(dpy, root, &rootReturn, &parentReturn, &children,
  475 	       &numChildren);
  476     for (i = 0; i < numChildren; i++) {
  477         Atom actual_type;
  478         int actual_format;
  479         unsigned long nitems, bytesafter;
  480         Window *newRoot = NULL;
  481 
  482         if (XGetWindowProperty(dpy, children[i], __SWM_VROOT, 0, 1,
  483 			       False, XA_WINDOW, &actual_type,
  484 			       &actual_format, &nitems, &bytesafter,
  485 			       (unsigned char **) &newRoot)
  486 					== Success && newRoot) {
  487 	    if (children) XFree(children);
  488 	    return *newRoot;
  489 	}
  490     }
  491     return root;
  492 }
  493 #endif
  494 
  495 static Atom wm_delete_window;
  496 
  497 /* Area the user would like us to use, derived from -geometry */
  498 static struct {
  499     int v, x, y;
  500     unsigned int w, h;
  501 } Area = {0, 0, 0};
  502 
  503 /* nominal border width */
  504 #define BW 4
  505 
  506 /* Figure out the zoom factor needed to fit the fax on the available display */
  507 static void
  508 SetupDisplay(int argc, char **argv)
  509 {
  510     int Width, Height, i;
  511     XSetWindowAttributes Attr;
  512     XSizeHints size_hints;
  513     Atom wm_protocols;
  514     int faxh = Pimage(thispage)->height;
  515     int faxw = Pimage(thispage)->width;
  516 
  517 #ifdef REAL_ROOT
  518     Root = RootWindow(Disp, Default_Screen);
  519     Width = Area.w = DisplayWidth(Disp, Default_Screen);
  520     Height = Area.h = DisplayHeight(Disp, Default_Screen);
  521 #elif TVTWM_BIGWINDOW
  522     XWindowAttributes RootWA;
  523     Root = GetVRoot(Disp, Default_Screen);
  524     XGetWindowAttributes(Disp, Root, &RootWA);
  525     Width = Area.w = RootWA.width;
  526     Height = Area.h = RootWA.height;
  527 #else
  528     Root = GetVRoot(Disp, Default_Screen);
  529     Width = Area.w = DisplayWidth(Disp, Default_Screen);
  530     Height = Area.h = DisplayHeight(Disp, Default_Screen);
  531 #endif
  532     if (Geometry)
  533 	Area.v = XParseGeometry(Geometry, &Area.x, &Area.y,
  534 				&Area.w, &Area.h);
  535     Area.w = max(64, Area.w);
  536     Area.h = max(64, Area.h);
  537 
  538     if (zfactor == 0)
  539 	for (zfactor = 1;
  540 	     faxw / zfactor > Area.w ||
  541 	     faxh / zfactor > Area.h;
  542 	     zfactor *= 2)
  543 	    ;
  544     Attr.background_pixel = WhitePixel(Disp, Default_Screen);
  545     Attr.border_pixel = BlackPixel(Disp, Default_Screen);
  546 
  547     for (size_hints.width = faxw, i = 1; i < zfactor; i *= 2)
  548 	size_hints.width = (size_hints.width + 1) /2;
  549     for (size_hints.height = faxh, i = 1; i < zfactor; i *= 2)
  550 	size_hints.height = (size_hints.height + 1) /2;
  551 
  552     switch (Area.v & (XValue|XNegative)) {
  553     case XValue:
  554 	size_hints.x = Area.x + BW;
  555 	break;
  556     case XValue|XNegative:
  557 	Area.x = Width + Area.x - 2*BW - Area.w;
  558 	size_hints.x = Area.x + Area.w - size_hints.width;
  559 	break;
  560     default:
  561 	size_hints.x = Area.x + (Area.w - size_hints.width)/2;
  562     }
  563     switch (Area.v & (YValue|YNegative)) {
  564     case YValue:
  565 	size_hints.y = Area.y + BW;
  566 	break;
  567     case YValue|YNegative:
  568 	Area.y = Height + Area.y - 2*BW - Area.h;
  569 	size_hints.y = Area.y + Area.h - size_hints.height;
  570 	break;
  571     default:
  572 	size_hints.y = Area.y + (Area.h - size_hints.height)/2;
  573     }
  574 
  575     size_hints.max_width = size_hints.width;
  576     size_hints.max_height = size_hints.height;
  577     size_hints.flags = PSize|PMaxSize;
  578     if (Area.v & (XValue|YValue)) size_hints.flags |= USPosition;
  579     if (Area.v & (HeightValue|WidthValue)) size_hints.flags |= USSize;
  580 
  581     Win = XCreateWindow(Disp, Root, size_hints.x, size_hints.y,
  582 			size_hints.width, size_hints.height,
  583 			BW, 0, InputOutput, CopyFromParent,
  584 			CWBackPixel|CWBorderPixel, &Attr);
  585 #ifdef PWinGravity
  586 {
  587     XWMHints wm_hints;
  588     XClassHint class_hints;
  589     XTextProperty windowName, iconName;
  590 
  591     if (!XStringListToTextProperty(&thispage->name, 1, &windowName) ||
  592 	!XStringListToTextProperty(&ProgName, 1, &iconName)) {
  593 	fprintf(stderr, "%s: can't make window/icon name\n", ProgName);
  594 	exit(EXIT_FAILURE);
  595     }
  596     wm_hints.initial_state = NormalState;
  597     wm_hints.input = True;
  598     wm_hints.flags = StateHint|InputHint;
  599     class_hints.res_name = ProgName;
  600     class_hints.res_class = "Faxview";
  601     XSetWMProperties(Disp, Win, &windowName, &iconName, argv, argc,
  602 		     &size_hints, &wm_hints, &class_hints);
  603 }
  604 #else
  605     XSetStandardProperties(Disp, Win, thispage->name, ProgName,
  606 			   None, argv, argc, &size_hints);
  607 #endif
  608 
  609     PaintGC = XCreateGC(Disp, Win, 0L, (XGCValues *) NULL);
  610     XSetForeground(Disp, PaintGC, BlackPixel(Disp, Default_Screen));
  611     XSetBackground(Disp, PaintGC, WhitePixel(Disp, Default_Screen));
  612     XSetFunction(Disp, PaintGC, GXcopy);
  613     WorkCursor = XCreateFontCursor(Disp, XC_watch);
  614     ReadyCursor = XCreateFontCursor(Disp, XC_plus);
  615     MoveCursor = XCreateFontCursor(Disp, XC_fleur);
  616     LRCursor = XCreateFontCursor(Disp, XC_sb_h_double_arrow);
  617     UDCursor = XCreateFontCursor(Disp, XC_sb_v_double_arrow);
  618     XSelectInput(Disp, Win, Button2MotionMask | ButtonPressMask |
  619 		 ButtonReleaseMask | ExposureMask | KeyPressMask |
  620 		 SubstructureNotifyMask | OwnerGrabButtonMask |
  621 #ifdef USE_MOTIONHINT
  622 		 PointerMotionHintMask |
  623 #endif
  624 		 StructureNotifyMask);
  625     wm_protocols = XInternAtom(Disp, "WM_PROTOCOLS", False);
  626     wm_delete_window = XInternAtom(Disp, "WM_DELETE_WINDOW", False);
  627     XChangeProperty(Disp, Win, wm_protocols, XA_ATOM, 32,
  628                     PropModeAppend, (unsigned char * ) &wm_delete_window, 1);
  629     XMapRaised(Disp, Win);
  630 }
  631 
  632 #define MAXZOOM	10
  633 
  634 /* After requesting a window size change, we throw away key and button presses
  635    until we get the notification that the size has changed.  If for some
  636    reason the notification does not come, we resume processing as normal after
  637    PATIENCE milliseconds */
  638 #define PATIENCE 10000
  639 
  640 static void
  641 ShowLoop(void)
  642 {
  643     XEvent Event;
  644 
  645     /* centre of image within window */
  646     int x = 0, ox = 0, offx = 0, nx;	/* x, old x, offset x, new x */
  647     int y = 0, oy = 0, offy = 0, ny;	/* y, old y, offset y, new y */
  648 
  649     int oz, Resize = 0, Refresh = 0;	/* old zoom, window size changed,
  650 					   needs updating */
  651     int PaneWidth, PaneHeight;		/* current size of our window */
  652     int AbsX, AbsY;		/* absolute position of centre of window */
  653     int FrameWidth, FrameHeight, FrameX, FrameY;/* size/offset of decoration */
  654     int Oversize = 0;		/* window manager insists on oversize window */
  655     int Reparented = 0;
  656     int i;
  657     XImage *Image, *Images[MAXZOOM];
  658     struct pagenode *viewpage = NULL;	/* page viewed when help requested */
  659     XSizeHints size_hints;
  660     Time Lasttime = 0;		/* time of last accepted key/button press */
  661     int ExpectConfNotify = 1;
  662 
  663     XDefineCursor(Disp, Win, WorkCursor);
  664     XFlush(Disp);
  665     for (oz = 0; oz < MAXZOOM; oz++)
  666 	Images[oz] = NULL;
  667     Image = Images[0] = Pimage(thispage);
  668     for (oz = 0; oz < MAXZOOM && zfactor > (1 << oz); oz++)
  669 	Images[oz+1] = ZoomImage(Images[oz]);
  670     Image = Images[oz];
  671 
  672     /* some reasonable values,
  673        just in case we do not get a configurenotify first */
  674     AbsX = Area.w/2;
  675     AbsY = Area.h/2;
  676     FrameWidth = FrameHeight = FrameX = FrameY = 0;
  677     PaneWidth = Image->width;
  678     PaneHeight = Image->height;
  679 
  680     XDefineCursor(Disp, Win, ReadyCursor);
  681 
  682     for (;;) {
  683 	XNextEvent(Disp, &Event);
  684 	do {
  685 	    switch(Event.type) {
  686 	    case MappingNotify:
  687 		XRefreshKeyboardMapping((XMappingEvent *)(&Event));
  688 		break;
  689 	    case ClientMessage:
  690 		if (Event.xclient.data.l[0] == wm_delete_window) {
  691 		    XCloseDisplay(Disp);
  692 		    exit(EXIT_FAILURE);
  693 		}
  694 		break;
  695 	    case Expose:
  696 	    {
  697 		XExposeEvent *p = (XExposeEvent *) &Event;
  698 		XPutImage(Disp, Win, PaintGC, Image,
  699 			  p->x + x - PaneWidth/2,
  700 			  p->y + y - PaneHeight/2,
  701 			  p->x, p->y,
  702 			  p->width, p->height);
  703 	    }
  704 		break;
  705 	    case ReparentNotify:
  706 	    {
  707 		Window Myroot = Root;
  708 		Window Parent = Event.xreparent.parent;
  709 		Window Frame = Parent;	/* I should be so lucky! */
  710 		Window *Mykids;
  711 		unsigned int Nkids;
  712 		XWindowAttributes MyWA, FrameWA;
  713 
  714 		if (Parent != Root)
  715 		    do {
  716 			Frame = Parent;
  717 			while (!XQueryTree(Disp, Frame, &Myroot,
  718 					   &Parent, &Mykids, &Nkids))
  719 			    release(1);
  720 			if (Mykids) XFree(Mykids);
  721 		    } while (Parent != Root);
  722 		while (!XGetWindowAttributes(Disp, Win, &MyWA))
  723 		    release(1);
  724 /* bang! */	while (!XGetWindowAttributes(Disp, Frame, &FrameWA))
  725 		    release(1);
  726 		/* if area is partly constrained, stay where the WM put you */
  727 		if ((Area.v & (XValue|WidthValue)) == WidthValue) {
  728 		    Area.v |= XValue;
  729 		    Area.x = FrameWA.x;
  730 		}
  731 		if ((Area.v & (YValue|HeightValue)) == HeightValue) {
  732 		    Area.v |= YValue;
  733 		    Area.y = FrameWA.y;
  734 		}
  735 		XTranslateCoordinates(Disp, Win, Frame, 0, 0,
  736 				      &FrameX, &FrameY, &Parent);
  737 		FrameWidth = FrameWA.width - MyWA.width;
  738 		FrameHeight = FrameWA.height - MyWA.height;
  739 		Reparented = ExpectConfNotify = 1;
  740 	    }
  741 		break;
  742 	    case ConfigureNotify:
  743 	    {
  744 		XConfigureEvent *p = (XConfigureEvent *) &Event;
  745 		int NewX = AbsX;
  746 		int NewY = AbsY;
  747 #ifdef REAL_ROOT
  748 		if (p->send_event || !Reparented) {
  749 		    NewX = p->x + p->width/2;
  750 		    NewY = p->y + p->height/2;
  751 		}
  752 #else
  753 		/* support tvtwm */
  754 		if (!Reparented) {
  755 		    NewX = p->x + p->border_width + p->width/2;
  756 		    NewY = p->y + p->border_width + p->height/2;
  757 		}
  758 		else if (p->send_event) {
  759 		    /* the event info is viewport-relative, we need absolute */
  760 		    Window w;
  761 		    XTranslateCoordinates(Disp, Win, Root, 0, 0,
  762 					  &NewX, &NewY, &w);
  763 		    NewX += p->width/2;
  764 		    NewY += p->height/2;
  765 		}
  766 #endif
  767 		if (!ExpectConfNotify) {
  768 		    /* user intervention */
  769 		    if (PaneWidth != p->width)
  770 			Area.w = p->width + FrameWidth;
  771 		    if (PaneHeight != p->height)
  772 			Area.h = p->height + FrameHeight;
  773 		    if (NewX != AbsX || NewY != AbsY) {
  774 			Area.x = NewX - p->width/2 - FrameX;
  775 			Area.y = NewY - p->height/2 - FrameY;
  776 		    }
  777 		}
  778 		AbsX = NewX; AbsY = NewY;
  779 		PaneWidth = p->width;
  780 		PaneHeight = p->height;
  781 		Oversize = PaneWidth > Image->width ||
  782 		    PaneHeight > Image->height;
  783 		ExpectConfNotify = 0;
  784 	    }
  785 		break;
  786 	    case KeyPress:
  787 		if (ExpectConfNotify &&
  788 		    (Event.xkey.time < (Lasttime + PATIENCE)))
  789 		    break;
  790 		Lasttime = Event.xkey.time;
  791 		ExpectConfNotify = 0;
  792 		switch(XKeycodeToKeysym(Disp, Event.xkey.keycode, 0)) {
  793 		case XK_Help:
  794 		case XK_h:
  795 		    if (helppage == NULL) {
  796 			if (!notetiff(HELPFILE))
  797 			    goto nopage;
  798 			else {
  799 			    helppage = lastpage;
  800 			    lastpage = helppage->prev;
  801 			    lastpage->next = helppage->prev = NULL;
  802 			}
  803 		    }
  804 		    viewpage = thispage;
  805 		    thispage = helppage;
  806 		    goto newpage;
  807 		    break;
  808 		case XK_m:
  809 		    XDefineCursor(Disp, Win, WorkCursor);
  810 		    XFlush(Disp);
  811 		    thispage->extra = Images[0] = MirrorImage(Images[0]);
  812 		    thispage->orient ^= TURN_M;
  813 		    for (i = 1; Images[i]; i++) {
  814 			FreeImage(Images[i]);
  815 			Images[i] = ZoomImage(Images[i-1]);
  816 		    }
  817 		    Image = Images[oz];
  818 		    if (Event.xkey.state & ShiftMask)
  819 			TurnFollowing(TURN_M, thispage->next);
  820 		    XPutImage(Disp, Win, PaintGC, Image,
  821 			      x-PaneWidth/2, y-PaneHeight/2,
  822 			      0, 0, PaneWidth, PaneHeight);
  823 		    XDefineCursor(Disp, Win, ReadyCursor);
  824 		    break;
  825 		case XK_z:
  826 		    if (Event.xkey.state & ShiftMask)
  827 			goto Zoomout;
  828 		    else
  829 			goto Zoomin;
  830 		case XK_Print:
  831 		    DoExtCmd(PrintCmd, Event.xkey.state & ShiftMask);
  832 		    break;
  833 		case XK_e:
  834 		    DoExtCmd(EditCmd, Event.xkey.state & ShiftMask);
  835 		    break;
  836 		case XK_Up:
  837 		    y -= PaneHeight / 2;
  838 		    break;
  839 		case XK_Down:
  840 		    y += PaneHeight / 2;
  841 		    break;
  842 		case XK_Left:
  843 		    x -= PaneWidth / 2;
  844 		    break;
  845 		case XK_Right:
  846 		    x += PaneWidth / 2;
  847 		    break;
  848 		case XK_Home:
  849 		case XK_R7:		/* sun4 keyboard */
  850 		    if (Event.xkey.state & ShiftMask) {
  851 			thispage = firstpage;
  852 			goto newpage;
  853 		    }
  854 		    x = 0;
  855 		    y = 0;
  856 		    break;
  857 		case XK_End:
  858 		case XK_R13:
  859 		    if (Event.xkey.state & ShiftMask) {
  860 			thispage = lastpage;
  861 			goto newpage;
  862 		    }
  863 		    x = Image->width;
  864 		    y = Image->height;
  865 		    break;
  866 		case XK_l:
  867 		    XDefineCursor(Disp, Win, WorkCursor);
  868 		    XFlush(Disp);
  869 		    thispage->extra = Image = RotImage(Images[0]);
  870 		    thispage->orient ^= TURN_L;
  871 		    for (i = 1; Images[i]; i++) {
  872 			FreeImage(Images[i]);
  873 			Images[i] = NULL;
  874 		    }
  875 		    Images[0] = Image;
  876 		    for (i = 1; i <= oz; i++)
  877 			Images[i] = ZoomImage(Images[i-1]);
  878 		    Image = Images[oz];
  879 		    if (Event.xkey.state & ShiftMask)
  880 			TurnFollowing(TURN_L, thispage->next);
  881 		{ int t = x; x = y; y = t; }
  882 		    Refresh = Resize = 1;
  883 		    XDefineCursor(Disp, Win, ReadyCursor);
  884 		    break;
  885 		case XK_p:
  886 		case XK_minus:
  887 		case XK_Prior:
  888 		case XK_R9:
  889 		case XK_BackSpace:
  890 		  prevpage:
  891 		    if (thispage->prev == NULL)
  892 			goto nopage;
  893 		    thispage = thispage->prev;
  894 		    goto newpage;
  895 		case XK_n:
  896 		case XK_plus:
  897 		case XK_space:
  898 		case XK_Next:
  899 		case XK_R15:
  900 		  nextpage:
  901 		    if (thispage->next == NULL) {
  902 		    nopage:
  903 			if (abell) {
  904 			    putchar('\a');
  905 			    fflush(stdout);
  906 			}
  907 			if (vbell) {
  908 			    XAddPixel(Image, 1);
  909 			    XPutImage(Disp, Win, PaintGC, Image,
  910 				      x-PaneWidth/2, y-PaneHeight/2,
  911 				      0, 0, PaneWidth, PaneHeight);
  912 			    XSync(Disp, 0);
  913 			    (void) usleep(200000);
  914 			    XAddPixel(Image, 1);
  915 			    XPutImage(Disp, Win, PaintGC, Image,
  916 				      x-PaneWidth/2, y-PaneHeight/2,
  917 				      0, 0, PaneWidth, PaneHeight);
  918 			}
  919 			break;
  920 		    }
  921 		    thispage = thispage->next;
  922 		newpage:
  923 		    XDefineCursor(Disp, Win, WorkCursor);
  924 		    XFlush(Disp);
  925 		    /* if old image was not resized by the user, fit new one */
  926 		    Resize = ((PaneWidth == Image->width ||
  927 			       PaneWidth == Area.w - FrameWidth) &&
  928 			      (PaneHeight == Image->height ||
  929 			       PaneHeight == Area.h - FrameHeight));
  930 		    for (i = 1; Images[i]; i++) {
  931 			FreeImage(Images[i]);
  932 			Images[i] = NULL;
  933 		    }
  934 		    if (Pimage(thispage) == NULL)
  935 			while (!GetImage(thispage))
  936 			    /* try again */;
  937 		    Images[0] = Pimage(thispage);
  938 		    XStoreName(Disp, Win, thispage->name);
  939 		    for (i = 1; i <= oz; i++)
  940 			Images[i] = ZoomImage(Images[i-1]);
  941 		    Image = Images[oz];
  942 		    Refresh = 1;
  943 		    XDefineCursor(Disp, Win, ReadyCursor);
  944 		    break;
  945 		case XK_u:
  946 		    XDefineCursor(Disp, Win, WorkCursor);
  947 		    XFlush(Disp);
  948 		    thispage->extra = Images[0] = FlipImage(Images[0]);
  949 		    thispage->orient ^= TURN_U;
  950 		    for (i = 1; Images[i]; i++) {
  951 			FreeImage(Images[i]);
  952 			Images[i] = ZoomImage(Images[i-1]);
  953 		    }
  954 		    Image = Images[oz];
  955 		    if (Event.xkey.state & ShiftMask)
  956 			TurnFollowing(TURN_U, thispage->next);
  957 		    XPutImage(Disp, Win, PaintGC, Image,
  958 			      x-PaneWidth/2, y-PaneHeight/2,
  959 			      0, 0, PaneWidth, PaneHeight);
  960 		    XDefineCursor(Disp, Win, ReadyCursor);
  961 		    break;
  962 		case XK_q:
  963 		    if (viewpage) {
  964 			thispage = viewpage;
  965 			viewpage = NULL;
  966 			goto newpage;
  967 		    }
  968 		    XCloseDisplay(Disp);
  969 #ifdef xmalloc
  970 		    malloc_shutdown();
  971 #endif
  972 		    exit((Event.xkey.state & ShiftMask) ? EXIT_FAILURE : 0);
  973 		}
  974 		break;
  975 	    case ButtonPress:
  976 		if (ExpectConfNotify &&
  977 		    (Event.xbutton.time < (Lasttime + PATIENCE)))
  978 		    break;
  979 		Lasttime = Event.xbutton.time;
  980 		ExpectConfNotify = 0;
  981 		switch (Event.xbutton.button) {
  982 		case Button1:
  983 		Zoomout:
  984 		    if (oz > 0) {
  985 			Image = Images[--oz];
  986 			zfactor >>= 1;
  987 			x *= 2;
  988 			y *= 2;
  989 			Resize = Refresh = 1;
  990 		    }
  991 		    break;
  992 		case Button2:
  993 		    switch (((Image->width > PaneWidth)<<1) |
  994 			    (Image->height > PaneHeight)) {
  995 		    case 0:
  996 			break;
  997 		    case 1:
  998 			XDefineCursor(Disp, Win, UDCursor);
  999 			break;
 1000 		    case 2:
 1001 			XDefineCursor(Disp, Win, LRCursor);
 1002 			break;
 1003 		    case 3:
 1004 			XDefineCursor(Disp, Win, MoveCursor);
 1005 		    }
 1006 		    XFlush(Disp);
 1007 		    offx = Event.xbutton.x;
 1008 		    offy = Event.xbutton.y;
 1009 		    break;
 1010 		case Button3:
 1011 		Zoomin:
 1012 		    if (oz < MAXZOOM && Image->width >= 64 && zfactor < 32) {
 1013 			Image = Images[++oz];
 1014 			zfactor <<= 1;
 1015 			x /= 2;
 1016 			y /= 2;
 1017 			Resize = Refresh = 1;
 1018 		    }
 1019 		    break;
 1020 		case Button4:
 1021 		    if ((Event.xkey.state & ShiftMask) ^ Wheelinv)
 1022 			goto prevpage;
 1023 		    y -= PaneHeight / 30;
 1024 		    break;
 1025 		case Button5:
 1026 		    if ((Event.xkey.state & ShiftMask) ^ Wheelinv)
 1027 			goto nextpage;
 1028 		    y += PaneHeight / 30;
 1029 		    break;
 1030 		}
 1031 		if (Image == NULL) {
 1032 		    for (i = oz; i && (Images[i] == NULL); i--)
 1033 			;
 1034 		    for (; i != oz; i++)
 1035 			Images[i+1] = ZoomImage(Images[i]);
 1036 		    Image = Images[oz];
 1037 		}
 1038 		break;
 1039 	    case MotionNotify:
 1040 #ifdef USE_MOTIONHINT
 1041 	    {
 1042 		unsigned int Junk;
 1043 		Window JunkW;
 1044 		XQueryPointer(Disp, Event.xmotion.window, &JunkW, &JunkW,
 1045 			      &Junk, &Junk, &nx, &ny, &Junk);
 1046 	    }
 1047 #else
 1048 		do {
 1049 		    nx = Event.xmotion.x;
 1050 		    ny = Event.xmotion.y;
 1051 		} while (XCheckTypedEvent(Disp, MotionNotify, &Event));
 1052 #endif
 1053 		x += offx - nx;
 1054 		y += offy - ny;
 1055 		offx = nx;
 1056 		offy = ny;
 1057 		break;
 1058 	    case ButtonRelease:
 1059 		if (Event.xbutton.button == Button2) {
 1060 		    XDefineCursor(Disp, Win, ReadyCursor);
 1061 		    XFlush(Disp);
 1062 		}
 1063 	    }
 1064 	} while (XCheckWindowEvent(Disp, Win,
 1065 				   KeyPressMask|ButtonPressMask, &Event));
 1066 
 1067 	/* if someone thinks we should resize the window and it is not
 1068 	   already the right size, or if the window is too big and we
 1069 	   have not already tried to make it smaller ... */
 1070 	if ((Resize && !(Image->width == PaneWidth &&
 1071 			 Image->height == PaneHeight)) ||
 1072 	    (!Oversize && (Image->width < PaneWidth ||
 1073 			   Image->height < PaneHeight))) {
 1074 	    int PosX = AbsX - PaneWidth/2 - FrameX;
 1075 	    int PosY = AbsY - PaneHeight/2 - FrameY;
 1076 	    XWindowChanges New;
 1077 	    int ChangeMask = 0;
 1078 
 1079 	    New.width = min(Area.w - FrameWidth, Image->width);
 1080 	    New.height = min(Area.h - FrameHeight, Image->height);
 1081 	    /* expect an expose if size must change */
 1082 	    Refresh &= (New.width == PaneWidth && New.height == PaneHeight);
 1083 	    New.x = max(Area.x, AbsX-New.width/2-FrameX);
 1084 	    New.x = min(New.x, Area.w-(New.width+FrameWidth)+Area.x);
 1085 	    New.y = max(Area.y, AbsY-New.height/2-FrameY);
 1086 	    New.y = min(New.y, Area.h-(New.height+FrameHeight)+Area.y);
 1087 
 1088 	    /* mwm takes max_size very seriously! */
 1089 	    size_hints.flags = 0;
 1090 	    XSetNormalHints(Disp, Win, &size_hints);
 1091 
 1092 	    size_hints.flags = PMaxSize;
 1093 	    size_hints.x = PosX;
 1094 	    size_hints.y = PosY;
 1095 	    size_hints.width = PaneWidth;
 1096 	    size_hints.height = PaneHeight;
 1097 	    /* only move a coordinate if the ideal new value is different
 1098 	       from the current value and either the other dimension has
 1099 	       changed or the current value is out of area */
 1100 	    if (PosX != New.x &&
 1101 		(New.height != PaneHeight || PosX < Area.x ||
 1102 		 PosX > Area.w-New.width-FrameWidth-Area.x)) {
 1103 		ChangeMask |= CWX;
 1104 		size_hints.x = New.x;
 1105 		size_hints.flags |= PPosition;
 1106 	    }
 1107 	    if (PosY != New.y &&
 1108 		(New.width != PaneWidth || PosY < Area.y ||
 1109 		 PosY > Area.h-New.height-FrameHeight-Area.y)) {
 1110 		ChangeMask |= CWY;
 1111 		size_hints.y = New.y;
 1112 		size_hints.flags |= PPosition;
 1113 	    }
 1114 	    if (New.width != PaneWidth) {
 1115 		ChangeMask |= CWWidth;
 1116 		size_hints.width = New.width;
 1117 		size_hints.flags |= PSize;
 1118 		ExpectConfNotify = Reparented;
 1119 	    }
 1120 	    if (New.height != PaneHeight) {
 1121 		ChangeMask |= CWHeight;
 1122 		size_hints.height = New.height;
 1123 		size_hints.flags |= PSize;
 1124 		ExpectConfNotify = Reparented;
 1125 	    }
 1126 	    New.border_width = 1;	/* ICCCM 4.1.5 */
 1127 	    ChangeMask |= CWBorderWidth;
 1128 	    XConfigureWindow(Disp, Win, ChangeMask, &New);
 1129 	    size_hints.max_width = Image->width;
 1130 	    size_hints.max_height = Image->height;
 1131 	    XSetNormalHints(Disp, Win, &size_hints);
 1132 	}
 1133 	x = max(x, PaneWidth/2);
 1134 	x = min(x, Image->width-PaneWidth/2);
 1135 	y = max(y, PaneHeight/2);
 1136 	y = min(y, Image->height-PaneHeight/2);
 1137 	if (x != ox || y != oy || Refresh)
 1138 	    XPutImage(Disp, Win, PaintGC, Image,
 1139 		      x-PaneWidth/2, y-PaneHeight/2,
 1140 		      0, 0, PaneWidth, PaneHeight);
 1141 	ox = x;
 1142 	oy = y;
 1143 	Resize = Refresh = 0;
 1144     }
 1145 }
 1146 
 1147 /* run this region through perl to generate the zoom table:
 1148 $lim = 1;
 1149 @c = ("0", "1", "1", "2");
 1150 print "static unsigned char Z[] = {\n";
 1151 for ($i = 0; $i < 16; $i++) {
 1152     for ($j = 0; $j < 16; $j++) {
 1153 	$b1 = ($c[$j&3]+$c[$i&3]) > $lim;
 1154 	$b2 = ($c[($j>>2)&3]+$c[($i>>2)&3]) > $lim;
 1155 	printf " %X,", ($b2 << 1) | $b1;
 1156     }
 1157     print "\n";
 1158 }
 1159 print "};\n";
 1160 */
 1161 static unsigned char Z[] = {
 1162  0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 3,
 1163  0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3,
 1164  0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 3, 3, 3,
 1165  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3,
 1166  0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3,
 1167  0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3,
 1168  0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3,
 1169  1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
 1170  0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3,
 1171  0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3,
 1172  0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3,
 1173  1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
 1174  2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3, 2, 2, 2, 3,
 1175  2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3,
 1176  2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3,
 1177  3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
 1178 };
 1179 
 1180 #define nib(n,w)	(((w)>>((n)<<2))&15)
 1181 #define zak(a,b)	Z[(a<<4)|b]
 1182 
 1183 /* 2 -> 1 zoom, 4 pixels -> 1 pixel
 1184    if #pixels <= $lim (see above), new pixel is white,
 1185    else black.
 1186 */
 1187 static XImage *
 1188 ZoomImage(XImage *Big)
 1189 {
 1190     XImage *Small;
 1191     int w, h;
 1192     int i, j;
 1193 
 1194     XDefineCursor(Disp, Win, WorkCursor);
 1195     XFlush(Disp);
 1196     w = (Big->width+1) / 2;
 1197     h = (Big->height+1) / 2;
 1198     Small = NewImage(w, h, NULL, Big->bitmap_bit_order);
 1199     Small->xoffset = (Big->xoffset+1)/2;
 1200     for (i = 0; i < Big->height; i += 2) {
 1201 	t32bits *pb0 = (t32bits *) (Big->data + i * Big->bytes_per_line);
 1202 	t32bits *pb1 = pb0 + ((i == Big->height-1) ? 0 : Big->bytes_per_line/4);
 1203 	t32bits *ps = (t32bits *) (Small->data + i * Small->bytes_per_line / 2);
 1204 	for (j = 0; j < Big->bytes_per_line/8; j++) {
 1205 	    t32bits r1, r2;
 1206 	    t32bits t0 = *pb0++;
 1207 	    t32bits t1 = *pb1++;
 1208 	    r1 = (zak(nib(7,t0),nib(7,t1))<<14) |
 1209 		 (zak(nib(6,t0),nib(6,t1))<<12) |
 1210 		 (zak(nib(5,t0),nib(5,t1))<<10) |
 1211 		 (zak(nib(4,t0),nib(4,t1))<<8) |
 1212 		 (zak(nib(3,t0),nib(3,t1))<<6) |
 1213 		 (zak(nib(2,t0),nib(2,t1))<<4) |
 1214 		 (zak(nib(1,t0),nib(1,t1))<<2) |
 1215 		 (zak(nib(0,t0),nib(0,t1)));
 1216 	    t0 = *pb0++;
 1217 	    t1 = *pb1++;
 1218 	    r2 = (zak(nib(7,t0),nib(7,t1))<<14) |
 1219 		 (zak(nib(6,t0),nib(6,t1))<<12) |
 1220 		 (zak(nib(5,t0),nib(5,t1))<<10) |
 1221 		 (zak(nib(4,t0),nib(4,t1))<<8) |
 1222 		 (zak(nib(3,t0),nib(3,t1))<<6) |
 1223 		 (zak(nib(2,t0),nib(2,t1))<<4) |
 1224 		 (zak(nib(1,t0),nib(1,t1))<<2) |
 1225 		 (zak(nib(0,t0),nib(0,t1)));
 1226 	    *ps++ = (Big->bitmap_bit_order) ?
 1227 		(r1<<16)|r2 : (r2<<16)|r1;
 1228 	}
 1229 	for ( ; j < Small->bytes_per_line/4; j++) {
 1230 	    t32bits r1;
 1231 	    t32bits t0 = *pb0++;
 1232 	    t32bits t1 = *pb1++;
 1233 	    r1 = (zak(nib(7,t0),nib(7,t1))<<14) |
 1234 		 (zak(nib(6,t0),nib(6,t1))<<12) |
 1235 		 (zak(nib(5,t0),nib(5,t1))<<10) |
 1236 		 (zak(nib(4,t0),nib(4,t1))<<8) |
 1237 		 (zak(nib(3,t0),nib(3,t1))<<6) |
 1238 		 (zak(nib(2,t0),nib(2,t1))<<4) |
 1239 		 (zak(nib(1,t0),nib(1,t1))<<2) |
 1240 		 (zak(nib(0,t0),nib(0,t1)));
 1241 	    *ps++ = (Big->bitmap_bit_order) ?
 1242 		(r1<<16) : r1;
 1243 	}
 1244     }
 1245     XDefineCursor(Disp, Win, ReadyCursor);
 1246     return Small;
 1247 }
 1248 
 1249 static XImage *
 1250 FlipImage(XImage *Image)
 1251 {
 1252     XImage *New = NewImage(Image->width, Image->height,
 1253 			   Image->data, !Image->bitmap_bit_order);
 1254     t32bits *p1 = (t32bits *) Image->data;
 1255     t32bits *p2 = (t32bits *) (Image->data + Image->height *
 1256 			     Image->bytes_per_line - 4);
 1257 
 1258     /* the first shall be last ... */
 1259     while (p1 < p2) {
 1260 	t32bits t = *p1;
 1261 	*p1++ = *p2;
 1262 	*p2-- = t;
 1263     }
 1264 
 1265     /* let Xlib twiddle the bits */
 1266     New->xoffset = 32 - (Image->width & 31) - Image->xoffset;
 1267     New->xoffset &= 31;
 1268 
 1269     Image->data = NULL;
 1270     FreeImage(Image);
 1271     return New;
 1272 }
 1273 
 1274 static XImage *
 1275 MirrorImage(XImage *Image)
 1276 {
 1277     int i;
 1278     XImage *New = NewImage(Image->width, Image->height,
 1279 			   Image->data, !Image->bitmap_bit_order);
 1280 
 1281     /* reverse order of 32-bit words in each line */
 1282     for (i = 0; i < Image->height; i++) {
 1283 	t32bits *p1 = (t32bits *) (Image->data + Image->bytes_per_line * i);
 1284 	t32bits *p2 = p1 + Image->bytes_per_line/4 - 1;
 1285 	while (p1 < p2) {
 1286 	    t32bits t = *p1;
 1287 	    *p1++ = *p2;
 1288 	    *p2-- = t;
 1289 	}
 1290     }
 1291 
 1292     /* let Xlib twiddle the bits */
 1293     New->xoffset = 32 - (Image->width & 31) - Image->xoffset;
 1294     New->xoffset &= 31;
 1295 
 1296     Image->data = NULL;
 1297     FreeImage(Image);
 1298     return New;
 1299 }
 1300 
 1301 static XImage *
 1302 RotImage(XImage *Image)
 1303 {
 1304     XImage *New;
 1305     int w = Image->height;
 1306     int h = Image->width;
 1307     int i, j, k, shift;
 1308     int order = Image->bitmap_bit_order;
 1309     int offs = h+Image->xoffset-1;
 1310 
 1311     New = NewImage(w, h, NULL, 1);
 1312 
 1313     k = (32 - Image->xoffset) & 3;
 1314     for (i = h - 1; i && k; i--, k--) {
 1315 	t32bits *sp = (t32bits *) Image->data + (offs-i)/32;
 1316 	t32bits *dp = (t32bits *) (New->data+i*New->bytes_per_line);
 1317 	t32bits d0;
 1318 	shift = (offs-i)&31;
 1319 	if (order) shift = 31-shift;
 1320 	for (j = 0; j < w; j++) {
 1321 	    t32bits t = *sp;
 1322 	    sp += Image->bytes_per_line/4;
 1323 	    d0 |= ((t >> shift) & 1);
 1324 	    if ((j & 31) == 31)
 1325 		*dp++ = d0;
 1326 	    d0 <<= 1;;
 1327 	}
 1328 	if (j & 31)
 1329 	    *dp++ = d0<<(31-j);
 1330     }
 1331     for ( ; i >= 3; i-=4) {
 1332 	t32bits *sp = (t32bits *) Image->data + (offs-i)/32;
 1333 	t32bits *dp0 = (t32bits *) (New->data+i*New->bytes_per_line);
 1334 	t32bits *dp1 = dp0 - New->bytes_per_line/4;
 1335 	t32bits *dp2 = dp1 - New->bytes_per_line/4;
 1336 	t32bits *dp3 = dp2 - New->bytes_per_line/4;
 1337 	t32bits d0, d1, d2, d3;
 1338 	shift = (offs-i)&31;
 1339 	if (order) shift = 28-shift;
 1340 	for (j = 0; j < w; j++) {
 1341 	    t32bits t = *sp >> shift;
 1342 	    sp += Image->bytes_per_line/4;
 1343 	    d0 |= t & 1; t >>= 1;
 1344 	    d1 |= t & 1; t >>= 1;
 1345 	    d2 |= t & 1; t >>= 1;
 1346 	    d3 |= t & 1; t >>= 1;
 1347 	    if ((j & 31) == 31) {
 1348 		if (order) {
 1349 		    *dp0++ = d3;
 1350 		    *dp1++ = d2;
 1351 		    *dp2++ = d1;
 1352 		    *dp3++ = d0;
 1353 		}
 1354 		else {
 1355 		    *dp0++ = d0;
 1356 		    *dp1++ = d1;
 1357 		    *dp2++ = d2;
 1358 		    *dp3++ = d3;
 1359 		}
 1360 	    }
 1361 	    d0 <<= 1; d1 <<= 1; d2 <<= 1; d3 <<= 1;
 1362 	}
 1363 	if (j & 31) {
 1364 	    if (order) {
 1365 		*dp0++ = d3<<(31-j);
 1366 		*dp1++ = d2<<(31-j);
 1367 		*dp2++ = d1<<(31-j);
 1368 		*dp3++ = d0<<(31-j);
 1369 	    }
 1370 	    else {
 1371 		*dp0++ = d0<<(31-j);
 1372 		*dp1++ = d1<<(31-j);
 1373 		*dp2++ = d2<<(31-j);
 1374 		*dp3++ = d3<<(31-j);
 1375 	    }
 1376 	}
 1377     }
 1378     for (; i >= 0; i--) {
 1379 	t32bits *sp = (t32bits *) Image->data + (offs-i)/32;
 1380 	t32bits *dp = (t32bits *) (New->data+i*New->bytes_per_line);
 1381 	t32bits d0;
 1382 	shift = (offs-i)&31;
 1383 	if (order) shift = 31-shift;
 1384 	for (j = 0; j < w; j++) {
 1385 	    t32bits t = *sp;
 1386 	    sp += Image->bytes_per_line/4;
 1387 	    d0 |= ((t >> shift) & 1);
 1388 	    if ((j & 31) == 31)
 1389 		*dp++ = d0;
 1390 	    d0 <<= 1;;
 1391 	}
 1392 	if (j & 31)
 1393 	    *dp++ = d0<<(31-j);
 1394     }
 1395     FreeImage(Image);
 1396     return New;
 1397 }
 1398 
 1399 /* release some non-essential memory or abort */
 1400 #define Try(n)								\
 1401     if (n && n != thispage && n->extra) {				\
 1402 	FreeImage(n->extra);						\
 1403 	n->extra = NULL;						\
 1404 	return 1;							\
 1405     }
 1406 
 1407 static int
 1408 release(int quit)
 1409 {
 1410     struct pagenode *pn;
 1411 
 1412     if (thispage) {
 1413 	/* first consider "uninteresting" pages */
 1414 	for (pn = firstpage->next; pn; pn = pn->next)
 1415 	    if (pn->extra && pn != thispage && pn != thispage->prev &&
 1416 		pn != thispage->next && pn != lastpage) {
 1417 		FreeImage(Pimage(pn));
 1418 		pn->extra = NULL;
 1419 		return 1;
 1420 	    }
 1421 	Try(lastpage);
 1422 	Try(firstpage);
 1423 	Try(thispage->prev);
 1424 	Try(thispage->next);
 1425     }
 1426     if (!quit)
 1427 	return 0;
 1428     fprintf(stderr, "%s(release): insufficient memory\n", ProgName);
 1429     exit(EXIT_FAILURE);
 1430 }
 1431 
 1432 static XImage *
 1433 NewImage(int w, int h, char *data, int bit_order)
 1434 {
 1435     XImage *new;
 1436     /* This idea is taken from xwud/xpr.  Use a fake display with the
 1437        desired bit/byte order to get the image routines initialised
 1438        correctly */
 1439     Display fake;
 1440 
 1441     fake = *Disp;
 1442     if (data == NULL)
 1443 	data = xmalloc(((w + 31) & ~31) * h / 8);
 1444     fake.byte_order = ByteOrder;
 1445     fake.bitmap_unit = 32;
 1446     fake.bitmap_bit_order = bit_order;
 1447 
 1448     while ((new = XCreateImage(&fake, DefaultVisual(Disp, Default_Screen),
 1449 			       1, XYBitmap, 0, data, w, h, 32, 0)) == NULL)
 1450 	(void) release(1);
 1451     Memused += new->bytes_per_line * new->height;
 1452     return new;
 1453 }
 1454 
 1455 static void
 1456 FreeImage(XImage *Image)
 1457 {
 1458     if (Image->data)
 1459 	Memused -= Image->bytes_per_line * Image->height;
 1460     XDestroyImage(Image);
 1461 }
 1462 
 1463 #ifndef xmalloc
 1464 char *
 1465 xmalloc(unsigned int size)
 1466 {
 1467     char *p;
 1468 
 1469     while (Memused + size > Memlimit && release(0))
 1470 	;
 1471     while ((p = malloc(size)) == NULL)
 1472 	(void) release(1);
 1473     return p;
 1474 }
 1475 #endif