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