wdvi

network DVI viewer
Log | Files | Refs

events.c (44321B)


      1 /*========================================================================*\
      2 
      3 Copyright (c) 1990-2004  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
     26 	in xdvi.c.
     27 
     28 \*========================================================================*/
     29 
     30 #include <ctype.h>	/* tolower() */
     31 #include <err.h>
     32 #include <limits.h>	/* abs() */
     33 #include <math.h>	/* pow() */
     34 #include <stdlib.h>	/* free(), atoi(), atof(), exit(), abs() */
     35 
     36 #include <X11/IntrinsicI.h>	/* _XtComputeLateBindings */
     37 #include <X11/XKBlib.h>
     38 #define XBell(dpy, percent) XkbBell(dpy, mane.win, percent, (Atom) None)
     39 
     40 #include "dvi-init.h"	/* init_page(), full_reset_colors(), reset_colors() */
     41 #include "dvi-draw.h"	/* prescan(), draw_page(), draw_links() */
     42 #include "events.h"
     43 #include "http.h"	/* http_get() */
     44 #include "popups.h"	/* WARN, Act_print() */
     45 #include "special.h"	/* page_colors, scanned_page, ... */
     46 #include "util.h"	/* xmalloc() */
     47 #include "xdvi.h"
     48 
     49 
     50 #define	MAGBORD	1	/* border size for magnifier */
     51 
     52 /*
     53  * Command line flags.
     54  */
     55 
     56 #define	fore_Pixel	resource._fore_Pixel
     57 #define	back_Pixel	resource._back_Pixel
     58 extern	struct _resource	resource;
     59 #define	brdr_Pixel	resource._brdr_Pixel
     60 
     61 #define	clip_w	mane.width
     62 #define	clip_h	mane.height
     63 static	Position main_x, main_y;
     64 static	Position mag_x, mag_y, new_mag_x, new_mag_y;
     65 static	int	mag_conv_x, mag_conv_y;
     66 static	Boolean	dragcurs	= False;
     67 
     68 typedef	void	(*home_proc)(Boolean);
     69 typedef void	(*mouse_proc)(XEvent *);
     70 
     71 static	void	can_exposures(struct WindowRec *);
     72 void		null_mouse(XEvent *);
     73 
     74 mouse_proc	mouse_motion	= null_mouse;
     75 mouse_proc	mouse_release	= null_mouse;
     76 
     77 
     78 /*
     79  *	The cursor shape in the magnifying glass is determined by which
     80  *	window received the button press event.  Under XAW, that is a parent
     81  *	of mane.win, so we have to define the cursor of that window instead
     82  *	of mane.win in that case.
     83  */
     84 
     85 #define CURSORWIN	XtWindow(form_widget)
     86 
     87 #define	ACTION_DECL(name)				\
     88 		void name(Widget, XEvent *, String *, Cardinal *)
     89 
     90 #define	ACTION(name)					\
     91 		void name(Widget w, XEvent *event,	\
     92 			String *params, Cardinal *num_params)
     93 
     94 /* ARGSUSED */
     95 void
     96 null_mouse(XEvent *event)
     97 {
     98 }
     99 
    100 static ACTION_DECL(Act_digit);
    101 static ACTION_DECL(Act_minus);
    102 static ACTION_DECL(Act_quit);
    103 static ACTION_DECL(Act_goto_page);
    104 static ACTION_DECL(Act_forward_page);
    105 static ACTION_DECL(Act_back_page);
    106 static ACTION_DECL(Act_declare_page_number);
    107 static ACTION_DECL(Act_home);
    108 static ACTION_DECL(Act_center);
    109 static ACTION_DECL(Act_left);
    110 static ACTION_DECL(Act_right);
    111 static ACTION_DECL(Act_up);
    112 static ACTION_DECL(Act_down);
    113 static ACTION_DECL(Act_up_or_previous);
    114 static ACTION_DECL(Act_down_or_next);
    115 static ACTION_DECL(Act_show_display_attributes);
    116 static ACTION_DECL(Act_set_shrink_factor);
    117 static ACTION_DECL(Act_shrink_to_dpi);
    118 static ACTION_DECL(Act_set_density);
    119 static ACTION_DECL(Act_set_color);
    120 static ACTION_DECL(Act_discard_number);
    121 
    122 static ACTION_DECL(Act_magnifier);
    123 static ACTION_DECL(Act_drag);
    124 static ACTION_DECL(Act_wheel);
    125 static ACTION_DECL(Act_hwheel);
    126 static ACTION_DECL(Act_wheel_actions);
    127 static ACTION_DECL(Act_motion);
    128 static ACTION_DECL(Act_release);
    129 static ACTION_DECL(Act_addr_go);
    130 static ACTION_DECL(Act_show_links);
    131 
    132 
    133 XtActionsRec	Actions[]	= {
    134 	{"digit",		Act_digit},
    135 	{"minus",		Act_minus},
    136 	{"quit",		Act_quit},
    137 	{"goto-page",		Act_goto_page},
    138 	{"forward-page",	Act_forward_page},
    139 	{"back-page",		Act_back_page},
    140 	{"declare-page-number",	Act_declare_page_number},
    141 	{"home",		Act_home},
    142 	{"center",		Act_center},
    143 	{"left",		Act_left},
    144 	{"right",		Act_right},
    145 	{"up",			Act_up},
    146 	{"down",		Act_down},
    147 	{"up-or-previous",	Act_up_or_previous},
    148 	{"down-or-next",	Act_down_or_next},
    149 	{"show-display-attributes", Act_show_display_attributes},
    150 	{"set-shrink-factor",	Act_set_shrink_factor},
    151 	{"shrink-to-dpi",	Act_shrink_to_dpi},
    152 	{"set-density",		Act_set_density},
    153 	{"print",		Act_print},
    154 	{"set-color",		Act_set_color},
    155 	{"discard-number",	Act_discard_number},
    156 	{"magnifier",		Act_magnifier},
    157 	{"drag",		Act_drag},
    158 	{"wheel",		Act_wheel},
    159 	{"hwheel",		Act_hwheel},
    160 	{"wheel-actions",	Act_wheel_actions},
    161 	{"motion",		Act_motion},
    162 	{"release",		Act_release},
    163 	{"addr-go",		Act_addr_go},
    164 	{"show-links",		Act_show_links},
    165 };
    166 
    167 Cardinal	num_actions	= XtNumber(Actions);
    168 
    169 
    170 Bool
    171 compile_action(str, app)
    172 	const char	*str;
    173 	struct xdvi_action **app;
    174 {
    175 	const char	*p, *p1, *p2;
    176 	XtActionsRec	*actp;
    177 	struct xdvi_action *ap;
    178 
    179 	for (;;) {
    180 	    while (*str == ' ' || *str == '\t') ++str;
    181 
    182 	    if (*str == '\0' || *str == '\n')
    183 		break;
    184 
    185 	    p = str;
    186 	    while (((*p | ('a' ^ 'A')) >= 'a' && (*p | ('a' ^ 'A')) <= 'z')
    187 	      || (*p >= '0' && *p <= '9') || *p == '-' || *p == '_')
    188 		++p;
    189 
    190 	    for (actp = Actions;; ++actp) {
    191 		if (actp >= Actions + XtNumber(Actions)) {
    192 		    warnx("cannot compile action '%.*s'", (int) (p - str), str);
    193 		    *app = NULL;
    194 		    return False;
    195 		}
    196 		if (memcmp(str, actp->string, p - str) == 0
    197 		  && actp->string[p - str] == '\0')
    198 		    break;
    199 	    }
    200 
    201 	    while (*p == ' ' || *p == '\t') ++p;
    202 	    if (*p != '(') {
    203 		while (*p != '\0' && *p != '\n') ++p;
    204 		warnx("syntax error in action '%.*s'", (int) (p - str), str);
    205 		*app = NULL;
    206 		return False;
    207 	    }
    208 	    ++p;
    209 	    while (*p == ' ' || *p == '\t') ++p;
    210 	    for (p1 = p;; ++p1) {
    211 		if (*p1 == '\0' || *p1 == '\n') {
    212 		    warnx("syntax error in action '%.*s'", (int) (p1 - str), str);
    213 		    *app = NULL;
    214 		    return False;
    215 		}
    216 		if (*p1 == ')') break;
    217 	    }
    218 
    219 	    ap = xmalloc(sizeof *ap);
    220 	    ap->proc = actp->proc;
    221 	    for (p2 = p1;; --p2)
    222 		if (p2 <= p) {	/* if no args */
    223 		    ap->num_params = 0;
    224 		    ap->param = NULL;
    225 		    break;
    226 		}
    227 		else if (p2[-1] != ' ' && p2[-1] != '\t') {
    228 		    char	*arg;
    229 
    230 		    arg = xmalloc(p2 - p + 1);
    231 		    bcopy(p, arg, p2 - p);
    232 		    arg[p2 - p] = '\0';
    233 		    ap->num_params = 1;
    234 		    ap->param = arg;
    235 		    break;
    236 		}
    237 
    238 	    *app = ap;
    239 	    app = &ap->next;
    240 
    241 	    str = p1 + 1;
    242 	}
    243 
    244 	*app = NULL;
    245 	return True;
    246 }
    247 
    248 
    249 
    250 static	Widget	x_bar, y_bar;	/* horizontal and vertical scroll bars */
    251 
    252 static	Arg	resize_args[] = {
    253 	{XtNwidth,	(XtArgVal) 0},
    254 	{XtNheight,	(XtArgVal) 0},
    255 };
    256 
    257 #define	XdviResizeWidget(widget, w, h)	\
    258 		(width_arg.value = (XtArgVal) (w), \
    259 		resize_args[1].value = (XtArgVal) (h), \
    260 		XtSetValues(widget, resize_args, XtNumber(resize_args)) )
    261 
    262 #define	width_arg	(resize_args[0])
    263 #define	height_arg	(resize_args[1])
    264 
    265 
    266 
    267 /*
    268  *	Mechanism to keep track of the magnifier window.  The problems are,
    269  *	(a) if the button is released while the window is being drawn, this
    270  *	could cause an X error if we continue drawing in it after it is
    271  *	destroyed, and
    272  *	(b) creating and destroying the window too quickly confuses the window
    273  *	manager, which is avoided by waiting for an expose event before
    274  *	destroying it.
    275  */
    276 static	short	alt_stat;	/* 1 = wait for expose, */
    277 				/* -1 = destroy upon expose */
    278 
    279 /*
    280  *	Data for buffered events.
    281  */
    282 
    283 #define	gamma	resource._gamma
    284 
    285 static	void
    286 mask_shifts(mask, pshift1, pshift2)
    287 	Pixel	mask;
    288 	int	*pshift1, *pshift2;
    289 {
    290 	int	k, l;
    291 
    292 	for (k = 0; (mask & 1) == 0; ++k)
    293 	    mask >>= 1;
    294 	for (l = 0; (mask & 1) == 1; ++l)
    295 	    mask >>= 1;
    296 	*pshift1 = sizeof(short) * 8 - l;
    297 	*pshift2 = k;
    298 }
    299 
    300 
    301 /*
    302  *	Warn about overstrike characters.
    303  */
    304 
    305 static	void
    306 warn_overstrike()
    307 {
    308 	static	Boolean	warned_already = False;
    309 
    310 	if (!warned_already) {
    311 	    warned_already = True;
    312 	    WARN(XmDIALOG_WARNING, "Overstrike characters may be incorrect");
    313 	}
    314 }
    315 
    316 /*
    317  *	Allocate a color and add it to our list of pixels to be returned
    318  *	upon opening a new document.
    319  */
    320 
    321 #define	SHIFTIFY(x, shift1, shift2)	((((Pixel)(x)) >> (shift1)) << (shift2))
    322 
    323 static	int	shift1_r, shift1_g, shift1_b;
    324 static	int	shift2_r, shift2_g, shift2_b;
    325 static	Boolean	shifts_good	= False;
    326 
    327 static Pixel
    328 alloc_color(const struct rgb *colorp, Pixel fallback_pixel)
    329 {
    330 	Visual *visual = DefaultVisualOfScreen(SCRN);
    331 
    332 	if (!shifts_good) {
    333 		mask_shifts(visual->red_mask,   &shift1_r, &shift2_r);
    334 		mask_shifts(visual->green_mask, &shift1_g, &shift2_g);
    335 		mask_shifts(visual->blue_mask,  &shift1_b, &shift2_b);
    336 		shifts_good = True;
    337 	}
    338 	return SHIFTIFY(colorp->r, shift1_r, shift2_r) |
    339 		SHIFTIFY(colorp->g, shift1_g, shift2_g) |
    340 		SHIFTIFY(colorp->b, shift1_b, shift2_b);
    341 }
    342 
    343 #undef SHIFTIFY
    344 
    345 
    346 /*
    347  *	Switch colors of GCs, etc.  Called when we're about to use a GC.
    348  */
    349 
    350 #define	SetGC(gc, fcn, fg, bg) \
    351 	    { \
    352 		values.function = fcn; \
    353 		values.foreground = fg; \
    354 		values.background = bg; \
    355 		if (gc != NULL) \
    356 		    XChangeGC(DISP, gc, \
    357 		      GCFunction | GCForeground | GCBackground, &values); \
    358 		else \
    359 		    gc = XCreateGC(DISP, XtWindow(top_level), \
    360 		      GCFunction | GCForeground | GCBackground, &values); \
    361 	    }
    362 
    363 void
    364 do_color_change()
    365 {
    366 	static	int	shrink_allocated_for = 0;
    367 	static	GC	foreGC2_bak	= NULL;
    368 	XGCValues	values;
    369 	Pixel		set_bits;
    370 	Pixel		clr_bits;
    371 	Visual		*visual;
    372 
    373 	/*
    374 	 * use_grey
    375 	 */
    376 	if (!fg_current->pixel_good) {
    377 		fg_current->pixel = alloc_color(&fg_current->color,
    378 			fore_color_data.pixel);
    379 		fg_current->pixel_good = True;
    380 	}
    381 
    382 	set_bits = fg_current->pixel & ~bg_current->pixel;
    383 	clr_bits = bg_current->pixel & ~fg_current->pixel;
    384 
    385 	visual = DefaultVisualOfScreen(SCRN);
    386 
    387 	if (set_bits & visual->red_mask)
    388 		set_bits |= visual->red_mask;
    389 	if (clr_bits & visual->red_mask)
    390 		clr_bits |= visual->red_mask;
    391 	if (set_bits & visual->green_mask)
    392 		set_bits |= visual->green_mask;
    393 	if (clr_bits & visual->green_mask)
    394 		clr_bits |= visual->green_mask;
    395 	if (set_bits & visual->blue_mask)
    396 		set_bits |= visual->blue_mask;
    397 	if (clr_bits & visual->blue_mask)
    398 		clr_bits |= visual->blue_mask;
    399 
    400 	/*
    401 	 * Make the GCs
    402 	 */
    403 
    404 	SetGC(ruleGC, GXcopy, fg_current->pixel, bg_current->pixel);
    405 	foreGC2 = NULL;
    406 
    407 	if (resource.copy || (set_bits && clr_bits && !resource.thorough)) {
    408 		if (!resource.copy)
    409 			warn_overstrike();
    410 		SetGC(foreGC, GXcopy, fg_current->pixel, bg_current->pixel);
    411 	}
    412 	else {
    413 		if (set_bits) {
    414 			SetGC(foreGC, GXor, fg_current->pixel & set_bits, 0);
    415 			if (clr_bits) {
    416 			    SetGC(foreGC2_bak, GXandInverted,
    417 			      ~fg_current->pixel & clr_bits, 0);
    418 			    foreGC2 = foreGC2_bak;
    419 			}
    420 		}
    421 		else
    422 			SetGC(foreGC, GXandInverted,
    423 				~fg_current->pixel & clr_bits, 0);
    424 	}
    425 
    426 	if (debug & DBG_DVI)
    427 		printf("do_color_change: fg = %lx, bg = %lx, with%s foreGC2\n",
    428 		      fg_current->pixel, bg_current->pixel,
    429 		      foreGC2 == NULL ? "out" : "");
    430 
    431 	if (mane.shrinkfactor <= 1) {
    432 		fg_active = fg_current;
    433 		return;
    434 	}
    435 
    436 	/*
    437 	 * mane.shrinkfactor > 1
    438 	 */
    439 	int i;
    440 	unsigned int sf_squared;
    441 
    442 	sf_squared = mane.shrinkfactor * mane.shrinkfactor;
    443 
    444 	if (shrink_allocated_for < mane.shrinkfactor) {
    445 		if (pixeltbl != NULL) {
    446 			free((char *) pixeltbl);
    447 			if (pixeltbl_t != NULL) {
    448 				free((char *) pixeltbl_t);
    449 				pixeltbl_t = NULL;
    450 			}
    451 		}
    452 		pixeltbl = xmalloc((sf_squared + 1) * sizeof(Pixel));
    453 		shrink_allocated_for = mane.shrinkfactor;
    454 	}
    455 	if (foreGC2 != NULL && pixeltbl_t == NULL)
    456 		/* Can't use sf_squared (or mane.shrinkfactor) here */
    457 		pixeltbl_t = xmalloc((shrink_allocated_for
    458 			* shrink_allocated_for + 1) * sizeof(Pixel));
    459 
    460 	/*
    461 	 * Compute pixel values directly.
    462 	 */
    463 	for (i = 0; i <= sf_squared; ++i) {
    464 		double frac = gamma > 0
    465 			? pow((double) i / sf_squared, 1 / gamma)
    466 			: 1 - pow((double) (sf_squared - i) / sf_squared,
    467 			  -gamma);
    468 		unsigned int	red, green, blue;
    469 		Pixel		pixel;
    470 
    471 		red = frac * ((double) fg_current->color.r
    472 			- bg_current->color.r)
    473 			+ bg_current->color.r;
    474 		green = frac * ((double) fg_current->color.g
    475 			- bg_current->color.g)
    476 			+ bg_current->color.g;
    477 		blue = frac * ((double) fg_current->color.b
    478 			- bg_current->color.b)
    479 			+ bg_current->color.b;
    480 
    481 #define	SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2))
    482 		pixel = SHIFTIFY(red,   shift1_r, shift2_r) |
    483 			SHIFTIFY(green, shift1_g, shift2_g) |
    484 			SHIFTIFY(blue,  shift1_b, shift2_b);
    485 #undef	SHIFTIFY
    486 
    487 		if (foreGC2 != NULL) {	/* if thorough */
    488 			pixeltbl[i] = pixel & ~bg_current->pixel;
    489 			pixeltbl_t[i] = ~pixel & bg_current->pixel;
    490 		}
    491 		else if (resource.copy || (set_bits && clr_bits))
    492 			pixeltbl[i] = pixel;
    493 		else
    494 			pixeltbl[i] = set_bits ? pixel & set_bits
    495 				: ~pixel & clr_bits;
    496 	}
    497 
    498 	fg_active = fg_current;
    499 }
    500 
    501 #undef SetGC
    502 
    503 
    504 /*
    505  *	Event-handling routines
    506  */
    507 
    508 static void
    509 expose(windowrec, x, y, w, h)
    510 	struct WindowRec *windowrec;
    511 	int		x, y;
    512 	unsigned int	w, h;
    513 {
    514 	if (windowrec->min_x > x) windowrec->min_x = x;
    515 	if (windowrec->max_x < x + w)
    516 	    windowrec->max_x = x + w;
    517 	if (windowrec->min_y > y) windowrec->min_y = y;
    518 	if (windowrec->max_y < y + h)
    519 	    windowrec->max_y = y + h;
    520 	ev_flags |= EV_EXPOSE;
    521 }
    522 
    523 static	void
    524 clearexpose(windowrec, x, y, w, h)
    525 	struct WindowRec *windowrec;
    526 	int		x, y;
    527 	unsigned int	w, h;
    528 {
    529 	XClearArea(DISP, windowrec->win, x, y, w, h, False);
    530 	expose(windowrec, x, y, w, h);
    531 }
    532 
    533 static	void
    534 scrollwindow(windowrec, x0, y0)
    535 	struct WindowRec *windowrec;
    536 	int	x0, y0;
    537 {
    538 	int	x, y;
    539 	int	x2 = 0, y2 = 0;
    540 	int	ww, hh;
    541 
    542 	x = x0 - windowrec->base_x;
    543 	y = y0 - windowrec->base_y;
    544 	ww = windowrec->width - x;
    545 	hh = windowrec->height - y;
    546 	windowrec->base_x = x0;
    547 	windowrec->base_y = y0;
    548 	if (currwin.win == windowrec->win) {
    549 	    currwin.base_x = x0;
    550 	    currwin.base_y = y0;
    551 	}
    552 	windowrec->min_x -= x;
    553 	if (windowrec->min_x < 0) windowrec->min_x = 0;
    554 	windowrec->max_x -= x;
    555 	if (windowrec->max_x > windowrec->width)
    556 	    windowrec->max_x = windowrec->width;
    557 	windowrec->min_y -= y;
    558 	if (windowrec->min_y < 0) windowrec->min_y = 0;
    559 	windowrec->max_y -= y;
    560 	if (windowrec->max_y > windowrec->height)
    561 	    windowrec->max_y = windowrec->height;
    562 	if (x < 0) {
    563 	    x2 = -x;
    564 	    x = 0;
    565 	    ww = windowrec->width - x2;
    566 	}
    567 	if (y < 0) {
    568 	    y2 = -y;
    569 	    y = 0;
    570 	    hh = windowrec->height - y2;
    571 	}
    572 	if (ww <= 0 || hh <= 0) {
    573 	    XClearWindow(DISP, windowrec->win);
    574 	    windowrec->min_x = windowrec->min_y = 0;
    575 	    windowrec->max_x = windowrec->width;
    576 	    windowrec->max_y = windowrec->height;
    577 	}
    578 	else {
    579 	    XCopyArea(DISP, windowrec->win, windowrec->win, copyGC,
    580 		x, y, (unsigned int) ww, (unsigned int) hh, x2, y2);
    581 	    if (x > 0)
    582 		clearexpose(windowrec, ww, 0,
    583 		    (unsigned int) x, windowrec->height);
    584 	    if (x2 > 0)
    585 		clearexpose(windowrec, 0, 0,
    586 		    (unsigned int) x2, windowrec->height);
    587 	    if (y > 0)
    588 		clearexpose(windowrec, 0, hh,
    589 		    windowrec->width, (unsigned int) y);
    590 	    if (y2 > 0)
    591 		clearexpose(windowrec, 0, 0,
    592 		    windowrec->width, (unsigned int) y2);
    593 	}
    594 }
    595 
    596 
    597 /*
    598  *	routines for X11 toolkit
    599  */
    600 
    601 Dimension	window_w, window_h;
    602 
    603 static	Arg	arg_wh[] = {
    604 	{XtNwidth,	(XtArgVal) &window_w},
    605 	{XtNheight,	(XtArgVal) &window_h},
    606 };
    607 
    608 static	Position	window_x, window_y;
    609 static	Arg	arg_xy[] = {
    610 	{XtNx,		(XtArgVal) &window_x},
    611 	{XtNy,		(XtArgVal) &window_y},
    612 };
    613 
    614 #define	get_xy()	XtGetValues(draw_widget, arg_xy, XtNumber(arg_xy))
    615 
    616 #define	mane_base_x	0
    617 #define	mane_base_y	0
    618 
    619 
    620 
    621 
    622 static void
    623 home(Boolean scrl)
    624 {
    625 	if (!scrl) XUnmapWindow(DISP, mane.win);
    626 	get_xy();
    627 	if (x_bar != NULL) {
    628 	    XtCallCallbacks(x_bar, XtNscrollProc,
    629 		(XtPointer) (ptrdiff_t) window_x);
    630 	}
    631 	if (y_bar != NULL) {
    632 	    XtCallCallbacks(y_bar, XtNscrollProc,
    633 		(XtPointer) (ptrdiff_t) window_y);
    634 	}
    635 	if (!scrl) {
    636 	    XMapWindow(DISP, mane.win);
    637 	    /* Wait for the server to catch up---this eliminates flicker. */
    638 	    XSync(DISP, False);
    639 	}
    640 }
    641 
    642 /*
    643  *	Same as home(), except move to the bottom of the page.
    644  */
    645 
    646 static	void
    647 home_bottom(Boolean scrl)
    648 {
    649 	XUnmapWindow(DISP, mane.win);
    650 	get_xy();
    651 	if (x_bar != NULL) {
    652 	    XtCallCallbacks(x_bar, XtNscrollProc,
    653 		(XtPointer) (ptrdiff_t) (window_x));
    654 	}
    655 	if (y_bar != NULL)
    656 	    XtCallCallbacks(y_bar, XtNscrollProc,
    657 		(XtPointer) (ptrdiff_t) (window_y + (page_h - clip_h)));
    658 	XMapWindow(DISP, mane.win);
    659 	/* Wait for the server to catch up---this eliminates flicker. */
    660 	XSync(DISP, False);
    661 }
    662 
    663 
    664 	/*ARGSUSED*/
    665 static	void
    666 handle_destroy_bar(w, client_data, call_data)
    667 	Widget		w;
    668 	XtPointer	client_data;
    669 	XtPointer	call_data;
    670 {
    671 	* (Widget *) client_data = NULL;
    672 }
    673 
    674 
    675 static	Boolean	resized	= False;
    676 
    677 static	void
    678 get_geom()
    679 {
    680 	static	Dimension	new_clip_w, new_clip_h;
    681 	static	Arg		arg_wh_clip[] = {
    682 		{XtNwidth,	(XtArgVal) &new_clip_w},
    683 		{XtNheight,	(XtArgVal) &new_clip_h},
    684 	};
    685 	int		old_clip_w;
    686 
    687 	XtGetValues(vport_widget, arg_wh, XtNumber(arg_wh));
    688 	XtGetValues(clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
    689 	/* Note:  widgets may be destroyed but not forgotten */
    690 	if (x_bar == NULL) {
    691 	    x_bar = XtNameToWidget(vport_widget, "horizontal");
    692 	    if (x_bar != NULL)
    693 		XtAddCallback(x_bar, XtNdestroyCallback, handle_destroy_bar,
    694 		    (XtPointer) &x_bar);
    695 	}
    696 	if (y_bar == NULL) {
    697 	    y_bar = XtNameToWidget(vport_widget, "vertical");
    698 	    if (y_bar != NULL)
    699 		XtAddCallback(y_bar, XtNdestroyCallback, handle_destroy_bar,
    700 		    (XtPointer) &y_bar);
    701 	}
    702 	old_clip_w = clip_w;
    703 			/* we need to do this because */
    704 			/* sizeof(Dimension) != sizeof(int) */
    705 	clip_w = new_clip_w;
    706 	clip_h = new_clip_h;
    707 	if (old_clip_w == 0) ev_flags |= EV_NEWPAGE;
    708 	resized = False;
    709 }
    710 
    711 /*
    712  *	callback routines
    713  */
    714 
    715 	/*ARGSUSED*/
    716 void
    717 handle_resize(widget, junk, event, cont)
    718 	Widget	widget;
    719 	XtPointer junk;
    720 	XEvent	*event;
    721 	Boolean	*cont;		/* unused */
    722 {
    723 	resized = True;
    724 }
    725 
    726 
    727 static void
    728 reconfig()
    729 {
    730 	XdviResizeWidget(draw_widget, page_w, page_h);
    731 	get_geom();
    732 }
    733 
    734 
    735 
    736 /*
    737  *	goto_page is the only place where current_page should be set,
    738  *	other than when reading a new dvi file.
    739  */
    740 
    741 static	home_proc	home_action	= home;
    742 
    743 static void
    744 goto_page(n, proc)
    745 	int		n;
    746 	home_proc	proc;
    747 {
    748 	current_page = n;
    749 	home_action = proc;
    750 	warn_spec_now = resource._warn_spec;
    751 }
    752 
    753 
    754 
    755 
    756 
    757 /* |||
    758  *	Currently the event handler does not coordinate XCopyArea requests
    759  *	with GraphicsExpose events.  This can lead to problems if the window
    760  *	is partially obscured and one, for example, drags a scrollbar.
    761  */
    762 
    763 /*
    764  *	Actions for the translation mechanism.
    765  */
    766 
    767 static	Boolean	have_arg	= False;
    768 static	int	number		= 0;
    769 static	int	sign		= 1;
    770 
    771 
    772 #define	GET_ARG4(arg, param, param2, default)		\
    773 		if (*num_params > 0)			\
    774 		    {param;}				\
    775 		else {					\
    776 		    if (have_arg) {			\
    777 			arg = (param2);			\
    778 			have_arg = False;		\
    779 			number = 0;			\
    780 			sign = 1;			\
    781 		    }					\
    782 		    else				\
    783 			{default}			\
    784 		}
    785 
    786 #define	GET_ARG(arg, default)				\
    787 		GET_ARG4(arg, arg = atoi(*params), sign * number, \
    788 		  arg = (default);)
    789 
    790 #define	GET_ARG6(arg, param, c, param_c, param2, default)\
    791 		if (*num_params > 0) {			\
    792 		    if (**params == (c))		\
    793 			{param_c;}			\
    794 		    else				\
    795 			{param;}			\
    796 		}					\
    797 		else {					\
    798 		    if (have_arg) {			\
    799 			arg = (param2);			\
    800 			have_arg = False;		\
    801 			number = 0;			\
    802 			sign = 1;			\
    803 		    }					\
    804 		    else				\
    805 			{default;}			\
    806 		}
    807 
    808 #define	TOGGLE(arg)							\
    809 	if (*num_params > 0) {						\
    810 	    if (**params != 't' && (atoi(*params) != 0) == arg)		\
    811 		return;							\
    812 	}								\
    813 	else {								\
    814 	    if (have_arg) {						\
    815 		int	tmparg = number;				\
    816 									\
    817 		have_arg = False;					\
    818 		number = 0;						\
    819 		sign = 1;						\
    820 									\
    821 		if ((tmparg != 0) == arg)				\
    822 		    return;						\
    823 	    }								\
    824 	}
    825 
    826 
    827 
    828 static
    829 ACTION(Act_digit)
    830 {
    831 	unsigned int	digit;
    832 
    833 	if (*num_params != 1 || (digit = **params - '0') > 9) {
    834 	    XBell(DISP, 0);
    835 	    return;
    836 	}
    837 	have_arg = True;
    838 	number = number * 10 + digit;
    839 }
    840 
    841 static
    842 ACTION(Act_minus)
    843 {
    844 	have_arg = True;
    845 	sign = -sign;
    846 }
    847 
    848 static
    849 ACTION(Act_quit)
    850 {
    851 	exit(1);
    852 }
    853 
    854 static
    855 ACTION(Act_goto_page)
    856 {
    857 	int	arg;
    858 
    859 	GET_ARG6(arg, arg = atoi(*params) - pageno_correct,
    860 	  'e', arg = total_pages - 1,
    861 	  sign * number - pageno_correct, arg = total_pages - 1);
    862 
    863 	if (arg < 0 || arg >= total_pages) {
    864 	    XBell(DISP, 0);
    865 	    return;
    866 	}
    867 
    868 	if (current_page != arg)
    869 	    goto_page(arg, home);
    870 
    871 	ev_flags |= EV_NEWPAGE;
    872 	XFlush(DISP);
    873 	return;		/* Don't use longjmp here:  it might be called from
    874 			 * within the toolkit, and we don't want to longjmp out
    875 			 * of Xt routines. */
    876 }
    877 
    878 static
    879 ACTION(Act_forward_page)
    880 {
    881 	int	arg;
    882 
    883 	GET_ARG(arg, 1);
    884 	arg += current_page;
    885 
    886 	if (arg >= 0 && arg < total_pages) {
    887 	    if (current_page != arg)
    888 		goto_page(arg, home);
    889 	    /* Control-L (and changing the page) clears this box */
    890 	    ev_flags |= EV_NEWPAGE;
    891 	    XFlush(DISP);
    892 	    return;	/* Don't use longjmp here:  it might be called from
    893 			 * within the toolkit, and we don't want to longjmp out
    894 			 * of Xt routines. */
    895 	}
    896 	XBell(DISP, 0);
    897 }
    898 
    899 static
    900 ACTION(Act_back_page)
    901 {
    902 	int	arg;
    903 
    904 	GET_ARG(arg, 1);
    905 	arg = current_page - arg;
    906 
    907 	if (arg >= 0 && arg < total_pages) {
    908 	    if (current_page != arg)
    909 		goto_page(arg, home);
    910 	    ev_flags |= EV_NEWPAGE;
    911 	    XFlush(DISP);
    912 	    return;	/* Don't use longjmp here:  it might be called from
    913 			 * within the toolkit, and we don't want to longjmp out
    914 			 * of Xt routines. */
    915 	}
    916 	XBell(DISP, 0);
    917 }
    918 
    919 static
    920 ACTION(Act_declare_page_number)
    921 {
    922 	int	arg;
    923 
    924 	GET_ARG(arg, 0);
    925 	pageno_correct = arg - current_page;
    926 }
    927 
    928 static
    929 ACTION(Act_home)
    930 {
    931 	home(True);
    932 }
    933 
    934 static
    935 ACTION(Act_center)
    936 {
    937 	int	x, y;
    938 
    939 	x = event->xkey.x - clip_w / 2;
    940 	y = event->xkey.y - clip_h / 2;
    941 	/* The clip widget gives a more exact value. */
    942 	if (x_bar != NULL)
    943 	    XtCallCallbacks(x_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) x);
    944 	if (y_bar != NULL)
    945 	    XtCallCallbacks(y_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) y);
    946 	XWarpPointer(XtDisplay(w), None, None, 0, 0, 0, 0, -x, -y);
    947 }
    948 
    949 static
    950 ACTION(Act_left)
    951 {
    952 	if (x_bar != NULL)
    953 	    XtCallCallbacks(x_bar, XtNscrollProc,
    954 	      (XtPointer) (*num_params == 0 ? (-2 * (ptrdiff_t) clip_w / 3)
    955 	      : (ptrdiff_t) (-atof(*params) * clip_w)));
    956 	else
    957 	    XBell(XtDisplay(w), 0);
    958 }
    959 
    960 static
    961 ACTION(Act_right)
    962 {
    963 	if (x_bar != NULL)
    964 	    XtCallCallbacks(x_bar, XtNscrollProc,
    965 	      (XtPointer) (*num_params == 0 ? (2 * (ptrdiff_t) clip_w / 3)
    966 	      : (ptrdiff_t) (atof(*params) * clip_w)));
    967 	else
    968 	    XBell(XtDisplay(w), 0);
    969 }
    970 
    971 static
    972 ACTION(Act_up)
    973 {
    974 	if (y_bar != NULL)
    975 	    XtCallCallbacks(y_bar, XtNscrollProc,
    976 	      (XtPointer) (*num_params == 0 ? (-2 * (ptrdiff_t) clip_h / 3)
    977 	      : (ptrdiff_t) (-atof(*params) * clip_h)));
    978 	else
    979 	    XBell(XtDisplay(w), 0);
    980 }
    981 
    982 static
    983 ACTION(Act_down)
    984 {
    985 	if (y_bar != NULL)
    986 	    XtCallCallbacks(y_bar, XtNscrollProc,
    987 	      (XtPointer) (*num_params == 0 ? (2 * (ptrdiff_t) clip_h / 3)
    988 	      : (ptrdiff_t) (atof(*params) * clip_h)));
    989 	else
    990 	    XBell(XtDisplay(w), 0);
    991 }
    992 
    993 static
    994 ACTION(Act_down_or_next)
    995 {
    996 	if (y_bar != NULL) {
    997 		get_xy();
    998 		if (window_y > (int) clip_h - (int) page_h) {
    999 		    XtCallCallbacks(y_bar, XtNscrollProc,
   1000 		      (XtPointer) (*num_params == 0
   1001 		      ? (2 * (ptrdiff_t) clip_h / 3)
   1002 		      : (ptrdiff_t) (atof(*params) * clip_h)));
   1003 		    return;
   1004 		}
   1005 	}
   1006 
   1007 	if (current_page < total_pages - 1) {
   1008 	    goto_page(current_page + 1, home);
   1009 	    ev_flags |= EV_NEWPAGE;
   1010 	    XFlush(XtDisplay(w));
   1011 	    return;	/* Don't use longjmp here:  it might be called from
   1012 			 * within the toolkit, and we don't want to longjmp out
   1013 			 * of Xt routines. */
   1014 	}
   1015 	else
   1016 	    XBell(XtDisplay(w), 0);
   1017 }
   1018 
   1019 static
   1020 ACTION(Act_up_or_previous)
   1021 {
   1022 	if (y_bar != NULL) {
   1023 		get_xy();
   1024 		if (window_y < 0) {
   1025 		    XtCallCallbacks(y_bar, XtNscrollProc,
   1026 		      (XtPointer) (*num_params == 0
   1027 		      ? (-2 * (ptrdiff_t) clip_h / 3)
   1028 		      : (ptrdiff_t) (-atof(*params) * clip_h)));
   1029 		    return;
   1030 		}
   1031 	}
   1032 
   1033 	if (current_page > 0) {
   1034 	    goto_page(current_page - 1, home_bottom);
   1035 	    ev_flags |= EV_NEWPAGE;
   1036 	    XFlush(XtDisplay(w));
   1037 	    return;	/* Don't use longjmp here:  it might be called from
   1038 			 * within the toolkit, and we don't want to longjmp out
   1039 			 * of Xt routines. */
   1040 	}
   1041 	else
   1042 	    XBell(XtDisplay(w), 0);
   1043 }
   1044 
   1045 static
   1046 ACTION(Act_show_display_attributes)
   1047 {
   1048 	Display *disp = XtDisplay(w);
   1049 
   1050 	printf("Unit = %d, bitord = %d, byteord = %d\n",
   1051 	    BitmapUnit(disp), BitmapBitOrder(disp), ImageByteOrder(disp));
   1052 }
   1053 
   1054 static	int
   1055 shrink_to_fit()
   1056 {
   1057 	int	value1;
   1058 	int	value2;
   1059 
   1060 	value1 = ROUNDUP(unshrunk_page_w, window_w - 2);
   1061 
   1062 	value2 = ROUNDUP(unshrunk_page_h, window_h - 2);
   1063 
   1064 	return value1 > value2 ? value1 : value2;
   1065 }
   1066 
   1067 static
   1068 ACTION(Act_set_shrink_factor)
   1069 {
   1070 	int	arg;
   1071 
   1072 	GET_ARG6(arg, arg = atoi(*params), 'a', arg = shrink_to_fit(),
   1073 	  number, arg = shrink_to_fit());
   1074 
   1075 	if (arg <= 0 || arg > 10) {
   1076 	    XBell(XtDisplay(w), 0);
   1077 	    return;
   1078 	}
   1079 
   1080 	if (arg == mane.shrinkfactor)
   1081 	    return;
   1082 	mane.shrinkfactor = arg;
   1083 	if (arg != 1 && arg != bak_shrink) {
   1084 	    bak_shrink = arg;
   1085 	    fg_active = NULL;
   1086 	    reset_fonts();
   1087 	}
   1088 
   1089 	if (dvi_file == NULL) {
   1090 	    unshrunk_page_w = unshrunk_page_h = 0;
   1091 	    return;
   1092 	}
   1093 
   1094 	init_page();
   1095 	reconfig();
   1096 	home(False);
   1097 	ev_flags |= EV_NEWPAGE;
   1098 	XFlush(XtDisplay(w));
   1099 }
   1100 
   1101 static
   1102 ACTION(Act_shrink_to_dpi)
   1103 {
   1104 	int	arg;
   1105 
   1106 	GET_ARG(arg, 0);
   1107 
   1108 	if (arg > 0)
   1109 	    arg = (double) pixels_per_inch / arg + 0.5;
   1110 
   1111 	if (arg <= 0) {
   1112 	    XBell(XtDisplay(w), 0);
   1113 	    return;
   1114 	}
   1115 
   1116 	if (arg == mane.shrinkfactor)
   1117 	    return;
   1118 	mane.shrinkfactor = arg;
   1119 	if (arg != 1 && arg != bak_shrink) {
   1120 	    bak_shrink = arg;
   1121 	    fg_active = NULL;
   1122 	    reset_fonts();
   1123 	}
   1124 
   1125 	if (dvi_file == NULL) {
   1126 	    unshrunk_page_w = unshrunk_page_h = 0;
   1127 	    return;
   1128 	}
   1129 
   1130 	init_page();
   1131 	reconfig();
   1132 	home(False);
   1133 	ev_flags |= EV_NEWPAGE;
   1134 	XFlush(XtDisplay(w));
   1135 }
   1136 
   1137 static
   1138 ACTION(Act_set_density)
   1139 {
   1140 	int	arg;
   1141 
   1142 	GET_ARG4(arg, arg = atoi(*params), sign * number,
   1143 	  {XBell(XtDisplay(w), 0); return;});
   1144 
   1145 	float newgamma = arg != 0 ? arg / 100.0 : 1.0;
   1146 
   1147 	/*
   1148 	 * use_grey
   1149 	 */
   1150 	if (newgamma == gamma)
   1151 		return;
   1152 	gamma = newgamma;
   1153 	fg_active = NULL;
   1154 	reset_colors();
   1155 
   1156 	ev_flags |= EV_NEWPAGE;
   1157 	XFlush(XtDisplay(w));
   1158 }
   1159 
   1160 
   1161 static
   1162 ACTION(Act_set_color)
   1163 {
   1164 	TOGGLE(resource._use_color)
   1165 
   1166 	if (resource._use_color) {
   1167 	    resource._use_color = False;
   1168 	    full_reset_colors();
   1169 	    scanned_page_color = total_pages;
   1170 	}
   1171 	else {
   1172 	    resource._use_color = True;
   1173 	    scanned_page = scanned_page_color = scanned_page_reset;
   1174 	}
   1175 	ev_flags |= EV_NEWPAGE;
   1176 }
   1177 
   1178 
   1179 static
   1180 ACTION(Act_discard_number)
   1181 {
   1182 	have_arg = False;
   1183 	number = 0;
   1184 	sign = 1;
   1185 }
   1186 
   1187 
   1188 /* Actions to support the magnifying glass.  */
   1189 
   1190 static	void	mag_motion(XEvent *);
   1191 static	void	mag_release(XEvent *);
   1192 
   1193 static	void
   1194 compute_mag_pos(xp, yp)
   1195 	int	*xp, *yp;
   1196 {
   1197 	int	t;
   1198 
   1199 	t = mag_x + main_x - alt.width/2;
   1200 	if (t > WidthOfScreen(SCRN) - (int) alt.width - 2*MAGBORD)
   1201 	    t = WidthOfScreen(SCRN) - (int) alt.width - 2*MAGBORD;
   1202 	if (t < 0) t = 0;
   1203 	*xp = t;
   1204 	t = mag_y + main_y - alt.height/2;
   1205 	if (t > HeightOfScreen(SCRN) - (int) alt.height - 2*MAGBORD)
   1206 	    t = HeightOfScreen(SCRN) - (int) alt.height - 2*MAGBORD;
   1207 	if (t < 0) t = 0;
   1208 	*yp = t;
   1209 }
   1210 
   1211 static
   1212 ACTION(Act_magnifier)
   1213 {
   1214 	const char		*p;
   1215 	int			x, y;
   1216 	XSetWindowAttributes	attr;
   1217 	Window			throwaway;
   1218 	Display			*disp;
   1219 
   1220 	if (!check_dvi_file())
   1221 	    return;
   1222 
   1223 	disp = XtDisplay(w);
   1224 
   1225 	if (event->type != ButtonPress || mouse_release != null_mouse
   1226 	  || alt.win != (Window) 0 || mane.shrinkfactor == 1
   1227 	  || *num_params != 1) {
   1228 	    XBell(disp, 0);
   1229 	    return;
   1230 	}
   1231 
   1232 	p = *params;
   1233 	if (*p == '*') {
   1234 	    int n = atoi(p + 1) - 1;
   1235 
   1236 	    if (n < 0 || n >= 5 || mg_size[n].w <= 0) {
   1237 		XBell(disp, 0);
   1238 		return;
   1239 	    }
   1240 	    alt.width = mg_size[n].w;
   1241 	    alt.height = mg_size[n].h;
   1242 	}
   1243 	else {
   1244 	    alt.width = alt.height = atoi(p);
   1245 	    p = index(p, 'x');
   1246 	    if (p != NULL) {
   1247 		alt.height = atoi(p + 1);
   1248 		if (alt.height == 0) alt.width = 0;
   1249 	    }
   1250 	    if (alt.width == 0) {
   1251 		XBell(disp, 0);
   1252 		return;
   1253 	    }
   1254 	}
   1255 	(void) XTranslateCoordinates(XtDisplay(w), event->xbutton.window, mane.win,
   1256 	  0, 0, &mag_conv_x, &mag_conv_y, &throwaway);
   1257 
   1258 	mag_x = event->xbutton.x + mag_conv_x;
   1259 	mag_y = event->xbutton.y + mag_conv_y;
   1260 	main_x = event->xbutton.x_root - mag_x;
   1261 	main_y = event->xbutton.y_root - mag_y;
   1262 	compute_mag_pos(&x, &y);
   1263 	alt.base_x = (mag_x + mane_base_x) * mane.shrinkfactor - alt.width/2;
   1264 	alt.base_y = (mag_y + mane_base_y) * mane.shrinkfactor - alt.height/2;
   1265 	attr.save_under = True;
   1266 	attr.border_pixel = brdr_Pixel;
   1267 	attr.background_pixel = bg_current->pixel;
   1268 	attr.override_redirect = True;
   1269 	alt.win = XCreateWindow(disp, RootWindowOfScreen(SCRN),
   1270 		    x, y, alt.width, alt.height, MAGBORD,
   1271 		    CopyFromParent, InputOutput, CopyFromParent,
   1272 		    CWSaveUnder | CWBorderPixel | CWBackPixel |
   1273 		    CWOverrideRedirect, &attr);
   1274 	XSelectInput(disp, alt.win, ExposureMask);
   1275 	XMapWindow(disp, alt.win);
   1276 	alt_stat = 1;	/* waiting for exposure */
   1277 	mouse_motion = mag_motion;
   1278 	mouse_release = mag_release;
   1279 }
   1280 
   1281 static	void
   1282 mag_motion(XEvent *event)
   1283 {
   1284 	new_mag_x = event->xmotion.x + mag_conv_x;
   1285 	main_x = event->xmotion.x_root - new_mag_x;
   1286 	new_mag_y = event->xmotion.y + mag_conv_y;
   1287 	main_y = event->xmotion.y_root - new_mag_y;
   1288 
   1289 	if (new_mag_x != mag_x || new_mag_y != mag_y)
   1290 	    ev_flags |= EV_MAG_MOVE;
   1291 	else
   1292 	    ev_flags &= ~EV_MAG_MOVE;
   1293 }
   1294 
   1295 /* ARGSUSED */
   1296 static	void
   1297 mag_release(XEvent *event)
   1298 {
   1299 	if (alt.win != (Window) 0) {
   1300 	    if (alt_stat)
   1301 		alt_stat = -1;	/* destroy upon expose */
   1302 	    else {
   1303 		XDestroyWindow(DISP, alt.win);
   1304 		if (drawing_mag) ev_flags |= EV_MAG_GONE;
   1305 		alt.win = (Window) 0;
   1306 		mouse_motion = mouse_release = null_mouse;
   1307 		ev_flags &= ~EV_MAG_MOVE;
   1308 		can_exposures(&alt);
   1309 	    }
   1310 	}
   1311 }
   1312 
   1313 static	void
   1314 movemag(x, y)
   1315 	int	x, y;
   1316 {
   1317 	int	xx, yy;
   1318 
   1319 	mag_x = x;
   1320 	mag_y = y;
   1321 	if (mag_x == new_mag_x && mag_y == new_mag_y) ev_flags &= ~EV_MAG_MOVE;
   1322 	compute_mag_pos(&xx, &yy);
   1323 	XMoveWindow(DISP, alt.win, xx, yy);
   1324 	scrollwindow(&alt,
   1325 	    (x + mane_base_x) * mane.shrinkfactor - (int) alt.width/2,
   1326 	    (y + mane_base_y) * mane.shrinkfactor - (int) alt.height/2);
   1327 }
   1328 
   1329 
   1330 /* Actions to support dragging the image.  */
   1331 
   1332 static	int	drag_last_x, drag_last_y;	/* last position of cursor */
   1333 static	int	drag_flags;			/* 1 = vert, 2 = horiz */
   1334 
   1335 static	void	drag_motion(XEvent *);
   1336 static	void	drag_release(XEvent *);
   1337 
   1338 static
   1339 ACTION(Act_drag)
   1340 {
   1341 	if (mouse_release != null_mouse && mouse_release != drag_release)
   1342 	    return;
   1343 
   1344 
   1345 	if (*num_params != 1) return;
   1346 	switch (**params) {
   1347 	    case '|':  drag_flags = 1; break;
   1348 	    case '-':  drag_flags = 2; break;
   1349 	    case '+':  drag_flags = 3; break;
   1350 	    default:   return;
   1351 	}
   1352 
   1353 
   1354 	if (mouse_release == null_mouse) {
   1355 	    mouse_motion = drag_motion;
   1356 	    mouse_release = drag_release;
   1357 	    drag_last_x = event->xbutton.x_root;
   1358 	    drag_last_y = event->xbutton.y_root;
   1359 	}
   1360 	else
   1361 	    drag_motion(event);
   1362 
   1363 	XFlush(XtDisplay(w));
   1364 	dragcurs = True;
   1365 }
   1366 
   1367 
   1368 static	void
   1369 drag_motion(XEvent *event)
   1370 {
   1371 
   1372 	if (drag_flags & 2) {	/* if horizontal motion */
   1373 	    if (x_bar != NULL)
   1374 		XtCallCallbacks(x_bar, XtNscrollProc,
   1375 		  (XtPointer) (ptrdiff_t)
   1376 		  (drag_last_x - event->xbutton.x_root));
   1377 	    drag_last_x = event->xbutton.x_root;
   1378 	}
   1379 
   1380 	if (drag_flags & 1) {	/* if vertical motion */
   1381 	    if (y_bar != NULL)
   1382 		XtCallCallbacks(y_bar, XtNscrollProc,
   1383 		  (XtPointer) (ptrdiff_t)
   1384 		  (drag_last_y - event->xbutton.y_root));
   1385 	    drag_last_y = event->xbutton.y_root;
   1386 	}
   1387 }
   1388 
   1389 
   1390 static	void
   1391 drag_release(XEvent *event)
   1392 {
   1393 	drag_motion(event);
   1394 	mouse_motion = mouse_release = null_mouse;
   1395 
   1396 	XDefineCursor(DISP, CURSORWIN,
   1397 	  ev_flags & EV_CURSOR ? redraw_cursor : ready_cursor);
   1398 	XFlush(DISP);
   1399 	dragcurs = False;
   1400 }
   1401 
   1402 
   1403 
   1404 /* Wheel support.  */
   1405 
   1406 static	int	wheel_button	= -1;
   1407 
   1408 static
   1409 ACTION(Act_wheel)
   1410 {
   1411 	int	dist;
   1412 
   1413 	if (*num_params == 0) {
   1414 	    XBell(XtDisplay(w), 0);
   1415 	    return;
   1416 	}
   1417 	dist = (index(*params, '.') == NULL) ? atoi(*params)
   1418 	  : (int) (atof(*params) * resource.wheel_unit);
   1419 	if (y_bar != NULL)
   1420 	    XtCallCallbacks(y_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) dist);
   1421 
   1422 	if (event != NULL)
   1423 	    wheel_button = event->xbutton.button;
   1424 }
   1425 
   1426 static	int	wheel_h_button	= -1;
   1427 
   1428 static
   1429 ACTION(Act_hwheel)
   1430 {
   1431 	int	dist;
   1432 
   1433 	if (*num_params == 0) {
   1434 	    XBell(XtDisplay(w), 0);
   1435 	    return;
   1436 	}
   1437 	dist = (index(*params, '.') == NULL) ? atoi(*params)
   1438 	  : (int) (atof(*params) * resource.wheel_unit);
   1439 	if (x_bar != NULL)
   1440 	    XtCallCallbacks(x_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) dist);
   1441 
   1442 	if (event != NULL)
   1443 	    wheel_h_button = event->xbutton.button;
   1444 }
   1445 
   1446 static
   1447 ACTION(Act_wheel_actions)
   1448 {
   1449 	struct wheel_acts *wactp;
   1450 	struct xdvi_action *actp;
   1451 
   1452 	for (wactp = wheel_actions; wactp != NULL; wactp = wactp->next)
   1453 	    if (event->xbutton.button == wactp->button || wactp->button == 0) {
   1454 		Modifiers mask = 0;
   1455 		Modifiers value = 0;
   1456 
   1457 		if (wactp->late_bindings == NULL
   1458 		  || _XtComputeLateBindings(XtDisplay(w), wactp->late_bindings,
   1459 		  &value, &mask)) {
   1460 		    mask |= wactp->mask;
   1461 		    value |= wactp->value;
   1462 		    if (((value ^ event->xbutton.state) & mask) == 0) {
   1463 			for (actp = wactp->action; actp != NULL;
   1464 			  actp = actp->next)
   1465 			    (actp->proc)(w, event,
   1466 			      &actp->param, &actp->num_params);
   1467 			return;
   1468 		    }
   1469 		}
   1470 	    }
   1471 }
   1472 
   1473 
   1474 /* Internal mouse actions.  */
   1475 
   1476 
   1477 static
   1478 ACTION(Act_motion)
   1479 {
   1480 	if (event->xbutton.button != wheel_button
   1481 	   && event->xbutton.button != wheel_h_button)
   1482 	    mouse_motion(event);
   1483 }
   1484 
   1485 
   1486 static
   1487 ACTION(Act_release)
   1488 {
   1489 	if (event->xbutton.button == wheel_button) {
   1490 	    wheel_button = -1;
   1491 	    return;
   1492 	}
   1493 
   1494 	if (event->xbutton.button == wheel_h_button) {
   1495 	    wheel_h_button = -1;
   1496 	    return;
   1497 	}
   1498 
   1499 	mouse_release(event);
   1500 }
   1501 
   1502 static Boolean
   1503 new_dvi(const char *url)
   1504 {
   1505 	FILE *tmp;
   1506 
   1507 	if ((tmp = http_get(url)) == NULL)
   1508 		return False;
   1509 
   1510 	if (dvi_magic_ok(tmp) == False) {
   1511 		warning_popup_long("%s: Not a DVI file", "OK", NULL, url);
   1512 		return False;
   1513 	}
   1514 
   1515 	if (fseek(tmp, 0L, SEEK_SET) < 0)
   1516 		err(1, "fseek");
   1517 
   1518 	fclose(dvi_file);
   1519 	dvi_file = tmp;
   1520 	ev_flags |= EV_NEWDOC;
   1521 	goto_page(0, home);
   1522 
   1523 	return True;
   1524 }
   1525 
   1526 static void
   1527 cb_addr_go(Widget w, XtPointer client_data, XtPointer call_data)
   1528 {
   1529 	const char	*s;
   1530 
   1531 	if (w == NULL)
   1532 		s = addr_default;
   1533 	else
   1534 		XtVaGetValues(addr_widget, XtNstring, &s, NULL);
   1535 
   1536 	new_dvi(s);
   1537 }
   1538 
   1539 static
   1540 ACTION(Act_addr_go)
   1541 {
   1542 	cb_addr_go(w, NULL, NULL);
   1543 }
   1544 
   1545 
   1546 static Boolean link_mode = False;
   1547 struct htex_data htex_data;
   1548 
   1549 static
   1550 ACTION(Act_show_links)
   1551 {
   1552 	bzero(&htex_data, sizeof(struct htex_data));
   1553 	hypertex_search(&htex_data);
   1554 
   1555 	link_mode = False;
   1556 
   1557 #define numitems(_a)	(sizeof(_a) / sizeof((_a)[0]))
   1558 	for (int i = 0; i < numitems(htex_data.links); i++)
   1559 		if (htex_data.links[i] != NULL) {
   1560 			link_mode = True;
   1561 			free(htex_data.links[i]);
   1562 		}
   1563 
   1564 }
   1565 
   1566 #undef	ACTION
   1567 #undef	GET_ARG4
   1568 #undef	GET_ARG
   1569 #undef	TOGGLE
   1570 
   1571 	/*ARGSUSED*/
   1572 void
   1573 handle_expose(widget, closure, ev, cont)
   1574 	Widget	widget;
   1575 	XtPointer closure;
   1576 	XEvent	*ev;
   1577 #define	event	(&(ev->xexpose))
   1578 	Boolean	*cont;		/* unused */
   1579 {
   1580 	struct WindowRec *windowrec = (struct WindowRec *) closure;
   1581 
   1582 	if (windowrec == &alt) {
   1583 	    if (alt_stat < 0) {	/* destroy upon exposure */
   1584 		alt_stat = 0;
   1585 		mag_release(ev);
   1586 		return;
   1587 	    }
   1588 	    else
   1589 		alt_stat = 0;
   1590 	}
   1591 
   1592 	expose(windowrec, event->x, event->y,
   1593 	    (unsigned int) event->width, (unsigned int) event->height);
   1594 }
   1595 
   1596 #undef	event
   1597 
   1598 void
   1599 handle_messages(widget, closure, event, cont)
   1600 	Widget	widget;
   1601 	XtPointer closure;
   1602 	XEvent	*event;
   1603 	Boolean	*cont;		/* unused */
   1604 {
   1605 	if (event->type == ClientMessage
   1606 	  && event->xclient.message_type == XA_WM_PROTOCOLS
   1607 	  && event->xclient.data.l[0] == XA_WM_DELETE_WINDOW) {
   1608 	    if (closure != 0)
   1609 		((XtCallbackProc) closure) (widget, NULL, NULL);
   1610 	    else
   1611 		Act_quit(widget, event, NULL, 0);
   1612 	}
   1613 }
   1614 
   1615 static void
   1616 handle_keypress(Widget w, XtPointer closure, XEvent *event, Boolean *cont)
   1617 {
   1618 	Modifiers	 modifiers;
   1619 	KeySym		 keysym;
   1620 	int		 index;
   1621 	const char	*url;
   1622 
   1623 	/*
   1624 	 * Clear the highlights. This second call to the drawing routines
   1625 	 * clears existing highlights.
   1626 	 */
   1627 	bzero(&htex_data, sizeof(struct htex_data));
   1628 	hypertex_search(&htex_data);
   1629 	link_mode = False;
   1630 
   1631 	XtTranslateKeycode(DISP, event->xkey.keycode, 0, &modifiers, &keysym);
   1632 
   1633 	index = tolower(keysym) - 'a';
   1634 	if (index < 0 || index > 26)
   1635 		return;
   1636 
   1637 	url = htex_data.links[index];
   1638 	if (url == NULL)
   1639 		return;
   1640 
   1641 	if (new_dvi(url) == False)
   1642 		return;
   1643 
   1644 	XtVaSetValues(addr_widget, XtNstring, url, NULL);
   1645 	XtVaSetValues(addr_widget, XtNinsertPosition, strlen(url), NULL);
   1646 }
   1647 
   1648 
   1649 /*
   1650  *	Process-related routines.  Call set_chld() to indicate that a given
   1651  *	child process should be watched for when it terminates.  Call
   1652  *	clear_chld() to remove the process from the list.  When the child
   1653  *	terminates, the record is removed from the list and the indicated
   1654  *	process is called.
   1655  */
   1656 
   1657 static	struct xchild	*child_recs	= NULL;	/* head of child process list */
   1658 
   1659 void
   1660 set_chld(cp)
   1661 	struct xchild	*cp;
   1662 {
   1663 	cp->next = child_recs;
   1664 	child_recs = cp;
   1665 }
   1666 
   1667 void
   1668 clear_chld(cp)
   1669 	struct xchild	*cp;
   1670 {
   1671 	struct xchild	**cpp;
   1672 
   1673 	for (cpp = &child_recs;;) {
   1674 	    struct xchild *cp2;
   1675 
   1676 	    cp2 = *cpp;
   1677 	    if (cp2 == cp) break;
   1678 	    cpp = &cp2->next;
   1679 	}
   1680 	*cpp = cp->next;
   1681 }
   1682 
   1683 
   1684 /*
   1685  *	File-related routines.  Call set_io() to indicate that a given fd
   1686  *	should be watched for ability to input or output data.  Call clear_io()
   1687  *	to remove it from the list.  When poll()/select() indicates that the fd
   1688  *	is available for the indicated type of i/o, the corresponding routine
   1689  *	is called.  Call clear_io() to remove an fd from the list.
   1690  *	Both set_io() and clear_io() can be called from within read_proc or
   1691  *	write_proc (although turning an io descriptor on or off is better
   1692  *	accomplished by setting the events flag in the xio structure, and
   1693  *	in the corresponding pollfd structure if the pfd pointer is not NULL
   1694  *	(it is always non-NULL when read_proc and write_proc are called)).
   1695  *	We allocate space for one additional record in the pollfd array, to
   1696  *	accommodate the fd for the X connection; this is done by initializing
   1697  *	num_fds to 1 instead of zero.
   1698  */
   1699 
   1700 static	struct xio	*iorecs	= NULL;	/* head of xio list */
   1701 
   1702 static	struct pollfd	*fds	= NULL;
   1703 static	int		num_fds	= 1;	/* current number of fds */
   1704 static	int		max_fds	= 0;	/* max allocated number of fds */
   1705 static	Boolean		io_dirty= True;	/* need to recompute fds[] array */
   1706 
   1707 void
   1708 set_io(ip)
   1709 	struct xio	*ip;
   1710 {
   1711 	ip->next = iorecs;
   1712 	iorecs = ip;
   1713 
   1714 	++num_fds;
   1715 	if (!io_dirty && num_fds <= max_fds) {
   1716 	    fds[num_fds - 1].fd = ip->fd;
   1717 	    fds[num_fds - 1].events = ip->xio_events;
   1718 	    ip->pfd = &fds[num_fds - 1];
   1719 	}
   1720 	else {
   1721 	    ip->pfd = NULL;
   1722 	    io_dirty = True;
   1723 	}
   1724 }
   1725 
   1726 void
   1727 clear_io(ip)
   1728 	struct xio	*ip;
   1729 {
   1730 	struct xio	**ipp;
   1731 
   1732 	for (ipp = &iorecs;;) {
   1733 	    struct xio *ip2;
   1734 
   1735 	    ip2 = *ipp;
   1736 	    if (ip2 == ip) break;
   1737 	    ipp = &ip2->next;
   1738 	}
   1739 	*ipp = ip->next;
   1740 
   1741 	--num_fds;
   1742 	io_dirty = True;
   1743 }
   1744 
   1745 
   1746 /*
   1747  *	Ultimately, the goal is to have this be the only place in xdvi where
   1748  *	blocking occurs.
   1749  *
   1750  *	The argument to this function should be a mask of event types (EV_*)
   1751  *	indicating which event types should cause read_events to return instead
   1752  *	of waiting for more events.  This function will always process all
   1753  *	pending events and signals before returning.
   1754  *	The return value is the value of ev_flags.
   1755  */
   1756 
   1757 unsigned int
   1758 read_events(unsigned int ret_mask)
   1759 {
   1760 	XEvent	event;
   1761 
   1762 	for(;;) {
   1763 
   1764 		if (!XtPending() && (ev_flags & ret_mask))
   1765 			return ev_flags;
   1766 
   1767 		XtNextEvent(&event);
   1768 
   1769 		if (resized)
   1770 			get_geom();
   1771 		if (event.xany.window == alt.win && event.type == Expose) {
   1772 			handle_expose((Widget) NULL, (XtPointer) &alt, &event,
   1773 				(Boolean *) NULL);
   1774 			continue;
   1775 		}
   1776 		if (link_mode == True && event.type == KeyPress) {
   1777 			handle_keypress(NULL, NULL, &event, NULL);
   1778 			continue;
   1779 		}
   1780 
   1781 		XtDispatchEvent(&event);
   1782 	}
   1783 }
   1784 
   1785 
   1786 /*
   1787  *	Higher-level routines for managing events.
   1788  */
   1789 
   1790 static	void
   1791 can_exposures(windowrec)
   1792 	struct WindowRec *windowrec;
   1793 {
   1794 	windowrec->min_x = windowrec->min_y = MAXDIM;
   1795 	windowrec->max_x = windowrec->max_y = 0;
   1796 }
   1797 
   1798 static	void
   1799 redraw(windowrec)
   1800 	struct WindowRec *windowrec;
   1801 {
   1802 
   1803 	currwin = *windowrec;
   1804 	min_x = currwin.min_x + currwin.base_x;
   1805 	min_y = currwin.min_y + currwin.base_y;
   1806 	max_x = currwin.max_x + currwin.base_x;
   1807 	max_y = currwin.max_y + currwin.base_y;
   1808 	can_exposures(windowrec);
   1809 
   1810 	if (debug & DBG_EVENT)
   1811 	    printf("Redraw %d x %d at (%d, %d) (base=%d,%d)\n", max_x - min_x,
   1812 		max_y - min_y, min_x, min_y, currwin.base_x, currwin.base_y);
   1813 
   1814 	if (!(ev_flags & EV_CURSOR)) {
   1815 	    if (!dragcurs) {
   1816 		XDefineCursor(DISP, CURSORWIN, redraw_cursor);
   1817 		XFlush(DISP);
   1818 	    }
   1819 	    ev_flags |= EV_CURSOR;
   1820 	}
   1821 
   1822 	draw_page();
   1823 	warn_spec_now = False;
   1824 }
   1825 
   1826 static	void
   1827 redraw_page()
   1828 {
   1829 	const struct rgb *rgbp;
   1830 
   1831 	if (debug & DBG_EVENT)
   1832 		puts("Redraw page:  ");
   1833 
   1834 	if (scanned_page < current_page) {
   1835 	    if (!check_dvi_file())
   1836 		return;
   1837 	    prescan();
   1838 	    if (ev_flags & EV_GE_NEWPAGE)	/* if we need to re-prescan */
   1839 		return;
   1840 	}
   1841 
   1842 	if (page_info[current_page].ww != unshrunk_page_w
   1843 	  || page_info[current_page].wh != unshrunk_page_h) {
   1844 	    init_page();
   1845 	    reconfig();
   1846 	}
   1847 
   1848 	/* We can't call home() without proper unshrunk_page_*, which requires
   1849 	 * prescan(), which can't be done from within read_events() */
   1850 
   1851 	if (home_action != NULL) {
   1852 		home_action(False);
   1853 	    home_action = NULL;
   1854 
   1855 	    /* This discards the expose event generated by home() */
   1856 	    if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE)
   1857 		return;
   1858 	    can_exposures(&mane);
   1859 	}
   1860 
   1861 	rgbp = &bg_initial;
   1862 	if (page_colors != NULL)
   1863 	    rgbp = &page_colors[current_page].bg;
   1864 
   1865 	/* Set background color */
   1866 	if (bg_current == NULL
   1867 	  || rgbp->r != bg_current->color.r
   1868 	  || rgbp->g != bg_current->color.g
   1869 	  || rgbp->b != bg_current->color.b) {
   1870 	    struct bgrec **bgpp;
   1871 
   1872 	    for (bgpp = &bg_head;;) {
   1873 		bg_current = *bgpp;
   1874 		if (bg_current == NULL) {	/* if bg is not in list */
   1875 		    bg_current = *bgpp = xmalloc(sizeof *bg_current);
   1876 		    bg_current->next = NULL;
   1877 		    bg_current->color = *rgbp;
   1878 		    bg_current->fg_head = NULL;
   1879 		    bg_current->pixel_good = False;
   1880 		    break;
   1881 		}
   1882 		if (bg_current->color.r == rgbp->r
   1883 		  && bg_current->color.g == rgbp->g
   1884 		  && bg_current->color.b == rgbp->b)
   1885 		    break;
   1886 		bgpp = &bg_current->next;
   1887 	    }
   1888 	    fg_current = NULL;	/* force change of foreground color */
   1889 	    if (debug & DBG_DVI)
   1890 		printf("Changing background color to %5d %5d %5d\n",
   1891 		  bg_current->color.r, bg_current->color.g,
   1892 		  bg_current->color.b);
   1893 
   1894 	    if (!bg_current->pixel_good) {
   1895 		bg_current->pixel = alloc_color(&bg_current->color,
   1896 		  back_color_data.pixel);
   1897 		bg_current->pixel_good = True;
   1898 	    }
   1899 	    XSetWindowBackground(DISP, mane.win, bg_current->pixel);
   1900 	}
   1901 
   1902 	XWindowAttributes attrs;
   1903 	XGetWindowAttributes(DISP, mane.win, &attrs);
   1904 	int backing_store = attrs.backing_store;
   1905 
   1906 	XClearWindow(DISP, mane.win);
   1907 	if (backing_store != NotUseful) {
   1908 	    mane.min_x = mane.min_y = 0;
   1909 	    mane.max_x = page_w;
   1910 	    mane.max_y = page_h;
   1911 	}
   1912 	else {
   1913 	    get_xy();
   1914 	    mane.min_x = -window_x;
   1915 	    mane.max_x = -window_x + clip_w;
   1916 	    mane.min_y = -window_y;
   1917 	    mane.max_y = -window_y + clip_h;
   1918 	}
   1919 	redraw(&mane);
   1920 }
   1921 
   1922 void
   1923 do_pages()
   1924 {
   1925 	(void) read_events(EV_GT_IDLE);
   1926 
   1927 	if (ev_flags & (EV_NEWPAGE | EV_NEWDOC | EV_PS_TOGGLE)) {
   1928 		ev_flags &= ~(EV_NEWPAGE | EV_EXPOSE | EV_PS_TOGGLE);
   1929 		if (ev_flags & EV_NEWDOC) {
   1930 			ev_flags &= ~EV_NEWDOC;
   1931 			if (reload_dvi_file() == False)
   1932 				warning_popup("Bad DVI document", "OK", NULL);
   1933 	    	}
   1934 		can_exposures(&mane);
   1935 		can_exposures(&alt);
   1936 
   1937 		if (dvi_file != NULL)
   1938 			redraw_page();
   1939 		else
   1940 			(void) check_dvi_file();
   1941 	}
   1942 	else if (ev_flags & EV_MAG_MOVE) {
   1943 		if (alt.win == (Window) 0)
   1944 			ev_flags &= ~EV_MAG_MOVE;
   1945 		else if (abs(new_mag_x - mag_x) > 2 * abs(new_mag_y - mag_y))
   1946 			movemag(new_mag_x, mag_y);
   1947 		else if (abs(new_mag_y - mag_y) > 2 * abs(new_mag_x - mag_x))
   1948 			movemag(mag_x, new_mag_y);
   1949 		else
   1950 			movemag(new_mag_x, new_mag_y);
   1951 	}
   1952 	else if (ev_flags & EV_EXPOSE) {
   1953 		if (alt.min_x < MAXDIM) {
   1954 			if (mane.min_x >= MAXDIM)
   1955 				ev_flags &= ~EV_EXPOSE;
   1956 			redraw(&alt);
   1957 		}
   1958 		else {
   1959 			ev_flags &= ~EV_EXPOSE;
   1960 			if (mane.min_x < MAXDIM)
   1961 				redraw(&mane);
   1962 		}
   1963 	}
   1964 	else if (ev_flags & EV_CURSOR) {
   1965 		/*
   1966 		 * This code eliminates unnecessary calls to XDefineCursor,
   1967 		 * since this is a slow operation on some hardware
   1968 		 * (e.g., S3 chips).
   1969 		 */
   1970 		XSync(DISP, False);
   1971 		if (!XtPending()) {
   1972 			ev_flags &= ~EV_CURSOR;
   1973 			if (!dragcurs)
   1974 				XDefineCursor(DISP, CURSORWIN, ready_cursor);
   1975 		}
   1976 	}
   1977 
   1978 	XFlush(DISP);
   1979 }