wdvi

network DVI viewer
Log | Files | Refs

xdvi.c (26096B)


      1 /*========================================================================*\
      2 
      3 Copyright (c) 1990-2013  Paul Vojta
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining a copy
      6 of this software and associated documentation files (the "Software"), to
      7 deal in the Software without restriction, including without limitation the
      8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      9 sell copies of the Software, and to permit persons to whom the Software is
     10 furnished to do so, subject to the following conditions:
     11 
     12 The above copyright notice and this permission notice shall be included in
     13 all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     18 IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF OR CONTRIBUTOR TO
     19 THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     22 IN THE SOFTWARE.
     23 
     24 NOTE:
     25 	xdvi is based on prior work as noted in the modification history, below.
     26 
     27 \*========================================================================*/
     28 
     29 /*
     30  * DVI previewer for X.
     31  *
     32  * Eric Cooper, CMU, September 1985.
     33  *
     34  * Code derived from dvi-imagen.c.
     35  *
     36  * Modification history:
     37  * 1/1986	Modified for X.10	--Bob Scheifler, MIT LCS.
     38  * 7/1988	Modified for X.11	--Mark Eichin, MIT
     39  * 12/1988	Added 'R' option, toolkit, magnifying glass
     40  *					--Paul Vojta, UC Berkeley.
     41  * 2/1989	Added tpic support	--Jeffrey Lee, U of Toronto
     42  * 4/1989	Modified for System V	--Donald Richardson, Clarkson Univ.
     43  * 3/1990	Added VMS support	--Scott Allendorf, U of Iowa
     44  * 7/1990	Added reflection mode	--Michael Pak, Hebrew U of Jerusalem
     45  * 1/1992	Added greyscale code	--Till Brychcy, Techn. Univ. Muenchen
     46  *					  and Lee Hetherington, MIT
     47  * 4/1994	Added DPS support, bounding box
     48  *					--Ricardo Telichevesky
     49  *					  and Luis Miguel Silveira, MIT RLE.
     50  * 1/2001	Added source specials	--including ideas from Stefan Ulrich,
     51  *					  U Munich
     52  *
     53  *	Compilation options:
     54  *	WORDS_BIGENDIAN	store bitmaps internally with most significant bit first
     55  *	BMTYPE	store bitmaps in unsigned BMTYPE
     56  *	BMBYTES	sizeof(unsigned BMTYPE)
     57  *	ALTFONT	default for -altfont option
     58  *	SHRINK	default for -s option (shrink factor)
     59  *	MFMODE	default for -mfmode option
     60  *	A4	use European size paper, and change default dimension to cm
     61  */
     62 
     63 #include <ctype.h>	/* isdigit() */
     64 #include <err.h>
     65 #include <stdarg.h>
     66 #include <stdlib.h>	/* exit(), atoi() */
     67 
     68 #include <X11/cursorfont.h>	/* XC_cross */
     69 #include <X11/Shell.h>		/* XtNiconX */
     70 #include <X11/IntrinsicI.h>
     71 #include <X11/IntrinsicP.h>
     72 #include <X11/Xaw/Viewport.h>
     73 #include <X11/Xaw/AsciiText.h>
     74 
     75 #include "default.h"	/* default_dvi, default_dvi_len */
     76 #include "dvi-init.h"	/* init_dvi_file(), init_page() */
     77 #include "dvi-draw.h"	/* prescan() */
     78 #include "events.h"	/* compile_action(), ... */
     79 #include "font-open.h"	/* init_font_open() */
     80 #include "util.h"	/* xmalloc() */
     81 #include "version.h"
     82 #include "xdvi.h"
     83 #include "xdvi.icon"
     84 
     85 #define	ALTFONT		"cmr10"
     86 #define	SHRINK		3
     87 #define	BDPI		600
     88 #define	MFMODE		"cx:600"
     89 
     90 #if A4
     91 #define	DEFAULT_PAPER		"a4"
     92 #else
     93 #define	DEFAULT_PAPER		"letter"
     94 #endif
     95 
     96 
     97 struct WindowRec mane	= {(Window) 0, 1, 0, 0, 0, 0, MAXDIM, 0, MAXDIM, 0};
     98 struct WindowRec alt	= {(Window) 0, 1, 0, 0, 0, 0, MAXDIM, 0, MAXDIM, 0};
     99 /*	currwin is temporary storage except for within redraw() */
    100 struct WindowRec currwin= {(Window) 0, 1, 0, 0, 0, 0, MAXDIM, 0, MAXDIM, 0};
    101 
    102 /*
    103  *	Data for options processing
    104  */
    105 
    106 static	XrmOptionDescRec	options[] = {
    107 	{"-s",		".shrinkFactor", XrmoptionSepArg,	NULL},
    108 	{"-nocolor",	".color",	XrmoptionNoArg,		"off"},
    109 	{"+nocolor",	".color",	XrmoptionNoArg,		"on"},
    110 	{"-gamma",	".gamma",	XrmoptionSepArg,	NULL},
    111 	{"-p",		".pixelsPerInch", XrmoptionSepArg,	NULL},
    112 	{"-altfont",	".altFont",	XrmoptionSepArg,	NULL},
    113 	{"-mfmode",	".mfMode",	XrmoptionSepArg,	NULL},
    114 	{"-l",		".listFonts",	XrmoptionNoArg,		"on"},
    115 	{"+l",		".listFonts",	XrmoptionNoArg,		"off"},
    116 	{"-fg",		".foreground",	XrmoptionSepArg,	NULL},
    117 	{"-foreground",	".foreground",	XrmoptionSepArg,	NULL},
    118 	{"-bg",		".background",	XrmoptionSepArg,	NULL},
    119 	{"-background",	".background",	XrmoptionSepArg,	NULL},
    120 	{"-copy",	".copy",	XrmoptionNoArg,		"on"},
    121 	{"+copy",	".copy",	XrmoptionNoArg,		"off"},
    122 	{"-thorough",	".thorough",	XrmoptionNoArg,		"on"},
    123 	{"+thorough",	".thorough",	XrmoptionNoArg,		"off"},
    124 	{"-debug",	".debugLevel",	XrmoptionSepArg,	NULL},
    125 	{"-v",		".version",	XrmoptionNoArg,		"on"},
    126 };
    127 
    128 static	const char	base_translations[] = ""
    129 	"^<Key>c:quit()\n"	/* Control keys first */
    130 	"^<Key>d:quit()\n"
    131 	"^<Key>m:forward-page()\n"
    132 	"^<Key>j:forward-page()\n"
    133 	"^<Key>l:forward-page(0)\n"
    134 	"^<Key>h:back-page()\n"
    135 	"^<Key>p:print()\n"
    136 	"a<Key>p:show-display-attributes()\n"
    137 	"\"q\":quit()\n"
    138 	"\"d\":down()\n"
    139 	"\"f\":show-links()\n"
    140 	"\"n\":forward-page()\n"
    141 	"\" \":down-or-next()\n"
    142 	"<Key>Return:addr-go()\n"
    143 	"\"p\":back-page()\n"
    144 	"\"b\":back-page()\n"
    145 	"<Key>Delete:up-or-previous()\n"
    146 	"<Key>BackSpace:up-or-previous()\n"
    147 	"\"P\":declare-page-number()\n"
    148 	"\"g\":goto-page()\n"
    149 	"\"\\^\":home()\n"
    150 	"\"c\":center()\n"
    151 	"\"l\":left()\n"
    152 	"\"r\":right()\n"
    153 	"\"u\":up()\n"
    154 	"\"s\":set-shrink-factor()\n"
    155 	"\"S\":set-density()\n"
    156 	"<Key>Home:home()\n"
    157 	"<Key>Left:left()\n"
    158 	"<Key>Up:up()\n"
    159 	"<Key>Right:right()\n"
    160 	"<Key>Down:down()\n"
    161 	"<Key>Prior:back-page()\n"
    162 	"<Key>Next:forward-page()\n"
    163 	"\"C\":set-color()\n"
    164 	"<Key>Escape:discard-number()\n"	/* Esc */
    165 	"s<Btn1Down>:drag(|)\n"
    166 	"s<Btn2Down>:drag(+)\n"
    167 	"s<Btn3Down>:drag(-)\n"
    168 	"<Btn1Down>:magnifier(*1)\n"
    169 	"<Btn2Down>:magnifier(*2)\n"
    170 	"<Btn3Down>:magnifier(*3)\n";
    171 
    172 #define	offset(field)	XtOffsetOf(struct _resource, field)
    173 
    174 static	XtResource	application_resources[] = {
    175 {"shrinkFactor", "ShrinkFactor", XtRInt, sizeof(int),
    176   offset(shrinkfactor), XtRImmediate, (XtPointer) SHRINK},
    177 {"mainTranslations", "MainTranslations", XtRString, sizeof(char *),
    178   offset(main_translations), XtRString, NULL},
    179 {"wheelTranslations", "WheelTranslations", XtRString, sizeof(char *),
    180   offset(wheel_translations), XtRString, "<Btn4Down>:wheel(-1.)\n\
    181     <Btn5Down>:wheel(1.)\n<Btn6Down>:hwheel(-1.)\n<Btn7Down>:hwheel(1.)"},
    182 {"gamma", "Gamma", XtRFloat, sizeof(float),
    183   offset(_gamma), XtRString, "1"},
    184 {"pixelsPerInch", "PixelsPerInch", XtRInt, sizeof(int),
    185   offset(_pixels_per_inch), XtRImmediate, (XtPointer) BDPI},
    186 {"altFont", "AltFont", XtRString, sizeof(char *),
    187   offset(_alt_font), XtRString,  ALTFONT},
    188 {"mfMode", "MfMode", XtRString, sizeof(char *),
    189   offset(mfmode), XtRString, MFMODE},
    190 {"listFonts", "ListFonts", XtRBoolean, sizeof(Boolean),
    191   offset(_list_fonts), XtRString, "false"},
    192 {"reverseVideo", "ReverseVideo", XtRBoolean, sizeof(Boolean),
    193   offset(reverse), XtRString, "false"},
    194 {"copy", "Copy", XtRBoolean, sizeof(Boolean),
    195   offset(copy), XtRString, "false"},
    196 {"thorough", "Thorough", XtRBoolean, sizeof(Boolean),
    197   offset(thorough), XtRString, "false"},
    198 {"debugLevel", "DebugLevel", XtRString, sizeof(char *),
    199   offset(debug_arg), XtRString, NULL},
    200 {"version", "Version", XtRBoolean, sizeof(Boolean),
    201   offset(version_flag), XtRString, "false"},
    202 {"color", "Color", XtRBoolean, sizeof(Boolean),
    203   offset(_use_color), XtRString, "true"},
    204 };
    205 
    206 static	XtResource	app_pixel_resources[] = {	/* get these later */
    207 {"foreground", "Foreground", XtRPixel, sizeof(Pixel),
    208   offset(_fore_Pixel), XtRString, XtDefaultForeground},
    209 {"background", "Background", XtRPixel, sizeof(Pixel),
    210   offset(_back_Pixel), XtRString, XtDefaultBackground},
    211 };
    212 #undef	offset
    213 
    214 
    215 static	XtGeometryResult
    216 QueryGeometry(Widget w, XtWidgetGeometry *constraints, XtWidgetGeometry *reply)
    217 {
    218 	reply->request_mode = CWWidth | CWHeight;
    219 	reply->width = page_w;
    220 	reply->height = page_h;
    221 	return XtGeometryAlmost;
    222 }
    223 
    224 static	WidgetClassRec	drawingWidgetClass = {
    225     /* superclass         */    &widgetClassRec,
    226     /* class_name         */    "Draw",
    227     /* widget_size        */    sizeof(WidgetRec),
    228     /* class_initialize   */    NULL,
    229     /* class_part_initialize*/  NULL,
    230     /* class_inited       */    FALSE,
    231     /* initialize         */    NULL,
    232     /* initialize_hook    */    NULL,
    233     /* realize            */    XtInheritRealize,
    234     /* actions            */    NULL,
    235     /* num_actions        */    0,
    236     /* resources          */    NULL,
    237     /* num_resources      */    0,
    238     /* xrm_class          */    NULLQUARK,
    239     /* compress_motion    */    FALSE,
    240     /* compress_exposure  */    TRUE,
    241     /* compress_enterleave*/    FALSE,
    242     /* visible_interest   */    FALSE,
    243     /* destroy            */    NULL,
    244     /* resize             */    XtInheritResize,
    245     /* expose             */    XtInheritExpose,
    246     /* set_values         */    NULL,
    247     /* set_values_hook    */    NULL,
    248     /* set_values_almost  */    XtInheritSetValuesAlmost,
    249     /* get_values_hook    */    NULL,
    250     /* accept_focus       */    XtInheritAcceptFocus,
    251     /* version            */    XtVersion,
    252     /* callback_offsets   */    NULL,
    253     /* tm_table           */    XtInheritTranslations,
    254     /* query_geometry       */  QueryGeometry,
    255     /* display_accelerator  */  XtInheritDisplayAccelerator,
    256     /* extension            */  NULL
    257 };
    258 
    259 static void
    260 usage(const char *message, ...)
    261 {
    262 	va_list	args;
    263 
    264 	va_start(args, message);
    265 	vwarnx(message, args);
    266 	va_end(args);
    267 
    268 	fprintf(stderr, "\nUsage: wdvi ");
    269 	fprintf(stderr,
    270 	"[-s <shrink>] [-nocolor] [-gamma <g>] [-p <pixels>] [-altfont <font>]\n"
    271 	"\t[-mfmode <mode-def>] [-l] [-rv]\n"
    272 	"\t[-display <host:display>]\n"
    273 	"\t[-fg <color>] [-bg <color>] [-bd <color>]\n"
    274 	"\t[-geometry <geometry>] [-font <font>] [-copy] [-thorough]\n"
    275 	"\t[-debug <bitmask>] [-v] url\n");
    276 
    277 	exit(1);
    278 }
    279 
    280 
    281 /**
    282  **	Main program starts here.
    283  **/
    284 
    285 struct modifierinf {
    286 	int		len;
    287 	const char	*name;
    288 	Modifiers	mask;
    289 	KeySym		keysym;
    290 };
    291 
    292 /* Allowed modifiers, sorted by length and then lexicographically.  */
    293 
    294 static	struct modifierinf	modifiers[] = {
    295 	{1,	"a",		0,		XK_Alt_L},
    296 	{1,	"c",		ControlMask,	0},
    297 	{1,	"h",		0,		XK_Hyper_L},
    298 	{1,	"l",		LockMask,	0},
    299 	{1,	"m",		0,		XK_Meta_L},
    300 	{1,	"s",		ShiftMask,	0},
    301 	{2,	"su",		0,		XK_Super_L},
    302 	{3,	"Alt",		0,		XK_Alt_L},
    303 	{4,	"Ctrl",		ControlMask,	0},
    304 	{4,	"Lock",		LockMask,	0},
    305 	{4,	"Meta",		0,		XK_Meta_L},
    306 	{4,	"Mod1",		Mod1Mask,	0},
    307 	{4,	"Mod2",		Mod2Mask,	0},
    308 	{4,	"Mod3",		Mod3Mask,	0},
    309 	{4,	"Mod4",		Mod4Mask,	0},
    310 	{4,	"Mod5",		Mod5Mask,	0},
    311 	{5,	"Hyper",	0,		XK_Hyper_L},
    312 	{5,	"Shift",	ShiftMask,	0},
    313 	{5,	"Super",	0,		XK_Super_L},
    314 	{7,	"Button1",	Button1Mask,	0},
    315 	{7,	"Button2",	Button2Mask,	0},
    316 	{7,	"Button3",	Button3Mask,	0},
    317 	{7,	"Button4",	Button4Mask,	0},
    318 	{7,	"Button5",	Button5Mask,	0},
    319 };
    320 
    321 #define	MODSCTRLINDEX	1	/* index of "c" in the above array */
    322 #define	MODSMETAINDEX	4	/* index of "m" */
    323 
    324 
    325 static Bool
    326 compile_modifiers(const char **pp, struct wheel_acts *wactp)
    327 {
    328 	const char		*p = *pp;
    329 	const char		*p1;
    330 	LateBindingsPtr		latep = NULL;
    331 	int			nlate;
    332 
    333 	while (*p == ' ' || *p == '\t') ++p;
    334 
    335 	p1 = p;
    336 	while ((*p1 | ('a' ^ 'A')) >= 'a' && (*p1 | ('a' ^ 'A')) <= 'z')
    337 	    ++p1;
    338 
    339 	if (p1 - p == 3 && memcmp(p, "Any", 3) == 0) {
    340 	    wactp->mask = wactp->value = 0;
    341 	    p = p1;
    342 	    while (*p == ' ' || *p == '\t') ++p;
    343 	    if (*p != '<')
    344 		return False;
    345 	}
    346 	else if (p1 - p == 4 && memcmp(p, "None", 4) == 0) {
    347 	    wactp->mask = ~0;
    348 	    wactp->value = 0;
    349 	    p = p1;
    350 	    while (*p == ' ' || *p == '\t') ++p;
    351 	    if (*p != '<')
    352 		return False;
    353 	}
    354 	else {
    355 	    if (*p == '!') {
    356 		do {
    357 		    ++p;
    358 		} while (*p == ' ' || *p == '\t');
    359 	    }
    360 
    361 	    for (;;) {
    362 		Bool	negated = False;
    363 		struct modifierinf *mp;
    364 
    365 		if (*p == '<')
    366 		    break;
    367 
    368 		if (*p == '~') {
    369 		    negated = True;
    370 		    ++p;
    371 		}
    372 
    373 		if (*p == '^') {
    374 		    mp = &modifiers[MODSCTRLINDEX];
    375 		    ++p;
    376 		}
    377 		else if (*p == '$') {
    378 		    mp = &modifiers[MODSMETAINDEX];
    379 		    ++p;
    380 		}
    381 		else {
    382 		    int min, max;
    383 
    384 		    p1 = p;
    385 		    while (((*p | ('a' ^ 'A')) >= 'a'
    386 		      && (*p | ('a' ^ 'A')) <= 'z')
    387 		      || (*p >= '0' && *p <= '9'))
    388 			++p;
    389 
    390 		    /* do binary search */
    391 		    min = -1;
    392 		    max = XtNumber(modifiers);
    393 		    for (;;) {
    394 			int i, diff;
    395 
    396 			i = (min + max) / 2;
    397 			if (i == min)
    398 			    return False;	/* if not found */
    399 			mp = &modifiers[i];
    400 
    401 			diff = (p - p1) - mp->len;
    402 			if (diff == 0)
    403 			    diff = memcmp(p1, mp->name, p - p1);
    404 
    405 			if (diff == 0)
    406 			    break;
    407 			if (diff > 0) min = i;
    408 			else max = i;
    409 		    }
    410 		}
    411 		if (mp->mask) {
    412 		    wactp->mask |= mp->mask;
    413 		    if (!negated) wactp->value |= mp->mask;
    414 		}
    415 		else {
    416 		    LateBindingsPtr lp1;
    417 
    418 		    if (latep == NULL) {
    419 			nlate = 3;
    420 			latep = xmalloc(3 * sizeof(LateBindings));
    421 			latep->ref_count = 1;
    422 		    }
    423 		    else {
    424 			nlate += 2;
    425 			latep = xrealloc(latep, nlate * sizeof(LateBindings));
    426 		    }
    427 		    lp1 = &latep[nlate - 3];
    428 		    lp1->knot = lp1[1].knot = negated;
    429 		    lp1->pair = True;
    430 		    lp1->keysym = mp->keysym;
    431 		    ++lp1;
    432 		    lp1->pair = False;
    433 		    lp1->ref_count = 0;
    434 		    lp1->keysym = mp->keysym + 1;
    435 		    ++lp1;
    436 		    lp1->knot = lp1->pair = False;
    437 		    lp1->ref_count = 0;
    438 		    lp1->keysym = 0;
    439 		}
    440 
    441 		while (*p == ' ' || *p == '\t') ++p;
    442 	    }
    443 	}
    444 
    445 	wactp->late_bindings = latep;
    446 	*pp = p;
    447 
    448 	return True;
    449 }
    450 
    451 static Bool
    452 compile_evtype(const char **pp, unsigned int *buttonp)
    453 {
    454 	const char		*p = *pp;
    455 	const char		*p0;
    456 
    457 	++p;	/* already assumed to be '<' */
    458 	while (*p == ' ' || *p == '\t') ++p;
    459 
    460 	p0 = p;
    461 	while (((*p | ('a' ^ 'A')) >= 'a' && (*p | ('a' ^ 'A')) <= 'z')
    462 	  && p - p0 < 3)
    463 	    ++p;
    464 
    465 	if (p - p0 != 3 || memcmp(p0, "Btn", 3) != 0)
    466 	    return False;
    467 
    468 	if (*p >= '1' && *p <= '9') {
    469 	    unsigned int n = *p - '0';
    470 
    471 	    while (*++p >= '0' && *p <= '9')
    472 		n = n * 10 + (*p - '0');
    473 
    474 	    *buttonp = n;
    475 	}
    476 
    477 	p0 = p;
    478 	while (((*p | ('a' ^ 'A')) >= 'a' && (*p | ('a' ^ 'A')) <= 'z'))
    479 	    ++p;
    480 
    481 	if (p - p0 != 4 || memcmp(p0, "Down", 4) != 0)
    482 	    return False;
    483 
    484 	while (*p == ' ' || *p == '\t') ++p;
    485 
    486 	if (*p++ != '>')
    487 	    return False;
    488 
    489 	while (*p == ' ' || *p == '\t') ++p;
    490 
    491 	if (*p++ != ':')
    492 	    return False;
    493 
    494 	*pp = p;
    495 
    496 	return True;
    497 }
    498 
    499 static void
    500 compile_wheel_actions(void)
    501 {
    502 	struct wheel_acts	**wactpp;
    503 	struct wheel_acts	*wactp;
    504 	const char		*p = resource.wheel_translations;
    505 	const char		*p_end = p + strlen(p);
    506 	struct wheel_acts	wact;
    507 
    508 	wactpp = &wheel_actions;
    509 
    510 	for (;;) {
    511 	    while (*p == ' ' || *p == '\t') ++p;
    512 
    513 	    if (*p == '\n') continue;
    514 	    if (*p == '\0') break;
    515 
    516 	    wact.mask = wact.value = 0;
    517 	    wact.button = 0;
    518 
    519 	    if (!compile_modifiers(&p, &wact)
    520 	      || !compile_evtype(&p, &wact.button))
    521 		warnx("syntax error in wheel translations");
    522 	    else if (compile_action(p, &wact.action) || wact.action != NULL) {
    523 		wactp = xmalloc(sizeof(struct wheel_acts));
    524 		*wactp = wact;
    525 
    526 		*wactpp = wactp;
    527 		wactpp = &wactp->next;
    528 	    }
    529 
    530 	    p = memchr(p, '\n', p_end - p);
    531 	    if (p == NULL) break;
    532 	    ++p;
    533 	}
    534 
    535 	*wactpp = NULL;
    536 }
    537 
    538 static void
    539 set_default_winsize(void)
    540 {
    541 	Dimension	 addr_bwidth;
    542 	Dimension	 bwidth;
    543 	Dimension	 screen_w, screen_h;
    544 	XtWidgetGeometry addr_reply;
    545 	XtWidgetGeometry constraints;
    546 	XtWidgetGeometry reply;
    547 	int		 i;
    548 	int		 addr_height;
    549 
    550 	XtQueryGeometry(addr_widget, NULL, &addr_reply);
    551 	XtVaGetValues(addr_widget, XtNborderWidth, &addr_bwidth, NULL);
    552 	addr_height = addr_reply.height + 2 * addr_bwidth;
    553 
    554 	/*
    555 	 * The total screen_{w,h} is what is left over after the address bar
    556 	 * and borders have been taken into account.
    557 	 */
    558 	XtVaGetValues(top_level, XtNborderWidth, &bwidth, NULL);
    559 	screen_w = WidthOfScreen(SCRN) - 2 * bwidth;
    560 	screen_h = HeightOfScreen(SCRN) - 2 * bwidth - addr_height;
    561 
    562 	Arg set_wh_args[] = {
    563 		{XtNwidth,	(XtArgVal) 0},
    564 		{XtNheight,	(XtArgVal) 0},
    565 	};
    566 	for (;;) {	/* actually, at most two passes */
    567 		constraints.request_mode = reply.request_mode = 0;
    568 		constraints.width = page_w;
    569 		if (page_w > screen_w) {
    570 			constraints.request_mode = CWWidth;
    571 			constraints.width = screen_w;
    572 		}
    573 		constraints.height = page_h;
    574 		if (page_h > screen_h) {
    575 			constraints.request_mode |= CWHeight;
    576 			constraints.height = screen_h;
    577 		}
    578 		if (constraints.request_mode != 0
    579 		    && constraints.request_mode != (CWWidth | CWHeight))
    580 			XtQueryGeometry(vport_widget, &constraints, &reply);
    581 		if (!(reply.request_mode & CWWidth))
    582 			reply.width = constraints.width;
    583 		if (reply.width >= screen_w)
    584 			reply.width = screen_w;
    585 		if (!(reply.request_mode & CWHeight))
    586 			reply.height = constraints.height;
    587 		if (reply.height >= screen_h)
    588 			reply.height = screen_h;
    589 
    590 		/* now reply.{width,height} contain max. usable window size */
    591 
    592 		if (shrink_factor != 0)
    593 			break;
    594 
    595 		shrink_factor = ROUNDUP(unshrunk_page_w, reply.width - 2);
    596 		i = ROUNDUP(unshrunk_page_h, reply.height - 2);
    597 		if (i >= shrink_factor)
    598 			shrink_factor = i;
    599 		if (shrink_factor > 1)
    600 			bak_shrink = shrink_factor;
    601 		mane.shrinkfactor = shrink_factor;
    602 		init_page();
    603 		set_wh_args[0].value = (XtArgVal) page_w;
    604 		set_wh_args[1].value = (XtArgVal) page_h;
    605 		XtSetValues(draw_widget, set_wh_args, XtNumber(set_wh_args));
    606 	}
    607 	set_wh_args[0].value = reply.width;
    608 	set_wh_args[1].value = reply.height + addr_height;
    609 	XtSetValues(top_level, set_wh_args, XtNumber(set_wh_args));
    610 
    611 	set_wh_args[0].value = reply.width - (2 * addr_bwidth);
    612 	XtSetValues(addr_widget, set_wh_args, 1);
    613 }
    614 
    615 /*
    616  *	main program
    617  */
    618 
    619 int
    620 main(int argc, char *argv[])
    621 {
    622 	const char	*url = NULL;
    623 	const char	*arg;
    624 	XrmDatabase	db;
    625 	int		i;
    626 
    627 
    628 	/*
    629 	 *	Step 1:  Process command-line options and resources.
    630 	 */
    631 
    632 	top_level = XtInitialize("wdvi", "XDvi", options, XtNumber(options),
    633 		&argc, argv);
    634 	XtAddActions(Actions, num_actions);
    635 
    636 	while (--argc > 0) {
    637 		arg = *++argv;
    638 
    639 		if (arg[0] == '-')
    640 			usage("Unknown option '%s'.", arg);
    641 		if (url != NULL)
    642 			usage("More than one url specified", arg);
    643 		else
    644 			url = arg;
    645 	}
    646 
    647 	DISP = XtDisplay(top_level);
    648 	SCRN = XtScreen(top_level);
    649 
    650 	db = XtScreenDatabase(SCRN);
    651 
    652 	/* It's easier to do this than track creations and deletions of the
    653 	       scrollbars.  */
    654 	XrmPutStringResource(&db, "XDvi.file.form.vport.vertical.translations",
    655 	    "#override<Btn4Down>:file-scroll(-w)\n<Btn5Down>:file-scroll(w)");
    656 	XrmPutStringResource(&db, "XDvi.file.form.vport.horizontal.translations",
    657 	    "#override<Btn4Down>:file-scroll(-w)\n<Btn5Down>:file-scroll(w)");
    658 
    659 	XtGetApplicationResources(top_level, &resource, application_resources,
    660 		XtNumber(application_resources), (ArgList) NULL, 0);
    661 	shrink_factor = resource.shrinkfactor;
    662 
    663 	if (resource.version_flag)
    664 		errx(0, "version %s", VERSION);
    665 
    666 	if (resource.debug_arg != NULL)
    667 		debug = isdigit(*resource.debug_arg) ? atoi(resource.debug_arg)
    668 		: DBG_ALL;
    669 
    670 	char *atom_names[]	= { "WM_DELETE_WINDOW", "WM_PROTOCOLS" };
    671 	if (!XInternAtoms(DISP, atom_names, XtNumber(atom_names), False, atoms))
    672 		errx(1, "XInternAtoms failed.");
    673 
    674 	if (debug & DBG_CLIENT) {
    675 	    for (i = 0; i < XtNumber(atom_names); ++i)
    676 		printf("Atom(%s) = %lu\n", atom_names[i], atoms[i]);
    677 	}
    678 
    679 	if (DefaultDepthOfScreen(SCRN) < 24)
    680 		errx(1, "X depth < 24 is not supported.");
    681 	if (DefaultVisualOfScreen(SCRN)->class != TrueColor)
    682 		errx(1, "X visual not 'TrueColor' is not supported.");
    683 
    684 	/*
    685 	 * The default document is always available and simplifies startup
    686 	 * by giving inital window sizes and no error popups.
    687 	 */
    688 	if ((dvi_file = fmemopen(default_dvi, default_dvi_len, "r")) == NULL)
    689 		err(1, "fmemopen");
    690 	dvi_name = "default.dvi";
    691 	ev_flags |= EV_NEWDOC;
    692 	read_events(EV_GE_NEWDOC);
    693 	ev_flags &= ~EV_NEWDOC;
    694 
    695 	if (resource.mfmode != NULL) {
    696 	    char *p;
    697 
    698 	    p = rindex(resource.mfmode, ':');
    699 	    if (p != NULL) {
    700 		unsigned int	len;
    701 		char		*p1;
    702 
    703 		++p;
    704 		len = p - resource.mfmode;
    705 		p1 = xmalloc(len);
    706 		bcopy(resource.mfmode, p1, len - 1);
    707 		p1[len - 1] = '\0';
    708 		resource.mfmode = p1;
    709 		pixels_per_inch = atoi(p);
    710 	    }
    711 	}
    712 	if (shrink_factor < 0 || shrink_factor > 10)
    713 		usage("Invalid shrink factor: %d.", shrink_factor);
    714 	if (pixels_per_inch <= 0)
    715 		usage("Invalid dpi value: %d.", pixels_per_inch);
    716 
    717 	if (shrink_factor > 1) {
    718 		bak_shrink = shrink_factor;
    719 		mane.shrinkfactor = shrink_factor;	/* otherwise it's 1 */
    720 	}
    721 
    722 	/* 1 inch */
    723 	offset_x = pixels_per_inch;
    724 	offset_y = pixels_per_inch;
    725 
    726 	unshrunk_paper_w = 8.5 * pixels_per_inch;
    727 	unshrunk_paper_h = 11 * pixels_per_inch;
    728 
    729 	/*
    730 	 *	Step 2:  Settle colormap issues.  This should be done before
    731 	 *	other widgets are created, so that they get the right
    732 	 *	pixel values.  (The top-level widget won't have the right
    733 	 *	values, but I don't think that makes any difference.)
    734 	 */
    735 
    736 	XtGetApplicationResources(top_level, &resource,
    737 	  app_pixel_resources, XtNumber(app_pixel_resources),
    738 	  (ArgList) NULL, 0);
    739 
    740 	fore_color_data.pixel = resource._fore_Pixel;
    741 	back_color_data.pixel = resource._back_Pixel;
    742 	XQueryColors(DISP, DefaultColormapOfScreen(SCRN), color_data, 2);
    743 
    744 	fg_initial.r = fore_color_data.red;
    745 	fg_initial.g = fore_color_data.green;
    746 	fg_initial.b = fore_color_data.blue;
    747 	bg_initial.r = back_color_data.red;
    748 	bg_initial.g = back_color_data.green;
    749 	bg_initial.b = back_color_data.blue;
    750 
    751 	/*
    752 	 *	Step 3:  Initialize the dvi file and set titles.
    753 	 */
    754 
    755 	init_font_open();
    756 	if (init_dvi_file() == False)
    757 		errx(1, "Bad default DVI document");
    758 	prescan();
    759 	init_page();
    760 
    761 	/*
    762 	 *	Step 4:  Set initial window size.
    763 	 *	This needs to be done before colors are assigned because if
    764 	 *	-s 0 is specified, then we need to compute the shrink factor
    765 	 *	(which in turn affects whether init_pix is called).
    766 	 */
    767 
    768 	/* Create icon. */
    769 	XtVaSetValues(top_level, XtNiconPixmap,
    770 		XCreateBitmapFromData( DISP, XtScreen(top_level)->root,
    771 			(const char *) xdvi_bits, xdvi_width, xdvi_height),
    772 		NULL);
    773 
    774 	XtVaSetValues(top_level, XtNinput, True, NULL);
    775 
    776 	XrmPutStringResource(&db, "XDvi.form.baseTranslations",
    777 		base_translations);
    778 
    779 	if (resource.main_translations != NULL)
    780 		XrmPutStringResource(&db, "XDvi.form.translations",
    781 			resource.main_translations);
    782 
    783 	/*
    784 	 * Widget creation.
    785 	 */
    786 	form_widget = XtVaCreateManagedWidget("form", formWidgetClass,
    787 		top_level, XtNdefaultDistance, 0,
    788 		NULL);
    789 
    790 	vport_widget = XtVaCreateManagedWidget("vport", viewportWidgetClass,
    791 		form_widget,
    792 		XtNborderWidth, 0,
    793 		XtNtop,		XawChainTop,
    794 		XtNbottom,	XawChainBottom,
    795 		XtNleft,	XawChainLeft,
    796 		XtNright,	XawChainRight,
    797 		XtNallowHoriz,	True,
    798 		XtNallowVert,	True,
    799 		NULL);
    800 
    801 	clip_widget = XtNameToWidget(vport_widget, "clip");
    802 
    803 	draw_widget = XtVaCreateManagedWidget("drawing", &drawingWidgetClass,
    804 		vport_widget,
    805 		XtNwidth,	page_w,
    806 		XtNheight,	page_h,
    807 		XtNx,		0,
    808 		XtNy,		0,
    809 		XtNlabel,	(XtArgVal) "",
    810 		NULL);
    811 
    812 	addr_widget = XtVaCreateManagedWidget("address", asciiTextWidgetClass,
    813 		form_widget,
    814 		XtNfromVert,	(XtArgVal) vport_widget,
    815 		XtNdataCompression, False,
    816 		XtNeditType,	XawtextEdit,
    817 		/* Don't resize this widget with the rest of the form. */
    818 		XtNtop,		XawChainBottom,
    819 		XtNbottom,	XawChainBottom,
    820 		XtNstring,	(XtArgVal) (url ? url : addr_default),
    821 		XtNinsertPosition, strlen(url ? url : addr_default),
    822 		NULL);
    823 
    824 	XtOverrideTranslations(addr_widget, XtParseTranslationTable(
    825 		"<Key>Return:addr-go()"));
    826 
    827 	XtOverrideTranslations(form_widget, XtParseTranslationTable(
    828 		"\"0\":digit(0)\n"
    829 		"\"1\":digit(1)\n"
    830 		"\"2\":digit(2)\n"
    831 		"\"3\":digit(3)\n"
    832 		"\"4\":digit(4)\n"
    833 		"\"5\":digit(5)\n"
    834 		"\"6\":digit(6)\n"
    835 		"\"7\":digit(7)\n"
    836 		"\"8\":digit(8)\n"
    837 		"\"9\":digit(9)\n"
    838 		"\"-\":minus()\n"
    839 		"<Motion>:motion()\n"
    840 		"<BtnUp>:release()"));
    841 
    842 	if (resource.wheel_translations != NULL) {
    843 		XtAugmentTranslations(form_widget, XtParseTranslationTable(
    844 			"<BtnDown>:wheel-actions()"));
    845 
    846 		compile_wheel_actions();
    847 	}
    848 
    849 	/* set background colors of the drawing and clip widgets */
    850 	Arg back_args = {XtNbackground, (XtArgVal) resource._back_Pixel};
    851 	XtSetValues(draw_widget, &back_args, 1);
    852 	XtSetValues(clip_widget, &back_args, 1);
    853 
    854 	set_default_winsize();
    855 
    856 	/*
    857 	 *	Step 5:  Realize the widgets (or windows).
    858 	 */
    859 
    860 
    861 	XtAddEventHandler(vport_widget, StructureNotifyMask, False,
    862 		handle_resize, NULL);
    863 	XtAddEventHandler(draw_widget, ExposureMask, False, handle_expose,
    864 		&mane);
    865 	XtRealizeWidget(top_level);
    866 
    867 	/*
    868 	 * Assume that if we have a vertical scroll bar its because of
    869 	 * vertical overflow. Make the window wider to account for the
    870 	 * bars width.
    871 	 */
    872 	Widget y_bar = XtNameToWidget(vport_widget, "vertical");
    873 	if (y_bar != NULL) {
    874 		Dimension width;
    875 		XtWidgetGeometry reply;
    876 
    877 		XtVaGetValues(y_bar, XtNwidth, &width, NULL);
    878 		XtQueryGeometry(top_level, NULL, &reply);
    879 
    880 		XtVaSetValues(top_level, XtNwidth, reply.width + width + 1,
    881 		    NULL);
    882 	}
    883 
    884 	XSetWMProtocols(DISP, XtWindow(top_level), &XA_WM_DELETE_WINDOW, 1);
    885 	XtAddEventHandler(top_level, NoEventMask, True, handle_messages, NULL);
    886 
    887 	currwin.win = mane.win = XtWindow(draw_widget);
    888 
    889 	XSetWindowAttributes wattrs;
    890 	wattrs.backing_store = Always;
    891 	XChangeWindowAttributes(DISP, mane.win, CWBackingStore, &wattrs);
    892 
    893 	image = XCreateImage(DISP, CopyFromParent, 1, XYBitmap, 0,
    894 			     (char *) NULL, 0, 0, BMBITS, 0);
    895 	image->bitmap_unit = BMBITS;
    896 #ifdef	WORDS_BIGENDIAN
    897 	image->bitmap_bit_order = MSBFirst;
    898 #else
    899 	image->bitmap_bit_order = LSBFirst;
    900 #endif
    901 	short endian = MSBFirst << 8 | LSBFirst;
    902 	image->byte_order = *((char *) &endian);
    903 
    904 	/*
    905 	 *	Step 6:  Assign colors and GCs.
    906 	 *		 Because of the latter, this has to go after Step 5.
    907 	 *		 In color mode, color changes affect foreGC, foreGC2,
    908 	 *		 and ruleGC, but not copyGC.
    909 	 */
    910 
    911 	if (resource._gamma == 0.0)
    912 	    resource._gamma = 1.0;
    913 	if (resource.reverse)
    914 		resource._gamma = 1.5;
    915 
    916 #define	MakeGC(fcn, fg, bg)	(values.function = fcn, \
    917 	  values.foreground=fg, values.background=bg, \
    918 	  XCreateGC(DISP, XtWindow(top_level), \
    919 	    GCFunction | GCForeground | GCBackground, &values))
    920 
    921 	XGCValues	values;
    922 
    923 	/* Not affected by color changes.  */
    924 	copyGC = MakeGC(GXcopy, resource._fore_Pixel, resource._back_Pixel);
    925 	linkGC = MakeGC(GXxor, resource._fore_Pixel ^ resource._back_Pixel,
    926 	    resource._back_Pixel);
    927 
    928 	ready_cursor = XCreateFontCursor(DISP, XC_cross);
    929 	redraw_cursor = XCreateFontCursor(DISP, XC_watch);
    930 
    931 	for(;;)		/* normal operation */
    932 		do_pages();
    933 }