"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