1 /*======================================================================*\
2 
3 Copyright (c) 1990-2014  Paul Vojta and others
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, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
19 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 OTHER DEALINGS IN THE SOFTWARE.
22 
23 NOTE:
24 xdvi is based on prior work, as noted in the modification history
25 in xdvi.c.
26 
27 \*========================================================================*/
28 
29 #include "xdvi-config.h"
30 
31 #ifdef STDC_HEADERS
32 # include <unistd.h>
33 # include <fcntl.h>
34 #endif
35 #include <signal.h>
36 #include <sys/file.h>	/* this defines FASYNC */
37 #ifdef HAVE_SYS_FCNTL_H
38 # include <sys/fcntl.h>  /* Or this might define FASYNC */
39 #endif
40 #include <sys/ioctl.h>	/* this defines SIOCSPGRP and FIOASYNC */
41 #include <sys/wait.h>	/* ignore HAVE_SYS_WAIT_H -- we always need WNOHANG */
42 
43 #include "xdvi.h" /* this includes Xlib and Xutil are already included */
44 #include "xdvi-debug.h"
45 
46 
47 #ifndef MOTIF
48 #include <X11/Xaw/Form.h> /* for XtNresizable */
49 #endif
50 
51 #include <setjmp.h>
52 #include <sys/time.h>	/* for gettimeofday */
53 
54 #include <X11/IntrinsicP.h>
55 
56 #include <X11/Xatom.h>
57 #include <X11/StringDefs.h>
58 #include <X11/Shell.h>	/* needed for def. of XtNiconX(??) */
59 
60 #include "pagesel.h"
61 #include "filehist.h"
62 #include "special.h"
63 #include "psgs.h"
64 
65 
66 #include <errno.h>
67 
68 #include <ctype.h>
69 
70 #include "util.h"
71 #include "x_util.h"
72 #include "string-utils.h"
73 #include "print-dialog.h"
74 #include "search-dialog.h"
75 #include "sfSelFile.h"
76 #include "mag.h"
77 #include "help-window.h"
78 #include "message-window.h"
79 #include "dvi-draw.h"
80 #include "statusline.h"
81 #include "hypertex.h"
82 #include "dvi-init.h"
83 #include "Tip.h"
84 #include "browser.h"
85 #include "search-internal.h"
86 #include "my-snprintf.h"
87 
88 #include "events.h"
89 #include "selection.h"
90 #include "encodings.h"
91 #include "pagehist.h"
92 #include "xm_colorsel.h"
93 #include "xm_toolbar.h"
94 #include "xaw_menu.h"
95 #include "xm_menu.h"
96 #include "xm_prefs.h"
97 
98 #include "xm_prefs_appearance.h" /* for update_preferences_expert() */
99 #include "xm_prefs_fonts.h"	 /* for update_preferences_color() */
100 #include "xm_prefs_page.h"	 /* for update_preferences_shrink() */
101 
102 #ifdef	X_NOT_STDC_ENV
103 extern int errno;
104 #endif /* X_NOT_STDC_ENV */
105 
106 #if HAVE_X11_INTRINSICI_H
107 # include <X11/IntrinsicI.h>
108 #else
109 
110 /* Taken from <X11/TranslateI.h> in libXt-1.1.3 (June 2012) */
111 typedef struct _LateBindings {
112     unsigned int knot:1;
113     unsigned int pair:1;
114     unsigned short ref_count;	/* garbage collection */
115     KeySym keysym;
116 } LateBindings, *LateBindingsPtr;
117 
118 extern Boolean _XtComputeLateBindings(
119     Display*		/* dpy */,
120     LateBindingsPtr	/* lateModifiers */,
121     Modifiers*		/* computed */,
122     Modifiers*		/* computedMask */
123 );
124 
125 #endif /* not HAVE_X11_INTRINSICI_H */
126 
127 #if HAVE_XKB_BELL_EXT
128 # include <X11/XKBlib.h>
129 # define XdviBell(display, window, percent)	\
130 	 XkbBell(display, window, percent, (Atom) None)
131 #else
132 # define XdviBell(display, window, percent)	XBell(display, percent)
133 #endif
134 
135 /* Linux prefers O_ASYNC over FASYNC; SGI IRIX does the opposite.  */
136 #if !defined(FASYNC) && defined(O_ASYNC)
137 # define FASYNC	O_ASYNC
138 #endif
139 
140 #if !defined(FLAKY_SIGPOLL) && !HAVE_STREAMS && !defined(FASYNC)
141 # if !defined(SIOCSPGRP) || !defined(FIOASYNC)
142 #  define FLAKY_SIGPOLL	1
143 # endif
144 #endif
145 
146 #ifndef FLAKY_SIGPOLL
147 
148 # ifndef SIGPOLL
149 #  define SIGPOLL	SIGIO
150 # endif
151 
152 # ifndef SA_RESTART
153 #  define SA_RESTART 0
154 # endif
155 
156 # if HAVE_STREAMS
157 #  include <stropts.h>
158 
159 #  ifndef S_RDNORM
160 #   define S_RDNORM S_INPUT
161 #  endif
162 
163 #  ifndef S_RDBAND
164 #   define S_RDBAND  0
165 #  endif
166 
167 #  ifndef S_HANGUP
168 #   define S_HANGUP  0
169 #  endif
170 
171 #  ifndef S_WRNORM
172 #   define S_WRNORM  S_OUTPUT
173 #  endif
174 # endif /* HAVE_STREAMS */
175 
176 #endif /* not FLAKY_SIGPOLL */
177 
178 #if HAVE_SIGACTION && !defined SA_RESETHAND
179 # ifdef SA_ONESHOT
180 #  define SA_RESETHAND SA_ONESHOT
181 # else
182 #  undef HAVE_SIGACTION         /* Needed for Mac OS X < 10.2 (9/2002) */
183 # endif
184 #endif
185 
186 #if HAVE_POLL
187 # include <poll.h>
188 #else
189 # if HAVE_SYS_SELECT_H
190 #  include <sys/select.h>
191 # else
192 #  if HAVE_SELECT_H
193 #   include <select.h>
194 #  endif
195 # endif
196 # define XIO_IN 1
197 # define XIO_OUT 2
198 #endif /* HAVE_POLL */
199 
200 /* cannot be const since Strings in Action routines aren't either */
201 static char *Act_true_retval = "true";
202 static char *Act_false_retval = "false";
203 
204 
205 #if HAVE_XI21
206 
207 int		xi2_opcode;
208 Boolean		xi2_active	= False;
209 struct xi2_master *xi2_masters;		/* linked list of master devs */
210 struct xi2_master *xi2_current;		/* current master device */
211 struct xi2_slave *xi2_slaves;		/* linked list of slave devs */
212 
213 struct xi2_slave xi2_no_slave;		/* if no slave assigned yet */
214 
215 /*
216  *	XInput 2.1 creates spurious enter/leave events around the time it
217  *	creates fake wheel button events.  So, we keep track of the last
218  *	such fake button event, and if an enter/leave occurs too soon after
219  *	that event, then we ignore the enter event.
220  *	I've managed to get rid of this (by making sure that the relevant
221  *	events come from the drawing window if possible), but I left the code
222  *	here just in case.
223  */
224 
225 static	Time	xi2_ign_time;	/* time of last XI 2.1 wheel button event */
226 
227 #endif
228 
229 
230 static sigset_t all_signals;
231 
232 /*
233  *	Interrupt system for receiving events.  The program sets a flag
234  *	whenever an event comes in, so that at the proper time (i.e., when
235  *	reading a new dvi item), we can check incoming events to see if we
236  *	still want to go on printing this page.  This way, one can stop
237  *	displaying a page if it is about to be erased anyway.  We try to read
238  *	as many events as possible before doing anything and base the next
239  *	action on all events read.
240  *	Note that the Xlib and Xt routines are not reentrant, so the most we
241  *	can do is set a flag in the interrupt routine and check it later.
242  *	Also, sometimes the interrupts are not generated (some systems only
243  *	guarantee that SIGIO is generated for terminal files, and on the system
244  *	I use, the interrupts are not generated if I use "(xdvi foo &)" instead
245  *	of "xdvi foo").  Therefore, there is also a mechanism to check the
246  *	event queue every 70 drawing operations or so.  This mechanism is
247  *	disabled if it turns out that the interrupts do work.
248  *	For a fuller discussion of some of the above, see xlife in
249  *	comp.sources.x.
250  */
251 
252 /*
253  *	Signal flags
254  */
255 
256 /* This could be static volatile, but we want to avoid any optimizer bugs.  */
257 VOLATILE unsigned int sig_flags	= 0;
258 
259 #define	SF_USR	1
260 #define	SF_ALRM	2
261 #define	SF_POLL	4
262 #define	SF_CHLD	8
263 #define	SF_TERM	16
264 #define	SF_SEGV	32
265 
266 static void do_sigusr(void);
267 static void do_sigalrm(void);
268 static void do_sigpoll(void);
269 static void do_sigchld(void);
270 static void do_sigterm(void);
271 static void do_sigsegv(void);
272 
273 /* these must be in the same order as SF_*.
274    The higher flags have higher priority, since these are
275    checked first when resolving flags_to_sigproc[sig_flags].
276 
277    Example: flag := SF_TERM | SF_CHLD = 24
278 
279    flags_to_sigproc[24] =>
280    do_sigterm, which sets flag to 24 & ~SF_TERM == 8
281    then, in next check:
282    flags_to_sigproc[8] =>
283    do_sigchld, which sets flag 8 & ~SF_CHLD == 0.
284 */
285 #define	SP0	do_sigusr
286 #define	SP1	do_sigalrm
287 #define	SP2	do_sigpoll
288 #define	SP3	do_sigchld
289 #define	SP4	do_sigterm
290 #define	SP5	do_sigsegv
291 
292 typedef	void	(*signalproc)(void);
293 
294 static const signalproc flags_to_sigproc[64] = {
295     NULL,
296     /* 1 */
297     SP0,
298     /* 2 - 3 */
299     SP1, SP1,
300     /* 4 - 7 */
301     SP2, SP2, SP2, SP2,
302     /* 8 - 15 */
303     SP3, SP3, SP3, SP3, SP3, SP3, SP3, SP3,
304     /* 16 - 31 */
305     SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4,
306     SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4,
307     /* 32 - 63 */
308     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
309     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
310     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
311     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5
312 };
313 
314 #undef	SP0
315 #undef	SP2
316 #undef	SP3
317 #undef	SP4
318 #undef	SP5
319 
320 /* file-static variables for prefix argument mechanism */
321 static Boolean m_have_arg = False; /* flag whether we have a possible prefix arg */
322 static int m_number = 0;        /* prefix arg value, without the sign */
323 static int m_sign = 1;          /* is prefix arg negative? */
324 
325 
326 /* to remember the scrollbar positions so that we can restore them
327    when `keep position' is active and window is resized to full size
328    (removing scrollbars) and then back (#810501) */
329 static int m_x_scroll = 0, m_y_scroll = 0;
330 
331 static int source_reverse_x, source_reverse_y;
332 static int source_show_all;
333 
334 /* globals for color stuff */
335 Pixel plane_masks[4];
336 XColor color_data[2];
337 struct pagecolor_info page_colors = { 0, NULL };
338 
339 struct rgb *color_bottom;
340 unsigned int color_bot_size;		/* number of entries */
341 struct colorframe *rcs_top;
342 struct rgb fg_initial;			/* Initial fg (from command line) */
343 struct rgb bg_initial;			/* Initial bg */
344 struct bgrec *bg_head = NULL;		/* head of list */
345 struct bgrec *bg_current = NULL;	/* current bg value */
346 struct fgrec *fg_current;		/* current fg value */
347 struct fgrec *fg_active = NULL;		/* where the GCs are */
348 Pixel *color_list;			/* list of colors */
349 unsigned int color_list_len = 0;	/* current len of list*/
350 unsigned int color_list_max = 0;	/* allocated size */
351 Boolean color_warned = False;
352 
null_mouse(XEvent * event)353 void null_mouse(XEvent *event)
354 {
355     UNUSED(event);
356 }
357 
358 mouse_proc mouse_motion = null_mouse;
359 mouse_proc mouse_release = null_mouse;
360 
361 static void Act_digit(Widget, XEvent *, String *, Cardinal *);
362 static void Act_find(Widget, XEvent *, String *, Cardinal *);
363 static void Act_incremental_find(Widget, XEvent *, String *, Cardinal *);
364 static void Act_find_next(Widget, XEvent *, String *, Cardinal *);
365 static void Act_minus(Widget, XEvent *, String *, Cardinal *);
366 static void Act_quit(Widget, XEvent *, String *, Cardinal *);
367 static void Act_quit_confirm(Widget, XEvent *, String *, Cardinal *);
368 static void Act_print(Widget, XEvent *, String *, Cardinal *);
369 static void Act_save(Widget, XEvent *, String *, Cardinal *);
370 static void Act_help(Widget, XEvent *, String *, Cardinal *);
371 static void Act_goto_page(Widget, XEvent *, String *, Cardinal *);
372 static void Act_declare_page_number(Widget, XEvent *, String *, Cardinal *);
373 static void Act_toggle_mark(Widget, XEvent *, String *, Cardinal *);
374 static void Act_home(Widget, XEvent *, String *, Cardinal *);
375 static void Act_home_or_top(Widget, XEvent *, String *, Cardinal *);
376 static void Act_end_or_bottom(Widget, XEvent *, String *, Cardinal *);
377 static void Act_center(Widget, XEvent *, String *, Cardinal *);
378 static void Act_left(Widget, XEvent *, String *, Cardinal *);
379 static void Act_right(Widget, XEvent *, String *, Cardinal *);
380 static void Act_up(Widget, XEvent *, String *, Cardinal *);
381 static void Act_down(Widget, XEvent *, String *, Cardinal *);
382 static void Act_up_or_previous(Widget, XEvent *, String *, Cardinal *);
383 static void Act_down_or_next(Widget, XEvent *, String *, Cardinal *);
384 static void Act_set_margins(Widget, XEvent *, String *, Cardinal *);
385 static void Act_show_display_attributes(Widget, XEvent *, String *, Cardinal *);
386 static void Act_set_density(Widget, XEvent *, String *, Cardinal *);
387 static void Act_change_density(Widget, XEvent *, String *, Cardinal *);
388 static void Act_fullscreen(Widget, XEvent *, String *, Cardinal *);
389 #ifdef GREY
390 static void Act_set_greyscaling(Widget, XEvent *, String *, Cardinal *);
391 #endif
392 #if COLOR
393 static void Act_set_color(Widget, XEvent *, String *, Cardinal *);
394 #endif
395 
396 static void Act_htex_anchorinfo(Widget, XEvent *, String *, Cardinal *);
397 
398 static void Act_reread_dvi_file(Widget, XEvent *, String *, Cardinal *);
399 static void Act_select_dvi_file(Widget, XEvent *, String *, Cardinal *);
400 static void Act_discard_number(Widget, XEvent *, String *, Cardinal *);
401 static void Act_drag(Widget, XEvent *, String *, Cardinal *);
402 static void Act_wheel(Widget, XEvent *, String *, Cardinal *);
403 static void Act_hwheel(Widget, XEvent *, String *, Cardinal *);
404 static void Act_press(Widget, XEvent *, String *, Cardinal *);
405 static void Act_motion(Widget, XEvent *, String *, Cardinal *);
406 static void Act_release(Widget, XEvent *, String *, Cardinal *);
407 static void Act_toggle_grid_mode(Widget, XEvent *, String *, Cardinal *);
408 static void Act_source_special(Widget, XEvent *, String *, Cardinal *);
409 static void Act_show_source_specials(Widget, XEvent *, String *, Cardinal *);
410 static void Act_source_what_special(Widget, XEvent *, String *, Cardinal *);
411 static void Act_unpause_or_next(Widget, XEvent *, String *, Cardinal *);
412 static void Act_ruler_snap_origin(Widget w, XEvent *event, String *params, Cardinal *num_params);
413 static void Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params);
414 #ifdef MOTIF
415 static void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params);
416 #endif
417 
418 static XtActionsRec m_actions[] = {
419     {"digit", Act_digit},
420     {"mouse-modes", Act_mouse_modes},
421     {"switch-mode", Act_switch_mode},
422     {"minus", Act_minus},
423     {"recent-files", Act_recent_files},
424     {"quit", Act_quit},
425     {"quit-confirm", Act_quit_confirm},
426     {"print", Act_print},
427     {"save", Act_save},
428     {"find", Act_find},
429     {"incremental-find", Act_incremental_find},
430     {"find-next", Act_find_next},
431     {"help", Act_help},
432     {"goto-page", Act_goto_page},
433     {"use-tex-pages", Act_use_tex_pages},
434     {"forward-page", Act_forward_page},
435     {"back-page", Act_back_page},
436     {"toggle-mark", Act_toggle_mark},
437     {"declare-page-number", Act_declare_page_number},
438     {"home", Act_home},
439     {"home-or-top", Act_home_or_top},
440     {"end-or-bottom", Act_end_or_bottom},
441     {"center", Act_center},
442     {"set-keep-flag", Act_set_keep_flag},
443     {"left", Act_left},
444     {"right", Act_right},
445     {"up", Act_up},
446     {"down", Act_down},
447     {"up-or-previous", Act_up_or_previous},
448     {"down-or-next", Act_down_or_next},
449     {"set-margins", Act_set_margins},
450     {"show-display-attributes", Act_show_display_attributes},
451     {"set-shrink-factor", Act_set_shrink_factor},
452     {"shrink-to-dpi", Act_shrink_to_dpi},
453     {"set-density", Act_set_density},
454     {"change-density", Act_change_density},
455     {"fullscreen", Act_fullscreen},
456 #ifdef GREY
457     {"set-greyscaling", Act_set_greyscaling},
458 #endif
459 #if COLOR
460     {"set-color", Act_set_color},
461 #endif
462 #ifdef PS
463     {"set-ps", Act_set_ps},
464 #endif
465     {"htex-back", Act_htex_back},
466     {"htex-forward", Act_htex_forward},
467     {"htex-anchorinfo", Act_htex_anchorinfo},
468 #ifdef PS_GS
469     {"set-gs-alpha", Act_set_gs_alpha},
470 #endif
471     {"set-expert-mode", Act_set_expert_mode},
472     {"reread-dvi-file", Act_reread_dvi_file},
473     {"select-dvi-file", Act_select_dvi_file},
474     {"discard-number", Act_discard_number},
475     {"drag", Act_drag},
476     {"wheel", Act_wheel},
477     {"hwheel", Act_hwheel},
478     {"press", Act_press},
479     {"motion", Act_motion},
480     {"release", Act_release},
481     {"toggle-grid-mode", Act_toggle_grid_mode},
482     {"source-special", Act_source_special},
483     {"show-source-specials", Act_show_source_specials},
484     {"source-what-special", Act_source_what_special},
485     {"unpause-or-next", Act_unpause_or_next},
486 #if 0 /* not implemented yet */
487     {"set-papersize", Act_set_papersize},
488     {"set-paper-landscape", Act_set_paper_landscape},
489 #endif
490     {"load-url", Act_load_url},
491     {"scroll-list-up", Act_scroll_list_up},
492     {"scroll-list-down", Act_scroll_list_down},
493     {"pagehistory-clear", Act_pagehistory_clear},
494     {"pagehistory-back", Act_pagehistory_back},
495     {"pagehistory-forward", Act_pagehistory_forward},
496     {"pagehistory-delete-backward", Act_pagehistory_delete_backward},
497     {"pagehistory-delete-forward", Act_pagehistory_delete_forward},
498 #ifdef MOTIF
499     {"prefs-dialog", Act_prefs_dialog},
500 #endif
501     {"magnifier", Act_magnifier},
502     {"ruler", Act_ruler},
503     {"ruler-snap-origin", Act_ruler_snap_origin},
504     {"text-selection", Act_text_selection},
505     {"do-href", Act_href},
506     {"do-href-newwindow", Act_href_newwindow},
507     {"switch-magnifier-units", Act_switch_magnifier_units},
508 };
509 
510 
511 Boolean
compile_action(const char * str,struct xdvi_action ** app)512 compile_action(const char *str, struct xdvi_action **app)
513 {
514     const char *p, *p1, *p2, *end_cmd;
515     XtActionsRec *actp;
516     struct xdvi_action *ap;
517     String *params;
518     Cardinal num_params;
519 
520     for (;;) {
521 
522 	while (*str == ' ' || *str == '\t')
523 	    ++str;
524 
525 	if (*str == '\0' || *str == '\n')
526 	    break;
527 
528 	p = str;
529 
530 	/* find end of command name */
531 	while (isalnum((int)*p) || *p == '-' || *p == '_')
532 	    ++p;
533 
534 	end_cmd = p;
535 
536 	for (actp = m_actions; ; ++actp) {
537 	    if (actp >= m_actions + XtNumber(m_actions)) {
538 		const char *tmp = strchr(str, '\0');
539 		if (tmp == NULL) {
540 		    tmp = p;
541 		}
542 		XDVI_WARNING((stderr, "Cannot compile action \"%.*s\".",
543 			     (int) (tmp - str), str));
544 		*app = NULL;
545 		return False;
546 	    }
547 	    if (memcmp(str, actp->string, p - str) == 0
548 	      && actp->string[p - str] == '\0')
549 		break;
550 	}
551 
552 	while (*p == ' ' || *p == '\t')
553 	    ++p;
554 	if (*p != '(') {
555 	    while (*p != '\0' && *p != '\n')
556 		++p;
557 	    XDVI_WARNING((stderr, "Syntax error in action %.*s.",
558 			 (int) (p - str), str));
559 	    *app = NULL;
560 	    return False;
561 	}
562 
563 	do {++p;}
564 	while (*p == ' ' || *p == '\t');
565 
566 	num_params = 0;
567 	if (*p == ')')
568 	    params = NULL;
569 	else {
570 	    Cardinal max_params = 4;
571 
572 	    params = xmalloc(max_params * sizeof (String));
573 
574 	    for (;;) {	/* loop over params */
575 		if (*p == '"') {
576 		    ++p;
577 		    p1 = strchr(p, '"');
578 		    if (p1 == NULL) {
579 			p1 = strchr(p, '\n');
580 			if (p1 == NULL)
581 			    p1 = p + strlen(p);
582 			XDVI_WARNING((stderr, "Syntax error in action %.*s.",
583 				     (int) (p1 - str), str));
584 			while (num_params > 0) {
585 			    --num_params;
586 			    free(params[num_params]);
587 			}
588 			free(params);
589 			*app = NULL;
590 			return False;
591 		    }
592 		    params[num_params++] = xstrndup(p, p1 - p);
593 
594 		    do {++p1;}
595 		    while (*p1 == ' ' || *p1 == '\t');
596 
597 		    if (*p1 != ')' && *p1 != ',') {
598 			p2 = strchr(p1, '\n');
599 			if (p2 == NULL)
600 			    p2 = p1 + strlen(p1);
601 			XDVI_WARNING((stderr, "Syntax error in action %.*s.",
602 				     (int) (p2 - str), str));
603 			while (num_params > 0) {
604 			    --num_params;
605 			    free(params[num_params]);
606 			}
607 			free(params);
608 			*app = NULL;
609 			return False;
610 		    }
611 		}
612 		else {	/* param is not quoted */
613 		    for (p1 = p;; ++p1) {
614 			if (*p1 == '\0' || *p1 == '\n') {
615 			    XDVI_WARNING((stderr,
616 					 "Syntax error in action %.*s.",
617 					 (int) (p1 - str), str));
618 			    while (num_params > 0) {
619 				--num_params;
620 				free(params[num_params]);
621 			    }
622 			    free(params);
623 			    *app = NULL;
624 			    return False;
625 			}
626 			if (*p1 == ')' || *p1 == ',')
627 			    break;
628 		    }
629 		    p2 = p1;
630 		    while (p2 > p && (p2[-1] == ' ' || p2[-1] == '\t'))
631 			--p2;
632 		    params[num_params++] = xstrndup(p, p2 - p);
633 		}
634 
635 		p = p1;
636 		if (*p == ')')
637 		    break;
638 
639 		do {++p;}
640 		while (*p == ' ' || *p == '\t');
641 
642 		if (num_params >= max_params) {
643 		    max_params *= 2;
644 		    params = xrealloc(params, max_params * sizeof (String));
645 		}
646 	    }
647 	} /* end if */
648 
649 	ap = xmalloc(sizeof *ap);
650 	ap->proc = actp->proc;
651 	ap->command = xstrndup(str, end_cmd - str);
652 	ap->params = params;
653 	ap->num_params = num_params;
654 
655 	*app = ap;
656 	app = &ap->next;
657 
658 	str = p + 1;
659     }
660 
661     *app = NULL;
662     return True;
663 }
664 
665 /*
666  *	cached_compile_action does the same as compile_action, but retains
667  *	the compiled actions.  It is used only in Act_mouse_modes(), which
668  *	otherwise would recompile the same actions over and over again.
669  */
670 
671 struct avl_cached_action {
672 	AVL_COMMON;
673 	struct xdvi_action *ap;
674 };
675 
676 
677 static void
cached_compile_action(const char * str,struct xdvi_action ** app)678 cached_compile_action(const char *str, struct xdvi_action **app)
679 {
680 	static struct avl_cached_action *avl_ca_head = NULL;
681 	struct avl_cached_action *avl_ca;
682 
683 	avl_ca = (struct avl_cached_action *)
684 	  avladd(str, strlen(str), (struct avl **) &avl_ca_head,
685 	  sizeof (struct avl_cached_action));
686 
687 	if (avl_ca->key == str) {	/* if new record */
688 	    avl_ca->key = xstrdup(str);	/* copy string to new storage */
689 	    compile_action(str, &avl_ca->ap);
690 	}
691 	*app = avl_ca->ap;
692 }
693 
694 
695 /*
696  * Access to m_actions
697  */
get_num_actions(void)698 int get_num_actions(void)
699 {
700     return XtNumber(m_actions);
701 }
702 
get_actions(void)703 XtActionsRec *get_actions(void)
704 {
705     return m_actions;
706 }
707 
708 /*
709  *	Data for buffered events.
710  */
711 
712 #ifndef FLAKY_SIGPOLL
713 static VOLATILE int event_freq = 70;
714 #else
715 #define	event_freq	70
716 #endif
717 
718 static void can_exposures(struct WindowRec *windowrec);
719 
720 
721 /*
722  *	Set the flag so that termination occurs, via the above routine.
723  *	This should be used in place of xdvi_exit() when there may be a
724  *	non-killable process running (e.g., anytime within read_events()).
725  */
726 
727 static void
xdvi_normal_exit(void)728 xdvi_normal_exit(void)
729 {
730     sig_flags |= SF_TERM;
731 }
732 
733 static void
xdvi_normal_exit_cb(XtPointer arg)734 xdvi_normal_exit_cb(XtPointer arg)
735 {
736     UNUSED(arg);
737     sig_flags |= SF_TERM;
738 }
739 
740 void
xdvi_exit_callback(Widget w,XtPointer client_data,XtPointer call_data)741 xdvi_exit_callback(Widget w, XtPointer client_data, XtPointer call_data)
742 {
743     UNUSED(w);
744     UNUSED(client_data);
745     UNUSED(call_data);
746 
747     sig_flags |= SF_TERM;
748 }
749 
750 /*
751  * Event-handling routines.
752  */
753 
754 
755 void
expose(struct WindowRec * windowrec,int x,int y,unsigned int w,unsigned int h)756 expose(struct WindowRec *windowrec,
757        int x, int y,
758        unsigned int w, unsigned int h)
759 {
760     if (windowrec->min_x > x)
761 	windowrec->min_x = x;
762     if (windowrec->max_x < (int)(x + w))
763 	windowrec->max_x = x + w;
764     if (windowrec->min_y > y)
765 	windowrec->min_y = y;
766     if (windowrec->max_y < (int)(y + h))
767 	windowrec->max_y = y + h;
768 
769     globals.ev.flags |= EV_EXPOSE;
770 }
771 
772 void
clearexpose(struct WindowRec * windowrec,int x,int y,unsigned w,unsigned h)773 clearexpose(struct WindowRec *windowrec,
774 	    int x, int y,
775 	    unsigned w, unsigned h)
776 {
777     XClearArea(DISP, windowrec->win, x, y, w, h, False);
778     expose(windowrec, x, y, w, h);
779 }
780 
781 /*
782  *	Routines for X11 toolkit.
783  */
784 
785 static Position m_window_x, m_window_y;
786 static Arg arg_xy[] = {
787     {XtNx, (XtArgVal) &m_window_x},
788     {XtNy, (XtArgVal) &m_window_y},
789 };
790 
791 #define	get_xy() XtGetValues(globals.widgets.draw_widget, arg_xy, XtNumber(arg_xy))
792 
793 
794 static void
warn_num_params(const char * act_name,String * params,int num_params,int max_params)795 warn_num_params(const char *act_name, String *params, int num_params, int max_params)
796 {
797     if (num_params > max_params) {
798 	XDVI_WARNING((stderr, "Too many parameters (%d) for action \"%s\", ignoring all after \"%s\"",
799 		      num_params, act_name, params[max_params - 1]));
800     }
801 }
802 
803 
804 void
handle_command(Widget widget,XtPointer client_data,XtPointer call_data)805 handle_command(Widget widget, XtPointer client_data, XtPointer call_data)
806 {
807     struct xdvi_action *actp;
808 
809     UNUSED(call_data);
810 
811     /* call all actions registered for this event */
812     for (actp = (struct xdvi_action *)client_data; actp != NULL; actp = actp->next) {
813 	if (globals.debug & DBG_EVENT)
814 	    fprintf(stderr, "calling action with param: %s\n",
815 		    actp->num_params ? actp->params[0] : "(null)");
816 	(actp->proc)(widget, NULL, actp->params, &actp->num_params);
817     }
818 }
819 
820 #ifdef MOTIF
821 int
set_bar_value(Widget bar,int value,int max)822 set_bar_value(Widget bar, int value, int max)
823 {
824     XmScrollBarCallbackStruct call_data;
825 
826 #ifdef TEST_SCROLLING
827     fprintf(stderr, "set_bar_value: val %d, max %d\n", value, max);
828 #endif
829     if (value > max)
830 	value = max;
831     if (value < 0)
832 	value = 0;
833     call_data.value = value;
834     XtVaSetValues(bar, XmNvalue, value, NULL);
835     XtCallCallbacks(bar, XmNvalueChangedCallback, &call_data);
836     return value;
837 }
838 #endif
839 
840 void
home(wide_bool scrl)841 home(wide_bool scrl)
842 {
843     if (!scrl)
844 	XUnmapWindow(DISP, mane.win);
845 # ifdef MOTIF
846     {
847 	int value;
848 
849 	if (!resource.keep_flag) {
850 	    value = (globals.page.w - mane.width) / 2;
851 	    if (value > resource.sidemargin_int / mane.shrinkfactor)
852 		value = resource.sidemargin_int / mane.shrinkfactor;
853 	    (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width));
854 	}
855 
856 	value = (globals.page.h - mane.height) / 2;
857 	if (value > resource.topmargin_int / mane.shrinkfactor)
858 	    value = resource.topmargin_int / mane.shrinkfactor;
859 	(void)set_bar_value(globals.widgets.y_bar, value, (int)(globals.page.h - mane.height));
860     }
861 # else
862     get_xy();
863     if (!resource.keep_flag && globals.widgets.x_bar != NULL) {
864 	int coord = (globals.page.w - mane.width) / 2;
865 
866 	if (coord > resource.sidemargin_int / mane.shrinkfactor)
867 	    coord = resource.sidemargin_int / mane.shrinkfactor;
868 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, cast_int_to_XtPointer(m_window_x + coord));
869     }
870     if (globals.widgets.y_bar != NULL) {
871 	int coord = (globals.page.h - mane.height) / 2;
872 
873 	if (coord > resource.topmargin_int / mane.shrinkfactor)
874 	    coord = resource.topmargin_int / mane.shrinkfactor;
875 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(m_window_y + coord));
876     }
877 # endif /* MOTIF */
878     if (!scrl) {
879 	XMapWindow(DISP, mane.win);
880 	/* Wait for the server to catch up---this eliminates flicker. */
881 	XSync(DISP, False);
882     }
883 #ifdef USE_PANNER
884     handle_x_scroll(NULL, NULL, NULL, NULL);
885     handle_y_scroll(NULL, NULL, NULL, NULL);
886 #endif
887 }
888 
889 /*
890  *	Same as home(), except move to the bottom of the page.
891  */
892 
893 static void
home_bottom(wide_bool scrl)894 home_bottom(wide_bool scrl)
895 {
896     UNUSED(scrl);
897     XUnmapWindow(DISP, mane.win);
898 #ifdef MOTIF
899     {
900 	int value;
901 
902 	if (!resource.keep_flag) {
903 	    value = (globals.page.w - mane.width) / 2;
904 	    if (value > resource.sidemargin_int / mane.shrinkfactor)
905 		value = resource.sidemargin_int / mane.shrinkfactor;
906 	    (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width));
907 	}
908 
909 	(void)set_bar_value(globals.widgets.y_bar, (int)(globals.page.h - mane.height), (int)(globals.page.h - mane.height));
910     }
911 #else /* MOTIF */
912     get_xy();
913     if (!resource.keep_flag && globals.widgets.x_bar != NULL) {
914 	int coord = (globals.page.w - mane.width) / 2;
915 
916 	if (coord > resource.sidemargin_int / mane.shrinkfactor)
917 	    coord = resource.sidemargin_int / mane.shrinkfactor;
918 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, cast_int_to_XtPointer(m_window_x + coord));
919     }
920     if (globals.widgets.y_bar != NULL)
921 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(m_window_y + (globals.page.h - mane.height)));
922 #endif /* MOTIF */
923     XMapWindow(DISP, mane.win);
924     /* Wait for the server to catch up---this eliminates flicker. */
925     XSync(DISP, False);
926 
927 #ifdef USE_PANNER
928     handle_x_scroll(NULL, NULL, NULL, NULL);
929     handle_y_scroll(NULL, NULL, NULL, NULL);
930 #endif
931 }
932 
933 
934 #ifndef MOTIF
935 static void
handle_destroy_bar(Widget w,XtPointer client_data,XtPointer call_data)936 handle_destroy_bar(Widget w, XtPointer client_data, XtPointer call_data)
937 {
938     UNUSED(w);
939     UNUSED(call_data);
940     *(Widget *) client_data = NULL;
941 }
942 #endif
943 
944 static Boolean resized = False;
945 
946 static void
get_geom(void)947 get_geom(void)
948 {
949     static Dimension new_clip_w, new_clip_h;
950 
951     static Arg arg_wh_clip[] = {
952 	{XtNwidth, (XtArgVal) &new_clip_w},
953 	{XtNheight, (XtArgVal) &new_clip_h},
954     };
955 
956     static Dimension window_w, window_h;
957 
958     static Arg arg_wh[] = {
959 	{XtNwidth, (XtArgVal) &window_w},
960 	{XtNheight, (XtArgVal) &window_h},
961     };
962 
963 
964     int old_clip_w;
965 
966 #ifdef MOTIF
967     /* event handlers for Motif scrollbars have already been added
968        in create_initialize_widgets(), xdvi.c */
969     XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
970 #else
971     XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
972     /* Note:  widgets may be destroyed but not forgotten */
973     if (globals.widgets.x_bar == NULL) {
974 	globals.widgets.x_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal");
975 	if (globals.widgets.x_bar != NULL) {
976 	    XtAddCallback(globals.widgets.x_bar, XtNdestroyCallback, handle_destroy_bar,
977 			  (XtPointer)&globals.widgets.x_bar);
978 #ifdef USE_PANNER
979 	    XtAddEventHandler(globals.widgets.x_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
980 			      False, handle_x_scroll, NULL);
981 #endif
982 	}
983     }
984     if (globals.widgets.y_bar == NULL) {
985 	globals.widgets.y_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical");
986 	if (globals.widgets.y_bar != NULL) {
987 	    XtAddCallback(globals.widgets.y_bar, XtNdestroyCallback, handle_destroy_bar,
988 			  (XtPointer)&globals.widgets.y_bar);
989 #ifdef USE_PANNER
990 	    XtAddEventHandler(globals.widgets.y_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
991 			      False, handle_y_scroll, NULL);
992 #endif
993 	}
994     }
995 #endif
996     XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
997 
998     old_clip_w = mane.width;
999 
1000     /* we need to do this because
1001        sizeof(Dimension) != sizeof(int)
1002     */
1003     mane.width = new_clip_w;
1004     mane.height = new_clip_h;
1005     if (old_clip_w == 0) {
1006 	globals.ev.flags |= EV_NEWPAGE;
1007     }
1008 
1009     if (resource.keep_flag) {
1010 #ifndef MOTIF
1011 	Dimension d;
1012 	int curr_scroll;
1013 	if ((globals.widgets.x_bar != NULL && m_x_scroll != 0) || (globals.widgets.y_bar != NULL && m_y_scroll != 0)) {
1014 	    get_xy();
1015 	}
1016 #endif
1017 	if (globals.widgets.x_bar != NULL && m_x_scroll != 0) {
1018 #ifdef MOTIF
1019 	    if (m_x_scroll > 0)
1020 		(void)set_bar_value(globals.widgets.x_bar, m_x_scroll, (int)(globals.page.w - mane.width));
1021 #else
1022 	    XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL);
1023 	    curr_scroll = d - m_window_x;
1024 	    if (m_x_scroll > curr_scroll) {
1025 		TRACE_GUI((stderr, "======== diff: %d", m_x_scroll - curr_scroll));
1026 		XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, cast_int_to_XtPointer(m_x_scroll - curr_scroll));
1027 	    }
1028 #endif
1029 	}
1030 	if (globals.widgets.y_bar != NULL && m_y_scroll != 0) {
1031 #ifdef MOTIF
1032 	    if (m_y_scroll > 0)
1033 		(void)set_bar_value(globals.widgets.y_bar, m_y_scroll, (int)(globals.page.h - mane.height));
1034 #else
1035 	    XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL);
1036 	    curr_scroll = d - m_window_y;
1037 	    if (m_y_scroll > curr_scroll) {
1038 		TRACE_GUI((stderr, "======== diff: %d", m_y_scroll - curr_scroll));
1039 		XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(m_y_scroll - curr_scroll));
1040 	    }
1041 #endif
1042 	}
1043     }
1044     /*     home(False); */
1045     resized = False;
1046 }
1047 
1048 /*
1049  * Callback routines
1050  */
1051 
1052 void
handle_resize(Widget widget,XtPointer junk,XEvent * event,Boolean * cont)1053 handle_resize(Widget widget, XtPointer junk, XEvent *event, Boolean *cont)
1054 {
1055     UNUSED(widget);
1056     UNUSED(junk);
1057     UNUSED(event);
1058     UNUSED(cont);
1059 
1060     resized = True;
1061 #ifndef MOTIF
1062     handle_statusline_resize();
1063     handle_pagelist_resize();
1064 #endif
1065 }
1066 
1067 void
reconfig(void)1068 reconfig(void)
1069 {
1070     /*      Dimension x, y; */
1071 
1072     if (globals.dvi_file.bak_fp == NULL)
1073 	return;
1074 
1075 #ifndef MOTIF
1076     XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)False, NULL);
1077 #endif
1078     TRACE_GUI((stderr, "globals.widgets.draw_widget: w %d, h %d", globals.page.w, globals.page.h));
1079     XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL);
1080 
1081 #ifdef TEST_SCROLLING
1082     /*     XtVaSetValues(globals.widgets.draw_background, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL); */
1083 #endif
1084 
1085 #ifndef MOTIF
1086     handle_statusline_resize(); /* without this, statusline will disappear */
1087     /* following not needed? */
1088     /*     handle_pagelist_resize(); */
1089 #endif
1090 
1091     get_geom();
1092 
1093     /*     set_windowsize(&x, &y */
1094     /* #ifndef MOTIF */
1095     /* 		   , get_panel_width() */
1096     /* #endif */
1097     /* 		   ); */
1098     /*     XResizeWindow(DISP, XtWindow(globals.widgets.top_level), x, y); */
1099     /*      reconfigure_window(False, x, y, True); */
1100     /*      reconfig_window(); */
1101 }
1102 
1103 
1104 int
check_goto_page(int pageno,Boolean insert_into_pagehist)1105 check_goto_page(int pageno, Boolean insert_into_pagehist)
1106 {
1107     int retval;
1108     if (pageno < 0) {
1109 	xdvi_bell();
1110 /* 	statusline_info(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1); */
1111 	retval = 0;
1112     }
1113     else if (pageno >= total_pages) {
1114 	xdvi_bell();
1115 /* 	statusline_info(STATUS_SHORT, */
1116 /* 			 "Can't go to page %d, going to last page (%d) instead", */
1117 /* 			 pageno + 1, total_pages); */
1118 	retval = total_pages - 1;
1119     }
1120     else
1121 	retval = pageno;
1122 
1123     if (insert_into_pagehist) {
1124 	page_history_insert(retval);
1125     }
1126     return retval;
1127 }
1128 
1129 
1130 static int
check_goto_tex_page(int pageno)1131 check_goto_tex_page(int pageno)
1132 {
1133     /*
1134       Translate from TeX page number to `real' page number if
1135       needed. Note that pageno is a C-style 0-based number, hence we
1136       add 1 for the argument of pageinfo_get_index_of_number().
1137     */
1138     int retval;
1139     if (resource.use_tex_pages) {
1140 	int res = pageinfo_get_index_of_number(pageno + 1);
1141 	if (res >= 0)
1142 	    retval = res;
1143 	else {
1144 	    xdvi_bell();
1145 	    if (pageno < 1) {
1146 /* 		statusline_info(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1); */
1147 		retval = 0;
1148 	    }
1149 	    else {
1150 		/* there is no quick way to determine the last page number in the TeX page index */
1151 /* 		statusline_info(STATUS_SHORT, */
1152 /* 				 "Can't go to page %d, going to last page instead", */
1153 /* 				 pageno + 1); */
1154 		retval = total_pages - 1;
1155 	    }
1156 	}
1157 	page_history_insert(retval);
1158     }
1159     else {
1160 	retval = check_goto_page(pageno, True);
1161     }
1162     return retval;
1163 }
1164 
1165 /* |||
1166  *	Currently the event handler does not coordinate XCopyArea requests
1167  *	with GraphicsExpose events.  This can lead to problems if the window
1168  *	is partially obscured and one, for example, drags a scrollbar.
1169  */
1170 
1171 /*
1172  *	Actions for the translation mechanism.
1173  */
1174 
1175 /* if there are global prefixes, return them in res and reset them to defaults */
1176 static Boolean
get_prefix_arg(int * res)1177 get_prefix_arg(int *res)
1178 {
1179     Boolean ret;
1180 
1181     *res = m_sign * m_number;
1182     ret = m_have_arg;
1183     /* reset global flags */
1184     m_have_arg = False;
1185     m_number = 0;
1186     m_sign = 1;
1187     return ret;
1188 }
1189 
1190 Boolean
get_int_arg(String * param,Cardinal * num_params,int * res)1191 get_int_arg(String *param, Cardinal *num_params, int *res)
1192 {
1193     if (*num_params > 0) {
1194 	*res = atoi(*param);
1195 	return True;
1196     }
1197     else {
1198 	if (get_prefix_arg(res)) { /* prefix argument? */
1199 	    return True;
1200 	}
1201     }
1202     return False;
1203 }
1204 
1205 
1206 Boolean
toggle_arg(int arg,String * param,Cardinal * num_params)1207 toggle_arg(int arg, String *param, Cardinal *num_params)
1208 {
1209     if (*num_params > 0) {
1210 	if (**param != 't' && (atoi(*param) != 0) == arg)
1211 	    return False;
1212     }
1213     else {
1214 	if (m_have_arg) {
1215 	    int	tmparg = m_number;
1216 
1217 	    m_have_arg = False;
1218 	    m_number = 0;
1219 	    m_sign = 1;
1220 
1221 	    if ((tmparg != 0) == arg)
1222 		return False;
1223 	}
1224     }
1225     return True;
1226 }
1227 
1228 Boolean
check_resource_expert(void * val,const char * param)1229 check_resource_expert(void *val, const char *param)
1230 {
1231     int j = strtol(param, (char **)NULL, 10);
1232     /* check if the j-1th bit is set: */
1233     return (*(int *)val >> (j - 1)) & 1;
1234 }
1235 
1236 Boolean
check_papersize(void * val,const char * param)1237 check_papersize(void *val, const char *param)
1238 {
1239     UNUSED(val);
1240     UNUSED(param);
1241     return False; /* TODO */
1242 }
1243 
1244 Boolean
check_paper_landscape(void * val,const char * param)1245 check_paper_landscape(void *val, const char *param)
1246 {
1247     UNUSED(val);
1248     UNUSED(param);
1249     return False; /* TODO */
1250 }
1251 
1252 /* comparison functions for the menu setting code */
1253 Boolean
check_toggle(void * val,const char * param)1254 check_toggle(void *val, const char *param)
1255 {
1256     Boolean *on = val;
1257     if (strcmp(param, "toggle") == 0) {
1258 	return *on;
1259     }
1260     else {
1261 	fprintf(stderr, "TODO: check_toggle: arg |%s|, curr: %d\n", param, *(int *)val);
1262 	return *on;
1263     }
1264 }
1265 
1266 Boolean
check_int(void * val,const char * param)1267 check_int(void *val, const char *param)
1268 {
1269     int i = strtol(param, (char **)NULL, 10);
1270     return i == *(int *)val;
1271 }
1272 
1273 void
Act_ruler(Widget w,XEvent * event,String * params,Cardinal * num_params)1274 Act_ruler(Widget w, XEvent *event,
1275 	  String *params, Cardinal *num_params)
1276 {
1277     UNUSED(w);
1278     UNUSED(params);
1279     UNUSED(num_params);
1280 
1281     MYTRACE((stderr, "ruler!\n"));
1282 
1283     if (dvi_file_changed()) {
1284 	globals.ev.flags |= EV_RELOAD;
1285 	return;
1286     }
1287 
1288 #ifdef MOTIF
1289     /* see xm_menu.c for an explanation of this */
1290     if (event && pulldown_menu_active(event->xany.serial)) {
1291 	return;
1292     }
1293 #endif
1294 
1295     if (bg_current == NULL) {
1296 	/*
1297 	  HACK ALERT: we can arrive here after loading a new file via the file selector
1298 	  for which not all fonts have been generated. In that case, dereferencing
1299 	  bg_current would bomb. Try to recover by simply returning here.
1300 	*/
1301 	return;
1302     }
1303 
1304     show_ruler(event);
1305     globals.curr_mode = RULER_MODE_ACTIVE;
1306     globals.ev.flags |= EV_CURSOR;
1307     XFlush(DISP);
1308     show_distance_from_ruler(event, False);
1309 }
1310 
1311 void
Act_text_selection(Widget w,XEvent * event,String * params,Cardinal * num_params)1312 Act_text_selection(Widget w, XEvent *event,
1313 		   String *params, Cardinal *num_params)
1314 {
1315     UNUSED(w);
1316     UNUSED(params);
1317     UNUSED(num_params);
1318 
1319     MYTRACE((stderr, "text selection!\n"));
1320 
1321     if (dvi_file_changed()) {
1322 	globals.ev.flags |= EV_RELOAD;
1323 	return;
1324     }
1325 
1326 #ifdef MOTIF
1327     /* see xm_menu.c for an explanation of this */
1328     if (pulldown_menu_active(event->xany.serial)) {
1329 	return;
1330     }
1331 #endif
1332 
1333     if (bg_current == NULL) {
1334 	/*
1335 	  HACK ALERT: we can arrive here after loading a new file via the file selector
1336 	  for which not all fonts have been generated. In that case, dereferencing
1337 	  bg_current would bomb. Try to recover by simply returning here.
1338 	*/
1339 	return;
1340     }
1341 
1342     globals.curr_mode = TEXT_MODE_ACTIVE;
1343     globals.ev.flags |= EV_CURSOR;
1344     XFlush(DISP);
1345     text_selection_start(event);
1346     text_motion(event);
1347 }
1348 
1349 /****************************************************************************
1350  * Actions specific to the handling of the magnifier
1351  */
1352 
1353 void
Act_magnifier(Widget w,XEvent * event,String * params,Cardinal * num_params)1354 Act_magnifier(Widget w, XEvent *event,
1355 	      String *params, Cardinal *num_params)
1356 {
1357     UNUSED(w);
1358 
1359     MYTRACE((stderr, "magnifier!\n"));
1360 
1361     if (dvi_file_changed()) {
1362 	globals.ev.flags |= EV_RELOAD;
1363 	return;
1364     }
1365 
1366 #ifdef MOTIF
1367     /* see xm_menu.c for an explanation of this */
1368     if (pulldown_menu_active(event->xany.serial)) {
1369 	return;
1370     }
1371 #endif
1372 
1373     if (bg_current == NULL) {
1374 	/*
1375 	  HACK ALERT: we can arrive here after loading a new file via the file selector
1376 	  for which not all fonts have been generated. In that case, dereferencing
1377 	  bg_current would bomb. Try to recover by simply returning here.
1378 	*/
1379 	return;
1380     }
1381 
1382     if (event->type != ButtonPress || mouse_release != null_mouse
1383 	|| MAGNIFIER_ACTIVE || mane.shrinkfactor == 1 || *num_params != 1) {
1384 	XdviBell(DISP, event->xany.window, 0);
1385 	if (mane.shrinkfactor == 1) {
1386 	    statusline_info(STATUS_SHORT,
1387 			     "No magnification available at shrink factor 1");
1388 	}
1389 	return;
1390     }
1391 
1392     magnifier_move(*params, event);
1393 }
1394 
1395 void
Act_switch_magnifier_units(Widget w,XEvent * event,String * params,Cardinal * num_params)1396 Act_switch_magnifier_units(Widget w, XEvent *event,
1397 			   String *params, Cardinal *num_params)
1398 {
1399     size_t k = 0;
1400     static char *TeX_units[] = {
1401 	"mm", "pt", "in", "sp", "bp", "cc", "dd", "pc", "px",
1402     };
1403 
1404     UNUSED(w);
1405     UNUSED(event);
1406     UNUSED(params);
1407     UNUSED(num_params);
1408 
1409     for (k = 0; k < XtNumber(TeX_units); ++k)
1410 	if (strcmp(resource.tick_units, TeX_units[k]) == 0)
1411 	    break;
1412     k++;
1413     if (k >= XtNumber(TeX_units))
1414 	k = 0;
1415     resource.tick_units = TeX_units[k];
1416     if (globals.curr_mode == RULER_MODE_ACTIVE) {
1417 	show_distance_from_ruler(event, False);
1418     }
1419     else {
1420 	statusline_info(STATUS_SHORT, "Ruler units: %s\n", resource.tick_units);
1421     }
1422 }
1423 
1424 void
Act_href(Widget w,XEvent * event,String * params,Cardinal * num_params)1425 Act_href(Widget w, XEvent *event,
1426 	 String *params, Cardinal *num_params)
1427 {
1428     int x, y;
1429     Window dummy;
1430 
1431     UNUSED(w);
1432     UNUSED(num_params);
1433 
1434     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
1435 				event->xkey.x, event->xkey.y, &x, &y, &dummy);
1436 
1437     if (params) {
1438 	if (htex_handleref(x, y, False))
1439 	    *params = Act_true_retval;
1440 	else
1441 	    *params = Act_false_retval;
1442     }
1443 }
1444 
1445 void
Act_href_newwindow(Widget w,XEvent * event,String * params,Cardinal * num_params)1446 Act_href_newwindow(Widget w, XEvent *event,
1447 		   String *params, Cardinal *num_params)
1448 {
1449     int x, y;
1450     Window dummy;
1451 
1452     UNUSED(w);
1453     UNUSED(num_params);
1454 
1455     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
1456 				event->xkey.x, event->xkey.y, &x, &y, &dummy);
1457 
1458     if (params) {
1459 	if (htex_handleref(x, y, True))
1460 	    *params = Act_true_retval;
1461 	else
1462 	    *params = Act_false_retval;
1463     }
1464 }
1465 
1466 static void
Act_digit(Widget w,XEvent * event,String * params,Cardinal * num_params)1467 Act_digit(Widget w, XEvent *event,
1468 	  String *params, Cardinal *num_params)
1469 {
1470     int digit;
1471     /* for overflow checks */
1472     static const int MAXINT_QUOT = INT_MAX / 10;
1473     static const int MAXINT_MOD = INT_MAX % 10;
1474 
1475 
1476     UNUSED(w);
1477     UNUSED(event);
1478 
1479     if (*num_params != 1 || (digit = **params - '0') > 9) {
1480 	xdvi_bell();
1481 	return;
1482     }
1483     m_have_arg = True;
1484 
1485     /* don't increment m_number if it would overflow */
1486     if (m_number < MAXINT_QUOT || (m_number == MAXINT_QUOT && digit <= MAXINT_MOD)) {
1487 	m_number = m_number * 10 + digit;
1488 	if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
1489 	    statusline_info(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number);
1490     }
1491     else {
1492 	xdvi_bell();
1493 	statusline_info(STATUS_SHORT, "numerical prefix: %s%d: no larger value possible", m_sign < 0 ? "-" : "", m_number);
1494     }
1495 }
1496 
1497 static void
Act_minus(Widget w,XEvent * event,String * params,Cardinal * num_params)1498 Act_minus(Widget w, XEvent *event,
1499 	  String *params, Cardinal *num_params)
1500 {
1501     UNUSED(w);
1502     UNUSED(event);
1503     UNUSED(params);
1504     UNUSED(num_params);
1505 
1506     m_have_arg = True;
1507     m_sign = -m_sign;
1508     if (m_number > 0) {
1509 	if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
1510 	    statusline_info(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number);
1511     }
1512     else {
1513 	if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
1514 	    statusline_info(STATUS_SHORT, "numerical prefix: %s", m_sign < 0 ? "-" : "");
1515     }
1516 }
1517 
1518 static void
Act_quit(Widget w,XEvent * event,String * params,Cardinal * num_params)1519 Act_quit(Widget w, XEvent *event,
1520 	 String *params, Cardinal *num_params)
1521 {
1522     UNUSED(w);
1523     UNUSED(event);
1524     UNUSED(params);
1525     UNUSED(num_params);
1526 
1527 #ifndef FLAKY_SIGPOLL
1528     if (globals.debug & DBG_EVENT)
1529 	puts(event_freq < 0
1530 	     ? "SIGPOLL is working"
1531 	     : "no SIGPOLL signals received");
1532 #endif
1533     xdvi_normal_exit();
1534 }
1535 
1536 static void
Act_quit_confirm(Widget w,XEvent * event,String * params,Cardinal * num_params)1537 Act_quit_confirm(Widget w, XEvent *event,
1538 		 String *params, Cardinal *num_params)
1539 {
1540     static Widget dialog = 0;
1541 
1542     UNUSED(w);
1543     UNUSED(event);
1544     UNUSED(params);
1545     UNUSED(num_params);
1546 
1547 #ifndef FLAKY_SIGPOLL
1548     if (globals.debug & DBG_EVENT)
1549 	puts(event_freq < 0
1550 	     ? "SIGPOLL is working"
1551 	     : "no SIGPOLL signals received");
1552 #endif
1553 
1554     /* already a quit dialog open? */
1555     if (dialog != 0) {
1556 	/* HACK ALERT: use brute force, since tests for XtIsRealized()
1557 	   or XtIsMapped() don't work?? Grabbing the server apparently
1558 	   has problems with Xaw, and it's not a nice solution anyway ... */
1559 	if (kill_message_window(dialog))
1560 	    xdvi_bell();
1561     }
1562 
1563     dialog = choice_dialog_sized(globals.widgets.top_level,
1564 				 MSG_QUESTION,
1565 				 SIZE_SMALL,
1566 				 NULL,
1567 #ifndef MOTIF
1568 				 "quit",
1569 #endif
1570 				 NULL, NULL, /* no pre_callbacks */
1571 				 "OK", xdvi_normal_exit_cb, NULL,
1572 				 "Cancel", NULL, NULL,
1573 				 "Really quit xdvi?");
1574 }
1575 
1576 static void
Act_load_url(Widget w,XEvent * event,String * params,Cardinal * num_params)1577 Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params)
1578 {
1579     UNUSED(w);
1580     UNUSED(event);
1581     UNUSED(num_params);
1582     launch_browser(*params);
1583 }
1584 
1585 static void
Act_print(Widget w,XEvent * event,String * params,Cardinal * num_params)1586 Act_print(Widget w, XEvent *event, String *params, Cardinal *num_params)
1587 {
1588     /* static so that we can pass them around to callbacks etc. */
1589     static struct save_or_print_info info;
1590     static struct select_pages_info pinfo = {
1591 	0, 0, check_marked, { 0, NULL }, NO_ERROR
1592     };
1593     static struct file_info finfo = {
1594 	NULL, NULL,
1595 	NULL, NULL,
1596 	NULL
1597     };
1598     static Boolean first_time = True;
1599 
1600     UNUSED(w);
1601     UNUSED(event);
1602     UNUSED(params);
1603     UNUSED(num_params);
1604 
1605     if (first_time) {
1606 	info.shell = info.printlog = NULL;
1607 	info.act = FILE_PRINT;
1608 	info.pinfo = &pinfo;
1609 	info.finfo = &finfo;
1610 	info.callbacks = NULL;
1611 	first_time = False;
1612     }
1613     save_or_print_callback(&info);
1614 }
1615 
1616 static void
Act_save(Widget w,XEvent * event,String * params,Cardinal * num_params)1617 Act_save(Widget w, XEvent *event, String *params, Cardinal *num_params)
1618 {
1619     /* static so that we can pass them around to callbacks etc. */
1620     static struct save_or_print_info info;
1621     static struct select_pages_info pinfo = {
1622 	0, 0, check_marked, { 0, NULL }, NO_ERROR
1623     };
1624     static struct file_info finfo = {
1625 	NULL, NULL,
1626 	NULL, NULL,
1627 	NULL
1628     };
1629     static Boolean first_time = True;
1630 
1631     UNUSED(w);
1632     UNUSED(event);
1633     UNUSED(params);
1634     UNUSED(num_params);
1635 
1636     if (first_time) {
1637 	info.shell = info.printlog = NULL;
1638 	info.act = FILE_SAVE;
1639 	info.pinfo = &pinfo;
1640 	info.finfo = &finfo;
1641 	info.callbacks = NULL;
1642 	first_time = False;
1643     }
1644     save_or_print_callback(&info);
1645 }
1646 
1647 static void
Act_find(Widget w,XEvent * event,String * params,Cardinal * num_params)1648 Act_find(Widget w, XEvent *event, String *params, Cardinal *num_params)
1649 {
1650     UNUSED(w);
1651     UNUSED(event);
1652     UNUSED(params);
1653     UNUSED(num_params);
1654     dvi_find_string(NULL, False);
1655 }
1656 
1657 static void
Act_incremental_find(Widget w,XEvent * event,String * params,Cardinal * num_params)1658 Act_incremental_find(Widget w, XEvent *event, String *params, Cardinal *num_params)
1659 {
1660     UNUSED(w);
1661     UNUSED(event);
1662     UNUSED(params);
1663     UNUSED(num_params);
1664     isearch_start();
1665 }
1666 
1667 static void
Act_find_next(Widget w,XEvent * event,String * params,Cardinal * num_params)1668 Act_find_next(Widget w, XEvent *event, String *params, Cardinal *num_params)
1669 {
1670     UNUSED(w);
1671     UNUSED(event);
1672     UNUSED(params);
1673     UNUSED(num_params);
1674     dvi_find_string(NULL, True);
1675 }
1676 
1677 static void
Act_help(Widget w,XEvent * event,String * params,Cardinal * num_params)1678 Act_help(Widget w, XEvent *event,
1679 	 String *params, Cardinal *num_params)
1680 {
1681     char *arg = NULL;
1682 
1683     UNUSED(w);
1684     UNUSED(event);
1685 
1686     if (*num_params > 0)
1687 	arg = *params;
1688 
1689     show_help(globals.widgets.top_level, arg);
1690 }
1691 
1692 static home_proc home_action = NULL;
1693 
1694 void
goto_page(int new_page,home_proc proc,Boolean force)1695 goto_page(int new_page, home_proc proc, Boolean force)
1696 {
1697     /* SU: added clearing the window here, otherwise old window content
1698        will survive switching pages as long as globals.pausing.flag is active
1699        (i.e. when accidentally changing page instead of unpausing)
1700     */
1701     if (globals.pausing.flag) {
1702 	XClearWindow(DISP, mane.win);
1703     }
1704 
1705     if (globals.dvi_file.bak_fp == NULL) {
1706 	current_page = new_page; /* so that xdvi gets the page change */
1707 	return;
1708     }
1709 
1710     xdvi_assert(XDVI_VERSION_INFO, __FILE__, __LINE__,
1711 		new_page >= 0 && new_page < total_pages,
1712 		"%d >= 0 && %d < %d", new_page, new_page, total_pages);
1713 
1714     if (current_page != new_page || force) {
1715 	globals.cursor.flags &= ~CURSOR_LINK; /* disable link cursor if needed */
1716 
1717 	current_page = new_page;
1718 	home_action = proc;
1719 	globals.warn_spec_now = resource.warn_spec;
1720 	/* this seems unneccessary */
1721 	/* 	if (!resource.keep_flag) */
1722 	/* 	    home(False); */
1723 #if defined(MOTIF) && HAVE_XPM
1724 	tb_check_navigation_sensitivity(current_page);
1725 	/*  	page_history_update_toolbar_navigation(); */
1726 #endif
1727 	maybe_scroll_pagelist(current_page, False);
1728 
1729 	if (globals.pausing.num_save)
1730 	    globals.pausing.num = globals.pausing.num_save[new_page];
1731 	else
1732 	    globals.pausing.num = 0;
1733 	/* Control-L (and changing the page) clears this box */
1734 	globals.src.fwd_box_page = -1;
1735 
1736 	globals.ev.flags |= EV_NEWPAGE;
1737 	XFlush(DISP);
1738     }
1739 }
1740 
1741 
1742 static void
Act_goto_page(Widget w,XEvent * event,String * params,Cardinal * num_params)1743 Act_goto_page(Widget w, XEvent *event,
1744 	      String *params, Cardinal *num_params)
1745 {
1746     int arg;
1747     Boolean clear_statusline = False;
1748 
1749     UNUSED(w);
1750     UNUSED(event);
1751 
1752     warn_num_params("goto-page()", params, *num_params, 1);
1753 
1754     if (*num_params > 0) {
1755 	if (**params == 'e') {
1756 	    arg = total_pages - 1;
1757 	}
1758 	else
1759 	    arg = atoi(*params) - globals.pageno_correct;
1760     }
1761     else {
1762 	if (get_prefix_arg(&arg)) {
1763 	    clear_statusline = True;
1764 	    arg -= globals.pageno_correct;
1765 	}
1766 	else {
1767 	    arg = total_pages - 1;
1768 	}
1769     }
1770     if (arg == total_pages - 1) {
1771 	/* with TeX numbers, total_pages - 1 might not be found in the page list,
1772 	   so don't use check_goto_tex_page() in this case */
1773 	goto_page(check_goto_page(arg, True), resource.keep_flag ? NULL : home, False);
1774     }
1775     else {
1776 	goto_page(check_goto_tex_page(arg), resource.keep_flag ? NULL : home, False);
1777     }
1778     search_signal_page_changed();
1779     if (clear_statusline)
1780 	statusline_clear();
1781     statusline_erase("Page history:");
1782 }
1783 
1784 void
Act_recent_files(Widget w,XEvent * event,String * params,Cardinal * num_params)1785 Act_recent_files(Widget w, XEvent *event,
1786 		 String *params, Cardinal *num_params)
1787 {
1788     UNUSED(w);
1789     UNUSED(event);
1790     UNUSED(params);
1791     UNUSED(num_params);
1792 }
1793 
1794 void
Act_use_tex_pages(Widget w,XEvent * event,String * params,Cardinal * num_params)1795 Act_use_tex_pages(Widget w, XEvent *event,
1796 		  String *params, Cardinal *num_params)
1797 {
1798     UNUSED(w);
1799     UNUSED(event);
1800 
1801 
1802     if (*num_params == 0) {
1803 	if (m_have_arg) {
1804 	    resource.use_tex_pages = (m_number != 0);
1805 	    m_have_arg = False;
1806 	    m_number = 0;
1807 	    m_sign = 1;
1808 	}
1809 	else {
1810 	    resource.use_tex_pages = !resource.use_tex_pages;
1811 	}
1812     }
1813     else
1814 	resource.use_tex_pages = (**params == 't' ? !resource.use_tex_pages : atoi(*params));
1815 
1816     if (resource.use_tex_pages) {
1817 	statusline_info(STATUS_SHORT, "Using TeX page numbers for \"g\", goto-page()");
1818     }
1819     else {
1820 	statusline_info(STATUS_SHORT, "Using physical page numbers for \"g\", goto-page()");
1821     }
1822 
1823     store_preference(NULL, "useTeXPages", "%s", resource.use_tex_pages ? "True" : "False");
1824 
1825     set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle);
1826 
1827     refresh_pagelist(total_pages, current_page);
1828 }
1829 
1830 void
Act_forward_page(Widget w,XEvent * event,String * params,Cardinal * num_params)1831 Act_forward_page(Widget w, XEvent *event,
1832 		 String *params, Cardinal *num_params)
1833 {
1834     int arg;
1835     Boolean clear_statusline = False;
1836 
1837     UNUSED(w);
1838     UNUSED(event);
1839 
1840     if (!get_int_arg(params, num_params, &arg))
1841 	arg = 1;
1842     else
1843 	clear_statusline = True;
1844 
1845     arg += current_page;
1846 
1847     if (arg == current_page) { /* zero argument -> redraw page */
1848 	globals.src.fwd_box_page = -1;
1849 	search_reset_info();
1850 	globals.ev.flags |= EV_NEWPAGE;
1851 	XFlush(DISP);
1852 	statusline_info(STATUS_SHORT, "Page redrawn.");
1853 	return;
1854     }
1855     else if (current_page >= total_pages - 1) {
1856 	xdvi_bell();
1857 /* 	statusline_info(STATUS_SHORT, "Last page of DVI file"); */
1858 	return;
1859     }
1860 
1861     goto_page(check_goto_page(arg, True), resource.keep_flag ? NULL : home, False);
1862     if (clear_statusline)
1863 	statusline_clear();
1864     statusline_erase("Page history:");
1865     search_signal_page_changed();
1866 }
1867 
1868 void
Act_back_page(Widget w,XEvent * event,String * params,Cardinal * num_params)1869 Act_back_page(Widget w, XEvent *event,
1870 	      String *params, Cardinal *num_params)
1871 {
1872     int arg;
1873     Boolean clear_statusline = False;
1874 
1875     UNUSED(w);
1876     UNUSED(event);
1877 
1878     if (!get_int_arg(params, num_params, &arg))
1879 	arg = 1;
1880     else
1881 	clear_statusline = True;
1882 
1883     arg = current_page - arg;
1884 
1885     if (current_page == 0) {
1886 	xdvi_bell();
1887 /* 	statusline_info(STATUS_SHORT, "First page of DVI file"); */
1888 	return;
1889     }
1890 
1891     goto_page(check_goto_page(arg, True), resource.keep_flag ? NULL : home, False);
1892     if (clear_statusline)
1893 	statusline_clear();
1894     statusline_erase("Page history:");
1895     search_signal_page_changed();
1896 }
1897 
1898 static void
Act_declare_page_number(Widget w,XEvent * event,String * params,Cardinal * num_params)1899 Act_declare_page_number(Widget w, XEvent *event,
1900 			String *params, Cardinal *num_params)
1901 {
1902     int arg;
1903 
1904     UNUSED(w);
1905     UNUSED(event);
1906 
1907     if (globals.curr_mode == RULER_MODE_ACTIVE) {
1908 	show_distance_from_ruler(event, True);
1909 	return;
1910     }
1911 
1912     if (!get_int_arg(params, num_params, &arg)) {
1913 	arg = 0;
1914     }
1915     globals.pageno_correct = arg - current_page;
1916     statusline_info(STATUS_SHORT, "Current page number set to %d", arg);
1917 }
1918 
1919 static void
Act_toggle_mark(Widget w,XEvent * event,String * params,Cardinal * num_params)1920 Act_toggle_mark(Widget w, XEvent *event,
1921 		String *params, Cardinal *num_params)
1922 {
1923     int arg;
1924 
1925     UNUSED(w);
1926     UNUSED(event);
1927 
1928     if (globals.dvi_file.bak_fp == NULL)
1929 	return;
1930 
1931     if (
1932 #ifdef MOTIF
1933 	(resource.expert_mode & XPRT_SHOW_PAGELIST) == 0
1934 #else
1935 	(resource.expert_mode & XPRT_SHOW_BUTTONS) == 0
1936 #endif
1937 	) {
1938 	xdvi_bell();
1939 	statusline_info(STATUS_SHORT,
1940 			 "Expert mode: Page list not available.");
1941 	return;
1942     }
1943 
1944     if (*num_params > 0) {
1945 	arg = atoi(*params);
1946 	if (arg < -1 || arg > 2) {
1947 	    xdvi_bell();
1948 	    statusline_error(STATUS_SHORT,
1949 			     "Possible arguments: none (toggle current), "
1950 			     "-1 (mark all), 0 (unmark all), 1 (toggle odd), 2 (toggle even)");
1951 	}
1952 	list_toggle_marks(arg);
1953     }
1954     else if (get_prefix_arg(&arg)) {
1955 	list_toggle_marks(arg);
1956 	statusline_clear();
1957     }
1958     else {
1959 	arg = current_page;
1960 	list_toggle_current(arg);
1961     }
1962 }
1963 
1964 static void
Act_home(Widget w,XEvent * event,String * params,Cardinal * num_params)1965 Act_home(Widget w, XEvent *event,
1966 	 String *params, Cardinal *num_params)
1967 {
1968     UNUSED(w);
1969     UNUSED(event);
1970     UNUSED(params);
1971     UNUSED(num_params);
1972 
1973     home(True);
1974 }
1975 
1976 static void
Act_home_or_top(Widget w,XEvent * event,String * params,Cardinal * num_params)1977 Act_home_or_top(Widget w, XEvent *event,
1978 		String *params, Cardinal *num_params)
1979 {
1980     UNUSED(w);
1981     UNUSED(event);
1982     UNUSED(params);
1983     UNUSED(num_params);
1984 
1985     if (resource.keep_flag) {
1986 	String args[1];
1987 	args[0] = "10"; /* should be large enough ... */
1988 	XtCallActionProc(globals.widgets.top_level, "up", NULL, args, 1);
1989     }
1990     else {
1991 	home(True);
1992     }
1993 }
1994 
1995 static void
Act_end_or_bottom(Widget w,XEvent * event,String * params,Cardinal * num_params)1996 Act_end_or_bottom(Widget w, XEvent *event,
1997 		  String *params, Cardinal *num_params)
1998 {
1999     String args[1];
2000 
2001     UNUSED(w);
2002     UNUSED(event);
2003     UNUSED(params);
2004     UNUSED(num_params);
2005 
2006     args[0] = "10"; /* should be large enough ... */
2007     XtCallActionProc(globals.widgets.top_level, "down", NULL, args, 1);
2008 
2009     if (!resource.keep_flag
2010 #ifndef MOTIF
2011 	&& globals.widgets.x_bar != NULL
2012 #endif
2013 	) {
2014 	XtCallActionProc(globals.widgets.top_level, "right", NULL, args, 1);
2015     }
2016 }
2017 
2018 static void
Act_center(Widget w,XEvent * event,String * params,Cardinal * num_params)2019 Act_center(Widget w, XEvent *event,
2020 	   String *params, Cardinal *num_params)
2021 {
2022     int x, y;
2023 
2024     UNUSED(w);
2025     UNUSED(params);
2026     UNUSED(num_params);
2027 
2028 #ifdef MOTIF
2029     /* The clip widget gives a more exact value. */
2030     x = event->xkey.x - mane.width / 2;
2031     y = event->xkey.y - mane.height / 2;
2032 
2033     x = set_bar_value(globals.widgets.x_bar, x, (int)(globals.page.w - mane.width));
2034     y = set_bar_value(globals.widgets.y_bar, y, (int)(globals.page.h - mane.height));
2035     get_xy();
2036     XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x - m_window_x, -y - m_window_y);
2037 #else
2038 
2039     if (event == NULL)
2040 	return;	/* button actions do not provide events */
2041 
2042     x = event->xkey.x - mane.width / 2;
2043     y = event->xkey.y - mane.height / 2;
2044     /* The clip widget gives a more exact value. */
2045     if (globals.widgets.x_bar != NULL)
2046 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, cast_int_to_XtPointer(x));
2047     if (globals.widgets.y_bar != NULL)
2048 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(y));
2049     XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x, -y);
2050 #endif
2051 #ifdef USE_PANNER
2052     handle_x_scroll(NULL, NULL, NULL, NULL);
2053     handle_y_scroll(NULL, NULL, NULL, NULL);
2054 #endif
2055 }
2056 
2057 void
Act_ruler_snap_origin(Widget w,XEvent * event,String * params,Cardinal * num_params)2058 Act_ruler_snap_origin(Widget w, XEvent *event,
2059 		      String *params, Cardinal *num_params)
2060 {
2061     UNUSED(w);
2062     UNUSED(params);
2063     UNUSED(num_params);
2064 
2065     if (globals.curr_mode == RULER_MODE_ACTIVE)
2066 	ruler_snap_origin(event);
2067 }
2068 
2069 #if 0
2070 void
2071 Act_set_papersize(Widget w, XEvent *event,
2072 		  String *params, Cardinal *num_params)
2073 {
2074     UNUSED(w);
2075     UNUSED(event);
2076     UNUSED(num_params);
2077 
2078     fprintf(stderr, "set_paperformat: %s\n", *params);
2079     resource.paper = *params;
2080     set_menu((void *)resource.paper, Act_set_papersize, check_papersize);
2081 }
2082 
2083 void
2084 Act_set_paper_landscape(Widget w, XEvent *event,
2085 			String *params, Cardinal *num_params)
2086 {
2087     UNUSED(w);
2088     UNUSED(event);
2089     UNUSED(num_params);
2090 
2091     resource.paper_landscape = !resource.paper_landscape;
2092     fprintf(stderr, "set_paper_landscape: %s\n", *params);
2093 
2094     set_menu((char *)resource.paper,  Act_set_paper_landscape, check_paper_landscape);
2095 }
2096 #endif /* 0 */
2097 
2098 
2099 void
Act_set_keep_flag(Widget w,XEvent * event,String * params,Cardinal * num_params)2100 Act_set_keep_flag(Widget w, XEvent *event,
2101 		  String *params, Cardinal *num_params)
2102 {
2103     UNUSED(event);
2104     UNUSED(w);
2105 
2106     if (*num_params == 0) {
2107 	if (m_have_arg) {
2108 	    resource.keep_flag = (m_number != 0);
2109 	    m_have_arg = False;
2110 	    m_number = 0;
2111 	    m_sign = 1;
2112 	}
2113 	else {
2114 	    resource.keep_flag = !resource.keep_flag;
2115 	}
2116     }
2117     else
2118 	resource.keep_flag = (**params == 't'
2119 			      ? !resource.keep_flag : atoi(*params));
2120     if (resource.keep_flag) {
2121 	statusline_info(STATUS_SHORT, "Keeping position when switching pages");
2122     }
2123     else {
2124 	statusline_info(STATUS_SHORT,
2125 			 "Not keeping position when switching pages");
2126     }
2127 
2128     store_preference(NULL, "keepPosition", "%s", resource.keep_flag ? "True" : "False");
2129 
2130     set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle);
2131 }
2132 
2133 static void
Act_left(Widget w,XEvent * event,String * params,Cardinal * num_params)2134 Act_left(Widget w, XEvent *event,
2135 	 String *params, Cardinal *num_params)
2136 {
2137     UNUSED(w);
2138     UNUSED(event);
2139 
2140     do_autoscroll = False;
2141 
2142     warn_num_params("left()", params, *num_params, 1);
2143 
2144 #ifdef MOTIF
2145     get_xy();
2146 #ifdef TEST_SCROLLING
2147     fprintf(stderr, "left for %p\n", (void *)w);
2148     fprintf(stderr, "window x: %d, y: %d\n", m_window_x, m_window_y);
2149 #endif
2150 
2151     (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (-2 * (int)mane.width / 3)
2152 						: (int)(-my_atof(*params) * mane.width)) - m_window_x,
2153 			(int)(globals.page.w - mane.width));
2154 #else
2155     if (globals.widgets.x_bar != NULL)
2156 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
2157 			cast_int_to_XtPointer(*num_params == 0 ? (-2 * (int)mane.width / 3)
2158 					      : (int)(-my_atof(*params) * mane.width)));
2159     else {
2160 	xdvi_bell();
2161 /* 	statusline_info(STATUS_SHORT, "Horizontal scrolling not possible"); */
2162     }
2163 #endif
2164 #ifdef USE_PANNER
2165     handle_x_scroll(NULL, NULL, NULL, NULL);
2166 #endif
2167 }
2168 
2169 static void
Act_right(Widget w,XEvent * event,String * params,Cardinal * num_params)2170 Act_right(Widget w, XEvent *event,
2171 	  String *params, Cardinal *num_params)
2172 {
2173     UNUSED(w);
2174     UNUSED(event);
2175 
2176     do_autoscroll = False;
2177 
2178     warn_num_params("right()", params, *num_params, 1);
2179 
2180 #ifdef MOTIF
2181     get_xy();
2182 #ifdef TEST_SCROLLING
2183     fprintf(stderr, "right for %p\n", (void *)w);
2184     fprintf(stderr, "window x: %d, y: %d\n", m_window_x, m_window_y);
2185 #endif
2186 
2187     (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (2 * (int)mane.width / 3)
2188 						: (int)(my_atof(*params) * mane.width)) - m_window_x,
2189 			(int)(globals.page.w - mane.width));
2190 #else
2191     if (globals.widgets.x_bar != NULL)
2192 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
2193 			cast_int_to_XtPointer(*num_params == 0 ? (2 * (int)mane.width / 3)
2194 					      : (int)(my_atof(*params) * mane.width)));
2195     else {
2196 	xdvi_bell();
2197 /* 	statusline_info(STATUS_SHORT, "Horizontal scrolling not possible"); */
2198     }
2199 #endif
2200 #ifdef USE_PANNER
2201     handle_x_scroll(NULL, NULL, NULL, NULL);
2202 #endif
2203 }
2204 
2205 static void
Act_up(Widget w,XEvent * event,String * params,Cardinal * num_params)2206 Act_up(Widget w, XEvent *event,
2207        String *params, Cardinal *num_params)
2208 {
2209     UNUSED(w);
2210     UNUSED(event);
2211 
2212     do_autoscroll = False;
2213 
2214     warn_num_params("up()", params, *num_params, 1);
2215 
2216 #ifdef MOTIF
2217 #ifdef TEST_SCROLLING
2218     fprintf(stderr, "up for %p\n", (void *)w);
2219 #endif
2220     get_xy();
2221     (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (-2 * (int)mane.height / 3)
2222 						: (int)(-my_atof(*params) * mane.height)) - m_window_y,
2223 			(int)(globals.page.h - mane.height));
2224 #else
2225     if (globals.widgets.y_bar != NULL)
2226 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
2227 			cast_int_to_XtPointer(*num_params == 0 ? (-2 * (int)mane.height / 3)
2228 					      : (int)(-my_atof(*params) * mane.height)));
2229     else {
2230 	xdvi_bell();
2231 /* 	statusline_info(STATUS_SHORT, "Vertical scrolling not possible"); */
2232     }
2233 #endif
2234 #ifdef USE_PANNER
2235     handle_y_scroll(NULL, NULL, NULL, NULL);
2236 #endif
2237 }
2238 
2239 static void
Act_down(Widget w,XEvent * event,String * params,Cardinal * num_params)2240 Act_down(Widget w, XEvent *event,
2241 	 String *params, Cardinal *num_params)
2242 {
2243     UNUSED(w);
2244     UNUSED(event);
2245 
2246     do_autoscroll = False;
2247 
2248     warn_num_params("down()", params, *num_params, 1);
2249 
2250 #ifdef MOTIF
2251 #ifdef TEST_SCROLLING
2252     fprintf(stderr, "down for %p\n", (void *)w);
2253 #endif
2254     get_xy();
2255     (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3)
2256 						: (int)(my_atof(*params) * mane.height)) - m_window_y,
2257 			(int)(globals.page.h - mane.height));
2258 #else
2259     if (globals.widgets.y_bar != NULL)
2260 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
2261 			cast_int_to_XtPointer(*num_params == 0 ? (2 * (int)mane.height / 3)
2262 					      : (int)(my_atof(*params) * mane.height)));
2263     else {
2264 	xdvi_bell();
2265 /* 	statusline_info(STATUS_SHORT, "Vertical scrolling not possible"); */
2266     }
2267 #endif
2268 #ifdef USE_PANNER
2269     handle_y_scroll(NULL, NULL, NULL, NULL);
2270 #endif
2271 }
2272 
2273 static void
Act_down_or_next(Widget w,XEvent * event,String * params,Cardinal * num_params)2274 Act_down_or_next(Widget w, XEvent *event,
2275 		 String *params, Cardinal *num_params)
2276 {
2277     UNUSED(w);
2278     UNUSED(event);
2279 
2280     do_autoscroll = False;
2281 
2282     warn_num_params("down-or-next()", params, *num_params, 1);
2283 
2284 #ifdef TEST_SCROLLING
2285     fprintf(stderr, "down-or-next for %p\n", (void *)w);
2286 #endif
2287 
2288 #ifdef MOTIF
2289     get_xy();
2290     if (m_window_y > (int)mane.height - (int)globals.page.h) {
2291 	(void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3)
2292 						    : (int)(my_atof(*params) * mane.height)) -
2293 			    m_window_y, (int)(globals.page.h - mane.height));
2294 	return;
2295     }
2296 #else
2297     if (globals.widgets.y_bar != NULL) {
2298 	get_xy();
2299 	if (m_window_y > (int)mane.height - (int)globals.page.h) {
2300 	    XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
2301 			    cast_int_to_XtPointer(*num_params ==
2302 						  0 ? (2 * (int)mane.height / 3)
2303 						  : (int)(my_atof(*params) * mane.height)));
2304 	    return;
2305 	}
2306     }
2307 #endif
2308 
2309     if (current_page < total_pages - 1) {
2310 	goto_page(current_page + 1, home, False);
2311 	search_signal_page_changed();
2312     }
2313     else {
2314 	xdvi_bell();
2315 /* 	statusline_info(STATUS_SHORT, "At bottom of last page"); */
2316     }
2317 #ifdef USE_PANNER
2318     handle_y_scroll(NULL, NULL, NULL, NULL);
2319 #endif
2320     statusline_erase("Page history:");
2321 }
2322 
2323 
2324 static void
Act_up_or_previous(Widget w,XEvent * event,String * params,Cardinal * num_params)2325 Act_up_or_previous(Widget w, XEvent *event,
2326 		   String *params, Cardinal *num_params)
2327 {
2328     UNUSED(w);
2329     UNUSED(event);
2330 
2331     do_autoscroll = False;
2332 
2333     warn_num_params("up-or-previous()", params, *num_params, 1);
2334 
2335 #ifdef TEST_SCROLLING
2336     fprintf(stderr, "up-or-previous for %p\n", (void *)w);
2337 #endif
2338 #ifdef MOTIF
2339     get_xy();
2340     if (m_window_y < 0) {
2341 	(void)set_bar_value(globals.widgets.y_bar,
2342 			    (*num_params == 0 ? (-2 * (int)mane.height / 3)
2343 			     : (int)(-my_atof(*params) * mane.height)) - m_window_y,
2344 			    (int)(globals.page.h - mane.height));
2345 	return;
2346     }
2347 #else
2348     if (globals.widgets.y_bar != NULL) {
2349 	get_xy();
2350 	if (m_window_y < 0) {
2351 	    XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
2352 			    cast_int_to_XtPointer(*num_params ==
2353 						  0 ? (-2 * (int)mane.height / 3)
2354 						  : (int)(-my_atof(*params) * mane.height)));
2355 	    return;
2356 	}
2357     }
2358 #endif
2359 
2360     if (current_page > 0) {
2361 	goto_page(current_page - 1, home_bottom, False);
2362 	search_signal_page_changed();
2363     }
2364     else {
2365 	xdvi_bell();
2366 /* 	statusline_info(STATUS_SHORT, "At top of first page"); */
2367     }
2368     statusline_erase("Page history:");
2369 #ifdef USE_PANNER
2370     handle_y_scroll(NULL, NULL, NULL, NULL);
2371 #endif
2372 }
2373 
2374 
2375 static void
Act_set_margins(Widget w,XEvent * event,String * params,Cardinal * num_params)2376 Act_set_margins(Widget w, XEvent *event,
2377 		String *params, Cardinal *num_params)
2378 {
2379     Window dummy;
2380 
2381     UNUSED(w);
2382     UNUSED(params);
2383     UNUSED(num_params);
2384 
2385 #ifndef MOTIF
2386     if (event == NULL)
2387 	return;	/* button actions do not provide events */
2388 #endif
2389 
2390     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
2391 				event->xkey.x, event->xkey.y, &resource.sidemargin_int, &resource.topmargin_int, &dummy);	/* throw away last argument */
2392     statusline_info(STATUS_SHORT, "Margins set to cursor position (%d, %d)", resource.sidemargin_int, resource.topmargin_int);
2393     resource.sidemargin_int *= mane.shrinkfactor;
2394     resource.topmargin_int *= mane.shrinkfactor;
2395 }
2396 
2397 static void
Act_show_display_attributes(Widget w,XEvent * event,String * params,Cardinal * num_params)2398 Act_show_display_attributes(Widget w, XEvent *event,
2399 			    String *params, Cardinal *num_params)
2400 {
2401     UNUSED(w);
2402     UNUSED(event);
2403     UNUSED(params);
2404     UNUSED(num_params);
2405 
2406     statusline_error(STATUS_SHORT, "Unit = %d, bitord = %d, byteord = %d",
2407 		     BitmapUnit(DISP), BitmapBitOrder(DISP),
2408 		     ImageByteOrder(DISP));
2409 }
2410 
2411 int
shrink_to_fit(void)2412 shrink_to_fit(void)
2413 {
2414     int value1;
2415     int value2;
2416     static Dimension window_w;
2417 #ifndef MOTIF
2418     static Dimension window_h;
2419 #endif
2420 
2421     static Arg arg_wh[] = {
2422 	{XtNwidth, (XtArgVal) &window_w}
2423 #ifndef MOTIF
2424 	, {XtNheight, (XtArgVal) &window_h}
2425 #endif
2426     };
2427 
2428 
2429 #ifdef MOTIF
2430     XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
2431 #else
2432     XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
2433 #endif
2434 
2435     value1 = ROUNDUP(globals.page.unshrunk_w, window_w - 2);
2436 
2437 #ifdef  MOTIF
2438     {	/* account for menubar */
2439 	static Dimension new_h;
2440 
2441 	/* get rid of scrollbar */
2442 	XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)1, XtNheight, (XtArgVal)1, NULL);
2443 	XtVaGetValues(globals.widgets.clip_widget, XtNheight, &new_h, NULL);
2444 	value2 = ROUNDUP(globals.page.unshrunk_h, new_h - 2);
2445     }
2446 #else
2447     /* FIXME: value seems too small here! */
2448     value2 = ROUNDUP(globals.page.unshrunk_h, window_h - 2);
2449 #endif
2450 
2451     return value1 > value2 ? value1 : value2;
2452 }
2453 
2454 void
do_set_shrinkfactor(int arg,Boolean set_resource)2455 do_set_shrinkfactor(int arg, Boolean set_resource)
2456 {
2457     static int shrink_bak = -1;
2458 
2459     /* We don't store this as preference since it may affect initial window geometry. */
2460     /*  store_preference(NULL, "shrinkFactor", "%d", arg); */
2461 
2462     if (globals.curr_mode == TEXT_MODE_ACTIVE) {
2463 	text_change_region(TEXT_SEL_CLEAR, NULL);
2464     }
2465 
2466     mane.shrinkfactor = arg;
2467     if (set_resource)
2468 	resource.shrinkfactor = mane.shrinkfactor;
2469 
2470     set_menu(&arg, Act_set_shrink_factor, check_int);
2471 #if HAVE_XMP
2472     tb_set_zoom_sensitivity(arg != 1);
2473 #endif
2474 
2475     if (arg != 1 && arg != shrink_bak) {
2476 	shrink_bak = arg;
2477 #if GREY
2478 #if COLOR
2479 	if (resource.use_grey)
2480 	    fg_active = NULL;
2481 #else
2482 	if (resource.use_grey)
2483 	    init_pix();
2484 #endif
2485 #endif /* GREY */
2486 	reset_fonts();
2487     }
2488     init_page();
2489     reconfig();
2490 
2491     htex_resize_page();
2492 
2493     /* this seems unneccessary */
2494     /*     if (!resource.keep_flag) */
2495     /* 	home(False); */
2496 
2497     globals.ev.flags |= EV_NEWPAGE;
2498     XFlush(DISP);
2499 }
2500 
2501 
2502 /* A 'meta' action that is bound to mouse buttons and invokes actions depending on current mouse mode */
2503 void
Act_mouse_modes(Widget w,XEvent * event,String * params,Cardinal * num_params)2504 Act_mouse_modes(Widget w, XEvent *event,
2505 		String *params, Cardinal *num_params)
2506 {
2507     int i;
2508     size_t mode_idx = 0; /* default: one action for all modes */
2509     struct xdvi_action *my_action = NULL, *ap = NULL;
2510 
2511     if (*num_params > 1) { /* different actions */
2512 	if (resource.mouse_mode >= *num_params) {
2513 	    size_t k;
2514 	    XDVI_WARNING((stderr, "Only %d parameters in X resource 'mouse-modes', should be %d",
2515 			  *num_params, resource.mouse_mode + 1));
2516 	    for (k = 0; k < *num_params; k++) {
2517 		XDVI_WARNING((stderr, "Param %d: `%s'", (int)(k + 1), params[k]));
2518 	    }
2519 	    return;
2520 	}
2521 	mode_idx = resource.mouse_mode;
2522     }
2523 
2524     cached_compile_action(params[mode_idx], &my_action);
2525     for (i = 0, ap = my_action; ap; ap = ap->next, i++) {
2526 	String *args;
2527 	String retval;
2528 
2529 	TRACE_EVENTS((stderr, "Action %d for mode %lu: '%s', %d args |%s| maps to proc |%p|",
2530 		      i, (unsigned long) mode_idx, ap->command, ap->num_params,
2531 		      ap->num_params > 0 ? ap->params[0] : "(none)",
2532 		      ap->proc));
2533 
2534 	/* copy args because the action proc may modify them (see below) */
2535 	if (ap->num_params) {
2536 	    args = xmalloc(ap->num_params * sizeof (String));
2537 	    memcpy(args, ap->params, ap->num_params * sizeof (String));
2538 	}
2539 	else args = xmalloc(sizeof (String));
2540 
2541 	/* now call the action proc directly */
2542 	XtCallActionProc(w, ap->command, event, args, ap->num_params);
2543 
2544 	retval = args[0];
2545 	free(args);
2546 	if (retval == Act_true_retval) { /* we may compare the pointers here */
2547 	    /* Special case: Only invoke first action, not subsequent ones;
2548 	     * e.g. if mouse is over a link and action is Act_href or
2549 	     * Act_href_newwindow (currently the only cases where this is used).
2550 	     */
2551 	    break;
2552 	}
2553     }
2554 }
2555 
2556 
2557 void
Act_set_shrink_factor(Widget w,XEvent * event,String * params,Cardinal * num_params)2558 Act_set_shrink_factor(Widget w, XEvent *event,
2559 		      String *params, Cardinal *num_params)
2560 {
2561     int arg;
2562     /* Set a limit to the shrink factors allowed; otherwise xdvi may
2563        consume a lot of memory (even run out of memory in malloc) in
2564        the allocation for the subpixel lookup table pixeltbl,
2565        dvi-draw.c. The `correct' solution would be to recognize that at a
2566        certain shrink, the only pixel for a character will be always
2567        set to `on' anyway, so that we could do without the lookup table for
2568        very large shrink factors ... */
2569     static const int SHRINK_MAX = 999;
2570 
2571     UNUSED(w);
2572     UNUSED(event);
2573 
2574     warn_num_params("set-shrink-factor()", params, *num_params, 1);
2575 
2576     if (*num_params > 0) {
2577 	if (**params == 'a')
2578 	    arg = shrink_to_fit();
2579 	else if (**params == '-')
2580 	    arg = mane.shrinkfactor + 1;
2581 	else if (**params == '+')
2582 	    arg = mane.shrinkfactor - 1;
2583 	else
2584 	    arg = atoi(*params);
2585     }
2586     else if (!get_prefix_arg(&arg)) {
2587 	arg = shrink_to_fit();
2588     }
2589 
2590     if (arg <= 0) {
2591 	xdvi_bell();
2592 /* 	statusline_info(STATUS_SHORT, */
2593 /* 			 "No more enlarging possible"); */
2594 	return;
2595     }
2596 
2597     if (arg > SHRINK_MAX) {
2598 	xdvi_bell();
2599 	statusline_info(STATUS_SHORT,
2600 			 "Shrink factor %d too large (maximum: %d)", arg, SHRINK_MAX);
2601 	return;
2602     }
2603 
2604     /* SU: added clearing the window here, else old window content
2605        will survive changing shrink
2606     */
2607     if (globals.pausing.flag) {
2608 	XClearWindow(DISP, mane.win);
2609     }
2610 
2611     statusline_info(STATUS_SHORT, "shrink factor: %d", arg);
2612 #if 0
2613     /* Uncommented this, otherwise selecting `shrink to fit' with same value as
2614        current mane.shrinkfactor will clear the canvas (only fixed by changing shrink
2615        factor again). That bug is also present in xdvik-22.48-alpha3. */
2616     if (arg == mane.shrinkfactor) {
2617 	return;
2618     }
2619 #endif
2620 
2621     do_set_shrinkfactor(arg, True);
2622 
2623 #if MOTIF
2624     /* note: mustn't do that inside do_set_shrinkfactor(),
2625        or we get into an endless loop! */
2626     update_preferences_shrink();
2627 #endif
2628 }
2629 
2630 void
Act_shrink_to_dpi(Widget w,XEvent * event,String * params,Cardinal * num_params)2631 Act_shrink_to_dpi(Widget w, XEvent *event,
2632 		  String *params, Cardinal *num_params)
2633 {
2634     int arg, arg_bak;
2635     Boolean clear_statusline = False;
2636 
2637     UNUSED(w);
2638     UNUSED(event);
2639 
2640     warn_num_params("shrink-to-dpi()", params, *num_params, 1);
2641 
2642     if (!get_int_arg(params, num_params, &arg))
2643 	arg = 0;
2644     else
2645 	clear_statusline = True;
2646 
2647     arg_bak = arg;
2648 
2649     if (arg > 0)
2650 	arg = (double)resource.pixels_per_inch / arg + 0.5;
2651 
2652     if (arg <= 0) {
2653 	xdvi_bell();
2654 	statusline_info(STATUS_SHORT,
2655 			 "shrink-to-dpi requires a positive argument");
2656 	return;
2657     }
2658 
2659     /* like elsewhere */
2660     if (globals.pausing.flag) {
2661 	XClearWindow(DISP, mane.win);
2662     }
2663 #if 0
2664     /* Uncommented this, otherwise selecting `shrink to fit' with same value as
2665        current mane.shrinkfactor will clear the canvas (only fixed by changing shrink
2666        factor again). That bug is also present in xdvik-22.48-alpha3. */
2667     if (arg == mane.shrinkfactor)
2668 	return;
2669 #endif
2670     do_set_shrinkfactor(arg, True);
2671 
2672 #if MOTIF
2673     /* note: mustn't do that inside do_set_shrinkfactor(),
2674        or we get into an endless loop! */
2675     update_preferences_shrink();
2676 #endif
2677 }
2678 
2679 void
do_set_density(double newgamma,Boolean force,Boolean update_resource)2680 do_set_density(double newgamma, Boolean force, Boolean update_resource)
2681 {
2682 
2683     /* like elsewhere */
2684     if (globals.pausing.flag) {
2685 	XClearWindow(DISP, mane.win);
2686     }
2687 
2688 #ifdef GREY
2689     if (resource.use_grey) {
2690 	if (newgamma == resource.gamma && !force) {
2691 	    statusline_info(STATUS_SHORT, "density value: %.3f", newgamma);
2692 	    return;
2693 	}
2694 	resource.gamma = newgamma;
2695 	if (update_resource)
2696 	    globals.curr_gamma = resource.gamma;
2697 
2698 #if COLOR
2699 	fg_active = NULL;
2700 	reset_colors();
2701 #else
2702 	init_pix();
2703 	if (G_visual->class != TrueColor) {
2704 	    return;
2705 	}
2706 	reset_fonts();
2707 #endif /* COLOR */
2708 	statusline_info(STATUS_SHORT, "density value: %.3f", newgamma);
2709     } else
2710 #endif /* GREY */
2711     {
2712 	reset_fonts();
2713 	if (mane.shrinkfactor == 1) {
2714 	    statusline_info(STATUS_SHORT,
2715 			     "set-density ignored at magnification 1");
2716 	    return;
2717 	}
2718 	statusline_info(STATUS_SHORT, "density value: %.3f", newgamma);
2719     }
2720 
2721     store_preference(NULL, "gamma", "%f", resource.gamma);
2722 
2723     globals.ev.flags |= EV_NEWPAGE;
2724     XFlush(DISP);
2725 }
2726 
2727 static void
Act_set_density(Widget w,XEvent * event,String * params,Cardinal * num_params)2728 Act_set_density(Widget w, XEvent *event,
2729 		String *params, Cardinal *num_params)
2730 {
2731     int arg;
2732 
2733     UNUSED(w);
2734     UNUSED(event);
2735 
2736     warn_num_params("set-density()", params, *num_params, 1);
2737 
2738     if (!get_int_arg(params, num_params, &arg)) {
2739 	xdvi_bell();
2740 	return;
2741     }
2742     /*     if (arg < 0) { */
2743     /* 	XBell(DISP, 0); */
2744     /* 	statusline_info(STATUS_SHORT, */
2745     /* 			 "set-density requires a positive value"); */
2746     /* 	return; */
2747     /*     } */
2748     do_set_density(arg != 0 ? arg / 100.0 : 1.0, False, True);
2749 #if MOTIF
2750     update_preferences_darkness();
2751 #else
2752     store_preference(NULL, "gamma", "%f", resource.gamma);
2753 #endif
2754 }
2755 
2756 
2757 static void
Act_change_density(Widget w,XEvent * event,String * params,Cardinal * num_params)2758 Act_change_density(Widget w, XEvent *event,
2759 		   String *params, Cardinal *num_params)
2760 {
2761     int arg;
2762     double diff;
2763     double oldgamma = resource.gamma;
2764     UNUSED(w);
2765     UNUSED(event);
2766 
2767     warn_num_params("change-density()", params, *num_params, 1);
2768 
2769     if (!get_int_arg(params, num_params, &arg)) {
2770 	xdvi_bell();
2771 	return;
2772     }
2773     diff = oldgamma / arg;
2774     if (oldgamma + diff <= 0.0)
2775 	oldgamma = 0.0;
2776     else
2777 	oldgamma += diff;
2778     do_set_density(oldgamma, False, True);
2779 #if MOTIF
2780     update_preferences_darkness();
2781 #endif
2782 }
2783 
h_restore_scroll_position(int orig_pos,Dimension * get_pos,Widget scrollbar)2784 static void h_restore_scroll_position(int orig_pos,
2785 #ifndef MOTIF
2786 				      Dimension *get_pos,
2787 #endif
2788 				      Widget scrollbar)
2789 {
2790     if (resource.keep_flag && orig_pos != 0 && scrollbar != NULL) {
2791 #ifdef MOTIF
2792 	if (!resource.fullscreen) {
2793 	    /* only scroll back in non-fullscreen mode */
2794 	    (void)set_bar_value(scrollbar, orig_pos, INT_MAX);
2795 	}
2796 #else
2797 	int curr_pos;
2798 	get_xy();
2799 	XtVaGetValues(globals.widgets.clip_widget, XtNx, get_pos, NULL);
2800 	curr_pos = -((*get_pos) - m_window_x);
2801 	if (curr_pos - orig_pos > 0) {
2802 	    XtCallCallbacks(scrollbar, XtNscrollProc, cast_int_to_XtPointer(curr_pos - orig_pos));
2803 	}
2804 #endif /* MOTIF */
2805     }
2806 }
2807 
2808 static void
Act_fullscreen(Widget w,XEvent * event,String * params,Cardinal * num_params)2809 Act_fullscreen(Widget w, XEvent *event,
2810 	       String *params, Cardinal *num_params)
2811 {
2812     Dimension main_win_w, main_win_h;
2813     static int orig_x = 0, orig_y = 0;
2814 #ifndef MOTIF
2815     static Dimension get_x, get_y;
2816 #endif
2817     static Dimension old_w = 0, old_h = 0;
2818     Dimension panel_width = 0;
2819 #if 0
2820     static Dimension w_old = 0, h_old = 0;
2821 #endif
2822 
2823     UNUSED(w);
2824     UNUSED(event);
2825 
2826     if (!toggle_arg(resource.fullscreen, params, num_params)) {
2827 	return;
2828     }
2829 
2830     resource.fullscreen = !resource.fullscreen;
2831 
2832 #ifndef MOTIF
2833     panel_width = get_panel_width();
2834 #endif
2835 
2836     if (resource.fullscreen) {
2837 	/* when toggling to fullscreen, save current scrollbar values
2838 	   and window geometry */
2839 	XtVaGetValues(globals.widgets.top_level, XtNwidth, &old_w, XtNheight, &old_h, NULL);
2840 	/*  	fprintf(stderr, "saved geometry: %dx%d\n", old_w, old_h); */
2841 #ifdef MOTIF
2842 	if (globals.widgets.x_bar != NULL)
2843 	    XtVaGetValues(globals.widgets.x_bar, XmNvalue, &orig_x, NULL);
2844 	if (globals.widgets.y_bar != NULL)
2845 	    XtVaGetValues(globals.widgets.y_bar, XmNvalue, &orig_y, NULL);
2846 #else
2847 	get_xy();
2848 	if (globals.widgets.x_bar != NULL)
2849 	    XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
2850 	if (globals.widgets.y_bar != NULL)
2851 	    XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
2852 	orig_x = -(get_x - m_window_x);
2853 	orig_y = -(get_y - m_window_y);
2854 #endif /* MOTIF */
2855 	/*  	fprintf(stderr, "Current offsets: %d, %d, %d, %d\n", orig_x, m_window_x, orig_y, m_window_y); */
2856     }
2857     if (resource.fullscreen) {
2858 	set_windowsize(&main_win_w, &main_win_h, panel_width, 0, False);
2859     }
2860     else { /* re-use old window sizes, or initialize them if started in fullscreen mode */
2861 	if (old_w == 0 || old_h == 0) /* started in fullscreen mode */
2862 	    set_windowsize(&old_w, &old_h, panel_width, 0, False);
2863 	else
2864 	    set_windowsize(&old_w, &old_h, panel_width, 0, True);
2865     }
2866 
2867     /*      fprintf(stderr, "reconfigure window!\n"); */
2868     if (resource.fullscreen)
2869 	reconfigure_window(resource.fullscreen, main_win_w, main_win_h, True);
2870     else {
2871 	reconfigure_window(resource.fullscreen, old_w, old_h, True);
2872     }
2873 
2874     /* restore horizontal/vertical scroll positions */
2875     h_restore_scroll_position(orig_x,
2876 #ifndef MOTIF
2877 			      &get_x,
2878 #endif
2879 			      globals.widgets.x_bar);
2880     h_restore_scroll_position(orig_y,
2881 #ifndef MOTIF
2882 			      &get_y,
2883 #endif
2884 			      globals.widgets.y_bar);
2885 
2886 #ifdef USE_PANNER
2887     handle_x_scroll(NULL, NULL, NULL, NULL);
2888     handle_y_scroll(NULL, NULL, NULL, NULL);
2889 #endif
2890 }
2891 
2892 #ifdef GREY
2893 
2894 static void
Act_set_greyscaling(Widget w,XEvent * event,String * params,Cardinal * num_params)2895 Act_set_greyscaling(Widget w, XEvent *event,
2896 		    String *params, Cardinal *num_params)
2897 {
2898     int arg;
2899 
2900     UNUSED(w);
2901     UNUSED(event);
2902 
2903     warn_num_params("set-greyscaling()", params, *num_params, 1);
2904 
2905     if (!get_int_arg(params, num_params, &arg)) { /* no arg, toggle */
2906 	if (!toggle_arg(resource.use_grey, params, num_params)) {
2907 	    return;
2908 	}
2909 	resource.use_grey = !resource.use_grey;
2910 	if (resource.use_grey) {
2911 	    statusline_info(STATUS_SHORT, "greyscaling on");
2912 	}
2913 	else {
2914 	    statusline_info(STATUS_SHORT, "greyscaling off");
2915 	}
2916 	globals.ev.flags |= EV_NEWPAGE;
2917 	XFlush(DISP);
2918 	return;
2919     }
2920 
2921     switch (arg) {
2922     case 0:
2923 	resource.use_grey = False;
2924 	statusline_info(STATUS_SHORT, "greyscaling off");
2925 	break;
2926     case 1:
2927 	resource.use_grey = True;
2928 	statusline_info(STATUS_SHORT, "greyscaling on");
2929 	break;
2930     default:
2931 	{
2932 	    float newgamma = arg != 0 ? arg / 100.0 : 1.0;
2933 	    resource.use_grey = newgamma;
2934 	    statusline_info(STATUS_SHORT, "greyscale value: %.1f", newgamma);
2935 	}
2936     }
2937 
2938     /* like elsewhere */
2939     if (globals.pausing.flag) {
2940 	XClearWindow(DISP, mane.win);
2941     }
2942 
2943     if (resource.use_grey) {
2944 	if (G_visual->class != TrueColor)
2945 	    init_plane_masks();
2946 #if COLOR
2947 	fg_active = NULL;
2948 #else
2949 	init_pix();
2950 #endif
2951     }
2952     reset_fonts();
2953     globals.ev.flags |= EV_NEWPAGE;
2954     XFlush(DISP);
2955 }
2956 
2957 #endif
2958 
2959 #if COLOR
2960 
2961 void
do_toggle_color(Boolean update_resource)2962 do_toggle_color(Boolean update_resource)
2963 {
2964     if (resource.use_color) {
2965 	resource.use_color = False;
2966 	full_reset_colors();
2967 	scanned_page_color = total_pages;
2968 #if PS
2969 	if (ignore_papersize_specials || scanned_page_ps <= total_pages) {
2970 	    scanned_page = scanned_page_ps;
2971 	}
2972 #endif
2973 	statusline_info(STATUS_SHORT, "color specials off");
2974     }
2975     else {
2976 	resource.use_color = True;
2977 	scanned_page = scanned_page_color = scanned_page_reset;
2978 	statusline_info(STATUS_SHORT, "color specials on");
2979     }
2980     if (update_resource)
2981 	globals.curr_use_color = resource.use_color;
2982 
2983 #ifdef MOTIF
2984     update_preferences_color();
2985 #endif
2986     globals.ev.flags |= EV_NEWPAGE;
2987 }
2988 
2989 static void
Act_set_color(Widget w,XEvent * event,String * params,Cardinal * num_params)2990 Act_set_color(Widget w, XEvent *event,
2991 	      String *params, Cardinal *num_params)
2992 {
2993     UNUSED(w);
2994     UNUSED(event);
2995 
2996     if (!toggle_arg(resource.use_color, params, num_params)) {
2997 	return;
2998     };
2999 
3000     /* like elsewhere */
3001     if (globals.pausing.flag) {
3002 	XClearWindow(DISP, mane.win);
3003     }
3004 
3005     do_toggle_color(True);
3006 }
3007 
3008 #endif /* COLOR */
3009 
3010 #if PS
3011 
3012 void
Act_set_ps(Widget w,XEvent * event,String * params,Cardinal * num_params)3013 Act_set_ps(Widget w, XEvent *event,
3014 	   String *params, Cardinal *num_params)
3015 {
3016     int arg;
3017 
3018     UNUSED(event);
3019     UNUSED(w);
3020 
3021     if (!get_int_arg(params, num_params, &arg))
3022 	resource.postscript++;
3023     else
3024 	resource.postscript = arg;
3025 
3026     if (resource.postscript > 2)
3027 	resource.postscript = 0;
3028 
3029 #ifdef PS_GS
3030     if (!resource.useGS) {
3031 	if (resource.postscript > 0) {
3032 	    popup_message(globals.widgets.top_level,
3033 			  MSG_WARN,
3034 			  "This version of xdvi depends on ghostscript for rendering Postscript images. "
3035 			  "Postscript rendering cannot be activated if the option ``-noghostscript'' is used "
3036 			  "or if the resource ``Ghostscript'' is set to false.",
3037 			  /* message */
3038 			  "Option ``-noghostscript'' is active; "
3039 			  "cannot enable Postscript rendering without ghostscript.");
3040 	}
3041 	resource.postscript = 0;
3042     }
3043 #endif
3044     if (resource.postscript > 0) {
3045 	scanned_page_ps = scanned_page_ps_bak;
3046 	if (scanned_page > scanned_page_ps)
3047 	    scanned_page = scanned_page_ps;
3048 	if (resource.postscript == 1) {
3049 	    statusline_info(STATUS_SHORT, "Postscript rendering on");
3050 	}
3051 	else {
3052 	    statusline_info(STATUS_SHORT, "Postscript rendering on (with bounding box)");
3053 	}
3054     }
3055     else {
3056 	scanned_page_ps_bak = scanned_page_ps;
3057 	scanned_page_ps = total_pages;
3058 #if COLOR
3059 	if (ignore_papersize_specials || scanned_page_color <= total_pages) {
3060 	    scanned_page = scanned_page_color;
3061 	}
3062 #endif
3063 	statusline_info(STATUS_SHORT, "Postscript rendering off; displaying bounding box instead");
3064     }
3065 
3066     store_preference(NULL, "postscript", "%d", resource.postscript);
3067 
3068     psp.toggle(resource.postscript);
3069 
3070     set_menu(&resource.postscript, Act_set_ps, check_int);
3071 
3072     /* like elsewhere */
3073     if (globals.pausing.flag) {
3074 	XClearWindow(DISP, mane.win);
3075     }
3076 
3077     globals.ev.flags |= EV_PS_TOGGLE;
3078     XFlush(DISP);
3079 }
3080 
3081 #endif /* PS */
3082 
3083 void
Act_htex_back(Widget w,XEvent * event,String * params,Cardinal * num_params)3084 Act_htex_back(Widget w, XEvent *event,
3085 	      String *params, Cardinal *num_params)
3086 {
3087     UNUSED(w);
3088     UNUSED(event);
3089     UNUSED(params);
3090     UNUSED(num_params);
3091 
3092     htex_back();
3093 }
3094 
3095 void
Act_htex_forward(Widget w,XEvent * event,String * params,Cardinal * num_params)3096 Act_htex_forward(Widget w, XEvent *event,
3097 		 String *params, Cardinal *num_params)
3098 {
3099     UNUSED(w);
3100     UNUSED(event);
3101     UNUSED(params);
3102     UNUSED(num_params);
3103 
3104     htex_forward();
3105 }
3106 
3107 static void
Act_htex_anchorinfo(Widget w,XEvent * event,String * params,Cardinal * num_params)3108 Act_htex_anchorinfo(Widget w, XEvent *event,
3109 		    String *params, Cardinal *num_params)
3110 {
3111     int x, y;
3112     Window dummy;
3113 
3114     UNUSED(w);
3115     UNUSED(params);
3116     UNUSED(num_params);
3117 
3118     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
3119 				event->xkey.x, event->xkey.y, &x, &y, &dummy);
3120     htex_displayanchor(x, y);
3121 }
3122 
3123 #ifdef PS_GS
3124 void
Act_set_gs_alpha(Widget w,XEvent * event,String * params,Cardinal * num_params)3125 Act_set_gs_alpha(Widget w, XEvent *event,
3126 		 String *params, Cardinal *num_params)
3127 {
3128     UNUSED(w);
3129     UNUSED(event);
3130 
3131     if (!toggle_arg(resource.gs_alpha, params, num_params)) {
3132 	return;
3133     }
3134     resource.gs_alpha = !resource.gs_alpha;
3135 
3136     if (resource.gs_alpha) {
3137 	statusline_info(STATUS_SHORT, "ghostscript alpha active");
3138     }
3139     else {
3140 	statusline_info(STATUS_SHORT, "ghostscript alpha inactive");
3141     }
3142 
3143     store_preference(NULL, "gsAlpha", "%s", resource.gs_alpha ? "True" : "False");
3144 
3145     set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle);
3146 
3147 #if GS_PIXMAP_CLEARING_HACK
3148     had_ps_specials = False; /* else infinite loop and/or crash in redraw! */
3149 #endif
3150     /* like elsewhere */
3151     if (globals.pausing.flag) {
3152 	XClearWindow(DISP, mane.win);
3153     }
3154 
3155     globals.ev.flags |= EV_PS_TOGGLE;
3156     XFlush(DISP);
3157 }
3158 #endif
3159 
3160 static void
Act_reread_dvi_file(Widget w,XEvent * event,String * params,Cardinal * num_params)3161 Act_reread_dvi_file(Widget w, XEvent *event,
3162 		    String *params, Cardinal *num_params)
3163 {
3164     UNUSED(w);
3165     UNUSED(event);
3166     UNUSED(params);
3167     UNUSED(num_params);
3168 
3169     /*      fprintf(stderr, "reread file!\n"); */
3170     globals.ev.flags |= EV_RELOAD;
3171 }
3172 
3173 static void
Act_discard_number(Widget w,XEvent * event,String * params,Cardinal * num_params)3174 Act_discard_number(Widget w, XEvent *event,
3175 		   String *params, Cardinal *num_params)
3176 {
3177     UNUSED(w);
3178     UNUSED(event);
3179     UNUSED(params);
3180     UNUSED(num_params);
3181 
3182     m_have_arg = False;
3183     m_number = 0;
3184     m_sign = 1;
3185 
3186     statusline_info(STATUS_SHORT, "numerical prefix discarded");
3187 }
3188 
3189 /*
3190  * Actions to support dragging the image.
3191  */
3192 
3193 static int drag_last_x, drag_last_y;	/* last position of cursor */
3194 
3195 static int text_last_x = -1;
3196 static int text_last_y = -1;
3197 static int text_last_page = -1;
3198 
3199 static void drag_motion(XEvent *);
3200 static void drag_release(XEvent *);
3201 
3202 static void
text_release(XEvent * event)3203 text_release(XEvent * event)
3204 {
3205     int ulx, uly, w, h;
3206     static char *text;
3207     int text_len = 0;
3208 
3209     UNUSED(event);
3210 
3211     MYTRACE((stderr, "text_release!\n"));
3212 
3213     if (mouse_motion == null_mouse)
3214 	return;
3215     mouse_motion = mouse_release = null_mouse;
3216 
3217     w = text_last_x - drag_last_x;
3218     h = text_last_y - drag_last_y;
3219 
3220     if (w == 0 || h == 0) {
3221 	text_last_x = text_last_y = -1;
3222 	drag_last_x = drag_last_y = -1;
3223 	text_last_page = -1;
3224 	return;
3225     }
3226 
3227     if (w < 0) {
3228 	ulx = drag_last_x + w;
3229 	w = -w;
3230     }
3231     else
3232 	ulx = drag_last_x;
3233 
3234     if (h < 0) {
3235 	uly = drag_last_y + h;
3236 	h = -h;
3237     }
3238     else
3239 	uly = drag_last_y;
3240 
3241     free(text);
3242     /* this allocates text */
3243     text = get_text_selection(&text_len,
3244 			      ulx * currwin.shrinkfactor,
3245 			      uly * currwin.shrinkfactor,
3246 			      (ulx + w) * currwin.shrinkfactor,
3247 			      (uly + h) * currwin.shrinkfactor);
3248     if (text[0] == '\0') {
3249 	/* fprintf(stdout, "Text selection empty.\n"); */
3250 	return;
3251     }
3252 
3253     TRACE_GUI((stderr, "Selected `%s'", text));
3254 
3255     if (text_len > 4 * XMaxRequestSize(DISP) - 32) {
3256 	xdvi_bell();
3257 	statusline_error(STATUS_MEDIUM, "Selection too large (%d bytes, maximum %d bytes)",
3258 			 text_len, 4 * XMaxRequestSize(DISP) - 32);
3259 	return;
3260     }
3261 
3262     if (!set_selection(text, globals.widgets.top_level)) {
3263 	xdvi_bell();
3264 	statusline_error(STATUS_MEDIUM, "Could not set primary selection!");
3265 	text_change_region(TEXT_SEL_CLEAR, NULL);
3266     }
3267 }
3268 
3269 void
text_motion(XEvent * event)3270 text_motion(XEvent *event)
3271 {
3272     MYTRACE((stderr, "motion!\n"));
3273     text_change_region(TEXT_SEL_MOVE, event);
3274 }
3275 
3276 static void
get_rectangle(XRectangle * rect,int x,int y,int last_x,int last_y)3277 get_rectangle(XRectangle *rect, int x, int y, int last_x, int last_y)
3278 {
3279     rect->width = ABS(x - last_x);
3280     rect->height = ABS(y - last_y);
3281     rect->x = (x < last_x) ? x : last_x;
3282     rect->y = (y < last_y) ? y : last_y;
3283 }
3284 
3285 static void
crop_to_window(int * x,int * y)3286 crop_to_window(int *x, int *y)
3287 {
3288     if (*x < -currwin.base_x + 1)
3289 	*x = -currwin.base_x + 1;
3290     else if (*x > -currwin.base_x + (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1)
3291 	*x = -currwin.base_x + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1;
3292 
3293     if (*y < -currwin.base_y + 1)
3294 	*y = -currwin.base_y + 1;
3295     else if (*y > -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1)
3296 	*y = -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1;
3297 }
3298 
3299 void
text_change_region(textSelectionT mode,XEvent * event)3300 text_change_region(textSelectionT mode, XEvent *event)
3301 {
3302     static GC bboxGC = 0;
3303     static GC redrawGC = 0; /* needed since bboxGC is clipped to diff of old and new region */
3304 
3305     if (bboxGC == 0) {
3306 	XGCValues values;
3307 	unsigned long valuemask;
3308 
3309 	values.function = GXinvert; /* as in search code */
3310 	if (values.function == GXinvert) {
3311 	    valuemask = GCFunction;
3312 	}
3313 	else {
3314 	    values.foreground = WhitePixelOfScreen(SCRN) ^ BlackPixelOfScreen(SCRN);
3315 	    /* 		fprintf(stderr, "foreground: 0x%lx, white pixel: 0x%lx, black pixel: 0x%lx\n", */
3316 	    /* 			values.foreground, WhitePixelOfScreen(SCRN), BlackPixelOfScreen(SCRN)); */
3317 	    valuemask = GCFunction | GCForeground;
3318 	}
3319 	values.line_width = 1;
3320 	valuemask |= GCLineWidth;
3321 	bboxGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values);
3322 	redrawGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values);
3323     }
3324 
3325     MYTRACE((stderr, "mode: %d\n", mode));
3326     switch (mode) {
3327     case TEXT_SEL_MOVE:
3328 	{
3329 	    int x, y;
3330 
3331 	    Window dummy;
3332 
3333 	    XRectangle redraw = { -1, -1, 0, 0 };
3334 
3335 	    ASSERT(event != NULL, "event in text_change_region() musn't be NULL for TEXT_SEL_MOVE");
3336 
3337 	    (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
3338 					event->xkey.x, event->xkey.y,
3339 					&x, &y,
3340 					&dummy);
3341 
3342 	    crop_to_window(&x, &y);
3343 
3344 	    get_rectangle(&redraw, x, y, drag_last_x, drag_last_y);
3345 
3346 	    /*
3347 	     * If we have an old region, we want to clip the GC to the area:
3348 	     *
3349 	     * (clip \cup redraw) - (clip \cap redraw)
3350 	     *
3351 	     * and redraw both rectangle areas; otherwise, just draw the redraw area.
3352 	     */
3353 
3354 	    if (text_last_x != -1 && text_last_y != -1) {
3355 
3356 		XRectangle clip = { -1, -1, 0, 0 };
3357 
3358 		Region clip_region = XCreateRegion();
3359 		Region redraw_region = XCreateRegion();
3360 		Region union_region = XCreateRegion();
3361 		Region intersect_region = XCreateRegion();
3362 
3363 		get_rectangle(&clip, text_last_x, text_last_y, drag_last_x, drag_last_y);
3364 
3365 		XUnionRectWithRegion(&clip, clip_region, clip_region);
3366 		XUnionRectWithRegion(&redraw, redraw_region, redraw_region);
3367 
3368 		XUnionRegion(clip_region, redraw_region, union_region);
3369 		XIntersectRegion(clip_region, redraw_region, intersect_region);
3370 
3371 		XSubtractRegion(union_region, intersect_region, redraw_region);
3372 
3373 		XSetRegion(DISP, bboxGC, redraw_region);
3374 
3375 		XDestroyRegion(clip_region);
3376 		XDestroyRegion(redraw_region);
3377 		XDestroyRegion(union_region);
3378 		XDestroyRegion(intersect_region);
3379 
3380 		XFillRectangle(DISP, mane.win, bboxGC, clip.x, clip.y, clip.width, clip.height);
3381 	    }
3382 
3383 	    XFillRectangle(DISP, mane.win, bboxGC, redraw.x, redraw.y, redraw.width, redraw.height);
3384 
3385 	    text_last_x = x;
3386 	    text_last_y = y;
3387 	    text_last_page = current_page;
3388 	}
3389 	break;
3390     case TEXT_SEL_CLEAR:
3391 	unset_selection(globals.widgets.top_level);
3392     case TEXT_SEL_ERASE:
3393     case TEXT_SEL_REDRAW:
3394 	if (text_last_page == current_page
3395 	    && text_last_x != -1 && text_last_y != -1
3396 	    && drag_last_x != -1 && drag_last_y != -1) {
3397 
3398 	    XRectangle clear = { -1, -1, 0, 0 };
3399 
3400 	    get_rectangle(&clear, text_last_x, text_last_y, drag_last_x, drag_last_y);
3401 
3402 	    if (mode == TEXT_SEL_CLEAR) {
3403 		text_last_x = text_last_y = drag_last_x = drag_last_y = -1;
3404 		text_last_page = -1;
3405 		/* Note ZLB: the region is erased instead of inverted to avoid
3406 		 * multiple inverting problem. An exposure is generated to
3407 		 * make the region redrawn */
3408  		clearexpose(&mane, clear.x, clear.y, clear.width, clear.height);
3409 	    }
3410 	    else if (clip_region_to_rect(&clear)) {
3411 		if (mode == TEXT_SEL_ERASE) {
3412 		    /* If width or height are 0, XClearArea will clear entire window
3413 		     * from coordinates x, y, so check for that: */
3414 		    if (clear.width > 0 && clear.height > 0)
3415 		    	XClearArea(DISP, mane.win, clear.x, clear.y, clear.width, clear.height, False);
3416 		}
3417 		else
3418 		    XFillRectangle(DISP, mane.win, redrawGC, clear.x, clear.y, clear.width, clear.height);
3419 	    }
3420 	}
3421 	break;
3422     }
3423 }
3424 
3425 void
text_selection_start(XEvent * event)3426 text_selection_start(XEvent *event)
3427 {
3428     int x, y;
3429     Window dummy;
3430 
3431     /* erase existing region */
3432     text_change_region(TEXT_SEL_CLEAR, NULL);
3433 
3434     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
3435 				event->xkey.x, event->xkey.y,
3436 				&x, &y,
3437 				&dummy);	/* throw away last argument */
3438 
3439     crop_to_window(&x, &y);
3440 
3441     drag_last_x = x;
3442     drag_last_y = y;
3443 
3444     text_last_x = text_last_y = -1;
3445 
3446     MYTRACE((stderr, "selection start; mouse_release: %p; null: %p!\n", mouse_release, null_mouse));
3447     if (mouse_release == null_mouse) {
3448 	MYTRACE((stderr, "init text_motion!\n"));
3449 	mouse_motion = text_motion;
3450 	mouse_release = text_release;
3451     }
3452 /*     resource.mouse_mode_text_selection_active = True; */
3453 }
3454 
3455 void
Act_switch_mode(Widget w,XEvent * event,String * params,Cardinal * num_params)3456 Act_switch_mode(Widget w, XEvent *event,
3457 		String *params, Cardinal *num_params)
3458 {
3459 /*     mouseModeT prev_mode = resource.mouse_mode; */
3460     int arg = -1;
3461 /*     char *ptr; */
3462     const char *mode_name = NULL;
3463     const char *mode_description = NULL;
3464 /*     XEvent ev; */
3465 
3466     /* if called the first time from main(), this is only used
3467      * to initialize the menu according to the mode.
3468      */
3469     Boolean initialization = (w == globals.widgets.top_level);
3470 
3471     UNUSED(event);
3472 
3473     if (get_int_arg(params, num_params, &arg)) {
3474 	if (arg < MOUSE_MODE1 || arg >= MOUSE_MODE_MAX) {
3475 	    statusline_info(STATUS_SHORT, "Argument for Act_switch_mode outside of range from %d to %d",
3476 			     MOUSE_MODE1, MOUSE_MODE_MAX - 1);
3477 	    resource.mouse_mode++;
3478 	}
3479 	else
3480 	    resource.mouse_mode = (mouseModeT)arg;
3481     }
3482     else
3483 	resource.mouse_mode++;
3484 
3485     /* check if wrapped */
3486     if (resource.mouse_mode >= MOUSE_MODE_MAX)
3487 	resource.mouse_mode = MOUSE_MODE1;
3488 
3489     if (globals.curr_mode == RULER_MODE_ACTIVE) {
3490 	clear_ruler();
3491 	mouse_motion = mouse_release = null_mouse;
3492     }
3493     else if (globals.curr_mode == TEXT_MODE_ACTIVE) {
3494 	text_change_region(TEXT_SEL_CLEAR, NULL);
3495 	mouse_motion = mouse_release = null_mouse;
3496     }
3497     globals.curr_mode = NO_MODE_ACTIVE;
3498 
3499     switch(resource.mouse_mode) {
3500     case MOUSE_MODE1:
3501 	mode_name = resource.mouse_mode1_name;
3502 	mode_description = resource.mouse_mode1_description;
3503 	break;
3504     case MOUSE_MODE2:
3505 	mode_name = resource.mouse_mode2_name;
3506 	mode_description = resource.mouse_mode2_description;
3507 /* 	globals.cursor.flags |= CURSOR_TEXT; /\*XXX FIXME: don't hardcode flag here; instead, have Act_text_selection set it *\/ */
3508 	break;
3509     case MOUSE_MODE3:
3510 	mode_name = resource.mouse_mode3_name;
3511 	mode_description = resource.mouse_mode3_description;
3512 /* 	globals.cursor.flags |= CURSOR_RULER; */
3513 	break;
3514     default: /* can't happen */
3515 	XDVI_WARNING((stderr, "resource.mouse_mode larger than %d!", resource.mouse_mode));
3516 	break;
3517     }
3518 
3519     globals.ev.flags |= EV_CURSOR;
3520     XFlush(DISP);
3521 
3522     if (!initialization) {
3523 	statusline_info(STATUS_SHORT, "%s mode; %s", mode_name, mode_description);
3524 	store_preference(NULL, "mouseMode", "%d", resource.mouse_mode);
3525     }
3526     set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
3527 
3528 
3529     /*XXX FIXME: Activate mode by synthesizing a mouse event??? */
3530 /*     synthesize_event(&ev, globals.widgets.draw_widget); */
3531 /*     Act_mouse_modes(w, event, params, num_params); */
3532 }
3533 
3534 static void
Act_drag(Widget w,XEvent * event,String * params,Cardinal * num_params)3535 Act_drag(Widget w, XEvent *event,
3536 	 String *params, Cardinal *num_params)
3537 {
3538     UNUSED(w);
3539 
3540     if (mouse_release != null_mouse && mouse_release != drag_release)
3541 	return;
3542 
3543     if ((globals.curr_mode == TEXT_MODE_ACTIVE) && text_last_page != -1
3544 	/* this was:
3545 	   && (text_last_x != drag_last_x || text_last_y != drag_last_y)
3546 	   but that isn't restrictive enough */
3547 	) { /* dragging would mess up region drawing */
3548 	xdvi_bell();
3549 	statusline_info(STATUS_SHORT, "Cannot drag page when selection is active");
3550 	return;
3551     }
3552 
3553     if (*num_params != 1) {
3554 	XDVI_WARNING((stderr, "drag() requires 1 argument (got %d)", *num_params));
3555 	return;
3556     }
3557     switch (**params) {
3558     case '|':
3559 	globals.cursor.flags |= CURSOR_DRAG_V;
3560 	break;
3561     case '-':
3562 	globals.cursor.flags |= CURSOR_DRAG_H;
3563 	break;
3564     case '+':
3565 	globals.cursor.flags |= CURSOR_DRAG_A;
3566 	break;
3567     default:
3568 	XDVI_WARNING((stderr, "drag(): Valid arguments are `+', `|' or `-'"));
3569     }
3570 
3571     globals.ev.flags |= EV_CURSOR;
3572 
3573     if (mouse_release == null_mouse) {
3574 	mouse_motion = drag_motion;
3575 	mouse_release = drag_release;
3576 	drag_last_x = event->xbutton.x_root;
3577 	drag_last_y = event->xbutton.y_root;
3578     }
3579     else
3580 	drag_motion(event);
3581 
3582     XFlush(DISP);
3583 }
3584 
3585 
3586 static void
drag_motion(XEvent * event)3587 drag_motion(XEvent * event)
3588 {
3589 #ifdef MOTIF
3590     get_xy();
3591 #endif
3592 
3593     if (globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_A)) { /* horizontal motion */
3594 #ifdef MOTIF
3595 	(void)set_bar_value(globals.widgets.x_bar,
3596 			    drag_last_x - event->xbutton.x_root - m_window_x,
3597 			    (int)(globals.page.w - mane.width));
3598 #else
3599 	if (globals.widgets.x_bar != NULL) {
3600 	    XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
3601 			    cast_int_to_XtPointer(drag_last_x - event->xbutton.x_root));
3602 	}
3603 #endif
3604 	drag_last_x = event->xbutton.x_root;
3605     }
3606 
3607     if (globals.cursor.flags & (CURSOR_DRAG_V | CURSOR_DRAG_A)) { /* vertical motion */
3608 #ifdef MOTIF
3609 	(void)set_bar_value(globals.widgets.y_bar,
3610 			    drag_last_y - event->xbutton.y_root - m_window_y,
3611 			    (int)(globals.page.h - mane.height));
3612 #else
3613 	if (globals.widgets.y_bar != NULL) {
3614 	    XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
3615 			    cast_int_to_XtPointer(drag_last_y - event->xbutton.y_root));
3616 	}
3617 #endif
3618 	drag_last_y = event->xbutton.y_root;
3619     }
3620 #ifdef USE_PANNER
3621     handle_x_scroll(NULL, NULL, NULL, NULL);
3622     handle_y_scroll(NULL, NULL, NULL, NULL);
3623 #endif
3624 }
3625 
3626 
3627 static void
drag_release(XEvent * event)3628 drag_release(XEvent * event)
3629 {
3630     drag_motion(event);
3631     mouse_motion = mouse_release = null_mouse;
3632 
3633     globals.cursor.flags &= ~(CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A);
3634     globals.ev.flags |= EV_CURSOR;
3635     XFlush(DISP);
3636 }
3637 
3638 
3639 
3640 /* Wheel mouse support.  */
3641 
3642 static int wheel_button = -1;
3643 
3644 static void
Act_wheel(Widget w,XEvent * event,String * params,Cardinal * num_params)3645 Act_wheel(Widget w, XEvent *event,
3646 	  String *params, Cardinal *num_params)
3647 {
3648     int dist;
3649 
3650     UNUSED(w);
3651 
3652     if (*num_params != 1) {
3653 	XDVI_WARNING((stderr, "wheel() requires 1 argument (got %d)", *num_params));
3654 	return;
3655     }
3656     dist = (strchr(*params, '.') == NULL) ? atoi(*params)
3657 	: (int)(my_atof(*params) * resource.wheel_unit);
3658 #ifdef MOTIF
3659     get_xy();
3660     set_bar_value(globals.widgets.y_bar, dist - m_window_y, (int)(globals.page.h - mane.height));
3661 #else
3662     if (globals.widgets.y_bar != NULL)
3663 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(dist));
3664 #endif
3665 
3666     if (event != NULL)
3667 	wheel_button = event->xbutton.button;
3668 
3669 #ifdef USE_PANNER
3670     handle_y_scroll(NULL, NULL, NULL, NULL);
3671 #endif
3672 }
3673 
3674 static int wheel_h_button = -1;
3675 
3676 static void
Act_hwheel(Widget w,XEvent * event,String * params,Cardinal * num_params)3677 Act_hwheel(Widget w, XEvent *event,
3678 	  String *params, Cardinal *num_params)
3679 {
3680     int dist;
3681 
3682     UNUSED(w);
3683 
3684     if (*num_params != 1) {
3685 	XDVI_WARNING((stderr, "wheel() requires 1 argument (got %d)", *num_params));
3686 	return;
3687     }
3688     dist = (strchr(*params, '.') == NULL) ? atoi(*params)
3689 	: (int)(my_atof(*params) * resource.wheel_unit);
3690 #ifdef MOTIF
3691     get_xy();
3692     set_bar_value(globals.widgets.x_bar, dist - m_window_x,
3693 		  (int) (globals.page.w - mane.width));
3694 #else
3695     if (globals.widgets.x_bar != NULL)
3696 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
3697 			cast_int_to_XtPointer(dist));
3698 #endif
3699 
3700     if (event != NULL)
3701 	wheel_h_button = event->xbutton.button;
3702 
3703 #ifdef USE_PANNER
3704     handle_x_scroll(NULL, NULL, NULL, NULL);
3705 #endif
3706 }
3707 
3708 
3709 /* Internal mouse actions.  */
3710 
3711 /*
3712  * Here xdvi does its own parsing and processing of translations for mouse
3713  * button presses.  This is necessary because the X Toolkit does not recognize
3714  * <Btn6Down> or <Btn7Down> event types.
3715  */
3716 
3717 #if HAVE_X11_INTRINSICI_H
3718 # include <X11/IntrinsicI.h>
3719 #else
3720 
3721 /* From <X11/TranslateI.h> */
3722 extern Boolean _XtComputeLateBindings(
3723     Display*		/* dpy */,
3724     struct _LateBindings* /* lateModifiers */,
3725     Modifiers*		/* computed */,
3726     Modifiers*		/* computedMask */
3727 );
3728 
3729 #endif /* HAVE_X11_INTRINSICI_H */
3730 
3731 
3732 static void
Act_press(Widget w,XEvent * event,String * params,Cardinal * num_params)3733 Act_press(Widget w, XEvent *event,
3734 	  String *params, Cardinal *num_params)
3735 {
3736     struct mouse_acts *mactp;
3737     struct xdvi_action *actp;
3738 
3739 #if HAVE_XI21
3740     if (xi2_active
3741       && (xi2_current->slave->btn_mask & (1 << event->xbutton.button))) {
3742 	TRACE_EVENTS((stderr, "Ignoring button %d press (XI 2.1 active)",
3743 	  event->xbutton.button));
3744 	if (event->xbutton.button <= 5)
3745 	    wheel_button = event->xbutton.button;
3746 	else
3747 	    wheel_h_button = event->xbutton.button;
3748 	xi2_ign_time = event->xbutton.time;
3749 	return;
3750     }
3751 #endif
3752 
3753     for (mactp = mouse_actions; mactp != NULL; mactp = mactp->next) {
3754 	if (event->xbutton.button == mactp->button || mactp->button == 0) {
3755 	    Modifiers mask = 0;
3756 	    Modifiers value = 0;
3757 
3758 	    if (mactp->late_bindings == NULL
3759 	      || _XtComputeLateBindings(DISP, mactp->late_bindings,
3760 	      &value, &mask)) {
3761 		mask |= mactp->mask;
3762 		value |= mactp->value;
3763 		if (((value ^ event->xbutton.state) & mask) == 0) {
3764 		    for (actp = mactp->action; actp != NULL;
3765 		      actp = actp->next)
3766 			(actp->proc)(w, event, actp->params, &actp->num_params);
3767 		    return;
3768 		}
3769 	    }
3770 	}
3771     }
3772 }
3773 
3774 
3775 static void
Act_motion(Widget w,XEvent * event,String * params,Cardinal * num_params)3776 Act_motion(Widget w, XEvent *event,
3777 	   String *params, Cardinal *num_params)
3778 {
3779     /* remember last position, to do this only when pointer is actually moved */
3780     static int old_x = -1, old_y = -1;
3781     int x, y;
3782 
3783     UNUSED(w);
3784     UNUSED(params);
3785     UNUSED(num_params);
3786 
3787     MYTRACE((stderr, "act_motion!\n"));
3788 
3789     if (globals.curr_mode == RULER_MODE_ACTIVE) {
3790 	show_distance_from_ruler(event, False);
3791     }
3792     /* This used to be:
3793        (abs(x - old_x) > x_threshold || abs(y - old_y) > y_threshold))
3794        but that didn't work too well either. Just change it whenever user
3795        moves the mouse. */
3796     if (!MAGNIFIER_ACTIVE && !(globals.curr_mode == RULER_MODE_ACTIVE)
3797 	&& pointerlocate(&x, &y) && (x != old_x || y != old_y)) {
3798 	htex_displayanchor(x, y);
3799 	old_x = x;
3800 	old_y = y;
3801     }
3802 
3803     if ((int)(event->xbutton.button) != wheel_button
3804 	&& ((int) (event->xbutton.button) != wheel_h_button)) {
3805 	MYTRACE((stderr, "mouse_motion!\n"));
3806 	mouse_motion(event);
3807     }
3808 }
3809 
3810 
3811 static void
Act_release(Widget w,XEvent * event,String * params,Cardinal * num_params)3812 Act_release(Widget w, XEvent *event,
3813 	    String *params, Cardinal *num_params)
3814 {
3815     UNUSED(w);
3816     UNUSED(params);
3817     UNUSED(num_params);
3818 
3819     if ((int)(event->xbutton.button) == wheel_button) {
3820 	wheel_button = -1;
3821 	return;
3822     }
3823 
3824     if ((int)(event->xbutton.button) == wheel_h_button) {
3825 	wheel_h_button = -1;
3826 	return;
3827     }
3828 
3829     mouse_release(event);
3830 }
3831 
3832 static void
Act_toggle_grid_mode(Widget w,XEvent * event,String * params,Cardinal * num_params)3833 Act_toggle_grid_mode(Widget w, XEvent *event,
3834 		     String *params, Cardinal *num_params)
3835 {
3836     int arg;
3837 
3838     UNUSED(w);
3839     UNUSED(event);
3840 
3841     if (!get_int_arg(params, num_params, &arg)) {
3842 	arg = -1;
3843     }
3844     switch (arg) {
3845     case -1:
3846 	resource.grid_mode = !resource.grid_mode;
3847 	if (resource.grid_mode) {
3848 	    statusline_info(STATUS_SHORT, "Grid mode on");
3849 	}
3850 	else {
3851 	    statusline_info(STATUS_SHORT, "Grid mode off");
3852 	}
3853 	break;
3854     case 1: /* fall through */
3855     case 2: /* fall through */
3856     case 3:
3857 	resource.grid_mode = arg;
3858 	statusline_info(STATUS_SHORT, "Grid mode %d", arg);
3859 	break;
3860     default:
3861 	xdvi_bell();
3862 	statusline_info(STATUS_SHORT,
3863 			 "Valid arguments for grid mode are: none (toggles), 1, 2, 3");
3864 	return;
3865     }
3866     init_page();
3867     reconfig();
3868     globals.ev.flags |= EV_NEWPAGE;
3869     XFlush(DISP);
3870 }
3871 
3872 
3873 /* Actions for source specials.  */
3874 
3875 void
Act_source_special(Widget w,XEvent * event,String * params,Cardinal * num_params)3876 Act_source_special(Widget w, XEvent *event,
3877 		   String *params, Cardinal *num_params)
3878 {
3879     Window dummy;
3880 
3881     UNUSED(w);
3882     UNUSED(params);
3883     UNUSED(num_params);
3884 
3885     if ((event->type == ButtonPress && mouse_release != null_mouse)
3886 	|| MAGNIFIER_ACTIVE) {
3887 	xdvi_bell();
3888 	return;
3889     }
3890 
3891     source_reverse_x = event->xbutton.x;
3892     source_reverse_y = event->xbutton.y;
3893     if (event->xbutton.window != mane.win)
3894 	(void)XTranslateCoordinates(DISP,
3895 				    RootWindowOfScreen(SCRN), mane.win,
3896 				    event->xbutton.x_root,
3897 				    event->xbutton.y_root,
3898 				    &source_reverse_x,
3899 				    &source_reverse_y,
3900 				    &dummy);	/* throw away last argument */
3901 
3902     source_reverse_x = (source_reverse_x + mane_base_x) * mane.shrinkfactor;
3903     source_reverse_y = (source_reverse_y + mane_base_y) * mane.shrinkfactor;
3904 
3905     globals.ev.flags |= EV_SRC;
3906 }
3907 
3908 void
Act_show_source_specials(Widget w,XEvent * event,String * params,Cardinal * num_params)3909 Act_show_source_specials(Widget w, XEvent *event,
3910 			 String *params, Cardinal *num_params)
3911 {
3912     int arg;
3913     Boolean clear_statusline = False;
3914 
3915     UNUSED(w);
3916 
3917     if (!get_int_arg(params, num_params, &arg))
3918 	arg = -1;
3919     else
3920 	clear_statusline = True;
3921 
3922     if ((event->type == ButtonPress && mouse_release != null_mouse)
3923 	|| MAGNIFIER_ACTIVE) {
3924 	xdvi_bell();
3925 	return;
3926     }
3927 
3928     if (!(globals.ev.flags & EV_SRC)) {
3929 	source_reverse_x = -1;
3930 	source_show_all = (arg == 1 ? True : False);
3931 
3932 	globals.ev.flags |= EV_SRC;
3933     }
3934     if (clear_statusline)
3935 	statusline_clear();
3936 }
3937 
3938 void
Act_source_what_special(Widget w,XEvent * event,String * params,Cardinal * num_params)3939 Act_source_what_special(Widget w, XEvent *event,
3940 			String *params, Cardinal *num_params)
3941 {
3942     int my_x, my_y;
3943     Window dummy;
3944 
3945     UNUSED(w);
3946     UNUSED(params);
3947     UNUSED(num_params);
3948 
3949     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
3950 				event->xkey.x, event->xkey.y, &my_x, &my_y, &dummy);	/* throw away last argument */
3951     my_x = (my_x + mane_base_x) * mane.shrinkfactor;
3952     my_y = (my_y + mane_base_y) * mane.shrinkfactor;
3953     source_reverse_search(my_x, my_y, False);
3954 }
3955 
3956 static void
select_cb(const char * filename,void * data)3957 select_cb(const char *filename, void *data)
3958 {
3959     UNUSED(data);
3960     if (filename != NULL) {
3961 	TRACE_FILES((stderr, "new filename: |%s|", filename));
3962 
3963 	if (resource.filesel_open_new_window) {
3964 	    /*  	    char *fname = expand_filename_append_dvi(filename); */
3965 	    launch_xdvi(filename, NULL);
3966 	}
3967 	else {
3968 	    /* the filename should be complete already, so don't append .dvi */
3969 	    set_dvi_name(expand_filename(filename, USE_CWD_PATH));
3970 	    current_page = 0; /* switch to first page */
3971 	    close_old_filep();
3972 	    globals.ev.flags |= EV_NEWDOC;
3973 	    globals.ev.flags |= EV_PAGEHIST_INSERT;
3974 	}
3975     }
3976 }
3977 
3978 static void
Act_select_dvi_file(Widget w,XEvent * event,String * params,Cardinal * num_params)3979 Act_select_dvi_file(Widget w, XEvent *event,
3980 		    String *params, Cardinal *num_params)
3981 {
3982     /* static so that we can pass its address */
3983     static struct filesel_callback cb = {
3984 	NULL, NULL, "xdvik: Open File", "Open file:", "OK", "Cancel",
3985 	NULL, "*.dvi", True, False, NULL, NULL
3986     };
3987     int arg = -1;
3988 
3989     UNUSED(w);
3990     UNUSED(event);
3991 
3992     if (get_int_arg(params, num_params, &arg)) { /* try item from file history */
3993 	char *fname;
3994 	int dummy_page;
3995 
3996 	if (arg < 1) {
3997 	    xdvi_bell();
3998 	    statusline_info(STATUS_MEDIUM, "Error: File history number must be >= 1");
3999 	}
4000 	else if ((fname = file_history_get_elem(arg - 1, &dummy_page)) == NULL) {
4001 	    statusline_info(STATUS_MEDIUM, "No file number %d in history (history size: %lu)",
4002 			     arg, (unsigned long)file_history_size());
4003 	}
4004 	else {
4005 	    file_history_open(fname);
4006 	}
4007 	return;
4008     }
4009 
4010     file_history_set_page(current_page);
4011 
4012     cb.func_ptr = select_cb;
4013     cb.init_path = globals.dvi_file.dirname;
4014     if (cb.shell == NULL)
4015 	cb.shell = XsraSelFile(globals.widgets.top_level, &cb);
4016     XsraSelFilePopup(&cb);
4017 }
4018 
4019 /*
4020  * If all GUI elements have been turned on/off, make this synonymous
4021  * with expert mode off/on, so that next `x' keystroke does something
4022  * reasonable.
4023  */
4024 void
update_expert_mode(void)4025 update_expert_mode(void)
4026 {
4027     if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE | XPRT_SHOW_SCROLLBARS
4028 #ifdef MOTIF
4029 				 | XPRT_SHOW_PAGELIST | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR
4030 #else
4031 				 | XPRT_SHOW_BUTTONS
4032 #endif
4033 				 )) == XPRT_SHOW_ALL) {
4034 	resource.expert = False;
4035     }
4036     else if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE
4037 #ifdef MOTIF
4038 				      | XPRT_SHOW_SCROLLBARS | XPRT_SHOW_PAGELIST
4039 				      | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR
4040 #else
4041 				      | BROKEN_RECONFIG ? 0 : XPRT_SHOW_SCROLLBARS | XPRT_SHOW_BUTTONS
4042 #endif
4043 				      )) == XPRT_SHOW_NONE) {
4044 	resource.expert = True;
4045     }
4046 }
4047 
4048 void
Act_set_expert_mode(Widget w,XEvent * event,String * params,Cardinal * num_params)4049 Act_set_expert_mode(Widget w, XEvent *event,
4050 		    String *params, Cardinal *num_params)
4051 {
4052     int arg;
4053     Boolean clear_statusline = False;
4054 
4055     UNUSED(w);
4056     UNUSED(event);
4057 
4058     if (!get_int_arg(params, num_params, &arg))
4059 	arg = -1;
4060     else
4061 	clear_statusline = True;
4062 
4063     switch(arg) {
4064     case 1:
4065 	resource.expert_mode ^= XPRT_SHOW_STATUSLINE;
4066 	toggle_statusline();
4067 	update_expert_mode();
4068 	break;
4069     case 2:
4070 #ifndef MOTIF
4071 	/* show this warning only when not toggling global expert mode
4072 	   (that's why it can't be inside toggle_scrollbars) */
4073 	if (BROKEN_RECONFIG) {
4074 	    popup_message(globals.widgets.top_level,
4075 			  MSG_WARN,
4076 			  NULL,
4077 			  "Sorry - cannot toggle scrollbars with this X Version.\n"
4078 			  "This version of XFree has a broken implementation of the viewportWidget, "
4079 			  "which would break the layout if the scrollbars are toggled. "
4080 			  "Versions that are known to work have a VendorRelease version below 4000 or above 4002. "
4081 			  "You will need to update your XFree server to fix this.");
4082 	    return;
4083 	}
4084 #endif
4085 	resource.expert_mode ^= XPRT_SHOW_SCROLLBARS;
4086 	toggle_scrollbars();
4087 	update_expert_mode();
4088 	break;
4089 
4090 #ifdef MOTIF
4091     case 3:
4092 	resource.expert_mode ^= XPRT_SHOW_PAGELIST;
4093 	toggle_pagelist();
4094 	update_expert_mode();
4095 	break;
4096 
4097     case 4: /* toolbar */
4098 	resource.expert_mode ^= XPRT_SHOW_TOOLBAR;
4099 	toggle_toolbar();
4100 	update_expert_mode();
4101 	break;
4102 
4103     case 5:
4104 	resource.expert_mode ^= XPRT_SHOW_MENUBAR;
4105 	toggle_menubar();
4106 	update_expert_mode();
4107 	break;
4108 #else
4109     case 3:
4110 	/*  	fprintf(stderr, "sidebar\n"); */
4111 	resource.expert_mode ^= XPRT_SHOW_BUTTONS;
4112 	toggle_buttons();
4113 	update_expert_mode();
4114 	break;
4115 #endif
4116     default:
4117 	/* warn 'em */
4118 	if (
4119 #ifdef MOTIF
4120 	    arg > 5
4121 #else
4122 	    arg > 3
4123 #endif
4124 	    ) {
4125 	    statusline_info(STATUS_SHORT, "Number %d too large for `set-expert-mode', using 0 (= toggle) instead.",
4126 			     arg);
4127 	}
4128 	/* toggle all items */
4129 	resource.expert = !resource.expert;
4130 	if (resource.expert)
4131 	    resource.expert_mode = XPRT_SHOW_NONE;
4132 	else
4133 	    resource.expert_mode = XPRT_SHOW_ALL;
4134 
4135 	toggle_statusline();
4136 #ifndef MOTIF
4137 	if (!BROKEN_RECONFIG)
4138 	    toggle_scrollbars();
4139 #else
4140 	toggle_scrollbars();
4141 #endif
4142 
4143 #ifdef MOTIF
4144 	toggle_pagelist();
4145 	toggle_toolbar();
4146 	toggle_menubar();
4147 #else
4148 	toggle_buttons();
4149 #endif
4150     }
4151 
4152 #ifdef MOTIF
4153     update_preferences_expert();
4154 #endif
4155 
4156     store_preference(NULL, "expertMode", "%d", resource.expert_mode);
4157 
4158     if (clear_statusline)
4159 	statusline_clear();
4160 }
4161 
4162 Boolean have_src_specials = False;
4163 static Boolean do_update_property = False;
4164 
4165 #ifdef USE_PANNER
4166 void
handle_x_scroll(Widget w,XtPointer closure,XEvent * ev,Boolean * cont)4167 handle_x_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
4168 {
4169 #if !defined(MOTIF)
4170     Dimension get_x = 0;
4171 #endif
4172 
4173     UNUSED(w);
4174     UNUSED(closure);
4175     UNUSED(ev);
4176     UNUSED(cont);
4177 
4178     if (/* !resource.keep_flag || */ globals.widgets.x_bar == NULL)
4179 	return;
4180 
4181 #ifdef MOTIF
4182     XtVaGetValues(globals.widgets.x_bar, XmNvalue, &m_x_scroll, NULL);
4183 #else
4184     get_xy();
4185     XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
4186     m_x_scroll = get_x - m_window_x;
4187     scroll_x_panner(m_x_scroll);
4188 #endif /* MOTIF */
4189 }
4190 
4191 void
handle_y_scroll(Widget w,XtPointer closure,XEvent * ev,Boolean * cont)4192 handle_y_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
4193 {
4194 #if !defined(MOTIF)
4195     Dimension get_y = 0;
4196 #endif
4197 
4198     UNUSED(w);
4199     UNUSED(closure);
4200     UNUSED(ev);
4201     UNUSED(cont);
4202 
4203     if (/* !resource.keep_flag || */ globals.widgets.y_bar == NULL)
4204 	return;
4205 
4206 #ifdef MOTIF
4207     XtVaGetValues(globals.widgets.y_bar, XmNvalue, &m_y_scroll, NULL);
4208 #else
4209     get_xy();
4210     XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
4211     m_y_scroll = get_y - m_window_y;
4212     scroll_y_panner(m_y_scroll);
4213 #endif /* MOTIF */
4214 }
4215 #endif /* USE_PANNER */
4216 
4217 void
handle_expose(Widget w,XtPointer closure,XEvent * ev,Boolean * cont)4218 handle_expose(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
4219 {
4220     struct WindowRec *windowrec = (struct WindowRec *)closure;
4221 
4222     UNUSED(w);
4223     UNUSED(cont);
4224 
4225     if (windowrec == &magnifier) {
4226 	if (magnifier_stat < 0) { /* destroy upon exposure */
4227 	    magnifier_stat = 0;
4228 	    mag_release(ev);
4229 	    return;
4230 	}
4231 	else
4232 	    magnifier_stat = 0;
4233     }
4234 
4235     do_update_property = False;
4236 
4237     if (have_src_specials && !MAGNIFIER_ACTIVE) {
4238 	do_update_property = True;
4239     }
4240 
4241     expose(windowrec, (&(ev->xexpose))->x, (&(ev->xexpose))->y,
4242 	   (unsigned int)(&(ev->xexpose))->width, (unsigned int)(&(ev->xexpose))->height);
4243 }
4244 
4245 
4246 void
handle_property_change(Widget w,XtPointer junk,XEvent * ev,Boolean * cont)4247 handle_property_change(Widget w, XtPointer junk,
4248 		       XEvent *ev, Boolean *cont)
4249 {
4250     char *prop_ret;
4251     size_t prop_len;
4252 
4253     UNUSED(w);
4254     UNUSED(junk);
4255     UNUSED(cont);
4256 
4257     if ((&(ev->xproperty))->window != XtWindow(globals.widgets.top_level)) /* if spurious event */
4258 	return;
4259 
4260     if ((&(ev->xproperty))->atom == atom_src_goto()) {
4261 	/* forward search requested */
4262 	if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_src_goto(),
4263 					  &prop_ret,
4264 					  XGetWindowProperty)) == 0) {
4265 	    TRACE_CLIENT((stderr, "property_get_data() failed for atom_src_goto()!"));
4266 	    return;
4267 	}
4268 	TRACE_CLIENT((stderr, "got back atom_src_goto: |%s|", prop_ret));
4269 	globals.src.fwd_string = prop_ret;
4270 	globals.ev.flags |= EV_SRC;
4271     }
4272     else if ((&(ev->xproperty))->atom == atom_find_string()) {
4273 	/* string search requested */
4274 	if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_find_string(),
4275 					  &prop_ret,
4276 					  XGetWindowProperty)) == 0) {
4277 	    TRACE_CLIENT((stderr, "property_get_data() failed for atom_find_string()!"));
4278 	    return;
4279 	}
4280 	TRACE_FIND((stderr, "got back atom_find_string: |%s|", prop_ret));
4281 	resource.find_string = prop_ret;
4282 	globals.ev.flags |= EV_FIND;
4283     }
4284     else if ((&(ev->xproperty))->atom == atom_reload()) {
4285 	/* like do_sigusr(); there's no data in this case. */
4286 	TRACE_CLIENT((stderr, "atom_reload()"));
4287 	globals.ev.flags |= EV_RELOAD;
4288     }
4289     else if ((&(ev->xproperty))->atom == atom_newdoc()) {
4290 	/* loading a new file */
4291 	FILE *new_fp;
4292 	if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newdoc(),
4293 					  &prop_ret,
4294 					  XGetWindowProperty)) == 0) {
4295 	    TRACE_CLIENT((stderr, "property_get_data() returned zero length for atom_newdoc()"));
4296 	    /* just raise it in this case */
4297 	    XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
4298 	    raise_message_windows();
4299 	    return;
4300 	}
4301 	TRACE_CLIENT((stderr, "got back atom_newdoc: |%s|", prop_ret));
4302 	if ((new_fp = XFOPEN(prop_ret, "r")) == NULL) {
4303 	    popup_message(globals.widgets.top_level,
4304 			  MSG_ERR, NULL, "Loading %s failed: %s",
4305 			  prop_ret, strerror(errno));
4306 	    return;
4307 	}
4308 	set_dvi_name_expand(prop_ret);
4309 
4310 	globals.ev.flags |= EV_NEWDOC;
4311     }
4312     else if ((&(ev->xproperty))->atom == atom_newpage()) {
4313 	/* jumping to a different page */
4314 	int newpage;
4315 	char *testptr;
4316 	if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newpage(),
4317 					  &prop_ret,
4318 					  XGetWindowProperty)) == 0) {
4319 	    TRACE_CLIENT((stderr, "property_get_data() failed for atom_newpage(): |%s|", prop_ret));
4320 	    return;
4321 	}
4322 	TRACE_CLIENT((stderr, "got back atom_newpage: |%s|", prop_ret));
4323 	if (strcmp(prop_ret, "+") == 0) { /* special case: treat `+' as last page */
4324 	    newpage = total_pages - 1;
4325 	}
4326 	else {
4327 	    newpage = strtol(prop_ret, &testptr, 10) - 1;
4328 	    if (*testptr != '\0') {
4329 		XDVI_FATAL((stderr, "Invalid page number: `%s'.", prop_ret));
4330 	    }
4331 	}
4332 
4333 	if (newpage == total_pages - 1) { /* as in Act_goto_page() */
4334 	    goto_page(check_goto_page(newpage, True), resource.keep_flag ? NULL : home, False);
4335 	    search_signal_page_changed();
4336 	}
4337 	else {
4338 	    goto_page(check_goto_tex_page(newpage), resource.keep_flag ? NULL : home, False);
4339 	    search_signal_page_changed();
4340 	}
4341     }
4342     else if ((&(ev->xproperty))->atom == atom_raise()) {
4343 	XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
4344 	raise_message_windows();
4345     }
4346     else if ((&(ev->xproperty))->atom == atom_reread_prefs()) {
4347 	read_user_preferences(globals.widgets.top_level, ".xdvirc.tmp");
4348     }
4349 }
4350 
4351 
4352 /*
4353  *	Signal routines.  At the signal level, all we do is set flags.
4354  */
4355 
4356 #ifndef FLAKY_SIGPOLL
4357 static RETSIGTYPE
handle_sigpoll(int signo)4358 handle_sigpoll(int signo)
4359 {
4360     UNUSED(signo);
4361 
4362     globals.ev.ctr = 1;
4363     event_freq = -1;	/* forget Plan B */
4364     sig_flags |= SF_POLL;
4365 # ifndef HAVE_SIGACTION
4366     (void) signal(SIGPOLL, handle_sigpoll);	/* reset the signal */
4367 # endif
4368 }
4369 #endif
4370 
4371 static RETSIGTYPE
handle_sigterm(int signo)4372 handle_sigterm(int signo)
4373 {
4374     UNUSED(signo);
4375 
4376     sig_flags |= SF_TERM;
4377 }
4378 
4379 static RETSIGTYPE
handle_sigchld(int signo)4380 handle_sigchld(int signo)
4381 {
4382     UNUSED(signo);
4383 
4384     sig_flags |= SF_CHLD;
4385 }
4386 
4387 static RETSIGTYPE
handle_sigalrm(int signo)4388 handle_sigalrm(int signo)
4389 {
4390     UNUSED(signo);
4391 
4392     sig_flags |= SF_ALRM;
4393 }
4394 
4395 static RETSIGTYPE
ignore_sigusr(int signo)4396 ignore_sigusr(int signo)
4397 {
4398     UNUSED(signo);
4399     fprintf(stderr, "IGNORE SIGUSR1!\n");
4400 }
4401 
4402 static RETSIGTYPE
handle_sigusr(int signo)4403 handle_sigusr(int signo)
4404 {
4405     UNUSED(signo);
4406 
4407     globals.ev.ctr = 1;
4408     sig_flags |= SF_USR;
4409 }
4410 
4411 static RETSIGTYPE
handle_sigsegv(int signo)4412 handle_sigsegv(int signo)
4413 {
4414     UNUSED(signo);
4415 
4416     XDVI_ABORT((stderr, "Segmentation fault - trying to clean up and aborting ..."));
4417 }
4418 
4419 static Boolean sigalarm_initialized = False;
4420 
4421 void
setup_sigalarm(void)4422 setup_sigalarm(void)
4423 {
4424 #if HAVE_SIGACTION
4425     struct sigaction a;
4426 
4427     a.sa_handler = handle_sigalrm;
4428     (void) sigemptyset(&a.sa_mask);
4429     (void) sigaddset(&a.sa_mask, SIGALRM);
4430     a.sa_flags = 0;
4431     sigaction(SIGALRM, &a, NULL);
4432 #else /* not HAVE_SIGACTION */
4433     (void) signal(SIGALRM, handle_sigalrm);
4434 #endif /* not HAVE_SIGACTION */
4435 
4436     sigalarm_initialized = True;
4437 }
4438 
4439 /*
4440  * Called from main to set up the signal handlers.
4441  */
4442 void
setup_signal_handlers(Boolean early)4443 setup_signal_handlers(Boolean early)
4444 {
4445 #ifndef FLAKY_SIGPOLL
4446     int	sock_fd	= 0;
4447 #endif
4448 #if HAVE_SIGACTION
4449     struct sigaction a;
4450 #endif
4451 
4452     if (early) {
4453 #if HAVE_SIGACTION
4454 	a.sa_handler = ignore_sigusr;
4455 	(void) sigemptyset(&a.sa_mask);
4456 	(void) sigaddset(&a.sa_mask, SIGUSR1);
4457 	a.sa_flags = 0;
4458 	sigaction(SIGUSR1, &a, NULL);
4459 #else /* not HAVE_SIGACTION */
4460 	(void) signal(SIGUSR1, ignore_sigusr);
4461 #endif /* not HAVE_SIGACTION */
4462 
4463 	(void)sigemptyset(&all_signals);
4464 	(void)sigaddset(&all_signals, SIGUSR1);
4465 	return;
4466     }
4467 
4468 
4469 #if HAVE_SIGACTION
4470     a.sa_handler = handle_sigusr;
4471     (void) sigemptyset(&a.sa_mask);
4472     (void) sigaddset(&a.sa_mask, SIGUSR1);
4473     a.sa_flags = 0;
4474     sigaction(SIGUSR1, &a, NULL);
4475 #else /* not HAVE_SIGACTION */
4476     (void) signal(SIGUSR1, handle_sigusr);
4477 #endif /* not HAVE_SIGACTION */
4478 
4479 #ifndef FLAKY_SIGPOLL
4480 #if HAVE_SIGACTION
4481     /* Subprocess handling, e.g., MakeTeXPK, fails on the Alpha without
4482        this, because SIGPOLL interrupts the call of system(3), since OSF/1
4483        doesn't retry interrupted wait calls by default.  From code by
4484        maj@cl.cam.ac.uk.  */
4485     a.sa_handler = handle_sigpoll;
4486     (void) sigemptyset(&a.sa_mask);
4487     (void) sigaddset(&a.sa_mask, SIGPOLL);
4488     a.sa_flags = SA_RESTART;
4489     sigaction(SIGPOLL, &a, NULL);
4490 #else /* not HAVE_SIGACTION */
4491     (void) signal(SIGPOLL, handle_sigpoll);
4492 #endif /* not HAVE_SIGACTION */
4493 
4494     sock_fd = ConnectionNumber(DISP);
4495     prep_fd(sock_fd, False);
4496 #endif	/* not FLAKY_SIGPOLL */
4497 
4498 #if HAVE_SIGACTION
4499     a.sa_handler = handle_sigterm;
4500     (void) sigemptyset(&a.sa_mask);
4501     (void) sigaddset(&a.sa_mask, SIGINT);
4502     (void) sigaddset(&a.sa_mask, SIGQUIT);
4503     (void) sigaddset(&a.sa_mask, SIGTERM);
4504     (void) sigaddset(&a.sa_mask, SIGHUP);
4505     a.sa_flags = SA_RESETHAND;
4506     sigaction(SIGINT, &a, NULL);
4507     sigaction(SIGQUIT, &a, NULL);
4508     sigaction(SIGTERM, &a, NULL);
4509     sigaction(SIGHUP, &a, NULL);
4510     a.sa_handler = handle_sigsegv;
4511     (void)sigemptyset(&a.sa_mask);
4512     (void)sigaddset(&a.sa_mask, SIGSEGV);
4513     a.sa_flags = 0;
4514     sigaction(SIGSEGV, &a, NULL);
4515 #else /* not HAVE_SIGACTION */
4516     (void) signal(SIGINT, handle_sigterm);
4517     (void) signal(SIGQUIT, handle_sigterm);
4518     (void) signal(SIGTERM, handle_sigterm);
4519     (void) signal(SIGHUP, handle_sigterm);
4520     (void)signal(SIGSEGV, handle_sigsegv);
4521 #endif /* not HAVE_SIGACTION */
4522 
4523 #if HAVE_SIGACTION
4524     a.sa_handler = handle_sigchld;
4525     (void) sigemptyset(&a.sa_mask);
4526     (void) sigaddset(&a.sa_mask, SIGCHLD);
4527     a.sa_flags = 0;
4528     sigaction(SIGCHLD, &a, NULL);
4529 #else /* not HAVE_SIGACTION */
4530     (void) signal(SIGCHLD, handle_sigchld);
4531 #endif /* not HAVE_SIGACTION */
4532 
4533     (void)sigemptyset(&all_signals);
4534     (void)sigaddset(&all_signals, SIGPOLL);
4535     (void)sigaddset(&all_signals, SIGINT);
4536     (void)sigaddset(&all_signals, SIGQUIT);
4537     (void)sigaddset(&all_signals, SIGTERM);
4538     (void)sigaddset(&all_signals, SIGHUP);
4539     (void)sigaddset(&all_signals, SIGCHLD);
4540     (void)sigaddset(&all_signals, SIGALRM);
4541     (void)sigaddset(&all_signals, SIGUSR1);
4542     (void)sigaddset(&all_signals, SIGSEGV);
4543 
4544 }
4545 
4546 
4547 /*
4548  *	Mid-level signal handlers.  These are called from within read_events(),
4549  *	and do the actual work appropriate for the given signal.
4550  */
4551 
4552 /*
4553  *	Process-related routines.  Call set_chld(xchild *) to indicate
4554  *	that a given child process should be watched for when it
4555  *	terminates.  Call clear_chld() to remove the process from the
4556  *	list.  When the child terminates, the record is removed from
4557  *	the list and xchild->proc is called.
4558  *	The caller can set the `io' member in xchild to a pointer to
4559  *	an xio structure for reading from the file descriptor; see
4560  *	psgs.c and util.c for examples.
4561  */
4562 
4563 static struct xchild *child_recs = NULL;	/* head of child process list */
4564 
4565 void
set_chld(struct xchild * cp)4566 set_chld(struct xchild *cp)
4567 {
4568     cp->next = child_recs;
4569     child_recs = cp;
4570 }
4571 
4572 void
clear_chld(struct xchild * cp)4573 clear_chld(struct xchild *cp)
4574 {
4575     struct xchild	**cpp;
4576     struct xchild	*cp2;
4577 
4578     if (child_recs == NULL) {
4579 	if (globals.debug & DBG_EVENT)
4580 	    fprintf(stderr, "child_recs: %p\n", (void *)child_recs);
4581 	return;
4582     }
4583     for (cpp = &child_recs;;) {
4584 	cp2 = *cpp;
4585 	if (cp2 == cp)
4586 	    break;
4587 	cpp = &cp2->next;
4588     }
4589     *cpp = cp->next;
4590 }
4591 
4592 static void
do_sigchld(void)4593 do_sigchld(void)
4594 {
4595     pid_t pid;
4596     int	status;
4597 
4598     sig_flags &= ~SF_CHLD;
4599 
4600 #if ! HAVE_SIGACTION
4601     (void) signal(SIGCHLD, handle_sigchld);	/* reset the signal */
4602 #endif
4603     for (;;) {
4604 #if HAVE_WAITPID
4605 	pid = waitpid(-1, &status, WNOHANG);
4606 #else
4607 	pid = wait3(&status, WNOHANG, NULL);
4608 #endif
4609 	if (pid == 0) break;
4610 
4611 	if (pid != -1) {
4612 	    struct xchild	**cpp;
4613 	    struct xchild	*cp;
4614 
4615 	    for (cpp = &child_recs;;) {
4616 		cp = *cpp;
4617 		if (cp == NULL)
4618 		    break;
4619 		if (cp->pid == pid) {
4620 		    *cpp = cp->next;	/* unlink it */
4621 		    /* call exit reporting procedure for this child */
4622 		    (cp->proc)(status, cp);
4623 		    break;
4624 		}
4625 		cpp = &cp->next;
4626 	    }
4627 	    break;
4628 	}
4629 
4630 	if (errno == EINTR) continue;
4631 	if (errno == ECHILD) break;
4632 #if HAVE_WAITPID
4633 	perror("xdvi: waitpid");
4634 #else
4635 	perror("xdvi: wait3");
4636 #endif
4637 	break;
4638     }
4639 }
4640 
4641 
4642 /*
4643  *	File-related routines.  Call set_io() to indicate that a given fd
4644  *	should be watched for ability to input or output data.  Call clear_io()
4645  *	to remove it from the list.  When poll()/select() indicates that the fd
4646  *	is available for the indicated type of i/o, the corresponding routine
4647  *	is called.  Call clear_io() to remove an fd from the list.
4648  *	Both set_io() and clear_io() can be called from within read_proc or
4649  *	write_proc (although turning an io descriptor on or off is better
4650  *	accomplished by setting the events flag in the xio structure, and
4651  *	in the corresponding pollfd structure if the pfd pointer is not NULL
4652  *	(it is always non-NULL when read_proc and write_proc are called)).
4653  *	We allocate space for one additional record in the pollfd array, to
4654  *	accommodate the fd for the X connection; this is done by initializing
4655  *	num_fds to 1 instead of zero.
4656  */
4657 
4658 static	struct xio	*iorecs	= NULL;	/* head of xio list */
4659 
4660 #if HAVE_POLL
4661 static	struct pollfd	*fds	= NULL;
4662 static	int		num_fds	= 1;	/* current number of fds */
4663 static	int		max_fds	= 0;	/* max allocated number of fds */
4664 static	Boolean		io_dirty= True;	/* need to recompute fds[] array */
4665 #else
4666 static	int		numfds	= 0;
4667 static	fd_set		readfds;
4668 static	fd_set		writefds;
4669 #endif
4670 
4671 void
set_io(struct xio * ip)4672 set_io(struct xio *ip)
4673 {
4674     ip->next = iorecs;
4675     iorecs = ip;
4676 
4677 #if HAVE_POLL
4678     ++num_fds;
4679     if (!io_dirty && num_fds <= max_fds) {
4680 	fds[num_fds - 1].fd = ip->fd;
4681 	fds[num_fds - 1].events = ip->xio_events;
4682 	ip->pfd = &fds[num_fds - 1];
4683     }
4684     else {
4685 	ip->pfd = NULL;
4686 	io_dirty = True;
4687     }
4688 #else
4689     if (numfds <= ip->fd) numfds = ip->fd + 1;
4690 #endif
4691 }
4692 
4693 void
clear_io(struct xio * ip)4694 clear_io(struct xio *ip)
4695 {
4696     struct xio	**ipp;
4697 
4698     for (ipp = &iorecs;;) {
4699 	struct xio *ip2;
4700 
4701 	ip2 = *ipp;
4702 	if (ip2 == ip) break;
4703 	ipp = &ip2->next;
4704     }
4705     *ipp = ip->next;
4706 
4707 #if HAVE_POLL
4708     --num_fds;
4709     io_dirty = True;
4710 #else
4711 # if FLAKY_SIGPOLL
4712     numfds = ConnectionNumber(DISP);
4713 # else
4714     numfds = (event_freq < 0 ? -1 : ConnectionNumber(DISP));
4715 # endif
4716     for (ip = iorecs; ip != NULL; ip = ip->next)
4717 	if (ip->fd > numfds)
4718 	    numfds = ip->fd;
4719     ++numfds;
4720 #endif /* !HAVE_POLL */
4721 }
4722 
4723 static void
do_sigpoll(void)4724 do_sigpoll(void)
4725 {
4726 #if FLAKY_SIGPOLL
4727     sig_flags &= ~SF_POLL;
4728 #else
4729     struct xio	*ip;
4730 
4731     sig_flags &= ~SF_POLL;
4732 
4733 # if HAVE_POLL
4734 
4735     if (io_dirty) {
4736 	struct pollfd *fp;
4737 
4738 	if (num_fds > max_fds) {
4739 	    if (fds != NULL) free(fds);
4740 	    fds = xmalloc(num_fds * sizeof *fds);
4741 	    memset(fds, 0, num_fds * sizeof *fds);
4742 	    max_fds = num_fds;
4743 	    fds->fd = ConnectionNumber(DISP);
4744 	    fds->events = POLLIN;
4745 	}
4746 	fp = fds + 1;
4747 	for (ip = iorecs; ip != NULL; ip = ip->next) {
4748 	    fp->fd = ip->fd;
4749 	    fp->events = ip->xio_events;
4750 	    ip->pfd = fp;
4751 	    ++fp;
4752 	}
4753 	io_dirty = False;
4754     }
4755 
4756     for (;;) {
4757 	if (poll(fds + 1, num_fds - 1, 0) >= 0)
4758 	    break;
4759 
4760 	if (errno != EAGAIN && errno != EINTR) {
4761 	    perror("xdvi: poll");
4762 	    return;
4763 	}
4764     }
4765 
4766     for (ip = iorecs; ip != NULL; ip = ip->next) {
4767 	int revents = ip->pfd->revents;
4768 	if (revents & POLLIN && ip->read_proc != NULL)
4769 	    (void)(ip->read_proc)(ip->fd, ip->data);
4770 	if (revents & POLLOUT && ip->write_proc != NULL)
4771 	    (ip->write_proc)(ip->fd, ip->data);
4772     }
4773 
4774 # else
4775 
4776     FD_ZERO(&readfds);
4777     FD_ZERO(&writefds);
4778     for (ip = iorecs; ip != NULL; ip = ip->next) {
4779 	if (ip->xio_events & XIO_IN)
4780 	    FD_SET(ip->fd, &readfds);
4781 	if (ip->xio_events & XIO_OUT)
4782 	    FD_SET(ip->fd, &writefds);
4783     }
4784 
4785     for (;;) {
4786 	struct timeval tv;
4787 
4788 	tv.tv_sec = tv.tv_usec = 0;
4789 	if (select(numfds, &readfds, &writefds, (fd_set *) NULL, &tv) >= 0)
4790 	    break;
4791 
4792 	if (errno != EAGAIN && errno != EINTR) {
4793 	    perror("select (xdvi read_events)");
4794 	    return;
4795 	}
4796     }
4797 
4798     for (ip = iorecs; ip != NULL; ip = ip->next) {
4799 	if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL)
4800 	    (void)(ip->read_proc)(ip->fd, ip->data);
4801 	if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL)
4802 	    (ip->write_proc)(ip->fd, ip->data);
4803     }
4804 
4805 # endif
4806 #endif /* not FLAKY_SIGPOLL */
4807 }
4808 
4809 
4810 /*
4811  *	Timer-related routines.  Call set_timer() to set a timer a given number
4812  *	of milliseconds in the future.  At that time, the timer will be cleared
4813  *	and the given procedure will be called with argument set to the struct
4814  *	passed to set_timer().  The timer routine may call set_timer() or
4815  *	cancel_timer().
4816  */
4817 
4818 
4819 static struct xtimer *timers = NULL;	/* head of timer list */
4820 
4821 static struct itimerval itv = {{0, 0}, {0, 0}};
4822 
4823 #ifndef	timercmp
4824 #define	timercmp(a, b, OP)	(((a)->tv_sec OP (b)->tv_sec || \
4825 	((a)->tv_sec == (b)->tv_sec && (a)->tv_usec OP (b)->tv_usec)))
4826 #endif	/* timercmp */
4827 
4828 
4829 static void
show_timers(const char * what)4830 show_timers(const char *what)
4831 {
4832     struct xtimer *tp;
4833     fprintf(stderr, "=======%s; timers:\n", what);
4834     for (tp = timers; tp != NULL; tp = tp->next) {
4835 	fprintf(stderr, "timer %p: %lu\n", (void *)tp, (unsigned long)tp->when.tv_sec);
4836     }
4837     fprintf(stderr, "=======\n");
4838 }
4839 
4840 void
set_timer(struct xtimer * tp,int ms)4841 set_timer(struct xtimer *tp, int ms)
4842 {
4843     struct xtimer	**tpp;
4844     struct xtimer	*tp2;
4845 
4846     if (globals.debug & DBG_EVENT)
4847 	fprintf(stderr, "%s:%d: set_timer\n", __FILE__, __LINE__);
4848 
4849     gettimeofday(&tp->when, NULL);
4850     itv.it_value.tv_sec = ms / 1000;
4851     itv.it_value.tv_usec = (ms % 1000) * 1000;
4852     tp->when.tv_sec += itv.it_value.tv_sec;
4853     tp->when.tv_usec += itv.it_value.tv_usec;
4854     if (tp->when.tv_usec >= 1000000) {
4855 	tp->when.tv_usec -= 1000000;
4856 	++tp->when.tv_sec;
4857     }
4858 
4859     for (tpp = &timers;;) {		/* add timer to list */
4860 	tp2 = *tpp;
4861 	if (tp2 == NULL || timercmp(&tp->when, &tp2->when, <))
4862 	    break;
4863 	tpp = &tp2->next;
4864     }
4865     tp->next = tp2;
4866     *tpp = tp;
4867 
4868     if (tpp == &timers) {
4869 	setitimer(ITIMER_REAL, &itv, NULL);
4870 	if (ms == 0)
4871 	    sig_flags |= SF_ALRM;
4872     }
4873     if (globals.debug & DBG_EVENT)
4874 	show_timers("after set_timer");
4875 }
4876 
4877 void
cancel_timer(struct xtimer * tp)4878 cancel_timer(struct xtimer *tp)
4879 {
4880     struct xtimer **tpp;
4881 
4882     if (globals.debug & DBG_EVENT)
4883 	show_timers("beginning of cancel_timer");
4884 
4885     if (timers == NULL) {
4886 	fprintf(stderr, "%s:%d: BUG? timers == NULL!\n", __FILE__, __LINE__);
4887 	return;
4888     }
4889 
4890     if (globals.debug & DBG_EVENT)
4891 	fprintf(stderr, "%s:%d: cancel_timer %p from %p\n", __FILE__, __LINE__, (void *)&timers, (void *)tp);
4892 
4893     ASSERT(timers != NULL, "timers in cancel_timer() mustn't be NULL");
4894     for (tpp = &timers; ; ) {		/* remove from list */
4895 	if (*tpp == tp)
4896 	    break;
4897 	tpp = &(*tpp)->next;
4898     }
4899 
4900     *tpp = (*tpp)->next;	/* unlink it */
4901 
4902     if (timers == NULL) {	/* cancel SIGALRM */
4903 	itv.it_value.tv_sec = itv.it_value.tv_usec = 0;
4904 	setitimer(ITIMER_REAL, &itv, NULL);
4905     }
4906 }
4907 
4908 #if XDVI_XT_TIMER_HACK
4909 /*
4910  * Original comment by Paul:
4911  *	Newer versions of the Motif toolkit use the timer facility
4912  *	(XtAppAddTimeOut(), etc.) in the X Toolkit.  Proper functioning of
4913  *	this mechanism, however, requires that the X Toolkit be in charge of
4914  *	blocking.  Since xdvi does its own blocking, this means that we need
4915  *	to provide working alternatives to these X Toolkit routines (by
4916  *	redefining them ...).
4917  *	One symptom of the above-mentioned bug is that the printlog window
4918  *	eventually stops showing dvips progress until you move the mouse.
4919  *
4920  * Comment SU:
4921  *	Xdvik also uses XtAppAddTimeOut() in other places (hyperref/statusline/...),
4922  *	so we also need these redefinitions also in the Xaw version.
4923  *
4924  */
4925 
4926 static void xt_alarm(struct xtimer *this, void *data);
4927 
4928 static struct xtimer *xt_free_timers = NULL;
4929 
4930 XtIntervalId
XtAddTimeOut(unsigned long interval,XtTimerCallbackProc proc,XtPointer closure)4931 XtAddTimeOut(unsigned long interval, XtTimerCallbackProc proc, XtPointer closure)
4932 {
4933     return XtAppAddTimeOut(NULL, interval, proc, closure);
4934 }
4935 
4936 XtIntervalId
XtAppAddTimeOut(XtAppContext app,unsigned long interval,XtTimerCallbackProc proc,XtPointer closure)4937 XtAppAddTimeOut(XtAppContext app, unsigned long interval, XtTimerCallbackProc proc, XtPointer closure)
4938 {
4939     struct xtimer *tp;
4940 
4941     UNUSED(app);
4942 
4943     /* FIXME: better way of checking this instead of static boolean sigalarm_initialized?
4944        The following doesn't work, even after
4945        sigaddset(&all_signals, SIGALRM);
4946 
4947        static sigset_t sig_set;
4948        (void)sigprocmask(0, NULL, &sig_set);
4949        if (sigismember(&sig_set, SIGALRM))
4950        ... OK ...
4951        else
4952        ... NOT OK ...
4953     */
4954     ASSERT(sigalarm_initialized, "Shouldn't invoke XtAppAddTimeOut() before setup_sigalarm()");
4955 
4956     if (globals.debug & DBG_EVENT)
4957 	fprintf(stderr, "XtAppAddTimeOut: %lu msecs\n", interval);
4958 
4959     if (xt_free_timers == NULL)
4960 	tp = xmalloc(sizeof *tp);
4961     else {
4962 	tp = xt_free_timers;
4963 	xt_free_timers = xt_free_timers->next;
4964     }
4965 
4966     tp->proc = xt_alarm;
4967     tp->data = closure;
4968     tp->xt_proc = proc;
4969     tp->closure = closure;
4970 
4971     set_timer(tp, interval);
4972 
4973     if (globals.debug & DBG_EVENT)
4974 	show_timers("XtAppAddTimeOut");
4975 
4976     return (XtIntervalId)tp;
4977 }
4978 
4979 void
XtRemoveTimeOut(XtIntervalId id)4980 XtRemoveTimeOut(XtIntervalId id)
4981 {
4982     struct xtimer *tp;
4983 
4984     ASSERT(id != 0, "XtIntervalId argument in XtRemoveTimeOut() mustn't be NULL");
4985     tp = (struct xtimer *)id;
4986 
4987     /* Motif (2.1 on Solaris 9, 2003) sometimes calls XtRemoveTimeOut() after
4988        the timer event has occurred, so we need to be sure not to remove
4989        the timer record twice.  */
4990     if (tp->proc == NULL)
4991 	return;
4992 
4993     cancel_timer(tp);
4994 
4995     tp->next = xt_free_timers;
4996     xt_free_timers = tp;
4997 
4998     if (globals.debug & DBG_EVENT)
4999 	show_timers("XtRemoveTimeOut");
5000 }
5001 
5002 void
xt_alarm(struct xtimer * tp,void * data)5003 xt_alarm(struct xtimer *tp, void *data)
5004 {
5005     XtIntervalId id;
5006     UNUSED(data);
5007 
5008     tp->proc = NULL;	/* flag timer as used-up */
5009     id = (XtIntervalId)tp;
5010     (tp->xt_proc)(tp->closure, &id);
5011 
5012     tp->next = xt_free_timers;
5013     xt_free_timers = tp;
5014 
5015     if (globals.debug & DBG_EVENT)
5016 	show_timers("xt_alarm");
5017 }
5018 #endif /* XDVI_XT_TIMER_HACK */
5019 
5020 static	void
do_sigalrm(void)5021 do_sigalrm(void)
5022 {
5023     struct timeval now;
5024 
5025     sig_flags &= ~SF_ALRM;
5026 #ifndef HAVE_SIGACTION
5027     (void) signal(SIGALRM, handle_sigalrm);	/* reset the signal */
5028 #endif
5029 
5030     gettimeofday(&now, NULL);
5031 
5032     while (timers != NULL && timercmp(&timers->when, &now, <=)) {
5033 	struct xtimer *tp = timers;
5034 	timers = timers->next;	/* unlink it _first_ */
5035 	(tp->proc)(tp, tp->data);
5036     }
5037 
5038     if (timers != NULL) {		/* set next timer */
5039 	int i;
5040 	itv.it_value.tv_sec = timers->when.tv_sec - now.tv_sec;
5041 	i = timers->when.tv_usec - now.tv_usec;
5042 	if (i < 0) {
5043 	    --itv.it_value.tv_sec;
5044 	    i += 1000000;
5045 	}
5046 	itv.it_value.tv_usec = i;
5047 
5048 	setitimer(ITIMER_REAL, &itv, NULL);
5049     }
5050 }
5051 
5052 
5053 
5054 /*
5055  *	Handle SIGUSR1 signal.  Pretty straightforward.
5056  */
5057 
5058 static void
do_sigusr(void)5059 do_sigusr(void)
5060 {
5061     sig_flags &= ~SF_USR;
5062 #ifndef HAVE_SIGACTION
5063     (void) signal(SIGUSR1, handle_sigusr);	/* reset the signal */
5064 #endif
5065     globals.ev.flags |= EV_RELOAD;
5066 }
5067 
5068 
5069 static void
do_sigsegv(void)5070 do_sigsegv(void)
5071 {
5072     sig_flags &= ~SF_SEGV;
5073 #ifndef HAVE_SIGACTION
5074     (void) signal(SIGSEGV, handle_sigsegv);	/* reset the signal */
5075 #endif
5076     handle_sigsegv(SIGSEGV);
5077 }
5078 
5079 
5080 /*
5081  *	Handle termination signals.  Kill child processes, if permitted.
5082  *	Otherwise, leave it up to the caller.  This is the only place where
5083  *	the EV_TERM event flag is set, and it can only happen if there's an
5084  *	active non-killable process.  This should only happen if read_events()
5085  *	is called from one of a very few select locations.
5086  */
5087 
5088 static void
do_sigterm(void)5089 do_sigterm(void)
5090 {
5091     struct xchild   *cp;
5092 
5093     sig_flags &= ~SF_TERM;
5094 
5095     /* loop through child processes */
5096     for (cp = child_recs; cp != NULL; cp = cp->next) {
5097 	if (cp->killable)
5098 	    kill(cp->pid, SIGKILL);
5099     }
5100 
5101     /* SU: Unlike non-k xdvi, we don't care about whether all children have been
5102        killed, since processes forked via fork_process() (e.g. the web browser)
5103        may still continue running. So we just exit here.
5104     */
5105     xdvi_exit(EXIT_SUCCESS);
5106 
5107     /* BUG ALERT: since xdvi_exit() may return (checks before writing to ~/.xdvirc),
5108        we mustn't do the following which is in non-k xdvi, else we'll end up in a busy loop.
5109        I don't know who the `caller' is anyway ...
5110 
5111        globals.ev.flags |= EV_TERM; /\* otherwise, let the caller handle it *\/
5112     */
5113 }
5114 
5115 
5116 #if HAVE_XI21
5117 
5118 /*
5119  *	XInput 2.1 event processing.
5120  *
5121  *	The XInput system has (virtual) "master" devices, including a master
5122  *	pointer and master keyboard (and maybe others), and "slave" devices
5123  *	that can be assigned to a master.  Each master can have only one slave
5124  *	at a time.  If you have two mice plugged in, for example, each is a
5125  *	separate slave device, and the one assigned to the master pointer device
5126  *	is whichever one was moved or clicked most recently.  Moving or clicking
5127  *	the other mouse would then cause an XInput SlaveSwitch event, which
5128  *	notifies the X client that a different pointer is now active (and the
5129  *	previously active mouse would then become inactive).
5130  *
5131  *	The code here looks not just at events from the master pointer, it also
5132  *	tracks events from the slave pointer devices.  This is because the
5133  *	valuators for each slave pointer may have different scales.
5134  *
5135  *	The XI_Motion events contain the master and slave ids of the device
5136  *	sending the event, so in principle it wouldn't be necessary to track
5137  *	SlaveSwitch events.  However, we do track these events, because we need
5138  *	to know the valuator values at the time of the switch (otherwise, e.g.,
5139  *	the first click of a wheel mouse would be lost).
5140  *
5141  *	It is also possible that a valuator may change while the pointer is
5142  *	outside of the drawing window.  So, we need to reset the valuator
5143  *	values upon XI_Enter events.  For some strange reason, we get extra
5144  *	XI_Enter events when using a traditional wheel mouse, and so the code
5145  *	takes care to ignore those.  This only happens with the Xaw toolkit.
5146  *
5147  *	Slave devices have events that notify clients when they are (i) added
5148  *	or removed, (ii) attached or detached, and (iii) enabled or disabled.
5149  *	We only worry about (iii), and for disabled devices we don't bother to
5150  *	delete the data structure.
5151  *
5152  */
5153 
5154 /*
5155  *	xi2_find_master:  set xi2_current to record for given device id
5156  *	Return True if found, False if not.
5157  */
5158 
5159 static Boolean
xi2_find_master(int id)5160 xi2_find_master(int id)
5161 {
5162 	struct xi2_master *mp;
5163 
5164 	for (mp = xi2_masters; mp != NULL; mp = mp->next)
5165 	    if (mp->id == id) {
5166 		xi2_current = mp;
5167 		return True;
5168 	    }
5169 	return False;
5170 }
5171 
5172 /*
5173  *	xi2_find_slave:  set xi2_current->slave to record for given slave device
5174  *	Return True if found, False if not.
5175  */
5176 
5177 static Boolean
xi2_find_slave(int id)5178 xi2_find_slave(int id)
5179 {
5180 	struct xi2_slave *sp;
5181 
5182 	for (sp = xi2_slaves; sp != NULL; sp = sp->next)
5183 	    if (sp->id == id) {
5184 		xi2_current->slave = sp;
5185 		return True;
5186 	    }
5187 	return False;
5188 }
5189 
5190 /*
5191  *	Handle XI2 Enter event.  We need to look at these events because if the
5192  *	user scrolls in some other window, then the valuator will be changed
5193  *	by a large amount, and that scrolling should not occur also in the
5194  *	main xdvi window when the user resumes scrolling here.
5195  */
5196 
5197 static void
xi2_ev_enter(XIEnterEvent * xi_event)5198 xi2_ev_enter(XIEnterEvent *xi_event)
5199 {
5200 	XIDeviceInfo	*info;
5201 	int		ndevices;
5202 	int		i;
5203 
5204 	if (xi_event->time < xi2_ign_time + 5) {
5205 	    TRACE_EVENTS((stderr, "Ignoring XI_Enter event as spurious."));
5206 	    return;
5207 	}
5208 
5209 	if (xi2_current->id != xi_event->deviceid
5210 	  && !xi2_find_master(xi_event->deviceid)) {
5211 	    TRACE_EVENTS((stderr,
5212 		"Ignoring XI_Enter event: master device %d not found",
5213 		  xi_event->deviceid));
5214 	    return;
5215 	}
5216 
5217 	if (xi_event->sourceid == xi2_current->id) {
5218 	    TRACE_EVENTS((stderr,
5219 	      "Ignoring XI_Enter event for master device."));
5220 	    return;
5221 	}
5222 	else if (xi_event->sourceid != xi2_current->slave->id) {
5223 	    struct xi2_slave *prev_slave = xi2_current->slave;
5224 
5225 	    if (!xi2_find_slave(xi_event->sourceid)) {
5226 		TRACE_EVENTS((stderr,
5227 		  "Ignoring XI_Enter event: device %d->%d not found.",
5228 		  xi_event->deviceid, xi_event->sourceid));
5229 		return;
5230 	    }
5231 	    TRACE_EVENTS((stderr,
5232 	      "%s %d->%d",
5233 	      prev_slave == &xi2_no_slave
5234 		? "Implicit switch in XI_Enter to"
5235 		: "Received out-of-turn XI_Enter event for",
5236 	      xi_event->deviceid, xi_event->sourceid));
5237 	    if (!xi2_current->slave->enabled)
5238 		TRACE_EVENTS((stderr, "Caution: slave is not enabled."));
5239 	}
5240 	else {
5241 	    TRACE_EVENTS((stderr, "Received XI_Enter event"));
5242 	}
5243 
5244 	info = XIQueryDevice(DISP, xi_event->sourceid, &ndevices);
5245 	if (info == NULL || ndevices != 1) {
5246 	    TRACE_EVENTS((stderr, "xi2_ev_enter:  XIQueryDevice failed for %d",
5247 	      xi_event->sourceid));
5248 	    return;
5249 	}
5250 
5251 	for (i = 0; i < info->num_classes; ++i)
5252 	    if (info->classes[i]->type == XIValuatorClass) {
5253 		XIValuatorClassInfo *valuator;
5254 
5255 		valuator = (XIValuatorClassInfo *) info->classes[i];
5256 		if (valuator->number == xi2_current->slave->vert.number) {
5257 		    xi2_current->slave->vert.lastexact =
5258 		    xi2_current->slave->vert.lastval = valuator->value;
5259 		    xi2_current->slave->vert.serial =
5260 		      LastKnownRequestProcessed(DISP);
5261 		}
5262 		else if (valuator->number == xi2_current->slave->horiz.number) {
5263 		    xi2_current->slave->horiz.lastexact =
5264 		    xi2_current->slave->horiz.lastval = valuator->value;
5265 		    xi2_current->slave->horiz.serial =
5266 		      LastKnownRequestProcessed(DISP);
5267 		}
5268 	    }
5269 	XIFreeDeviceInfo(info);
5270 }
5271 
5272 static void
xi2_emulate_action(struct xdvi_action * actp,struct xi2_valinfo * valinfo,double value)5273 xi2_emulate_action(struct xdvi_action *actp, struct xi2_valinfo *valinfo,
5274 	double value)
5275 {
5276 	double factor;
5277 	int dist;
5278 
5279 	for (; actp != NULL; actp = actp->next) {
5280 	    if (actp->proc == Act_wheel || actp->proc == Act_hwheel) {
5281 		if (actp->num_params == 0)
5282 		    factor = resource.wheel_unit;
5283 		else if (index(actp->params[0], '.') == NULL)
5284 		    factor = atoi(actp->params[0]);
5285 		else
5286 		    factor = atof(actp->params[0]) * resource.wheel_unit;
5287 		factor /= valinfo->increment;
5288 
5289 		if (valinfo->factor != fabs(factor)) {
5290 		    valinfo->lastval = valinfo->lastexact;
5291 		    valinfo->factor = fabs(factor);
5292 		}
5293 
5294 		/*
5295 		 * In extreme cases, the server may reset the valuator to zero
5296 		 * to avoid large values.  The next few lines avoid the extreme
5297 		 * scrolling event that would otherwise occur in this event.
5298 		 */
5299 		if (fabs(value - valinfo->lastval) > INT_MAX / 2) {
5300 		    valinfo->lastval +=
5301 		      value > valinfo->lastval ? INT_MAX : -INT_MAX;
5302 		    TRACE_EVENTS((stderr,
5303 		      "Adjusted for pointer overflow; lastval = %g, new = %g\n",
5304 		      valinfo->lastval, value));
5305 		}
5306 
5307 		dist = (value - valinfo->lastval) * factor;
5308 		if (dist == 0) {
5309 		    valinfo->lastexact = value;
5310 		    continue;
5311 		}
5312 
5313 		if (actp->proc == Act_wheel) {
5314 #  if XAW
5315 		    if (globals.widgets.y_bar != NULL)
5316 			XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
5317 			  cast_int_to_XtPointer(dist));
5318 #  else /* MOTIF */
5319 		    get_xy();
5320 		    set_bar_value(globals.widgets.y_bar,
5321 		      dist - m_window_y, (int) (globals.page.h - mane.height));
5322 #  endif /* MOTIF */
5323 		}
5324 		else {	/* Act_hwheel */
5325 #  if XAW
5326 		    if (globals.widgets.x_bar != NULL)
5327 			XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
5328 			  cast_int_to_XtPointer(dist));
5329 #  else /* MOTIF */
5330 		    get_xy();
5331 		    set_bar_value(globals.widgets.x_bar,
5332 		      dist - m_window_x, (int) (globals.page.w - mane.width));
5333 #  endif /* MOTIF */
5334 		}
5335 		/* The next line puts back any rounding error */
5336 		valinfo->lastval += dist / factor;
5337 		valinfo->lastexact = value;
5338 	    }
5339 	    else if (actp->proc == Act_mouse_modes) {
5340 		size_t mode_idx = 0; /* default: one action for all modes */
5341 		struct xdvi_action *my_action = NULL;
5342 
5343 		if (actp->num_params > 1) { /* if different actions */
5344 		    if (resource.mouse_mode >= actp->num_params) {
5345 			size_t k;
5346 
5347 			XDVI_WARNING((stderr, "X action 'mouse-modes' called "
5348 			    "with only %d parameters, should be %d",
5349 			  actp->num_params, resource.mouse_mode + 1));
5350 			for (k = 0; k < actp->num_params; k++) {
5351 			    XDVI_WARNING((stderr, "  Param %d: `%s'",
5352 			      (int)(k + 1), actp->params[k]));
5353 			}
5354 			return;
5355 		    }
5356 		    mode_idx = resource.mouse_mode;
5357 		}
5358 
5359 		cached_compile_action(actp->params[mode_idx], &my_action);
5360 		xi2_emulate_action(my_action, valinfo, value);
5361 	    }
5362 	    else
5363 		TRACE_EVENTS((stderr, "Mouse_translations action is neither "
5364 		    "wheel() nor hwheel(); ignoring."));
5365 	}
5366 }
5367 
5368 static void
xi2_do_valuator(XIDeviceEvent * xi_event,struct xi2_valinfo * valinfo,int button,double value)5369 xi2_do_valuator(XIDeviceEvent *xi_event, struct xi2_valinfo *valinfo,
5370 	int button, double value)
5371 {
5372 	struct mouse_acts *mactp;
5373 	Modifiers state;
5374 
5375 	state = xi_event->buttons.mask[0];
5376 	if (xi_event->buttons.mask_len >= 2) {
5377 	    state |= xi_event->buttons.mask[1] << 8;
5378 	    if (xi_event->buttons.mask_len >= 3) {
5379 		state |= xi_event->buttons.mask[2] << 16;
5380 		if (xi_event->buttons.mask_len >= 4)
5381 		    state |= xi_event->buttons.mask[3] << 24;
5382 	    }
5383 	}
5384 
5385 	for (mactp = mouse_actions; mactp != NULL; mactp = mactp->next)
5386 	    if (mactp->button == button || mactp->button == 0) {
5387 		Modifiers mask = 0;
5388 		Modifiers mods_value = 0;
5389 
5390 		if (mactp->late_bindings == NULL
5391 		  || _XtComputeLateBindings(DISP, mactp->late_bindings,
5392 		  &mods_value, &mask)) {
5393 		    mask |= mactp->mask;
5394 		    mods_value |= mactp->value;
5395 		    if (((mods_value ^ state) & mask) == 0) {
5396 			xi2_emulate_action(mactp->action, valinfo, value);
5397 			return;
5398 		    }
5399 		}
5400 	    }
5401 }
5402 
5403 static void
xi2_ev_motion(XIDeviceEvent * xi_event)5404 xi2_ev_motion(XIDeviceEvent *xi_event)
5405 {
5406 	double *val;
5407 	int i;
5408 	XEvent event;
5409 
5410 	if (xi2_current->id != xi_event->deviceid
5411 	  && !xi2_find_master(xi_event->deviceid)) {
5412 	    TRACE_EVENTS((stderr,
5413 	      "Ignoring XI_Motion event: master device %d not found",
5414 	      xi_event->deviceid));
5415 	    return;
5416 	}
5417 
5418 	if (xi2_current->slave->id != xi_event->sourceid) {
5419 	    struct xi2_slave *prev_slave = xi2_current->slave;
5420 
5421 	    if (!xi2_find_slave(xi_event->sourceid)) {
5422 		TRACE_EVENTS((stderr,
5423 		  "Ignoring XI_Motion event: device %d->%d not found.",
5424 		  xi_event->deviceid, xi_event->sourceid));
5425 		return;
5426 	    }
5427 	    TRACE_EVENTS((stderr, "%s %d->%d\n",
5428 	      prev_slave == &xi2_no_slave
5429 		? "Implicit switch in XI_Motion to"
5430 		: "Received out-of-turn XI_Motion event for",
5431 	      xi_event->deviceid, xi_event->sourceid));
5432 	    if (!xi2_current->slave->enabled)
5433 		TRACE_EVENTS((stderr, "Caution: slave is not enabled."));
5434 	}
5435 
5436 	val = xi_event->valuators.values;
5437 	for (i = 0; i < xi_event->valuators.mask_len * 8; ++i)
5438 	    if (XIMaskIsSet(xi_event->valuators.mask, i)) {
5439 		if (i == xi2_current->slave->vert.number) {
5440 		    if ((long) (xi_event->serial
5441 		      - xi2_current->slave->vert.serial) < 0) {
5442 			TRACE_EVENTS((stderr,
5443 			  "Vertical valuator in EnterEvent was outdated "
5444 			    "(%lu < %lu)",
5445 			  xi_event->serial, xi2_current->slave->vert.serial));
5446 			xi2_current->slave->vert.serial = xi_event->serial;
5447 			xi2_current->slave->vert.lastexact =
5448 			xi2_current->slave->vert.lastval = *val;
5449 		    }
5450 		    else
5451 			xi2_do_valuator(xi_event, &xi2_current->slave->vert, 5,
5452 			  *val);
5453 		}
5454 		else if (i == xi2_current->slave->horiz.number) {
5455 		    if ((long) (xi_event->serial
5456 		      - xi2_current->slave->horiz.serial) < 0) {
5457 			TRACE_EVENTS((stderr,
5458 			  "Horizontal valuator in EnterEvent was outdated"));
5459 			xi2_current->slave->horiz.serial = xi_event->serial;
5460 			xi2_current->slave->horiz.lastexact =
5461 			xi2_current->slave->horiz.lastval = *val;
5462 		    }
5463 		    else
5464 			xi2_do_valuator(xi_event, &xi2_current->slave->horiz, 7,
5465 			  *val);
5466 		}
5467 		++val;
5468 	    }
5469 
5470 	/* For some reason the X server doesn't want to send a core motion
5471 	 * event if an XInput motion event is already being sent.
5472 	 * So we need to fake it.  The routines called from Act_motion()
5473 	 * should be sure to use only the fields provided here.  */
5474 	event.xmotion.window = xi_event->event;
5475 	event.xmotion.x = xi_event->event_x;
5476 	event.xmotion.y = xi_event->event_y;
5477 	event.xmotion.x_root = xi_event->root_x;
5478 	event.xmotion.y_root = xi_event->root_y;
5479 	Act_motion(NULL, &event, NULL, NULL);
5480 }
5481 
5482 static void
xi2_ev_devchange(XIDeviceChangedEvent * xi_event)5483 xi2_ev_devchange(XIDeviceChangedEvent *xi_event)
5484 {
5485 	struct xi2_slave *sp;
5486 
5487 	if (xi_event->reason == XISlaveSwitch) {
5488 	    struct xi2_master *mp;
5489 
5490 	    TRACE_EVENTS((stderr,
5491 	      "Received XI_DeviceChanged event, XISlaveSwitch, device = %d->%d",
5492 	      xi_event->deviceid, xi_event->sourceid));
5493 
5494 	    for (sp = xi2_slaves;; sp = sp->next) {
5495 		if (sp == NULL) {
5496 		    TRACE_EVENTS((stderr, "ignoring (device not found)"));
5497 		    return;
5498 		}
5499 		if (sp->id == xi_event->sourceid)
5500 		    break;
5501 	    }
5502 
5503 	    for (mp = xi2_masters;; mp = mp->next) {
5504 		if (mp == NULL) {
5505 		    TRACE_EVENTS((stderr,
5506 			"Cannot switch slave (master device not found)"));
5507 		    return;
5508 		}
5509 		if (mp->id == xi_event->deviceid)
5510 		    break;
5511 	    }
5512 	    mp->slave = sp;
5513 
5514 	    /* A valuator may change when a slave is switched off */
5515 	    xi2_init_valuators(sp, xi_event->classes, xi_event->num_classes);
5516 
5517 	    if (!sp->enabled)
5518 		TRACE_EVENTS((stderr, "Caution: slave device is not enabled."));
5519 	}
5520 	else if (xi_event->reason == XIDeviceChange) {
5521 	    /* In principle, this will add any device, but there's no "use" */
5522 	    /* field, so no easy way to check if it's a suitable slave device.*/
5523 	    TRACE_EVENTS((stderr,
5524 	     "Received XI_DeviceChanged event, XIDeviceChange, device = %d->%d",
5525 	      xi_event->deviceid, xi_event->sourceid));
5526 
5527 	    for (sp = xi2_slaves;; sp = sp->next) {
5528 		if (sp == NULL) {
5529 		    TRACE_EVENTS((stderr, " (new device)"));
5530 		    sp = xmalloc(sizeof (struct xi2_slave));
5531 		    sp->id = xi_event->sourceid;
5532 		    sp->enabled = 0;
5533 		    sp->next = xi2_slaves;
5534 		    xi2_slaves = sp;
5535 		    break;
5536 		}
5537 		if (sp->id == xi_event->sourceid)
5538 		    break;
5539 	    }
5540 
5541 	    xi2_init_valuators(sp, xi_event->classes, xi_event->num_classes);
5542 
5543 	    if (!xi2_active && sp->btn_mask) {
5544 		TRACE_EVENTS((stderr, "Activating XI2 now."));
5545 		xi2_activate();
5546 	    }
5547 	}
5548 	else {
5549 	    TRACE_EVENTS((stderr,
5550 	      "Ignoring XI_DeviceChanged event with unknown reason %x, "
5551 		"device = %d->%d",
5552 	      xi_event->reason, xi_event->deviceid, xi_event->sourceid));
5553 	}
5554 }
5555 
5556 
5557 static void
xi2_ev_hierchange(XIHierarchyEvent * xi_event)5558 xi2_ev_hierchange(XIHierarchyEvent *xi_event)
5559 {
5560 	XIHierarchyInfo	*infp, *inf_end;
5561 
5562 	if (globals.debug & DBG_EVENT) {
5563 	    fprintf(stderr, "%s:%d: XI_HierarchyChanged event: ",
5564 	       __FILE__, __LINE__);
5565 	    if (xi_event->flags & XIMasterAdded)
5566 		fputs(" XIMasterAdded", stderr);
5567 	    if (xi_event->flags & XIMasterRemoved)
5568 		fputs(" XIMasterRemoved", stderr);
5569 	    if (xi_event->flags & XISlaveAdded)
5570 		fputs(" XISlaveAdded", stderr);
5571 	    if (xi_event->flags & XISlaveRemoved)
5572 		fputs(" XISlaveRemoved", stderr);
5573 	    if (xi_event->flags & XISlaveAttached)
5574 		fputs(" XISlaveAttached", stderr);
5575 	    if (xi_event->flags & XISlaveDetached)
5576 		fputs(" XISlaveDetached", stderr);
5577 	    if (xi_event->flags & XIDeviceEnabled)
5578 		fputs(" XIDeviceEnabled", stderr);
5579 	    if (xi_event->flags & XIDeviceDisabled)
5580 		fputs(" XIDeviceDisabled", stderr);
5581 	    if (!xi_event->flags)
5582 		fputs("no flags(?)", stderr);
5583 	    fputc('\n', stderr);
5584 	}
5585 
5586 	if (!(xi_event->flags & (XIDeviceEnabled | XIDeviceDisabled)))
5587 	    return;
5588 
5589 	inf_end = xi_event->info + (xi_event->num_info - 1);
5590 	for (infp = xi_event->info; infp <= inf_end; ++infp) {
5591 	    struct xi2_slave *sp;
5592 
5593 	    if (infp->use != XISlavePointer && infp->use != XIFloatingSlave)
5594 		continue;
5595 
5596 	    for (sp = xi2_slaves;; sp = sp->next) {
5597 		if (sp == NULL) {
5598 		    if (infp->enabled)
5599 			TRACE_EVENTS((stderr,
5600 			  "  Ignoring enable request for %d->%d (not found)",
5601 			  infp->attachment, infp->deviceid));
5602 		    break;
5603 		}
5604 		if (sp->id == infp->deviceid) {
5605 		    if (infp->enabled != sp->enabled) {
5606 			TRACE_EVENTS((stderr, "  %s %d->%d",
5607 			  infp->enabled ? "Enabling" : "Disabling",
5608 			  infp->attachment, infp->deviceid));
5609 			sp->enabled = infp->enabled;
5610 			if (!sp->enabled && xi2_current->slave == sp) {
5611 			    TRACE_EVENTS((stderr,
5612 				"  Removing from master device"));
5613 			    xi2_current->slave = &xi2_no_slave;
5614 			}
5615 		    }
5616 		    break;
5617 		}
5618 	    }
5619 	}
5620 }
5621 
5622 #endif /* HAVE_XI21 */
5623 
5624 
5625 /*
5626  *	Since redrawing the screen is (potentially) a slow task, xdvi checks
5627  *	for incoming events while this is occurring.  It does not register
5628  *	a work proc that draws and returns every so often, as the toolkit
5629  *	documentation suggests.  Instead, it checks for events periodically
5630  *	(or not, if SIGPOLL can be used instead) and processes them in
5631  *	a subroutine called by the page drawing routine.  This routine (below)
5632  *	checks to see if anything has happened and processes those events and
5633  *	signals.  (Or, if it is called when there is no redrawing that needs
5634  *	to be done, it blocks until something happens.)
5635  *
5636  *	Ultimately, the goal is to have this be the only place in xdvi where
5637  *	blocking occurs.
5638  *
5639  *	The argument to this function should be a mask of event types (EV_*)
5640  *	indicating which event types should cause read_events to return instead
5641  *	of waiting for more events.  This function will always process all
5642  *	pending events and signals before returning.
5643  *	The return value is the value of globals.ev.flags.
5644  */
5645 
5646 unsigned int
read_events(unsigned int ret_mask)5647 read_events(unsigned int ret_mask)
5648 {
5649     XEvent event;
5650 
5651 #if !HAVE_POLL
5652     if (numfds == 0)
5653 	numfds = ConnectionNumber(DISP) + 1;
5654 #endif
5655 
5656     if (globals.debug & DBG_EVENT)
5657 	fprintf(stderr, "%s:%d: read_events %u\n", __FILE__, __LINE__, ret_mask);
5658     for (;;) {
5659 	globals.ev.ctr = event_freq;
5660 	/*
5661 	 * The above line clears the flag indicating that an event is
5662 	 * pending.  So if an event comes in right now, the flag will be
5663 	 * set again needlessly, but we just end up making an extra call.
5664 	 * Also, be careful about destroying the magnifying glass while
5665 	 * drawing on it.
5666 	 */
5667 
5668 #if !FLAKY_SIGPOLL
5669 
5670 	if (event_freq < 0) {	/* if SIGPOLL works */
5671 	    if (!XtPending()) {
5672 		sigset_t oldsig;
5673 
5674 		(void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig);
5675 		for (;;) {
5676 #ifdef SHOW_SIG_FLAGS
5677 		    /* this gives HUGE output ... */
5678 		    if (globals.debug & DBG_EVENT)
5679 			fprintf(stderr, "%s:%d: sig_flags = %d\n",
5680 				__FILE__, __LINE__, sig_flags);
5681 #endif
5682 		    while (sig_flags) {
5683 			flags_to_sigproc[sig_flags]();
5684 		    }
5685 
5686 		    if (XtPending())
5687 			break;
5688 
5689 		    if (globals.ev.flags & ret_mask) {
5690 			(void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL);
5691 			return globals.ev.flags;
5692 		    }
5693 		    (void) sigsuspend(&oldsig);
5694 		}
5695 		(void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL);
5696 	    }
5697 	}
5698 	else
5699 
5700 #endif /* not FLAKY_SIGPOLL */
5701 
5702 	{
5703 	    for (;;) {
5704 		struct xio	*ip;
5705 
5706 		if (globals.debug & DBG_EVENT)
5707 		    fprintf(stderr, "%s:%d: (flaky) sig_flags = %d\n",
5708 			    __FILE__, __LINE__, sig_flags);
5709 		while (sig_flags) {
5710 		    sigset_t oldsig;
5711 
5712 		    (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig);
5713 
5714 		    while (sig_flags) {
5715 			flags_to_sigproc[sig_flags]();
5716 		    }
5717 
5718 		    (void) sigprocmask(SIG_SETMASK, &oldsig,
5719 				       (sigset_t *) NULL);
5720 		}
5721 
5722 		if (XtPending())
5723 		    break;
5724 
5725 		if (globals.ev.flags & ret_mask)
5726 		    return globals.ev.flags;
5727 
5728 		/* If a SIGUSR1 signal comes right now, then it will wait
5729 		   until an X event or another SIGUSR1 signal arrives. */
5730 
5731 #if HAVE_POLL
5732 		if (globals.debug & DBG_EVENT)
5733 		    fprintf(stderr, "%s:%d: have_poll!\n",
5734 			    __FILE__, __LINE__);
5735 		if (io_dirty) {
5736 		    struct pollfd *fp;
5737 
5738 		    if (num_fds > max_fds) {
5739 			if (fds != NULL) free(fds);
5740 			fds = xmalloc(num_fds * sizeof *fds);
5741 			max_fds = num_fds;
5742 			fds->fd = ConnectionNumber(DISP);
5743 			fds->events = POLLIN;
5744 		    }
5745 		    fp = fds + 1;
5746 		    for (ip = iorecs; ip != NULL; ip = ip->next) {
5747 			fp->fd = ip->fd;
5748 			fp->events = ip->xio_events;
5749 			ip->pfd = fp;
5750 			++fp;
5751 		    }
5752 		    io_dirty = False;
5753 		}
5754 
5755 		for (;;) {
5756 		    if (poll(fds, num_fds, -1) >= 0) {
5757 			for (ip = iorecs; ip != NULL; ip = ip->next) {
5758 			    int revents = ip->pfd->revents;
5759 
5760 			    if (revents & POLLIN && ip->read_proc != NULL)
5761 				(ip->read_proc)(ip->fd, ip->data);
5762 			    if (revents & POLLOUT && ip->write_proc != NULL)
5763 				(ip->write_proc)(ip->fd, ip->data);
5764 			}
5765 			break;
5766 		    }
5767 
5768 		    if (errno == EINTR)
5769 			break;
5770 
5771 		    if (errno != EAGAIN) {
5772 			perror("xdvi: poll");
5773 			break;
5774 		    }
5775 		}
5776 #else /* HAVE_POLL */
5777 		if (globals.debug & DBG_EVENT)
5778 		    fprintf(stderr, "%s:%d: NOT have_poll!\n",
5779 			    __FILE__, __LINE__);
5780 		FD_ZERO(&readfds);
5781 		FD_ZERO(&writefds);
5782 		FD_SET(ConnectionNumber(DISP), &readfds);
5783 		for (ip = iorecs; ip != NULL; ip = ip->next) {
5784 		    if (ip->xio_events & XIO_IN)
5785 			FD_SET(ip->fd, &readfds);
5786 		    if (ip->xio_events & XIO_OUT)
5787 			FD_SET(ip->fd, &writefds);
5788 		}
5789 
5790 		for (;;) {
5791 		    if (select(numfds, &readfds, &writefds, (fd_set *) NULL,
5792 			       (struct timeval *) NULL) >= 0) {
5793 			for (ip = iorecs; ip != NULL; ip = ip->next) {
5794 			    if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL) {
5795 				if (globals.debug & DBG_EVENT)
5796 				    fprintf(stderr, "%s:%d: reading from %d\n",
5797 					    __FILE__, __LINE__, ip->fd);
5798 				(ip->read_proc)(ip->fd, ip->data);
5799 			    }
5800 			    if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL) {
5801 				if (globals.debug & DBG_EVENT)
5802 				    fprintf(stderr, "%s:%d: writing to %d\n",
5803 					    __FILE__, __LINE__, ip->fd);
5804 				(ip->write_proc)(ip->fd, ip->data);
5805 			    }
5806 			}
5807 			break;
5808 		    }
5809 
5810 		    if (errno == EINTR)
5811 			break;
5812 
5813 		    if (errno != EAGAIN) {
5814 			perror("xdvi: select");
5815 			break;
5816 		    }
5817 		}
5818 #endif /* HAVE_POLL */
5819 	    }
5820 	}
5821 
5822 	XtAppNextEvent(globals.app, &event);
5823 
5824 #ifdef MOTIF
5825 	if ((resource.expert_mode & XPRT_SHOW_TOOLBAR) != 0)
5826 	    TipAppHandle(globals.app, &event);
5827 #endif
5828 
5829 #if HAVE_XI21
5830 	if (event.xany.type == GenericEvent
5831 	  && event.xcookie.extension == xi2_opcode) {
5832 	    if (!XGetEventData(DISP, &event.xcookie)) {
5833 		TRACE_EVENTS((stderr,
5834 		  "Received XI2 event, of type %d, with no cookie",
5835 		  event.xcookie.evtype));
5836 	    }
5837 	    else {
5838 		switch (event.xcookie.evtype) {
5839 
5840 		    case XI_HierarchyChanged:
5841 			xi2_ev_hierchange(event.xcookie.data);
5842 			break;
5843 
5844 		    case XI_DeviceChanged:
5845 			xi2_ev_devchange(event.xcookie.data);
5846 			break;
5847 
5848 		    case XI_Enter:
5849 			xi2_ev_enter(event.xcookie.data);
5850 			break;
5851 
5852 		    case XI_Motion:
5853 			/* This needs to be filled in by the client */
5854 			((XIDeviceEvent *) event.xcookie.data)->serial
5855 			  = event.xany.serial;
5856 			xi2_ev_motion(event.xcookie.data);
5857 			break;
5858 
5859 		    default:
5860 			TRACE_EVENTS((stderr,
5861 			  "Received XI2 event of unknown type %d",
5862 			  event.xcookie.evtype));
5863 
5864 		}
5865 		XFreeEventData(DISP, &event.xcookie);
5866 	    }
5867 	    continue;
5868 	}
5869 #endif /* HAVE_XI21 */
5870 
5871 	if (resized)
5872 	    get_geom();
5873 
5874 	if (event.xany.window == magnifier.win && event.type == Expose) {
5875 	    handle_expose((Widget) NULL, (XtPointer)&magnifier, &event,
5876 			  (Boolean *) NULL);
5877 	    continue;
5878 	}
5879 	else if (globals.broken_motif_event_handling &&
5880 		 (globals.cursor.flags & (CURSOR_RULER | CURSOR_TEXT))) {
5881 	    /* In this case, Act_motion() and Act_release() are not called properly
5882 	     * for updating the ruler/text selection (it works with the magnifier though),
5883 	     * so we need to invoke them ourselves here: */
5884 	    if (event.type == MotionNotify)
5885 		Act_motion(NULL, &event, NULL, NULL);
5886 	    else if (event.type == ButtonRelease)
5887 		Act_release(NULL, &event, NULL, NULL);
5888 	}
5889 
5890 #ifdef MOTIF
5891 	if (XtIsRealized(globals.widgets.top_level)
5892 	    && event.xany.window == XtWindow(globals.widgets.clip_widget)
5893 	    && event.type == KeyPress) { /* workaround for #610206 */
5894 	    motif_translations_hack();
5895 	}
5896 #else
5897 	if (resource.expert_mode & XPRT_SHOW_BUTTONS)
5898 	    SubMenuHandleEvent(globals.app, &event);
5899 #endif
5900 	XtDispatchEvent(&event);
5901     }
5902 }
5903 
5904 
5905 /*
5906  * Higher-level routines for managing events.
5907  */
5908 
5909 static void
can_exposures(struct WindowRec * windowrec)5910 can_exposures(struct WindowRec *windowrec)
5911 {
5912     windowrec->min_x = windowrec->min_y = MAXDIM;
5913     windowrec->max_x = windowrec->max_y = 0;
5914 }
5915 
5916 void
redraw(struct WindowRec * windowrec)5917 redraw(struct WindowRec *windowrec)
5918 {
5919     currwin = *windowrec;
5920     globals.win_expose.min_x = currwin.min_x + currwin.base_x;
5921     globals.win_expose.min_y = currwin.min_y + currwin.base_y;
5922     globals.win_expose.max_x = currwin.max_x + currwin.base_x;
5923     globals.win_expose.max_y = currwin.max_y + currwin.base_y;
5924     can_exposures(windowrec);
5925 
5926     /* fix for bug #619070 - the complicated flags (and do_update_property)
5927        are needed to avoid too many updates at exposures, especially for
5928        a window of another xdvi instance when the magnifier intersects with
5929        that window.
5930     */
5931     if (have_src_specials && do_update_property
5932 	&& globals.win_expose.min_x != 1
5933 	&& globals.win_expose.max_y - globals.win_expose.min_y != 1
5934 	&& currwin.base_x == 0 && currwin.base_y == 0) {
5935 	update_window_property(XtWindow(globals.widgets.top_level), True);
5936     }
5937 
5938     TRACE_EVENTS((stderr, "Redraw %d x %d at (%d, %d) (base=%d,%d)",
5939 		  globals.win_expose.max_x - globals.win_expose.min_x,
5940 		  globals.win_expose.max_y - globals.win_expose.min_y,
5941 		  globals.win_expose.min_x, globals.win_expose.min_y,
5942 		  currwin.base_x, currwin.base_y));
5943 
5944     /* can't use ev_cursor here, since the event loop might not see this change quick enough */
5945     if (!(globals.ev.flags & EV_CURSOR)) {
5946 	TRACE_EVENTS((stderr, "Cursor: %ld", globals.cursor.flags));
5947 	if (!(globals.cursor.flags & (CURSOR_MAG | CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A))) {
5948 	    if (resource.mouse_mode == MOUSE_MODE3)
5949 		XDefineCursor(DISP, CURSORWIN, globals.cursor.mode3);
5950 	    else
5951 		XDefineCursor(DISP, CURSORWIN, globals.cursor.wait);
5952 	    XFlush(DISP);
5953 	}
5954 	globals.ev.flags |= EV_CURSOR;
5955     }
5956 
5957     /* No longer needed since windows are correctly transient now */
5958     /*      raise_message_windows(); */
5959     raise_file_selector();
5960     draw_page();
5961     globals.warn_spec_now = False;
5962 }
5963 
5964 
5965 void
redraw_page(void)5966 redraw_page(void)
5967 {
5968 #if COLOR
5969     const struct rgb *rgbp;
5970 #endif
5971     TRACE_FILES((stderr, "Redraw page on %p", (void *)globals.dvi_file.bak_fp));
5972 
5973     if (globals.dvi_file.bak_fp == NULL)
5974 	return;
5975 
5976     if (scanned_page < current_page) {
5977 	TRACE_FILES((stderr, "redraw_page: scanned_page = %d, current_page = %d, prescanning %p\n",
5978 		     scanned_page, current_page, (void *)globals.dvi_file.bak_fp));
5979 
5980 	prescan(globals.dvi_file.bak_fp);
5981 
5982 	if (globals.ev.flags & EV_GE_NEWPAGE) {	/* if we need to re-prescan */
5983 	    return;
5984 	}
5985     }
5986 
5987     TRACE_FILES((stderr, "redraw_page: current_page = %d", current_page));
5988     if (pageinfo_get_window_width(current_page) != globals.page.unshrunk_w
5989 	|| pageinfo_get_window_height(current_page) != globals.page.unshrunk_h) {
5990 	TRACE_FILES((stderr, "NEW SIZE: %dx%d",
5991 		     pageinfo_get_window_width(current_page), pageinfo_get_window_height(current_page)));
5992 	init_page();
5993 	reconfig();
5994     }
5995 
5996     /* We can't call home() without proper unshrunk_page_*, which requires
5997      * prescan(), which can't be done from within read_events() */
5998 
5999     /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6000        There's some complicated interaction with Postscript specials
6001        here: if home_action check comes before the gs stuff, psp.drawfile
6002        might not get initialized correctly, resulting in empty PS figures
6003        (bounding box instead of figure). This is different in xdvi, due to
6004        different handling of the home_action stuff, but at the moment I can't
6005        remember the reason for this ...
6006        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
6007     */
6008 #if PS_GS
6009     if (gs_postpone_prescan) {
6010 	if (!setjmp(globals.ev.canit)) {
6011 	    gs_resume_prescan();
6012 	}
6013 	else
6014 	    return;
6015     }
6016 #endif
6017     if (home_action != NULL) {
6018 	home_action(False);
6019 	home_action = NULL;
6020 	/* This discards the expose event generated by home()
6021 	   (1 for each page) */
6022 	if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) {
6023 	    return;
6024 	}
6025 
6026 	can_exposures(&mane);
6027     }
6028 
6029 #if COLOR
6030     rgbp = &bg_initial;
6031     if (page_colors.stack != NULL) {
6032 	ASSERT(current_page < (int)page_colors.size, "page_colors.size too small");
6033 	rgbp = &page_colors.stack[current_page].bg;
6034     }
6035 
6036     /* Set background color */
6037     if (bg_current == NULL
6038 	|| rgbp->r != bg_current->color.r
6039 	|| rgbp->g != bg_current->color.g
6040 	|| rgbp->b != bg_current->color.b) {
6041 	struct bgrec **bgpp;
6042 
6043 	for (bgpp = &bg_head;;) {
6044 	    bg_current = *bgpp;
6045 	    if (bg_current == NULL) {	/* if bg is not in list */
6046 		bg_current = *bgpp = xmalloc(sizeof *bg_current);
6047 		bg_current->next = NULL;
6048 		bg_current->color = *rgbp;
6049 		bg_current->fg_head = NULL;
6050 		bg_current->pixel_good = False;
6051 		break;
6052 	    }
6053 	    if (bg_current->color.r == rgbp->r
6054 		&& bg_current->color.g == rgbp->g
6055 		&& bg_current->color.b == rgbp->b)
6056 		break;
6057 	    bgpp = &bg_current->next;
6058 	}
6059 	fg_current = NULL;	/* force change of foreground color */
6060 	/* globals.gc.high is only used in XDrawRectangle, so its background color
6061 	   doesn't need to be changed.  */
6062 	if (globals.debug & DBG_DVI)
6063 	    printf("Changing background color to %5d %5d %5d\n",
6064 		   bg_current->color.r, bg_current->color.g,
6065 		   bg_current->color.b);
6066 
6067 	if (!bg_current->pixel_good) {
6068 	    bg_current->pixel = alloc_color(&bg_current->color,
6069 					    color_data[1].pixel);
6070 	    bg_current->pixel_good = True;
6071 	}
6072 	XSetWindowBackground(DISP, mane.win, bg_current->pixel);
6073 #if MOTIF && !FIXED_FLUSHING_PAGING
6074 	XtVaSetValues(XtParent(globals.widgets.draw_widget), XtNbackground, bg_current->pixel, NULL);
6075 #endif
6076 	/* 	XSetWindowBackground(DISP, mane.win, bg_current->pixel); */
6077 	/*  	XClearWindow(DISP, mane.win); */
6078 #if 0 /* don't recolor the cursor - gives too low contrast on color backgrounds,
6079 	 and bad appearance when part of the background is white and cursor mask
6080 	 is colored */
6081 	{
6082 	    XColor	bg_Color;
6083 	    bg_Color.pixel = bg_current->pixel;
6084 	    XQueryColor(DISP, G_colormap, &bg_Color);
6085 	    XRecolorCursor(DISP, globals.cursor.ready, &globals.cr_color, &bg_Color);
6086 	    XRecolorCursor(DISP, globals.cursor.wait, &globals.cr_color, &bg_Color);
6087 	}
6088 #endif
6089     }
6090 #endif /* COLOR */
6091 
6092     if (!globals.pausing.flag) {
6093 	XClearWindow(DISP, mane.win);
6094     }
6095 
6096     if (G_backing_store != NotUseful) {
6097 	mane.min_x = mane.min_y = 0;
6098 	mane.max_x = globals.page.w;
6099 	mane.max_y = globals.page.h;
6100     }
6101     else {
6102 	get_xy();
6103 	mane.min_x = -m_window_x;
6104 	mane.max_x = -m_window_x + mane.width;
6105 	mane.min_y = -m_window_y;
6106 	mane.max_y = -m_window_y + mane.height;
6107     }
6108 
6109     /*      update_TOC(); */
6110     redraw(&mane);
6111 }
6112 
6113 void
do_pages(void)6114 do_pages(void)
6115 {
6116     if (globals.debug & DBG_BATCH) {
6117 
6118 	(void)read_events(EV_GT_IDLE);
6119 	for (current_page = 0; current_page < total_pages; ++current_page) {
6120 	    if (resource.keep_flag) {
6121 		home_action = NULL;
6122 	    }
6123 	    else {
6124 		home_action = home;
6125 	    }
6126 	    globals.warn_spec_now = resource.warn_spec;
6127 #if PS_GS
6128 	    for (;;) {
6129 		redraw_page();
6130 		(void) read_events(EV_NOWAIT);
6131 		if (!(globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD)))
6132 		    break;
6133 		globals.ev.flags = EV_IDLE;
6134 	    }
6135 #else
6136 	    redraw_page();
6137 #endif
6138 	}
6139 	xdvi_exit(EXIT_SUCCESS);
6140     }
6141     else {
6142 	for (;;) {	/* normal operation */
6143 	    (void) read_events(EV_GT_IDLE);
6144  	    TRACE_EVENTS((stderr, "globals.ev.flags: %d; ev_newpage: %d, ev_newdoc: %d, ev_reload: %d\n",
6145 			  globals.ev.flags, EV_NEWPAGE, EV_NEWDOC, EV_RELOAD));
6146 	    /* NOTE: reloading must be checked first! */
6147 	    if (globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD | EV_PS_TOGGLE)) {
6148 		TRACE_EVENTS((stderr, "EV_NEWPAGE | ..."));
6149 		globals.ev.flags &= ~(EV_NEWPAGE | EV_EXPOSE | EV_PS_TOGGLE);
6150 		if (globals.ev.flags & EV_RELOAD) {
6151 		    dviErrFlagT errflag;
6152 
6153 		    globals.ev.flags &= ~EV_RELOAD;
6154 		    if (load_dvi_file(
6155 #if !DELAYED_MKTEXPK
6156 				      True,
6157 #endif
6158 				      &errflag)) {
6159 #if PS
6160 			ps_clear_cache();
6161 #if PS_GS
6162 			if (resource.gs_alpha) {
6163 			    /* restart gs so that user has a method for fixing GS artifacts with gs_alpha
6164 			       by using `reload' (see also GS_PIXMAP_CLEARING_HACK) */
6165 			    ps_destroy();
6166 			}
6167 #endif
6168 #endif
6169 			statusline_info(STATUS_SHORT, "File reloaded.");
6170 		    }
6171 		    /* 		    else { */
6172 		    /* 			statusline_info(STATUS_MEDIUM, "File corrupted, not reloading."); */
6173 		    /* 		    } */
6174 		}
6175 		if (globals.ev.flags & EV_NEWDOC) {
6176 		    dviErrFlagT errflag;
6177 		    TRACE_EVENTS((stderr, "EV_NEWDOC!"));
6178 		    /*  		    fprintf(stderr, "newdoc!\n"); */
6179 		    TRACE_FILES((stderr, "current page: %d", current_page));
6180 		    /*  		    file_history_set_page(current_page); */
6181 		    globals.ev.flags &= ~EV_NEWDOC;
6182 		    if (load_dvi_file(
6183 #if !DELAYED_MKTEXPK
6184 				      True,
6185 #endif
6186 				      &errflag)) {
6187 			statusline_append(STATUS_SHORT, "Opened ", "Opened \"%s\"", globals.dvi_name);
6188 			/* 			statusline_info(STATUS_SHORT, "Opened \"%s\"", globals.dvi_name); */
6189 			TRACE_FILES((stderr, "Adding to history: |%s|\n", globals.dvi_name));
6190 			if (file_history_push(globals.dvi_name)) { /* it's a new file, add to history */
6191 			    TRACE_FILES((stderr, "New entry!"));
6192 			    filehist_menu_add_entry(globals.dvi_name);
6193 			}
6194 			else { /* only need to move existing elements to new positions */
6195 			    TRACE_FILES((stderr, "Existing entry!\n"));
6196 			    filehist_menu_refresh();
6197 			}
6198 		    }
6199 		}
6200 
6201 		can_exposures(&mane);
6202 		can_exposures(&magnifier);
6203 
6204 #if PS && PS_GS && GS_PIXMAP_CLEARING_HACK
6205 		if (had_ps_specials && !MAGNIFIER_ACTIVE) {
6206 		    erasepage_gs();
6207 		    had_ps_specials = False;
6208 		}
6209 #endif /* PS && PS_GS && GS_PIXMAP_CLEARING_HACK */
6210 
6211 		if (globals.dvi_file.bak_fp != NULL) {
6212 		    TRACE_EVENTS((stderr, "redraw_page()"));
6213 		    redraw_page();
6214 		}
6215 		else {
6216 		    TRACE_EVENTS((stderr, "dvi_file_changed()"));
6217 		    (void)dvi_file_changed();
6218 		}
6219 	    }
6220 	    else if (globals.ev.flags & EV_PAGEHIST_GOTO_PAGE) {
6221 		int pageno;
6222 		globals.ev.flags &= ~EV_PAGEHIST_GOTO_PAGE;
6223 		pageno = check_goto_page(page_history_get_page(), False);
6224 		goto_page(pageno, resource.keep_flag ? NULL : home, False);
6225 		TRACE_FILES((stderr, "got page: %d", pageno));
6226 	    }
6227 	    else if (globals.ev.flags & EV_FILEHIST_GOTO_PAGE) {
6228 		int pageno;
6229 		globals.ev.flags &= ~EV_FILEHIST_GOTO_PAGE;
6230 		pageno = check_goto_page(file_history_get_page(), True);
6231 		goto_page(pageno, resource.keep_flag ? NULL : home, False);
6232 		TRACE_FILES((stderr, "got page: %d", pageno));
6233 	    }
6234 	    else if (globals.ev.flags & EV_PAGEHIST_INSERT) {
6235 		globals.ev.flags &= ~EV_PAGEHIST_INSERT;
6236 		page_history_insert(current_page);
6237 	    }
6238 	    else if (globals.ev.flags & EV_FIND_CANCEL) {
6239 		/* NOTE: This must be done before checking for expose() */
6240 		globals.ev.flags &= ~EV_FIND_CANCEL;
6241 	    }
6242 	    else if (globals.ev.flags & EV_ANCHOR) {
6243 		/*
6244 		 * Similar to forward search: search for a htex anchor.
6245 		 * This needs to come before the next case which does the redraw_page(),
6246 		 * otherwise anchors for the current page might not be drawn at all:
6247 		 * anchor_search() sets the info later used by htex_draw_anchormarkers(),
6248 		 * which is invoked by redraw_page().
6249 		 */
6250 
6251 		/* switch off the link cursor */
6252 		globals.cursor.flags &= ~CURSOR_LINK;
6253 
6254 		if (dvi_file_changed())
6255 		    continue;
6256 
6257 		anchor_search(g_anchor_pos);
6258 
6259 		/* added, otherwise anchors on same page may not be drawn */
6260 		/* NOTE: This caused a crash when clicking on link "Langer" on p3 of diss.dvi
6261 		   since the color stack wasn't allocated for the target pages. Removed for the
6262 		   time being, since I can't reproduce the problem for which it was introduced.
6263 		   CVS commit message was:
6264 		   "fix anchor drawing for other window"
6265 		*/
6266 		/* redraw(&mane);  */
6267 
6268 		globals.ev.flags &= ~EV_ANCHOR;
6269 	    }
6270 	    else if (globals.ev.flags & EV_SRC) {
6271 		/*
6272 		 * Source special operations are deferred to here because
6273 		 * they call geom_scan(), which may call define_font(),
6274 		 * which may call makefont(), which may call read_events()
6275 		 * recursively.
6276 		 */
6277 		if (globals.src.fwd_string != NULL) {
6278 		    const char *s = globals.src.fwd_string;
6279 
6280 		    if (dvi_file_changed())
6281 			continue;
6282 
6283 		    source_forward_search(s);
6284 		    globals.ev.flags &= ~EV_SRC;
6285 		    globals.src.fwd_string = NULL;
6286 
6287 		    /* de-iconify window if needed, and raise it */
6288 		    XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
6289 		    raise_message_windows();
6290 		}
6291 		else if (source_reverse_x != -1) {
6292 		    if (dvi_file_changed())
6293 			continue;
6294 
6295 		    source_reverse_search(source_reverse_x, source_reverse_y, True);
6296 		    globals.ev.flags &= ~EV_SRC;
6297 		}
6298 		else {
6299 		    source_special_show(source_show_all);
6300 		    globals.ev.flags &= ~EV_SRC;
6301 		}
6302 	    }
6303 	    /* support for `-findstring' */
6304 	    else if (globals.ev.flags & EV_FIND) {
6305 		if (dvi_file_changed())
6306 		    continue;
6307 
6308 		if (resource.find_string != NULL) { /* not first call */
6309 		    dvi_find_string(resource.find_string, False);
6310 		    resource.find_string = NULL;
6311 		}
6312 		else { /* actually should never arrive here?? */
6313 		    dvi_find_string(NULL, True);
6314 		}
6315 		globals.ev.flags &= ~EV_FIND;
6316 	    }
6317 	    else if (globals.ev.flags & EV_MAG_MOVE) {
6318 		MYTRACE((stderr, "moving mag!\n"));
6319 		move_magnifier();
6320 	    }
6321 	    else if (globals.ev.flags & EV_EXPOSE) {
6322 		if (magnifier.min_x < MAXDIM) {
6323 		    /*  		    fprintf(stderr, "magnifier < maxdim!\n"); */
6324 		    if (mane.min_x >= MAXDIM) {
6325 			/*  			fprintf(stderr, "mane >= maxdim!\n"); */
6326 			globals.ev.flags &= ~EV_EXPOSE;
6327 		    }
6328 		    redraw(&magnifier);
6329 		}
6330 		else {
6331 		    /* see comment in mag.c */
6332 		    globals.ev.flags &= ~EV_EXPOSE;
6333 		    if (mane.min_x < MAXDIM) {
6334 			redraw(&mane);
6335 		    }
6336 		}
6337 	    }
6338 	    else if (globals.ev.flags & EV_CURSOR) {
6339 		/*
6340 		 * This code eliminates unnecessary calls to XDefineCursor,
6341 		 * since this is a slow operation on some hardware (e.g., S3
6342 		 * chips).
6343 		 */
6344 		XSync(DISP, False);
6345 		if (!XtPending()) {
6346 		    Cursor curr;
6347 
6348 		    if (globals.cursor.flags & CURSOR_DRAG_V)
6349 			curr = globals.cursor.drag_v;
6350 		    else if (globals.cursor.flags & CURSOR_DRAG_H)
6351 			curr = globals.cursor.drag_h;
6352 		    else if (globals.cursor.flags & CURSOR_DRAG_A)
6353 			curr = globals.cursor.drag_a;
6354 		    else if (resource.mouse_mode == MOUSE_MODE3)
6355 			curr = globals.cursor.mode3;
6356 		    else if (resource.mouse_mode == MOUSE_MODE2)
6357 			curr = globals.cursor.mode2;
6358 		    else if (globals.cursor.flags & CURSOR_LINK)
6359 			curr = globals.cursor.link;
6360 		    else if (globals.cursor.flags & CURSOR_MAG)
6361 			curr = globals.cursor.empty;
6362 		    else if (globals.cursor.flags & CURSOR_CORRUPTED)
6363 			curr = globals.cursor.corrupted;
6364 		    else if (globals.pausing.flag)
6365 			curr = globals.cursor.pause;
6366 		    else
6367 			curr = globals.cursor.mode1;
6368 		    XDefineCursor(DISP, CURSORWIN, curr);
6369 		    globals.ev.flags &= ~EV_CURSOR;
6370 		}
6371 	    }
6372 	    XFlush(DISP);
6373 	}
6374     }
6375 }
6376 
6377 static void
Act_unpause_or_next(Widget w,XEvent * event,String * params,Cardinal * num_params)6378 Act_unpause_or_next(Widget w, XEvent *event,
6379 		    String *params, Cardinal *num_params)
6380 {
6381     if (globals.pausing.flag) {
6382         globals.pausing.num++;
6383 	if (globals.pausing.num_save)
6384 	    globals.pausing.num_save[current_page] = globals.pausing.num;
6385 	redraw_page();
6386     }
6387     else {
6388 	Act_down_or_next(w, event, params, num_params);
6389     }
6390 }
6391 
6392 /*
6393  * timer callback for watching the DVI file.
6394  */
6395 void
watch_file_cb(XtPointer client_data,XtIntervalId * id)6396 watch_file_cb(XtPointer client_data, XtIntervalId *id)
6397 {
6398     static XtIntervalId timer = 0;
6399 
6400     UNUSED(client_data);
6401     UNUSED(id);
6402 
6403     if (resource.watch_file > 0.0) {
6404 	unsigned long watch_time_ms;
6405 
6406 	(void)dvi_file_changed();
6407 
6408 	if (timer) {
6409 	    XtRemoveTimeOut(timer);
6410 	    timer = (XtIntervalId)(XtIntervalId)0;
6411 	}
6412 
6413 	watch_time_ms = (unsigned long)(resource.watch_file * 1000);
6414 	timer = XtAppAddTimeOut(globals.app, watch_time_ms, watch_file_cb, (XtPointer)NULL);
6415     }
6416 }
6417 
6418 void
Act_scroll_list_up(Widget w,XEvent * event,String * params,Cardinal * num_params)6419 Act_scroll_list_up(Widget w, XEvent *event, String *params,
6420 	Cardinal *num_params)
6421 {
6422     UNUSED(w);
6423     UNUSED(event);
6424     UNUSED(params);
6425     UNUSED(num_params);
6426 
6427     if (current_page == 0) {
6428 	xdvi_bell();
6429 	/* statusline_info(STATUS_SHORT, "First page of DVI file"); */
6430 	return;
6431     }
6432     goto_page(check_goto_page(current_page - 1, True),
6433       resource.keep_flag ? NULL : home, False);
6434     search_signal_page_changed();
6435     statusline_erase("Page history:");
6436 }
6437 
6438 void
Act_scroll_list_down(Widget w,XEvent * event,String * params,Cardinal * num_params)6439 Act_scroll_list_down(Widget w, XEvent *event, String *params,
6440 	Cardinal *num_params)
6441 {
6442     UNUSED(w);
6443     UNUSED(event);
6444     UNUSED(params);
6445     UNUSED(num_params);
6446 
6447     if (current_page >= total_pages - 1) {
6448 	xdvi_bell();
6449 	/* statusline_info(STATUS_SHORT, "Last page of DVI file"); */
6450 	return;
6451     }
6452     goto_page(check_goto_page(current_page + 1, True),
6453       resource.keep_flag ? NULL : home, False);
6454     search_signal_page_changed();
6455     statusline_erase("Page history:");
6456 }
6457 
Act_pagehistory_back(Widget w,XEvent * event,String * params,Cardinal * num_params)6458 void Act_pagehistory_back(Widget w, XEvent *event, String *params, Cardinal *num_params)
6459 {
6460     int arg;
6461 
6462     UNUSED(w);
6463     UNUSED(event);
6464 
6465     if (!get_int_arg(params, num_params, &arg)) {
6466 	arg = 1;
6467     }
6468     page_history_move(-arg);
6469 }
6470 
Act_pagehistory_forward(Widget w,XEvent * event,String * params,Cardinal * num_params)6471 void Act_pagehistory_forward(Widget w, XEvent *event, String *params, Cardinal *num_params)
6472 {
6473     int arg;
6474 
6475     UNUSED(w);
6476     UNUSED(event);
6477 
6478     if (!get_int_arg(params, num_params, &arg)) {
6479 	arg = 1;
6480     }
6481     page_history_move(arg);
6482 }
6483 
Act_pagehistory_clear(Widget w,XEvent * event,String * params,Cardinal * num_params)6484 void Act_pagehistory_clear(Widget w, XEvent *event, String *params, Cardinal *num_params)
6485 {
6486     UNUSED(w);
6487     UNUSED(event);
6488     UNUSED(params);
6489     UNUSED(num_params);
6490 
6491     page_history_clear();
6492 }
6493 
Act_pagehistory_delete_backward(Widget w,XEvent * event,String * params,Cardinal * num_params)6494 void Act_pagehistory_delete_backward(Widget w, XEvent *event, String *params, Cardinal *num_params)
6495 {
6496     int arg;
6497 
6498     UNUSED(w);
6499     UNUSED(event);
6500 
6501     if (!get_int_arg(params, num_params, &arg)) {
6502 	arg = 1;
6503     }
6504     page_history_delete(-arg);
6505 }
6506 
Act_pagehistory_delete_forward(Widget w,XEvent * event,String * params,Cardinal * num_params)6507 void Act_pagehistory_delete_forward(Widget w, XEvent *event, String *params, Cardinal *num_params)
6508 {
6509     int arg;
6510 
6511     UNUSED(w);
6512     UNUSED(event);
6513 
6514     if (!get_int_arg(params, num_params, &arg)) {
6515 	arg = 1;
6516     }
6517     page_history_delete(arg);
6518 }
6519 
6520 #ifdef MOTIF
Act_prefs_dialog(Widget w,XEvent * event,String * params,Cardinal * num_params)6521 void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params)
6522 {
6523     int arg;
6524 
6525     UNUSED(w);
6526     UNUSED(event);
6527 
6528     if (!get_int_arg(params, num_params, &arg)) {
6529 	arg = -1;
6530     }
6531 
6532     popup_preferences_dialog(globals.widgets.top_level, arg);
6533 }
6534 #endif /* MOTIF */
6535 
6536