wdvi

network DVI viewer
Log | Files | Refs

events.c (43930B)


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