Back to index

tetex-bin  3.0
events.c
Go to the documentation of this file.
00001 /*======================================================================*\
00002 
00003 Copyright (c) 1990-2004  Paul Vojta and others
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining a copy
00006 of this software and associated documentation files (the "Software"), to
00007 deal in the Software without restriction, including without limitation the
00008 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00009 sell copies of the Software, and to permit persons to whom the Software is
00010 furnished to do so, subject to the following conditions:
00011 
00012 The above copyright notice and this permission notice shall be included in
00013 all copies or substantial portions of the Software.
00014 
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00018 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00019 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00020 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00021 OTHER DEALINGS IN THE SOFTWARE.
00022 
00023 NOTE:
00024        xdvi is based on prior work, as noted in the modification history
00025        in xdvi.c.
00026 
00027 \*========================================================================*/
00028 
00029 #include "xdvi-config.h"
00030 
00031 #ifdef STDC_HEADERS
00032 # include <unistd.h>
00033 # include <fcntl.h>
00034 #endif
00035 #include <signal.h>
00036 #include <sys/file.h>       /* this defines FASYNC */
00037 #ifdef HAVE_SYS_FCNTL_H
00038 # include <sys/fcntl.h>  /* Or this might define FASYNC */
00039 #endif
00040 #include <sys/ioctl.h>      /* this defines SIOCSPGRP and FIOASYNC */
00041 #include <sys/wait.h>       /* ignore HAVE_SYS_WAIT_H -- we always need WNOHANG */
00042 
00043 #include "xdvi.h" /* this includes Xlib and Xutil are already included */
00044 #include "xdvi-debug.h"
00045 
00046 
00047 #ifndef MOTIF
00048 #include <X11/Xaw/Form.h> /* for XtNresizable */
00049 #endif
00050 
00051 #include <setjmp.h>
00052 #include <sys/time.h>       /* for gettimeofday */
00053 
00054 #include <X11/IntrinsicP.h>
00055 
00056 #include <X11/Xatom.h>
00057 #include <X11/StringDefs.h>
00058 #include <X11/Shell.h>      /* needed for def. of XtNiconX(??) */
00059 
00060 #include "pagesel.h"
00061 #include "filehist.h"
00062 #include "special.h"
00063 #include "psgs.h"
00064 
00065 
00066 #include <errno.h>
00067 
00068 #include <ctype.h>
00069 
00070 #include "util.h"
00071 #include "x_util.h"
00072 #include "string-utils.h"
00073 #include "print-dialog.h"
00074 #include "search-dialog.h"
00075 #include "sfSelFile.h"
00076 #include "mag.h"
00077 #include "help-window.h"
00078 #include "message-window.h"
00079 #include "dvi-draw.h"
00080 #include "statusline.h"
00081 #include "hypertex.h"
00082 #include "dvi-init.h"
00083 #include "Tip.h"
00084 #include "browser.h"
00085 #include "search-internal.h"
00086 #include "my-snprintf.h"
00087 
00088 #include "events.h"
00089 #include "selection.h"
00090 #include "encodings.h"
00091 #include "pagehist.h"
00092 #include "xm_colorsel.h"
00093 #include "xm_toolbar.h"
00094 #include "xaw_menu.h"
00095 #include "xm_menu.h"
00096 #include "xm_prefs.h"
00097 
00098 #include "xm_prefs_appearance.h" /* for update_preferences_expert() */
00099 #include "xm_prefs_fonts.h"  /* for update_preferences_color() */
00100 #include "xm_prefs_page.h"   /* for update_preferences_shrink() */
00101 
00102 #ifdef X_NOT_STDC_ENV
00103 extern int errno;
00104 #endif /* X_NOT_STDC_ENV */
00105 
00106 /* Linux prefers O_ASYNC over FASYNC; SGI IRIX does the opposite.  */
00107 #if !defined(FASYNC) && defined(O_ASYNC)
00108 # define FASYNC      O_ASYNC
00109 #endif
00110 
00111 #if !defined(FLAKY_SIGPOLL) && !HAVE_STREAMS && !defined(FASYNC)
00112 # if !defined(SIOCSPGRP) || !defined(FIOASYNC)
00113 #  define FLAKY_SIGPOLL     1
00114 # endif
00115 #endif
00116 
00117 #ifndef FLAKY_SIGPOLL
00118 
00119 # ifndef SIGPOLL
00120 #  define SIGPOLL    SIGIO
00121 # endif
00122 
00123 # ifndef SA_RESTART
00124 #  define SA_RESTART 0
00125 # endif
00126 
00127 # if HAVE_STREAMS
00128 #  include <stropts.h>
00129 
00130 #  ifndef S_RDNORM
00131 #   define S_RDNORM S_INPUT
00132 #  endif
00133 
00134 #  ifndef S_RDBAND
00135 #   define S_RDBAND  0
00136 #  endif
00137 
00138 #  ifndef S_HANGUP
00139 #   define S_HANGUP  0
00140 #  endif
00141 
00142 #  ifndef S_WRNORM
00143 #   define S_WRNORM  S_OUTPUT
00144 #  endif
00145 # endif /* HAVE_STREAMS */
00146 
00147 #endif /* not FLAKY_SIGPOLL */
00148 
00149 #if HAVE_SIGACTION && !defined SA_RESETHAND
00150 # ifdef SA_ONESHOT
00151 #  define SA_RESETHAND SA_ONESHOT
00152 # else
00153 #  undef HAVE_SIGACTION         /* Needed for Mac OS X < 10.2 (9/2002) */
00154 # endif
00155 #endif
00156 
00157 #if HAVE_POLL
00158 # include <poll.h>
00159 #else
00160 # if HAVE_SYS_SELECT_H
00161 #  include <sys/select.h>
00162 # else
00163 #  if HAVE_SELECT_H
00164 #   include <select.h>
00165 #  endif
00166 # endif
00167 # define XIO_IN 1
00168 # define XIO_OUT 2
00169 #endif /* HAVE_POLL */
00170 
00171 static sigset_t all_signals;
00172 
00173 /*
00174  *     Interrupt system for receiving events.  The program sets a flag
00175  *     whenever an event comes in, so that at the proper time (i.e., when
00176  *     reading a new dvi item), we can check incoming events to see if we
00177  *     still want to go on printing this page.  This way, one can stop
00178  *     displaying a page if it is about to be erased anyway.  We try to read
00179  *     as many events as possible before doing anything and base the next
00180  *     action on all events read.
00181  *     Note that the Xlib and Xt routines are not reentrant, so the most we
00182  *     can do is set a flag in the interrupt routine and check it later.
00183  *     Also, sometimes the interrupts are not generated (some systems only
00184  *     guarantee that SIGIO is generated for terminal files, and on the system
00185  *     I use, the interrupts are not generated if I use "(xdvi foo &)" instead
00186  *     of "xdvi foo").  Therefore, there is also a mechanism to check the
00187  *     event queue every 70 drawing operations or so.  This mechanism is
00188  *     disabled if it turns out that the interrupts do work.
00189  *     For a fuller discussion of some of the above, see xlife in
00190  *     comp.sources.x.
00191  */
00192 
00193 /*
00194  *     Signal flags
00195  */
00196 
00197 /* This could be static volatile, but we want to avoid any optimizer bugs.  */
00198 VOLATILE unsigned int sig_flags    = 0;
00199 
00200 #define       SF_USR 1
00201 #define       SF_ALRM       2
00202 #define       SF_POLL       4
00203 #define       SF_CHLD       8
00204 #define       SF_TERM       16
00205 #define       SF_SEGV       32
00206 
00207 static void do_sigusr(void);
00208 static void do_sigalrm(void);
00209 static void do_sigpoll(void);
00210 static void do_sigchld(void);
00211 static void do_sigterm(void);
00212 static void do_sigsegv(void);
00213 
00214 /* these must be in the same order as SF_*.
00215    The higher flags have higher priority, since these are
00216    checked first when resolving flags_to_sigproc[sig_flags].
00217 
00218    Example: flag := SF_TERM | SF_CHLD = 24
00219    
00220    flags_to_sigproc[24] =>
00221        do_sigterm, which sets flag to 24 & ~SF_TERM == 8
00222    then, in next check:       
00223    flags_to_sigproc[8] =>
00224        do_sigchld, which sets flag 8 & ~SF_CHLD == 0.
00225  */
00226 #define       SP0    do_sigusr
00227 #define       SP1    do_sigalrm
00228 #define       SP2    do_sigpoll
00229 #define       SP3    do_sigchld
00230 #define       SP4    do_sigterm
00231 #define       SP5    do_sigsegv
00232 
00233 typedef       void   (*signalproc)(void);
00234 
00235 static const signalproc flags_to_sigproc[64] = {
00236     NULL,
00237     /* 1 */
00238     SP0,
00239     /* 2 - 3 */
00240     SP1, SP1,
00241     /* 4 - 7 */
00242     SP2, SP2, SP2, SP2,
00243     /* 8 - 15 */
00244     SP3, SP3, SP3, SP3, SP3, SP3, SP3, SP3,
00245     /* 16 - 31 */
00246     SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4,
00247     SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4,
00248     /* 32 - 63 */
00249     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
00250     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
00251     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
00252     SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5
00253 };
00254 
00255 #undef SP0
00256 #undef SP2
00257 #undef SP3
00258 #undef SP4
00259 #undef SP5
00260 
00261 /* file-static variables for prefix argument mechanism */
00262 static Boolean m_have_arg = False; /* flag whether we have a possible prefix arg */
00263 static int m_number = 0;        /* prefix arg value, without the sign */
00264 static int m_sign = 1;          /* is prefix arg negative? */
00265 
00266 
00267 /* to remember the scrollbar positions so that we can restore them
00268    when `keep position' is active and window is resized to full size
00269    (removing scrollbars) and then back (#810501) */
00270 static int m_x_scroll = 0, m_y_scroll = 0;
00271 
00272 static int source_reverse_x, source_reverse_y;
00273 static int source_show_all;
00274 
00275 /* globals for color stuff */
00276 Pixel plane_masks[4];
00277 XColor color_data[2];
00278 struct pagecolor_info page_colors = { 0, NULL };
00279 
00280 struct rgb *color_bottom;
00281 unsigned int color_bot_size;              /* number of entries */
00282 struct colorframe *rcs_top;
00283 struct rgb fg_initial;                    /* Initial fg (from command line) */
00284 struct rgb bg_initial;                    /* Initial bg */
00285 struct bgrec *bg_head = NULL;             /* head of list */
00286 struct bgrec *bg_current = NULL;   /* current bg value */
00287 struct fgrec *fg_current;          /* current fg value */
00288 struct fgrec *fg_active = NULL;           /* where the GCs are */
00289 Pixel *color_list;                 /* list of colors */
00290 unsigned int color_list_len = 0;   /* current len of list*/
00291 unsigned int color_list_max = 0;   /* allocated size */
00292 Boolean color_warned = False;
00293 /*
00294   FIXME: the following file-static flag is used to control whether
00295   a mouse-related Act_* should block further actions that have
00296   been registered for the same event.
00297   
00298   It is used for the Act_href_* routines.
00299   
00300   This used to be a
00301      longjmp(somewehre_in_the_event_loop);
00302   but that was pretty dreadful as well. Surely there must
00303   be some other (X(t)-specific?) way to do this?? (Apart from
00304   de-registering the action and re-registering it again.)
00305 */
00306 static Boolean m_block_mouse_event = False; 
00307 
00308 /* for calling it from mag.c */
00309 void
00310 block_next_mouse_event(void) {
00311     m_block_mouse_event = True;
00312 }
00313 
00314 Boolean
00315 block_this_mouse_event(void) {
00316     if (m_block_mouse_event == True) {
00317        m_block_mouse_event = False;
00318        return True;
00319     }
00320     return False;
00321 }
00322 
00323 
00324 void null_mouse(XEvent *event)
00325 {
00326     UNUSED(event);
00327 }
00328 
00329 mouse_proc mouse_motion = null_mouse;
00330 mouse_proc mouse_release = null_mouse;
00331 
00332 static void Act_digit(Widget, XEvent *, String *, Cardinal *);
00333 static void Act_find(Widget, XEvent *, String *, Cardinal *);
00334 static void Act_find_next(Widget, XEvent *, String *, Cardinal *);
00335 static void Act_minus(Widget, XEvent *, String *, Cardinal *);
00336 static void Act_quit(Widget, XEvent *, String *, Cardinal *);
00337 static void Act_quit_confirm(Widget, XEvent *, String *, Cardinal *);
00338 static void Act_print(Widget, XEvent *, String *, Cardinal *);
00339 static void Act_save(Widget, XEvent *, String *, Cardinal *);
00340 static void Act_help(Widget, XEvent *, String *, Cardinal *);
00341 static void Act_goto_page(Widget, XEvent *, String *, Cardinal *);
00342 static void Act_declare_page_number(Widget, XEvent *, String *, Cardinal *);
00343 static void Act_toggle_mark(Widget, XEvent *, String *, Cardinal *);
00344 static void Act_home(Widget, XEvent *, String *, Cardinal *);
00345 static void Act_home_or_top(Widget, XEvent *, String *, Cardinal *);
00346 static void Act_end_or_bottom(Widget, XEvent *, String *, Cardinal *);
00347 static void Act_center(Widget, XEvent *, String *, Cardinal *);
00348 static void Act_left(Widget, XEvent *, String *, Cardinal *);
00349 static void Act_right(Widget, XEvent *, String *, Cardinal *);
00350 static void Act_up(Widget, XEvent *, String *, Cardinal *);
00351 static void Act_down(Widget, XEvent *, String *, Cardinal *);
00352 static void Act_up_or_previous(Widget, XEvent *, String *, Cardinal *);
00353 static void Act_down_or_next(Widget, XEvent *, String *, Cardinal *);
00354 static void Act_set_margins(Widget, XEvent *, String *, Cardinal *);
00355 static void Act_show_display_attributes(Widget, XEvent *, String *, Cardinal *);
00356 static void Act_set_density(Widget, XEvent *, String *, Cardinal *);
00357 static void Act_change_density(Widget, XEvent *, String *, Cardinal *);
00358 static void Act_fullscreen(Widget, XEvent *, String *, Cardinal *);
00359 #ifdef GREY
00360 static void Act_set_greyscaling(Widget, XEvent *, String *, Cardinal *);
00361 #endif
00362 #if COLOR
00363 static void Act_set_color(Widget, XEvent *, String *, Cardinal *);
00364 #endif
00365 
00366 static void Act_htex_anchorinfo(Widget, XEvent *, String *, Cardinal *);
00367 
00368 static void Act_reread_dvi_file(Widget, XEvent *, String *, Cardinal *);
00369 static void Act_select_dvi_file(Widget, XEvent *, String *, Cardinal *);
00370 static void Act_discard_number(Widget, XEvent *, String *, Cardinal *);
00371 static void Act_drag(Widget, XEvent *, String *, Cardinal *);
00372 static void Act_wheel(Widget, XEvent *, String *, Cardinal *);
00373 static void Act_motion(Widget, XEvent *, String *, Cardinal *);
00374 static void Act_release(Widget, XEvent *, String *, Cardinal *);
00375 static void Act_source_special(Widget, XEvent *, String *, Cardinal *);
00376 static void Act_show_source_specials(Widget, XEvent *, String *, Cardinal *);
00377 static void Act_source_what_special(Widget, XEvent *, String *, Cardinal *);
00378 static void Act_unpause_or_next(Widget, XEvent *, String *, Cardinal *);
00379 static void Act_ruler_snap_origin(Widget w, XEvent *event, String *params, Cardinal *num_params);
00380 static void Act_text_mode(Widget w, XEvent *event, String *params, Cardinal *num_params);
00381 static void Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params);
00382 #ifdef MOTIF
00383 static void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params);
00384 #endif
00385 
00386 static XtActionsRec m_actions[] = {
00387     {"digit", Act_digit},
00388     {"switch-mode", Act_switch_mode},
00389     {"ruler-mode", Act_ruler_mode},
00390     {"text-mode", Act_text_mode},
00391     {"minus", Act_minus},
00392     {"recent-files", Act_recent_files},
00393     {"quit", Act_quit},
00394     {"quit-confirm", Act_quit_confirm},
00395     {"print", Act_print},
00396     {"save", Act_save},
00397     {"find", Act_find},
00398     {"find-next", Act_find_next},
00399     {"help", Act_help},
00400     {"goto-page", Act_goto_page},
00401     {"use-tex-pages", Act_use_tex_pages},
00402     {"forward-page", Act_forward_page},
00403     {"back-page", Act_back_page},
00404     {"toggle-mark", Act_toggle_mark},
00405     {"declare-page-number", Act_declare_page_number},
00406     {"home", Act_home},
00407     {"home-or-top", Act_home_or_top},
00408     {"end-or-bottom", Act_end_or_bottom},
00409     {"center", Act_center},
00410     {"set-keep-flag", Act_set_keep_flag},
00411     {"ruler-snap-origin", Act_ruler_snap_origin},
00412     {"left", Act_left},
00413     {"right", Act_right},
00414     {"up", Act_up},
00415     {"down", Act_down},
00416     {"up-or-previous", Act_up_or_previous},
00417     {"down-or-next", Act_down_or_next},
00418     {"set-margins", Act_set_margins},
00419     {"show-display-attributes", Act_show_display_attributes},
00420     {"set-shrink-factor", Act_set_shrink_factor},
00421     {"shrink-to-dpi", Act_shrink_to_dpi},
00422     {"set-density", Act_set_density},
00423     {"change-density", Act_change_density},
00424     {"fullscreen", Act_fullscreen},
00425 #ifdef GREY
00426     {"set-greyscaling", Act_set_greyscaling},
00427 #endif
00428 #if COLOR
00429     {"set-color", Act_set_color},
00430 #endif
00431 #ifdef PS
00432     {"set-ps", Act_set_ps},
00433 #endif
00434     {"htex-back", Act_htex_back},
00435     {"htex-forward", Act_htex_forward},
00436     {"htex-anchorinfo", Act_htex_anchorinfo},
00437 #ifdef PS_GS
00438     {"set-gs-alpha", Act_set_gs_alpha},
00439 #endif
00440     {"set-expert-mode", Act_set_expert_mode},
00441     {"reread-dvi-file", Act_reread_dvi_file},
00442     {"select-dvi-file", Act_select_dvi_file},
00443     {"discard-number", Act_discard_number},
00444     {"drag", Act_drag},
00445     {"wheel", Act_wheel},
00446     {"motion", Act_motion},
00447     {"release", Act_release},
00448     {"source-special", Act_source_special},
00449     {"show-source-specials", Act_show_source_specials},
00450     {"source-what-special", Act_source_what_special},
00451     {"unpause-or-next", Act_unpause_or_next},
00452 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
00453     {"set-papersize", Act_set_papersize},
00454     {"set-paper-landscape", Act_set_paper_landscape},
00455 #endif /* NEW_MENU_CREATION */
00456     {"load-url", Act_load_url},
00457     {"pagehistory-clear", Act_pagehistory_clear},
00458     {"pagehistory-back", Act_pagehistory_back},
00459     {"pagehistory-forward", Act_pagehistory_forward},
00460     {"pagehistory-delete-backward", Act_pagehistory_delete_backward},
00461     {"pagehistory-delete-forward", Act_pagehistory_delete_forward},    
00462 #ifdef MOTIF
00463     {"prefs-dialog", Act_prefs_dialog},    
00464 #endif
00465 };
00466 
00467 /*
00468  * Access to m_actions
00469  */
00470 int get_num_actions(void)
00471 {
00472     return XtNumber(m_actions);
00473 }
00474 
00475 XtActionsRec *get_actions(void)
00476 {
00477     return m_actions;
00478 }
00479 
00480 /*
00481  *     Data for buffered events.
00482  */
00483 
00484 #ifndef FLAKY_SIGPOLL
00485 static VOLATILE int event_freq = 70;
00486 #else
00487 #define       event_freq    70
00488 #endif
00489 
00490 static void can_exposures(struct WindowRec *windowrec);
00491 
00492 
00493 /*
00494  *     Set the flag so that termination occurs, via the above routine.
00495  *     This should be used in place of xdvi_exit() when there may be a
00496  *     non-killable process running (e.g., anytime within read_events()).
00497  */
00498 
00499 static void
00500 xdvi_normal_exit(void)
00501 {
00502     sig_flags |= SF_TERM;
00503 }
00504 
00505 static void
00506 xdvi_normal_exit_cb(XtPointer arg)
00507 {
00508     UNUSED(arg);
00509     sig_flags |= SF_TERM;
00510 }
00511 
00512 void
00513 xdvi_exit_callback(Widget w, XtPointer client_data, XtPointer call_data)
00514 {
00515     UNUSED(w);
00516     UNUSED(client_data);
00517     UNUSED(call_data);
00518 
00519     sig_flags |= SF_TERM;
00520 }
00521 
00522 /*
00523  * Event-handling routines.
00524  */
00525 
00526 
00527 void
00528 expose(struct WindowRec *windowrec,
00529        int x, int y,
00530        unsigned int w, unsigned int h)
00531 {
00532     if (windowrec->min_x > x)
00533        windowrec->min_x = x;
00534     if (windowrec->max_x < (int)(x + w))
00535        windowrec->max_x = x + w;
00536     if (windowrec->min_y > y)
00537        windowrec->min_y = y;
00538     if (windowrec->max_y < (int)(y + h))
00539        windowrec->max_y = y + h;
00540 
00541     globals.ev.flags |= EV_EXPOSE;
00542 }
00543 
00544 void
00545 clearexpose(struct WindowRec *windowrec,
00546            int x, int y,
00547            unsigned w, unsigned h)
00548 {
00549     XClearArea(DISP, windowrec->win, x, y, w, h, False);
00550     expose(windowrec, x, y, w, h);
00551 }
00552 
00553 /*
00554  *     Routines for X11 toolkit.
00555  */
00556 
00557 static Position window_x, window_y;
00558 static Arg arg_xy[] = {
00559     {XtNx, (XtArgVal) &window_x},
00560     {XtNy, (XtArgVal) &window_y},
00561 };
00562 
00563 #define       get_xy() XtGetValues(globals.widgets.draw_widget, arg_xy, XtNumber(arg_xy))
00564 
00565 
00566 static void
00567 warn_num_params(const char *act_name, String *params, int num_params, int max_params)
00568 {
00569     if (num_params > max_params) {
00570        XDVI_WARNING((stderr, "Too many parameters (%d) for action \"%s\", ignoring all after \"%s\"",
00571                     num_params, act_name, params[max_params - 1]));
00572     }
00573 }
00574 
00575 
00576 struct xdvi_action *
00577 compile_action(const char *str)
00578 {
00579     const char *p, *p1, *p2;
00580     XtActionsRec *actp;
00581     struct xdvi_action *ap;
00582 
00583     while (*str == ' ' || *str == '\t')
00584        ++str;
00585 
00586     if (*str == '\0' || *str == '\n')
00587        return NULL;
00588 
00589     p = str;
00590 
00591     /* find end of command name */
00592     while (isalnum((int)*p) || *p == '-' || *p == '_')
00593        ++p;
00594 
00595     for (actp = m_actions; ; ++actp) {
00596        if (actp >= m_actions + XtNumber(m_actions)) {
00597            const char *tmp = strchr(str, '\0');
00598            if (tmp == NULL) {
00599               tmp = p;
00600            }
00601            XDVI_WARNING((stderr, "Cannot compile action \"%.*s\".", (int)(tmp - str), str));
00602 
00603            return NULL;
00604        }
00605        if (memcmp(str, actp->string, p - str) == 0 && actp->string[p - str] == '\0')
00606            break;
00607     }
00608 
00609     while (*p == ' ' || *p == '\t')
00610        ++p;
00611     if (*p != '(') {
00612        while (*p != '\0' && *p != '\n')
00613            ++p;
00614        XDVI_WARNING((stderr, "Syntax error in action %.*s.", (int)(p - str), str));
00615 
00616        return NULL;
00617     }
00618     ++p;
00619     while (*p == ' ' || *p == '\t')
00620        ++p;
00621     for (p1 = p;; ++p1) {
00622        if (*p1 == '\0' || *p1 == '\n') {
00623            XDVI_WARNING((stderr, "Syntax error in action %.*s.", (int)(p1 - str), str));
00624            return NULL;
00625        }
00626        if (*p1 == ')')
00627            break;
00628     }
00629 
00630     ap = xmalloc(sizeof *ap);
00631     ap->proc = actp->proc;
00632     for (p2 = p1;; --p2) {
00633        if (p2 <= p) {       /* if no args */
00634            ap->num_params = 0;
00635            ap->param = NULL;
00636            break;
00637        }
00638        else if (p2[-1] != ' ' && p2[-1] != '\t') {
00639            char *arg;
00640 
00641            arg = xmalloc(p2 - p + 1);
00642            memcpy(arg, p, p2 - p);
00643            arg[p2 - p] = '\0';
00644            ap->num_params = 1;
00645            ap->param = arg;
00646            break;
00647        }
00648     }
00649     
00650     ap->next = compile_action(p1 + 1);
00651 
00652     return ap;
00653 }
00654 
00655 void
00656 handle_command(Widget widget, XtPointer client_data, XtPointer call_data)
00657 {
00658     struct xdvi_action *actp;
00659 
00660     UNUSED(call_data);
00661 
00662     /* call all actions registered for this event */
00663     for (actp = (struct xdvi_action *)client_data; actp != NULL; actp = actp->next) {
00664        if (globals.debug & DBG_EVENT)
00665            fprintf(stderr, "calling action with param: %s\n", actp->param);
00666        (actp->proc) (widget, NULL, &actp->param, &actp->num_params);
00667     }
00668 }
00669 
00670 #ifdef MOTIF
00671 int
00672 set_bar_value(Widget bar, int value, int max)
00673 {
00674     XmScrollBarCallbackStruct call_data;
00675 
00676 #ifdef TEST_SCROLLING
00677     fprintf(stderr, "set_bar_value: val %d, max %d\n", value, max);
00678 #endif
00679     if (value > max)
00680        value = max;
00681     if (value < 0)
00682        value = 0;
00683     call_data.value = value;
00684     XtVaSetValues(bar, XmNvalue, value, NULL);
00685     XtCallCallbacks(bar, XmNvalueChangedCallback, &call_data);
00686     return value;
00687 }
00688 #endif
00689 
00690 void
00691 home(wide_bool scrl)
00692 {
00693     if (!scrl)
00694        XUnmapWindow(DISP, mane.win);
00695 # ifdef MOTIF
00696     {
00697        int value;
00698 
00699        value = (globals.page.w - mane.width) / 2;
00700        if (value > resource.sidemargin_int / mane.shrinkfactor)
00701            value = resource.sidemargin_int / mane.shrinkfactor;
00702        (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width));
00703 
00704        value = (globals.page.h - mane.height) / 2;
00705        if (value > resource.topmargin_int / mane.shrinkfactor)
00706            value = resource.topmargin_int / mane.shrinkfactor;
00707        (void)set_bar_value(globals.widgets.y_bar, value, (int)(globals.page.h - mane.height));
00708     }
00709 # else
00710     get_xy();
00711     if (globals.widgets.x_bar != NULL) {
00712        int coord = (globals.page.w - mane.width) / 2;
00713 
00714        if (coord > resource.sidemargin_int / mane.shrinkfactor)
00715            coord = resource.sidemargin_int / mane.shrinkfactor;
00716        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (window_x + coord));
00717     }
00718     if (globals.widgets.y_bar != NULL) {
00719        int coord = (globals.page.h - mane.height) / 2;
00720 
00721        if (coord > resource.topmargin_int / mane.shrinkfactor)
00722            coord = resource.topmargin_int / mane.shrinkfactor;
00723        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (window_y + coord));
00724     }
00725 # endif /* MOTIF */
00726     if (!scrl) {
00727        XMapWindow(DISP, mane.win);
00728        /* Wait for the server to catch up---this eliminates flicker. */
00729        XSync(DISP, False);
00730     }
00731     handle_x_scroll(NULL, NULL, NULL, NULL);
00732     handle_y_scroll(NULL, NULL, NULL, NULL);
00733 }
00734 
00735 /*
00736  *     Same as home(), except move to the bottom of the page.
00737  */
00738 
00739 static void
00740 home_bottom(wide_bool scrl)
00741 {
00742     UNUSED(scrl);
00743     XUnmapWindow(DISP, mane.win);
00744 #ifdef MOTIF
00745     {
00746        int value;
00747 
00748        value = (globals.page.w - mane.width) / 2;
00749        if (value > resource.sidemargin_int / mane.shrinkfactor)
00750            value = resource.sidemargin_int / mane.shrinkfactor;
00751        (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width));
00752 
00753        (void)set_bar_value(globals.widgets.y_bar, (int)(globals.page.h - mane.height), (int)(globals.page.h - mane.height));
00754     }
00755 #else /* MOTIF */
00756     get_xy();
00757     if (globals.widgets.x_bar != NULL) {
00758        int coord = (globals.page.w - mane.width) / 2;
00759 
00760        if (coord > resource.sidemargin_int / mane.shrinkfactor)
00761            coord = resource.sidemargin_int / mane.shrinkfactor;
00762        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (window_x + coord));
00763     }
00764     if (globals.widgets.y_bar != NULL)
00765        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(window_y + (globals.page.h - mane.height)));
00766 #endif /* MOTIF */
00767     XMapWindow(DISP, mane.win);
00768     /* Wait for the server to catch up---this eliminates flicker. */
00769     XSync(DISP, False);
00770 
00771     handle_x_scroll(NULL, NULL, NULL, NULL);
00772     handle_y_scroll(NULL, NULL, NULL, NULL);
00773 }
00774 
00775 
00776 #ifndef MOTIF
00777 static void
00778 handle_destroy_bar(Widget w, XtPointer client_data, XtPointer call_data)
00779 {
00780     UNUSED(w);
00781     UNUSED(call_data);
00782     *(Widget *) client_data = NULL;
00783 }
00784 #endif
00785 
00786 static Boolean resized = False;
00787 
00788 static void
00789 get_geom(void)
00790 {
00791     static Dimension new_clip_w, new_clip_h;
00792     
00793     static Arg arg_wh_clip[] = {
00794        {XtNwidth, (XtArgVal) &new_clip_w},
00795        {XtNheight, (XtArgVal) &new_clip_h},
00796     };
00797 
00798     static Dimension window_w, window_h;
00799 
00800     static Arg arg_wh[] = {
00801        {XtNwidth, (XtArgVal) &window_w},
00802        {XtNheight, (XtArgVal) &window_h},
00803     };
00804 
00805     
00806     int old_clip_w;
00807 
00808 #ifdef MOTIF
00809     /* event handlers for Motif scrollbars have already been added
00810        in create_initialize_widgets(), xdvi.c */
00811     XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
00812 #else
00813     XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
00814     /* Note:  widgets may be destroyed but not forgotten */
00815     if (globals.widgets.x_bar == NULL) {
00816        globals.widgets.x_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal");
00817        if (globals.widgets.x_bar != NULL) {
00818            XtAddCallback(globals.widgets.x_bar, XtNdestroyCallback, handle_destroy_bar,
00819                        (XtPointer)&globals.widgets.x_bar);
00820            XtAddEventHandler(globals.widgets.x_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
00821                            False, handle_x_scroll, NULL);
00822        }
00823     }
00824     if (globals.widgets.y_bar == NULL) {
00825        globals.widgets.y_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical");
00826        if (globals.widgets.y_bar != NULL) {
00827            XtAddCallback(globals.widgets.y_bar, XtNdestroyCallback, handle_destroy_bar,
00828                        (XtPointer)&globals.widgets.y_bar);
00829            XtAddEventHandler(globals.widgets.y_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
00830                            False, handle_y_scroll, NULL);
00831        }
00832     }
00833 #endif
00834     XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
00835 
00836     old_clip_w = mane.width;
00837 
00838     /* we need to do this because 
00839        sizeof(Dimension) != sizeof(int)
00840     */
00841     mane.width = new_clip_w;
00842     mane.height = new_clip_h;
00843     if (old_clip_w == 0) {
00844        globals.ev.flags |= EV_NEWPAGE;
00845     }
00846 
00847     if (resource.keep_flag) {
00848 #ifndef MOTIF
00849        Dimension d;
00850        int curr_scroll;
00851        if ((globals.widgets.x_bar != NULL && m_x_scroll != 0) || (globals.widgets.y_bar != NULL && m_y_scroll != 0)) {
00852            get_xy();
00853        }
00854 #endif
00855        if (globals.widgets.x_bar != NULL && m_x_scroll != 0) {
00856 #ifdef MOTIF
00857            if (m_x_scroll > 0)
00858               (void)set_bar_value(globals.widgets.x_bar, m_x_scroll, (int)(globals.page.w - mane.width));
00859 #else
00860            XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL);
00861            curr_scroll = d - window_x;
00862            if (m_x_scroll > curr_scroll) {
00863               TRACE_GUI((stderr, "======== diff: %d", m_x_scroll - curr_scroll));
00864               XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(m_x_scroll - curr_scroll));
00865            }
00866 #endif
00867        }
00868        if (globals.widgets.y_bar != NULL && m_y_scroll != 0) {
00869 #ifdef MOTIF
00870            if (m_y_scroll > 0)
00871               (void)set_bar_value(globals.widgets.y_bar, m_y_scroll, (int)(globals.page.h - mane.height));
00872 #else
00873            XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL);
00874            curr_scroll = d - window_y;
00875            if (m_y_scroll > curr_scroll) {
00876               TRACE_GUI((stderr, "======== diff: %d", m_y_scroll - curr_scroll));
00877               XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(m_y_scroll - curr_scroll));
00878            }
00879 #endif
00880        }
00881     }
00882     /*     home(False); */
00883     resized = False;
00884 }
00885 
00886 /*
00887  * Callback routines
00888  */
00889 
00890 void
00891 handle_resize(Widget widget, XtPointer junk, XEvent *event, Boolean *cont)
00892 {
00893     UNUSED(widget);
00894     UNUSED(junk);
00895     UNUSED(event);
00896     UNUSED(cont);
00897     
00898     resized = True;
00899 #ifndef MOTIF
00900     handle_statusline_resize();
00901     handle_pagelist_resize();
00902 #endif
00903 }
00904 
00905 void
00906 reconfig(void)
00907 {
00908 /*      Dimension x, y; */
00909 
00910     if (globals.dvi_file.bak_fp == NULL)
00911        return;
00912     
00913 #ifndef MOTIF
00914     XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)False, NULL);
00915 #endif
00916     TRACE_GUI((stderr, "globals.widgets.draw_widget: w %d, h %d", globals.page.w, globals.page.h));
00917     XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL);
00918     
00919 #ifdef TEST_SCROLLING
00920 /*     XtVaSetValues(globals.widgets.draw_background, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL); */
00921 #endif
00922     
00923 #ifndef MOTIF
00924     handle_statusline_resize(); /* without this, statusline will disappear */
00925     /* following not needed? */
00926     /*     handle_pagelist_resize(); */
00927 #endif
00928     
00929     get_geom();
00930 
00931 /*     set_windowsize(&x, &y */
00932 /* #ifndef MOTIF */
00933 /*               , get_panel_width() */
00934 /* #endif */
00935 /*               ); */
00936 /*     XResizeWindow(DISP, XtWindow(globals.widgets.top_level), x, y); */
00937 /*      reconfigure_window(False, x, y, True); */
00938 /*      reconfig_window(); */
00939 }
00940 
00941 
00942 int
00943 check_goto_page(int pageno)
00944 {
00945     int retval;
00946     if (pageno < 0) {
00947        XBell(DISP, 0);
00948        statusline_print(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1);
00949        retval = 0;
00950     }
00951     else if (pageno >= total_pages) {
00952        XBell(DISP, 0);
00953        statusline_print(STATUS_SHORT,
00954                       "Can't go to page %d, going to last page (%d) instead",
00955                       pageno + 1, total_pages);
00956        retval = total_pages - 1;
00957     }
00958     else
00959        retval = pageno;
00960 
00961     page_history_insert(retval);
00962     return retval;
00963 }
00964 
00965 
00966 static int
00967 check_goto_tex_page(int pageno)
00968 {
00969     /*
00970       Translate from TeX page number to `real' page number if
00971       needed. Note that pageno is a C-style 0-based number, hence we
00972       add 1 for the argument of pageinfo_get_index_of_number().
00973     */
00974     int retval;
00975     if (resource.use_tex_pages) {
00976        int res = pageinfo_get_index_of_number(pageno + 1);
00977        if (res >= 0)
00978            retval = res;
00979        else {
00980            XBell(DISP, 0);
00981            if (pageno < 1) {
00982               statusline_print(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1);
00983               retval = 0;
00984            }
00985            else {
00986               /* there is no quick way to determine the last page number in the TeX page index */
00987               statusline_print(STATUS_SHORT,
00988                              "Can't go to page %d, going to last page instead",
00989                              pageno + 1);
00990               retval = total_pages - 1;
00991            }
00992        }
00993        page_history_insert(retval);
00994     }
00995     else {
00996        retval = check_goto_page(pageno);
00997     }
00998     return retval;
00999 }
01000 
01001 /* |||
01002  *     Currently the event handler does not coordinate XCopyArea requests
01003  *     with GraphicsExpose events.  This can lead to problems if the window
01004  *     is partially obscured and one, for example, drags a scrollbar.
01005  */
01006 
01007 /*
01008  *     Actions for the translation mechanism.
01009  */
01010 
01011 /* if there are global prefixes, return them in res and reset them to defaults */
01012 static Boolean
01013 get_prefix_arg(int *res)
01014 {
01015     Boolean ret;
01016 
01017     *res = m_sign * m_number;
01018     ret = m_have_arg;
01019     /* reset global flags */
01020     m_have_arg = False;
01021     m_number = 0;
01022     m_sign = 1;
01023     return ret;
01024 }
01025 
01026 Boolean
01027 get_int_arg(String *param, Cardinal *num_params, int *res)
01028 {
01029     if (*num_params > 0) {
01030        *res = atoi(*param);
01031        return True;
01032     }
01033     else {
01034        if (get_prefix_arg(res)) { /* prefix argument? */
01035            return True;
01036        }
01037     }
01038     return False;
01039 }
01040 
01041 
01042 Boolean
01043 toggle_arg(int arg, String *param, Cardinal *num_params)
01044 {
01045     if (*num_params > 0) {
01046        if (**param != 't' && (atoi(*param) != 0) == arg)
01047            return False;
01048     }
01049     else {
01050        if (m_have_arg) {
01051            int       tmparg = m_number;
01052 
01053            m_have_arg = False;
01054            m_number = 0;
01055            m_sign = 1;
01056 
01057            if ((tmparg != 0) == arg)
01058               return False;
01059        }
01060     }
01061     return True;
01062 }
01063 
01064 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
01065 Boolean
01066 check_resource_expert(void *val, const char *param)
01067 {
01068     int j = strtol(param, (char **)NULL, 10);
01069     /* check if the j-1th bit is set: */
01070     return (*(int *)val >> (j - 1)) & 1;
01071 }
01072 
01073 Boolean
01074 check_papersize(void *val, const char *param)
01075 {
01076     UNUSED(val);
01077     UNUSED(param);
01078     return False; /* TODO */
01079 }
01080 
01081 Boolean
01082 check_paper_landscape(void *val, const char *param)
01083 {
01084     UNUSED(val);
01085     UNUSED(param);
01086     return False; /* TODO */
01087 }
01088 
01089 /* comparison functions for the menu setting code */
01090 Boolean
01091 check_toggle(void *val, const char *param)
01092 {
01093     Boolean *on = val;
01094     if (strcmp(param, "toggle") == 0) {
01095        return *on;
01096     }
01097     else {
01098        fprintf(stderr, "TODO: check_toggle: arg |%s|, curr: %d\n", param, *(int *)val);
01099        return *on;
01100     }
01101 }
01102 
01103 Boolean
01104 check_int(void *val, const char *param)
01105 {
01106     int i = strtol(param, (char **)NULL, 10);
01107     return i == *(int *)val;
01108 }
01109 #endif /* NEW_MENU_CREATION */
01110 
01111 
01112 static void
01113 Act_digit(Widget w, XEvent *event,
01114          String *params, Cardinal *num_params)
01115 {
01116     int digit;
01117     /* for overflow checks */
01118     static const int MAXINT_QUOT = INT_MAX / 10;
01119     static const int MAXINT_MOD = INT_MAX % 10;
01120 
01121 
01122     UNUSED(w);
01123     UNUSED(event);
01124 
01125     if (*num_params != 1 || (digit = **params - '0') > 9) {
01126        XBell(DISP, 0);
01127        return;
01128     }
01129     m_have_arg = True;
01130 
01131     /* don't increment m_number if it would overflow */
01132     if (m_number < MAXINT_QUOT || (m_number == MAXINT_QUOT && digit <= MAXINT_MOD)) {
01133        m_number = m_number * 10 + digit;
01134        if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
01135            statusline_print(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number);
01136     }
01137     else {
01138        XBell(DISP, 0);
01139        statusline_print(STATUS_SHORT, "numerical prefix: %s%d: no larger value possible", m_sign < 0 ? "-" : "", m_number);
01140     }
01141 }
01142 
01143 static void
01144 Act_minus(Widget w, XEvent *event,
01145          String *params, Cardinal *num_params)
01146 {
01147     UNUSED(w);
01148     UNUSED(event);
01149     UNUSED(params);
01150     UNUSED(num_params);
01151     
01152     m_have_arg = True;
01153     m_sign = -m_sign;
01154     if (m_number > 0) {
01155        if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
01156            statusline_print(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number);
01157     }
01158     else {
01159        if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
01160            statusline_print(STATUS_SHORT, "numerical prefix: %s", m_sign < 0 ? "-" : "");
01161     }
01162 }
01163 
01164 static void
01165 Act_quit(Widget w, XEvent *event,
01166         String *params, Cardinal *num_params)
01167 {
01168     if (block_this_mouse_event())
01169        return;
01170     
01171     UNUSED(w);
01172     UNUSED(event);
01173     UNUSED(params);
01174     UNUSED(num_params);
01175     
01176 #ifndef FLAKY_SIGPOLL
01177     if (globals.debug & DBG_EVENT)
01178        puts(event_freq < 0
01179             ? "SIGPOLL is working"
01180             : "no SIGPOLL signals received");
01181 #endif
01182     xdvi_normal_exit();
01183 }
01184 
01185 static void
01186 Act_quit_confirm(Widget w, XEvent *event,
01187                String *params, Cardinal *num_params)
01188 {
01189     static Widget dialog = 0;
01190     
01191     if (block_this_mouse_event())
01192        return;
01193     
01194     UNUSED(w);
01195     UNUSED(event);
01196     UNUSED(params);
01197     UNUSED(num_params);
01198 
01199 #ifndef FLAKY_SIGPOLL
01200     if (globals.debug & DBG_EVENT)
01201        puts(event_freq < 0
01202             ? "SIGPOLL is working"
01203             : "no SIGPOLL signals received");
01204 #endif
01205 
01206     /* already a quit dialog open? */
01207     if (dialog != 0) {
01208        /* HACK ALERT: use brute force, since tests for XtIsRealized()
01209           or XtIsMapped() don't work?? Grabbing the server apparently
01210           has problems with Xaw, and it's not a nice solution anyway ... */
01211        if (kill_message_window(dialog))
01212            XBell(DISP, 0);
01213     }
01214 
01215     dialog = choice_dialog_sized(globals.widgets.top_level,
01216                              MSG_QUESTION,
01217                              SIZE_SMALL,
01218                              NULL,
01219 #ifndef MOTIF
01220                              "quit",
01221 #endif
01222                              NULL, NULL, /* no pre_callbacks */
01223                              "OK", xdvi_normal_exit_cb, NULL,
01224                              "Cancel", NULL, NULL,
01225                              "Really quit xdvi?");
01226 }
01227 
01228 static void
01229 Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params)
01230 {
01231     if (block_this_mouse_event())
01232        return;
01233     
01234     UNUSED(w);
01235     UNUSED(event);
01236     UNUSED(num_params);
01237     launch_browser(*params);
01238 }
01239 
01240 static void
01241 Act_print(Widget w, XEvent *event, String *params, Cardinal *num_params)
01242 {
01243     static struct save_or_print_info info;
01244 
01245     if (block_this_mouse_event())
01246        return;
01247     
01248     UNUSED(w);
01249     UNUSED(event);
01250     UNUSED(params);
01251     UNUSED(num_params);
01252     
01253     info.act = FILE_PRINT;
01254     
01255     save_or_print_callback(&info);
01256 }
01257 
01258 static void
01259 Act_save(Widget w, XEvent *event, String *params, Cardinal *num_params)
01260 {
01261     static struct save_or_print_info info;
01262 
01263     if (block_this_mouse_event())
01264        return;
01265     
01266     UNUSED(w);
01267     UNUSED(event);
01268     UNUSED(params);
01269     UNUSED(num_params);
01270     
01271     info.act = FILE_SAVE;
01272     
01273     save_or_print_callback(&info);
01274 }
01275 
01276 static void
01277 Act_find(Widget w, XEvent *event, String *params, Cardinal *num_params)
01278 {
01279     if (block_this_mouse_event())
01280        return;
01281     
01282     UNUSED(w);
01283     UNUSED(event);
01284     UNUSED(params);
01285     UNUSED(num_params);
01286     dvi_find_string(NULL, False);
01287 }
01288 
01289 static void
01290 Act_find_next(Widget w, XEvent *event, String *params, Cardinal *num_params)
01291 {
01292     if (block_this_mouse_event())
01293        return;
01294     
01295     UNUSED(w);
01296     UNUSED(event);
01297     UNUSED(params);
01298     UNUSED(num_params);
01299     dvi_find_string(NULL, True);
01300 }
01301 
01302 static void
01303 Act_help(Widget w, XEvent *event,
01304         String *params, Cardinal *num_params)
01305 {
01306     char *arg = NULL;
01307 
01308     if (block_this_mouse_event())
01309        return;
01310     
01311     UNUSED(w);
01312     UNUSED(event);
01313     
01314     if (*num_params > 0)
01315        arg = *params;
01316     
01317     show_help(globals.widgets.top_level, arg);
01318 }
01319 
01320 static home_proc home_action = NULL;
01321 
01322 void
01323 goto_page(int new_page, home_proc proc, Boolean force)
01324 {
01325     /* SU: added clearing the window here, else old window content
01326        will survive switching pages as long as globals.pausing.flag is active
01327        (i.e. when inadvertedly changing page instead of unpausing)
01328     */
01329     if (globals.pausing.flag) {
01330        XClearWindow(DISP, mane.win);
01331     }
01332            
01333     if (globals.dvi_file.bak_fp == NULL) {
01334        current_page = new_page; /* so that xdvi gets the page change */
01335        return;
01336     }
01337 
01338     ASSERT(new_page >= 0 && new_page < total_pages, "new_page in goto_page() out of range");   
01339     if (current_page != new_page || force) {
01340        globals.cursor.flags &= ~CURSOR_LINK; /* disable link cursor if needed */
01341 
01342        current_page = new_page;
01343        home_action = proc;
01344        globals.warn_spec_now = resource.warn_spec;
01345     /* this seems unneccessary */
01346 /*     if (!resource.keep_flag) */
01347 /*         home(False); */
01348 #if defined(MOTIF) && HAVE_XPM
01349        tb_check_navigation_sensitivity(current_page);
01350 /*     page_history_update_toolbar_navigation(); */
01351 #endif
01352        maybe_scroll_pagelist(current_page, False);
01353 
01354        if (globals.pausing.num_save)
01355            globals.pausing.num = globals.pausing.num_save[new_page];
01356        else
01357            globals.pausing.num = 0;
01358        /* Control-L (and changing the page) clears this box */
01359        globals.src.fwd_box_page = -1;
01360 
01361        globals.ev.flags |= EV_NEWPAGE;
01362        XFlush(DISP);       
01363     }
01364 }
01365 
01366 
01367 static void
01368 Act_goto_page(Widget w, XEvent *event,
01369              String *params, Cardinal *num_params)
01370 {
01371     int arg;
01372     Boolean clear_statusline = False;
01373 
01374     if (block_this_mouse_event())
01375        return;
01376     
01377     UNUSED(w);
01378     UNUSED(event);
01379 
01380     warn_num_params("goto-page()", params, *num_params, 1);
01381     
01382     if (*num_params > 0) {
01383        if (**params == 'e') {
01384            arg = total_pages - 1;
01385        }
01386        else
01387            arg = atoi(*params) - globals.pageno_correct;
01388     }
01389     else {
01390        if (get_prefix_arg(&arg)) {
01391            clear_statusline = True;
01392            arg -= globals.pageno_correct;
01393        }
01394        else {
01395            arg = total_pages - 1;
01396        }
01397     }
01398     if (arg == total_pages - 1) {
01399        /* with TeX numbers, total_pages - 1 might not be found in the page list,
01400           so don't use check_goto_tex_page() in this case */
01401        goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False);
01402     }
01403     else {
01404        goto_page(check_goto_tex_page(arg), resource.keep_flag ? NULL : home, False);
01405     }
01406     search_signal_page_changed();
01407     if (clear_statusline)
01408        statusline_clear();
01409 }
01410 
01411 void
01412 Act_recent_files(Widget w, XEvent *event,
01413                String *params, Cardinal *num_params)
01414 {
01415     UNUSED(w);
01416     UNUSED(event);
01417     UNUSED(params);
01418     UNUSED(num_params);
01419 }
01420 
01421 void
01422 Act_use_tex_pages(Widget w, XEvent *event,
01423                 String *params, Cardinal *num_params)
01424 {
01425     UNUSED(w);
01426     UNUSED(event);
01427 
01428     if (block_this_mouse_event())
01429        return;
01430     
01431     if (*num_params == 0) {
01432        if (m_have_arg) {
01433            resource.use_tex_pages = (m_number != 0);
01434            m_have_arg = False;
01435            m_number = 0;
01436            m_sign = 1;
01437        }
01438        else {
01439            resource.use_tex_pages = !resource.use_tex_pages;
01440        }
01441     }
01442     else
01443        resource.use_tex_pages = (**params == 't' ? !resource.use_tex_pages : atoi(*params));
01444     
01445     if (resource.use_tex_pages) {
01446        statusline_print(STATUS_SHORT, "Using TeX page numbers for \"g\", goto-page()");
01447     }
01448     else {
01449        statusline_print(STATUS_SHORT, "Using physical page numbers for \"g\", goto-page()");
01450     }
01451 
01452     store_preference(NULL, "useTeXPages", "%s", resource.use_tex_pages ? "True" : "False");
01453     
01454 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
01455     set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle);
01456 #else
01457 #ifdef MOTIF
01458    set_use_tex_option();
01459 #else
01460     toggle_menu(resource.use_tex_pages,  Act_use_tex_pages);
01461 #endif
01462 #endif /* NEW_MENU_CREATION */
01463     refresh_pagelist(total_pages, current_page);
01464 }
01465 
01466 void
01467 Act_forward_page(Widget w, XEvent *event,
01468                String *params, Cardinal *num_params)
01469 {
01470     int arg;
01471     Boolean clear_statusline = False;
01472 
01473     if (block_this_mouse_event())
01474        return;
01475     
01476     UNUSED(w);
01477     UNUSED(event);
01478 
01479     if (!get_int_arg(params, num_params, &arg))
01480        arg = 1;
01481     else
01482        clear_statusline = True;
01483     
01484     arg += current_page;
01485 
01486     if (arg == current_page) { /* zero argument -> redraw page */
01487        globals.src.fwd_box_page = -1;
01488        search_reset_info();
01489        globals.ev.flags |= EV_NEWPAGE;
01490        XFlush(DISP);
01491        statusline_print(STATUS_SHORT, "Page redrawn.");
01492        return;
01493     }
01494     else if (current_page >= total_pages - 1) {
01495        XBell(DISP, 0);
01496        statusline_print(STATUS_SHORT, "Last page of DVI file");
01497        return;
01498     }
01499     
01500     goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False);
01501     if (clear_statusline)
01502        statusline_clear();
01503     search_signal_page_changed();
01504 }
01505 
01506 void
01507 Act_back_page(Widget w, XEvent *event,
01508              String *params, Cardinal *num_params)
01509 {
01510     int arg;
01511     Boolean clear_statusline = False;
01512     
01513     if (block_this_mouse_event())
01514        return;
01515     
01516     UNUSED(w);
01517     UNUSED(event);
01518 
01519     if (!get_int_arg(params, num_params, &arg))
01520        arg = 1;
01521     else
01522        clear_statusline = True;
01523     
01524     arg = current_page - arg;
01525 
01526     if (current_page == 0) {
01527        XBell(DISP, 0);
01528        statusline_print(STATUS_SHORT, "First page of DVI file");
01529        return;
01530     }
01531 
01532     goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False);
01533     if (clear_statusline)
01534        statusline_clear();
01535     search_signal_page_changed();
01536 }
01537 
01538 static void
01539 Act_declare_page_number(Widget w, XEvent *event,
01540                      String *params, Cardinal *num_params)
01541 {
01542     int arg;
01543 
01544     if (block_this_mouse_event())
01545        return;
01546     
01547     UNUSED(w);
01548     UNUSED(event);
01549 
01550     if (resource.mouse_mode == MOUSE_RULER_MODE) {
01551        show_distance_from_ruler(event, True);
01552        return;
01553     }
01554     
01555     if (!get_int_arg(params, num_params, &arg)) {
01556        arg = 0;
01557     }
01558     globals.pageno_correct = arg - current_page;
01559     statusline_print(STATUS_SHORT, "Current page number set to %d", arg);
01560 }
01561 
01562 static void
01563 Act_toggle_mark(Widget w, XEvent *event,
01564               String *params, Cardinal *num_params)
01565 {
01566     int arg;
01567 
01568     if (block_this_mouse_event())
01569        return;
01570     
01571     UNUSED(w);
01572     UNUSED(event);
01573 
01574     if (globals.dvi_file.bak_fp == NULL)
01575        return;
01576     
01577     if (
01578 #ifdef MOTIF
01579        (resource.expert_mode & XPRT_SHOW_PAGELIST) == 0
01580 #else
01581        (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0
01582 #endif
01583        )
01584        return;
01585     
01586     if (*num_params > 0) {
01587        arg = atoi(*params);
01588        if (arg < -1 || arg > 2) {
01589            XBell(DISP, 0);
01590            statusline_print(STATUS_SHORT,
01591                           "Possible arguments: none (toggle current), "
01592                           "-1 (mark all), 0 (unmark all), 1 (toggle odd), 2 (toggle even)");
01593        }
01594        list_toggle_marks(arg);
01595     }
01596     else if (get_prefix_arg(&arg)) {
01597        list_toggle_marks(arg);
01598        statusline_clear();
01599     }
01600     else {
01601        arg = current_page;
01602        list_toggle_current(arg);
01603     }
01604 }
01605 
01606 static void
01607 Act_home(Widget w, XEvent *event,
01608         String *params, Cardinal *num_params)
01609 {
01610     UNUSED(w);
01611     UNUSED(event);
01612     UNUSED(params);
01613     UNUSED(num_params);
01614 
01615     if (block_this_mouse_event())
01616        return;
01617     
01618     home(True);
01619 }
01620 
01621 static void
01622 Act_home_or_top(Widget w, XEvent *event,
01623               String *params, Cardinal *num_params)
01624 {
01625     UNUSED(w);
01626     UNUSED(event);
01627     UNUSED(params);
01628     UNUSED(num_params);
01629     
01630     if (block_this_mouse_event())
01631        return;
01632     
01633     if (resource.keep_flag) {
01634        String args[1];
01635        args[0] = "10"; /* should be large enough ... */
01636        XtCallActionProc(globals.widgets.top_level, "up", NULL, args, 1);
01637     }
01638     else {
01639        home(True);
01640     }
01641 }
01642 
01643 static void
01644 Act_end_or_bottom(Widget w, XEvent *event,
01645               String *params, Cardinal *num_params)
01646 {
01647     String args[1];
01648 
01649     if (block_this_mouse_event())
01650        return;
01651     
01652     UNUSED(w);
01653     UNUSED(event);
01654     UNUSED(params);
01655     UNUSED(num_params);
01656 
01657     args[0] = "10"; /* should be large enough ... */
01658     XtCallActionProc(globals.widgets.top_level, "down", NULL, args, 1);
01659     
01660     if (!resource.keep_flag
01661 #ifndef MOTIF
01662        && globals.widgets.x_bar != NULL
01663 #endif
01664        ) {
01665        XtCallActionProc(globals.widgets.top_level, "right", NULL, args, 1);
01666     }
01667 }
01668 
01669 static void
01670 Act_center(Widget w, XEvent *event,
01671           String *params, Cardinal *num_params)
01672 {
01673     int x, y;
01674 
01675     if (block_this_mouse_event())
01676        return;
01677     
01678     UNUSED(w);
01679     UNUSED(params);
01680     UNUSED(num_params);
01681 
01682 #ifdef MOTIF
01683     /* The clip widget gives a more exact value. */
01684     x = event->xkey.x - mane.width / 2;
01685     y = event->xkey.y - mane.height / 2;
01686 
01687     x = set_bar_value(globals.widgets.x_bar, x, (int)(globals.page.w - mane.width));
01688     y = set_bar_value(globals.widgets.y_bar, y, (int)(globals.page.h - mane.height));
01689     get_xy();
01690     XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x - window_x, -y - window_y);
01691 #else
01692 
01693     if (event == NULL)
01694        return;       /* button actions do not provide events */
01695 
01696     x = event->xkey.x - mane.width / 2;
01697     y = event->xkey.y - mane.height / 2;
01698     /* The clip widget gives a more exact value. */
01699     if (globals.widgets.x_bar != NULL)
01700        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) x);
01701     if (globals.widgets.y_bar != NULL)
01702        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) y);
01703     XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x, -y);
01704 #endif
01705     handle_x_scroll(NULL, NULL, NULL, NULL);
01706     handle_y_scroll(NULL, NULL, NULL, NULL);
01707 }
01708 
01709 void
01710 Act_ruler_snap_origin(Widget w, XEvent *event,
01711                     String *params, Cardinal *num_params)
01712 {
01713     if (block_this_mouse_event())
01714        return;
01715     
01716     UNUSED(w);
01717     UNUSED(params);
01718     UNUSED(num_params);
01719 
01720     if (resource.mouse_mode == MOUSE_RULER_MODE)
01721        ruler_snap_origin(event);
01722 }
01723 
01724 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
01725 void
01726 Act_set_papersize(Widget w, XEvent *event,
01727                 String *params, Cardinal *num_params)
01728 {
01729     if (block_this_mouse_event())
01730        return;
01731     
01732     UNUSED(w);
01733     UNUSED(event);
01734     UNUSED(num_params);
01735 
01736     fprintf(stderr, "set_paperformat: %s\n", *params);
01737     resource.paper = *params;
01738     set_menu((void *)resource.paper, Act_set_papersize, check_papersize);
01739 }
01740 
01741 void
01742 Act_set_paper_landscape(Widget w, XEvent *event,
01743                      String *params, Cardinal *num_params)
01744 {
01745     if (block_this_mouse_event())
01746        return;
01747     
01748     UNUSED(w);
01749     UNUSED(event);
01750     UNUSED(num_params);
01751 
01752     resource.paper_landscape = !resource.paper_landscape;
01753     fprintf(stderr, "set_paper_landscape: %s\n", *params);
01754 
01755     set_menu((char *)resource.paper,  Act_set_paper_landscape, check_paper_landscape);
01756 }
01757 #endif /* NEW_MENU_CREATION */
01758 
01759 void
01760 Act_set_keep_flag(Widget w, XEvent *event,
01761                 String *params, Cardinal *num_params)
01762 {
01763     if (block_this_mouse_event())
01764        return;
01765     
01766     UNUSED(event);
01767     UNUSED(w);
01768     
01769     if (*num_params == 0) {
01770        if (m_have_arg) {
01771            resource.keep_flag = (m_number != 0);
01772            m_have_arg = False;
01773            m_number = 0;
01774            m_sign = 1;
01775        }
01776        else {
01777            resource.keep_flag = !resource.keep_flag;
01778        }
01779     }
01780     else
01781        resource.keep_flag = (**params == 't'
01782                            ? !resource.keep_flag : atoi(*params));
01783     if (resource.keep_flag) {
01784        statusline_print(STATUS_SHORT, "Keeping position when switching pages");
01785     }
01786     else {
01787        statusline_print(STATUS_SHORT,
01788                       "Not keeping position when switching pages");
01789     }
01790 
01791     store_preference(NULL, "keepPosition", "%s", resource.keep_flag ? "True" : "False");
01792     
01793 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
01794     set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle);
01795 #else
01796     toggle_menu(resource.keep_flag,  Act_set_keep_flag);
01797 #endif /* NEW_MENU_CREATION */
01798 }
01799 
01800 static void
01801 Act_left(Widget w, XEvent *event,
01802         String *params, Cardinal *num_params)
01803 {
01804     if (block_this_mouse_event())
01805        return;
01806     
01807     UNUSED(w);
01808     UNUSED(event);
01809 
01810     do_autoscroll = False;
01811     
01812     warn_num_params("left()", params, *num_params, 1);
01813 
01814 #ifdef MOTIF
01815     get_xy();
01816 #ifdef TEST_SCROLLING
01817     fprintf(stderr, "left for %p\n", (void *)w);
01818     fprintf(stderr, "window x: %d, y: %d\n", window_x, window_y);
01819 #endif
01820 
01821     (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (-2 * (int)mane.width / 3)
01822                             : (int)(-my_atof(*params) * mane.width)) - window_x,
01823                      (int)(globals.page.w - mane.width));
01824 #else
01825     if (globals.widgets.x_bar != NULL)
01826        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
01827                      (XtPointer) (*num_params == 0 ? (-2 * (int)mane.width / 3)
01828                                  : (int)(-my_atof(*params) * mane.width)));
01829     else {
01830        XBell(DISP, 0);
01831        statusline_print(STATUS_SHORT, "Horizontal scrolling not possible");
01832     }
01833 #endif
01834     handle_x_scroll(NULL, NULL, NULL, NULL);
01835 }
01836 
01837 static void
01838 Act_right(Widget w, XEvent *event,
01839          String *params, Cardinal *num_params)
01840 {
01841     if (block_this_mouse_event())
01842        return;
01843     
01844     UNUSED(w);
01845     UNUSED(event);
01846 
01847     do_autoscroll = False;
01848     
01849     warn_num_params("right()", params, *num_params, 1);
01850 
01851 #ifdef MOTIF
01852     get_xy();
01853 #ifdef TEST_SCROLLING
01854     fprintf(stderr, "right for %p\n", (void *)w);
01855     fprintf(stderr, "window x: %d, y: %d\n", window_x, window_y);
01856 #endif
01857 
01858     (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (2 * (int)mane.width / 3)
01859                             : (int)(my_atof(*params) * mane.width)) - window_x,
01860                      (int)(globals.page.w - mane.width));
01861 #else
01862     if (globals.widgets.x_bar != NULL)
01863        XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
01864                      (XtPointer) (*num_params == 0 ? (2 * (int)mane.width / 3)
01865                                  : (int)(my_atof(*params) * mane.width)));
01866     else {
01867        XBell(DISP, 0);
01868        statusline_print(STATUS_SHORT, "Horizontal scrolling not possible");
01869     }
01870 #endif
01871     handle_x_scroll(NULL, NULL, NULL, NULL);
01872 }
01873 
01874 static void
01875 Act_up(Widget w, XEvent *event,
01876        String *params, Cardinal *num_params)
01877 {
01878     if (block_this_mouse_event())
01879        return;
01880     
01881     UNUSED(w);
01882     UNUSED(event);
01883 
01884     do_autoscroll = False;
01885     
01886     warn_num_params("up()", params, *num_params, 1);
01887     
01888 #ifdef MOTIF
01889 #ifdef TEST_SCROLLING
01890     fprintf(stderr, "up for %p\n", (void *)w);
01891 #endif
01892     get_xy();
01893     (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (-2 * (int)mane.height / 3)
01894                             : (int)(-my_atof(*params) * mane.height)) - window_y,
01895                      (int)(globals.page.h - mane.height));
01896 #else
01897     if (globals.widgets.y_bar != NULL)
01898        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
01899                      (XtPointer) (*num_params == 0 ? (-2 * (int)mane.height / 3)
01900                                  : (int)(-my_atof(*params) * mane.height)));
01901     else {
01902        XBell(DISP, 0);
01903        statusline_print(STATUS_SHORT, "Vertical scrolling not possible");
01904     }
01905 #endif
01906     handle_y_scroll(NULL, NULL, NULL, NULL);
01907 }
01908 
01909 static void
01910 Act_down(Widget w, XEvent *event,
01911         String *params, Cardinal *num_params)
01912 {
01913     if (block_this_mouse_event())
01914        return;
01915     
01916     UNUSED(w);
01917     UNUSED(event);
01918 
01919     do_autoscroll = False;
01920 
01921     warn_num_params("down()", params, *num_params, 1);
01922 
01923 #ifdef MOTIF
01924 #ifdef TEST_SCROLLING
01925     fprintf(stderr, "down for %p\n", (void *)w);
01926 #endif
01927     get_xy();
01928     (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3)
01929                             : (int)(my_atof(*params) * mane.height)) - window_y,
01930                      (int)(globals.page.h - mane.height));
01931 #else
01932     if (globals.widgets.y_bar != NULL)
01933        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
01934                      (XtPointer) (*num_params == 0 ? (2 * (int)mane.height / 3)
01935                                  : (int)(my_atof(*params) * mane.height)));
01936     else {
01937        XBell(DISP, 0);
01938        statusline_print(STATUS_SHORT, "Vertical scrolling not possible");
01939     }
01940 #endif
01941     handle_y_scroll(NULL, NULL, NULL, NULL);
01942 }
01943 
01944 static void
01945 Act_down_or_next(Widget w, XEvent *event,
01946                String *params, Cardinal *num_params)
01947 {
01948     if (block_this_mouse_event())
01949        return;
01950     
01951     UNUSED(w);
01952     UNUSED(event);
01953 
01954     do_autoscroll = False;
01955 
01956     warn_num_params("down-or-next()", params, *num_params, 1);
01957 
01958 #ifdef TEST_SCROLLING
01959     fprintf(stderr, "down-or-next for %p\n", (void *)w);
01960 #endif
01961 
01962 #ifdef MOTIF
01963     get_xy();
01964     if (window_y > (int)mane.height - (int)globals.page.h) {
01965        (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3)
01966                                 : (int)(my_atof(*params) * mane.height)) -
01967                          window_y, (int)(globals.page.h - mane.height));
01968        return;
01969     }
01970 #else
01971     if (globals.widgets.y_bar != NULL) {
01972        get_xy();
01973        if (window_y > (int)mane.height - (int)globals.page.h) {
01974            XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
01975                          (XtPointer) (*num_params ==
01976                                     0 ? (2 * (int)mane.height / 3)
01977                                     : (int)(my_atof(*params) * mane.height)));
01978            return;
01979        }
01980     }
01981 #endif
01982 
01983     if (current_page < total_pages - 1) {
01984        goto_page(current_page + 1, home, False);
01985        search_signal_page_changed();
01986     }
01987     else {
01988        XBell(DISP, 0);
01989        statusline_print(STATUS_SHORT, "At bottom of last page");
01990     }
01991     handle_y_scroll(NULL, NULL, NULL, NULL);
01992 }
01993 
01994 
01995 static void
01996 Act_up_or_previous(Widget w, XEvent *event,
01997                  String *params, Cardinal *num_params)
01998 {
01999     if (block_this_mouse_event())
02000        return;
02001     
02002     UNUSED(w);
02003     UNUSED(event);
02004 
02005     do_autoscroll = False;
02006 
02007     warn_num_params("up-or-previous()", params, *num_params, 1);
02008 
02009 #ifdef TEST_SCROLLING
02010     fprintf(stderr, "up-or-previous for %p\n", (void *)w);
02011 #endif
02012 #ifdef MOTIF
02013     get_xy();
02014     if (window_y < 0) {
02015        (void)set_bar_value(globals.widgets.y_bar,
02016                          (*num_params == 0 ? (-2 * (int)mane.height / 3)
02017                           : (int)(-my_atof(*params) * mane.height)) - window_y,
02018                          (int)(globals.page.h - mane.height));
02019        return;
02020     }
02021 #else
02022     if (globals.widgets.y_bar != NULL) {
02023        get_xy();
02024        if (window_y < 0) {
02025            XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
02026                          (XtPointer) (*num_params ==
02027                                     0 ? (-2 * (int)mane.height / 3)
02028                                     : (int)(-my_atof(*params) * mane.height)));
02029            return;
02030        }
02031     }
02032 #endif
02033 
02034     if (current_page > 0) {
02035        goto_page(current_page - 1, home_bottom, False);
02036        search_signal_page_changed();
02037     }
02038     else {
02039        XBell(DISP, 0);
02040        statusline_print(STATUS_SHORT, "At top of first page");
02041     }
02042     handle_y_scroll(NULL, NULL, NULL, NULL);
02043 }
02044 
02045 
02046 static void
02047 Act_set_margins(Widget w, XEvent *event,
02048               String *params, Cardinal *num_params)
02049 {
02050     Window dummy;
02051 
02052     if (block_this_mouse_event())
02053        return;
02054     
02055     UNUSED(w);
02056     UNUSED(params);
02057     UNUSED(num_params);
02058 
02059 #ifndef MOTIF
02060     if (event == NULL)
02061        return;       /* button actions do not provide events */
02062 #endif
02063 
02064     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
02065                             event->xkey.x, event->xkey.y, &resource.sidemargin_int, &resource.topmargin_int, &dummy);  /* throw away last argument */
02066     statusline_print(STATUS_SHORT, "Margins set to cursor position (%d, %d)", resource.sidemargin_int, resource.topmargin_int);
02067     resource.sidemargin_int *= mane.shrinkfactor;
02068     resource.topmargin_int *= mane.shrinkfactor;
02069 }
02070 
02071 static void
02072 Act_show_display_attributes(Widget w, XEvent *event,
02073                          String *params, Cardinal *num_params)
02074 {
02075     if (block_this_mouse_event())
02076        return;
02077     
02078     UNUSED(w);
02079     UNUSED(event);
02080     UNUSED(params);
02081     UNUSED(num_params);
02082 
02083     statusline_print(STATUS_SHORT, "Unit = %d, bitord = %d, byteord = %d",
02084                    BitmapUnit(DISP), BitmapBitOrder(DISP),
02085                    ImageByteOrder(DISP));
02086 }
02087 
02088 int
02089 shrink_to_fit(void)
02090 {
02091     int value1;
02092     int value2;
02093     static Dimension window_w;
02094 #ifndef MOTIF
02095     static Dimension window_h;
02096 #endif
02097     
02098     static Arg arg_wh[] = {
02099        {XtNwidth, (XtArgVal) &window_w}
02100 #ifndef MOTIF
02101        , {XtNheight, (XtArgVal) &window_h}
02102 #endif
02103     };
02104 
02105     
02106 #ifdef MOTIF
02107     XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
02108 #else
02109     XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
02110 #endif
02111     
02112     value1 = ROUNDUP(globals.page.unshrunk_w, window_w - 2);
02113 
02114 #ifdef  MOTIF
02115     {  /* account for menubar */
02116        static Dimension new_h;
02117 
02118        /* get rid of scrollbar */
02119        XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)1, XtNheight, (XtArgVal)1, NULL);
02120        XtVaGetValues(globals.widgets.clip_widget, XtNheight, &new_h, NULL);
02121        value2 = ROUNDUP(globals.page.unshrunk_h, new_h - 2);
02122     }
02123 #else
02124     /* FIXME: value seems too small here! */
02125     value2 = ROUNDUP(globals.page.unshrunk_h, window_h - 2);
02126 #endif
02127 
02128     return value1 > value2 ? value1 : value2;
02129 }
02130 
02131 void
02132 do_set_shrinkfactor(int arg, Boolean set_resource)
02133 {
02134     static int shrink_bak = -1;
02135     
02136     /* We don't store this as preference since it may affect initial window geometry. */
02137     /*  store_preference(NULL, "shrinkFactor", "%d", arg); */
02138     
02139     if (resource.mouse_mode == MOUSE_TEXT_MODE) {
02140        text_change_region(TEXT_SEL_CLEAR, NULL);
02141     }
02142     
02143     mane.shrinkfactor = arg;
02144     if (set_resource)
02145        resource.shrinkfactor = mane.shrinkfactor;
02146     
02147 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
02148     set_menu(&arg, Act_set_shrink_factor, check_int);
02149 #if HAVE_XMP
02150     tb_set_zoom_sensitivity(arg != 1);
02151 #endif
02152 #else
02153     toggle_menu(arg, Act_set_shrink_factor);
02154 #endif /* NEW_MENU_CREATION */
02155     if (arg != 1 && arg != shrink_bak) {
02156        shrink_bak = arg;
02157 #if GREY
02158 #if COLOR
02159        if (resource.use_grey)
02160            fg_active = NULL;
02161 #else 
02162        if (resource.use_grey)
02163            init_pix();
02164 #endif
02165 #endif /* GREY */
02166        reset_fonts();
02167     }
02168     init_page();
02169     reconfig();
02170 
02171     htex_resize_page();
02172 
02173     /* this seems unneccessary */
02174 /*     if (!resource.keep_flag) */
02175 /*     home(False); */
02176 
02177     globals.ev.flags |= EV_NEWPAGE;
02178     XFlush(DISP);
02179 }
02180 
02181 void
02182 Act_set_shrink_factor(Widget w, XEvent *event,
02183                     String *params, Cardinal *num_params)
02184 {
02185     int arg;
02186     /* Set a limit to the shrink factors allowed; otherwise xdvi may
02187        consume a lot of memory (even run out of memory in malloc) in
02188        the allocation for the subpixel lookup table pixeltbl,
02189        dvi-draw.c. The `correct' solution would be to recognize that at a
02190        certain shrink, the only pixel for a character will be always
02191        set to `on' anyway, so that we could do without the lookup table for
02192        very large shrink factors ... */
02193     static const int SHRINK_MAX = 999;
02194      
02195     if (block_this_mouse_event())
02196        return;
02197     
02198     UNUSED(w);
02199     UNUSED(event);
02200 
02201     warn_num_params("set-shrink-factor()", params, *num_params, 1);
02202 
02203     if (*num_params > 0) {
02204        if (**params == 'a')
02205            arg = shrink_to_fit();
02206        else if (**params == '-')
02207            arg = mane.shrinkfactor + 1;
02208        else if (**params == '+')
02209            arg = mane.shrinkfactor - 1;
02210        else
02211            arg = atoi(*params);
02212     }
02213     else if (!get_prefix_arg(&arg)) {
02214        arg = shrink_to_fit();
02215     }
02216 
02217     if (arg <= 0) {
02218        XBell(DISP, 0);
02219        statusline_print(STATUS_SHORT,
02220                       "No more enlarging possible");
02221        return;
02222     }
02223     
02224     if (arg > SHRINK_MAX) {
02225        XBell(DISP, 0);
02226        statusline_print(STATUS_SHORT,
02227                       "Shrink factor %d too large (maximum: %d)", arg, SHRINK_MAX);
02228        return;
02229     }
02230     
02231     /* SU: added clearing the window here, else old window content
02232        will survive changing shrink
02233     */
02234     if (globals.pausing.flag) {
02235        XClearWindow(DISP, mane.win);
02236     }
02237     
02238     statusline_print(STATUS_SHORT, "shrink factor: %d", arg);
02239 #if 0
02240     /* Uncommented this, otherwise selecting `shrink to fit' with same value as
02241        current mane.shrinkfactor will clear the canvas (only fixed by changing shrink
02242        factor again). That bug is also present in xdvik-22.48-alpha3. */
02243     if (arg == mane.shrinkfactor) {
02244        return;
02245     }
02246 #endif
02247 
02248     do_set_shrinkfactor(arg, True);
02249 
02250 #if MOTIF    
02251     /* note: mustn't do that inside do_set_shrinkfactor(),
02252        or we get into an endless loop! */
02253     update_preferences_shrink();
02254 #endif
02255 }
02256 
02257 void
02258 Act_shrink_to_dpi(Widget w, XEvent *event,
02259                 String *params, Cardinal *num_params)
02260 {
02261     int arg, arg_bak;
02262     Boolean clear_statusline = False;
02263     
02264     if (block_this_mouse_event())
02265        return;
02266     
02267     UNUSED(w);
02268     UNUSED(event);
02269 
02270     warn_num_params("shrink-to-dpi()", params, *num_params, 1);
02271     
02272     if (!get_int_arg(params, num_params, &arg))
02273        arg = 0;
02274     else
02275        clear_statusline = True;
02276     
02277     arg_bak = arg;
02278     
02279     if (arg > 0)
02280        arg = (double)resource.pixels_per_inch / arg + 0.5;
02281 
02282     if (arg <= 0) {
02283        XBell(DISP, 0);
02284        statusline_print(STATUS_SHORT,
02285                       "shrink-to-dpi requires a positive argument");
02286        return;
02287     }
02288 
02289     /* like elsewhere */
02290     if (globals.pausing.flag) {
02291        XClearWindow(DISP, mane.win);
02292     }
02293 #if 0
02294     /* Uncommented this, otherwise selecting `shrink to fit' with same value as
02295        current mane.shrinkfactor will clear the canvas (only fixed by changing shrink
02296        factor again). That bug is also present in xdvik-22.48-alpha3. */
02297     if (arg == mane.shrinkfactor)
02298        return;
02299 #endif
02300     do_set_shrinkfactor(arg, True);
02301 
02302 #if MOTIF    
02303     /* note: mustn't do that inside do_set_shrinkfactor(),
02304        or we get into an endless loop! */
02305     update_preferences_shrink();
02306 #endif
02307 }
02308 
02309 void
02310 do_set_density(double newgamma, Boolean force, Boolean update_resource)
02311 {
02312     
02313     /* like elsewhere */
02314     if (globals.pausing.flag) {
02315        XClearWindow(DISP, mane.win);
02316     }
02317 
02318 #ifdef GREY
02319     if (resource.use_grey) {
02320        if (newgamma == resource.gamma && !force) {
02321            statusline_print(STATUS_SHORT, "density value: %.3f", newgamma);
02322            return;
02323        }
02324        resource.gamma = newgamma;
02325        if (update_resource)
02326            globals.curr_gamma = resource.gamma;
02327        
02328 #if COLOR
02329        fg_active = NULL;
02330        reset_colors();
02331 #else
02332        init_pix();
02333        if (G_visual->class != TrueColor) {
02334            return;
02335        }
02336        reset_fonts();
02337 #endif /* COLOR */
02338        statusline_print(STATUS_SHORT, "density value: %.3f", newgamma);
02339     } else
02340 #endif /* GREY */
02341     {
02342        reset_fonts();
02343        if (mane.shrinkfactor == 1) {
02344            statusline_print(STATUS_SHORT,
02345                   "set-density ignored at magnification 1");
02346            return;
02347        }
02348        statusline_print(STATUS_SHORT, "density value: %.3f", newgamma);
02349     }
02350 
02351     store_preference(NULL, "gamma", "%f", resource.gamma);
02352 
02353     globals.ev.flags |= EV_NEWPAGE;
02354     XFlush(DISP);
02355 }
02356 
02357 static void
02358 Act_set_density(Widget w, XEvent *event,
02359               String *params, Cardinal *num_params)
02360 {
02361     int arg;
02362 
02363     if (block_this_mouse_event())
02364        return;
02365     
02366     UNUSED(w);
02367     UNUSED(event);
02368 
02369     warn_num_params("set-density()", params, *num_params, 1);
02370     
02371     if (!get_int_arg(params, num_params, &arg)) {
02372        XBell(DISP, 0);
02373        return;
02374     }
02375 /*     if (arg < 0) { */
02376 /*     XBell(DISP, 0); */
02377 /*     statusline_print(STATUS_SHORT, */
02378 /*                    "set-density requires a positive value"); */
02379 /*     return; */
02380 /*     } */
02381     do_set_density(arg != 0 ? arg / 100.0 : 1.0, False, True);
02382 #if MOTIF
02383     update_preferences_darkness();
02384 #else
02385     store_preference(NULL, "gamma", "%f", resource.gamma);
02386 #endif
02387 }
02388 
02389 
02390 static void
02391 Act_change_density(Widget w, XEvent *event,
02392                  String *params, Cardinal *num_params)
02393 {
02394     int arg;
02395     double diff;
02396     double oldgamma = resource.gamma;
02397     UNUSED(w);
02398     UNUSED(event);
02399 
02400     if (block_this_mouse_event())
02401        return;
02402     
02403     warn_num_params("change-density()", params, *num_params, 1);
02404     
02405     if (!get_int_arg(params, num_params, &arg)) {
02406        XBell(DISP, 0);
02407        return;
02408     }
02409     diff = oldgamma / arg;
02410     if (oldgamma + diff <= 0.0)
02411        oldgamma = 0.0;
02412     else
02413        oldgamma += diff;
02414     do_set_density(oldgamma, False, True);
02415 #if MOTIF
02416     update_preferences_darkness();
02417 #endif
02418 }
02419 
02420 static void
02421 Act_fullscreen(Widget w, XEvent *event,
02422               String *params, Cardinal *num_params)
02423 {
02424     Dimension main_win_w, main_win_h;
02425     static int orig_x = 0, orig_y = 0;
02426 #ifndef MOTIF
02427     static Dimension get_x, get_y;
02428 #endif
02429     static Dimension old_w = 0, old_h = 0;
02430     Dimension panel_width = 0;
02431 #if 0
02432     static Dimension w_old = 0, h_old = 0;
02433 #endif
02434 
02435     if (block_this_mouse_event())
02436        return;
02437 
02438     UNUSED(w);
02439     UNUSED(event);
02440     
02441     if (!toggle_arg(resource.fullscreen, params, num_params)) {
02442        return;
02443     }
02444     
02445     resource.fullscreen = !resource.fullscreen;
02446     
02447 #ifndef MOTIF
02448     panel_width = get_panel_width();
02449 #endif
02450     
02451     if (resource.fullscreen) {
02452        /* when toggling to fullscreen, save current scrollbar values
02453           and window geometry */
02454        XtVaGetValues(globals.widgets.top_level, XtNwidth, &old_w, XtNheight, &old_h, NULL);
02455        /*     fprintf(stderr, "saved geometry: %dx%d\n", old_w, old_h); */
02456 #ifdef MOTIF
02457        if (globals.widgets.x_bar != NULL)
02458            XtVaGetValues(globals.widgets.x_bar, XmNvalue, &orig_x, NULL);
02459        if (globals.widgets.y_bar != NULL)
02460            XtVaGetValues(globals.widgets.y_bar, XmNvalue, &orig_y, NULL);
02461 #else
02462        get_xy();
02463        if (globals.widgets.x_bar != NULL)
02464            XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
02465        if (globals.widgets.y_bar != NULL)
02466            XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
02467        orig_x = -(get_x - window_x);
02468        orig_y = -(get_y - window_y);
02469 #endif /* MOTIF */
02470        /*     fprintf(stderr, "Current offsets: %d, %d, %d, %d\n", orig_x, window_x, orig_y, window_y); */
02471     }
02472 #if 0 /* TODO: don't hardcode w/h values -
02473         what's wrong with the set_windowsize() call below? */    
02474     if (resource.fullscreen) {
02475        XWindowAttributes attr;
02476        if (XGetWindowAttributes(DISP, XtWindow(globals.widgets.top_level), &attr)) {
02477            w_old = attr.width;
02478            h_old = attr.height;
02479        }
02480        main_win_w = WidthOfScreen(SCRN);
02481        main_win_h = HeightOfScreen(SCRN);
02482     }
02483     else if (w_old > 0 && h_old > 0) {
02484        main_win_w = w_old;
02485        main_win_h = h_old;
02486     }
02487     else {
02488        main_win_w = 800;
02489        main_win_h = 600;
02490     }
02491 #else
02492     if (resource.fullscreen) {
02493        set_windowsize(&main_win_w, &main_win_h, panel_width, 0, False);
02494     }
02495     else { /* re-use old window sizes, or initialize them if started in fullscreen mode */
02496        if (old_w == 0 || old_h == 0) /* started in fullscreen mode */
02497            set_windowsize(&old_w, &old_h, panel_width, 0, False);
02498        else
02499            set_windowsize(&old_w, &old_h, panel_width, 0, True);
02500     }
02501 #endif
02502 
02503 /*      fprintf(stderr, "reconfigure window!\n"); */
02504     if (resource.fullscreen)
02505        reconfigure_window(resource.fullscreen, main_win_w, main_win_h, True);
02506     else {
02507        reconfigure_window(resource.fullscreen, old_w, old_h, True);
02508     }
02509 
02510     /*      fprintf(stderr, "orig values: %d, %d\n", orig_x, orig_y); */
02511     /* restore horizontal scroll position */
02512     if (resource.keep_flag && orig_x != 0 && globals.widgets.x_bar != NULL) {
02513 #ifdef MOTIF
02514        if (!resource.fullscreen) {
02515            /* only scroll back in non-fullscreen mode. This was:
02516               if (resource.fullscreen)
02517                   (void)set_bar_value(globals.widgets.y_bar, orig_y, (int)(globals.page.h - mane.height));
02518               else ...
02519               but that was broken (bogus values in globals.page.h - mane.height).
02520            */
02521            (void)set_bar_value(globals.widgets.x_bar, orig_x, INT_MAX);
02522        }
02523 #else
02524        int curr_x;
02525        get_xy();
02526        XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
02527        curr_x = -(get_x - window_x);
02528        if (curr_x - orig_x > 0)
02529            XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(curr_x - orig_x));
02530 #endif /* MOTIF */
02531     }
02532 
02533     /* restore vertical scroll position */
02534     if (resource.keep_flag && orig_y != 0 && globals.widgets.y_bar != NULL) {
02535 #ifdef MOTIF
02536        if (!resource.fullscreen) {
02537            /* only scroll back in non-fullscreen mode. This was:
02538               if (resource.fullscreen)
02539                   (void)set_bar_value(globals.widgets.y_bar, orig_y, (int)(globals.page.h - mane.height));
02540               else ...
02541               but that was broken (bogus values in globals.page.h - mane.height).
02542            */
02543            (void)set_bar_value(globals.widgets.y_bar, orig_y, INT_MAX);
02544        }
02545 #else
02546        int curr_y;
02547        get_xy();
02548        XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
02549        curr_y = -(get_y - window_y);
02550        if (curr_y - orig_y > 0)
02551            XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(curr_y - orig_y));
02552 #endif /* MOTIF */
02553     }
02554     handle_x_scroll(NULL, NULL, NULL, NULL);
02555     handle_y_scroll(NULL, NULL, NULL, NULL);
02556 }
02557 
02558 #ifdef GREY
02559 
02560 static void
02561 Act_set_greyscaling(Widget w, XEvent *event,
02562                   String *params, Cardinal *num_params)
02563 {
02564     int arg;
02565 
02566     if (block_this_mouse_event())
02567        return;
02568     
02569     UNUSED(w);
02570     UNUSED(event);
02571 
02572     warn_num_params("set-greyscaling()", params, *num_params, 1);
02573     
02574     if (!get_int_arg(params, num_params, &arg)) { /* no arg, toggle */
02575        if (!toggle_arg(resource.use_grey, params, num_params)) {
02576            return;
02577        }
02578        resource.use_grey = !resource.use_grey;
02579        if (resource.use_grey) {
02580            statusline_print(STATUS_SHORT, "greyscaling on");
02581        }
02582        else {
02583            statusline_print(STATUS_SHORT, "greyscaling off");
02584        }
02585        globals.ev.flags |= EV_NEWPAGE;
02586        XFlush(DISP);
02587        return;
02588     }
02589 
02590     switch (arg) {
02591     case 0:
02592        resource.use_grey = False;
02593        statusline_print(STATUS_SHORT, "greyscaling off");
02594        break;
02595     case 1:
02596        resource.use_grey = True;
02597        statusline_print(STATUS_SHORT, "greyscaling on");
02598        break;
02599     default:
02600        {
02601            float newgamma = arg != 0 ? arg / 100.0 : 1.0;
02602            resource.use_grey = newgamma;
02603            statusline_print(STATUS_SHORT, "greyscale value: %.1f", newgamma);
02604        }
02605     }
02606 
02607     /* like elsewhere */
02608     if (globals.pausing.flag) {
02609        XClearWindow(DISP, mane.win);
02610     }
02611     
02612     if (resource.use_grey) {
02613        if (G_visual->class != TrueColor)
02614            init_plane_masks();
02615 #if COLOR
02616        fg_active = NULL;
02617 #else
02618        init_pix();
02619 #endif
02620     }
02621     reset_fonts();
02622     globals.ev.flags |= EV_NEWPAGE;
02623     XFlush(DISP);
02624 }
02625 
02626 #endif
02627 
02628 #if COLOR
02629 
02630 void
02631 do_toggle_color(Boolean update_resource)
02632 {
02633     if (resource.use_color) {
02634        resource.use_color = False;
02635        full_reset_colors();
02636        scanned_page_color = total_pages;
02637 #if PS
02638        if (ignore_papersize_specials || scanned_page_ps <= total_pages) {
02639            scanned_page = scanned_page_ps;
02640        }
02641 #endif
02642        statusline_print(STATUS_SHORT, "color specials off");
02643     }
02644     else {
02645        resource.use_color = True;
02646        scanned_page = scanned_page_color = scanned_page_reset;
02647        statusline_print(STATUS_SHORT, "color specials on");
02648     }
02649     if (update_resource)
02650        globals.curr_use_color = resource.use_color;
02651     
02652 #ifdef MOTIF
02653     update_preferences_color();
02654 #endif
02655     globals.ev.flags |= EV_NEWPAGE;
02656 }
02657 
02658 static void
02659 Act_set_color(Widget w, XEvent *event,
02660              String *params, Cardinal *num_params)
02661 {
02662     UNUSED(w);
02663     UNUSED(event);
02664 
02665     if (block_this_mouse_event())
02666        return;
02667     
02668     if (!toggle_arg(resource.use_color, params, num_params)) {
02669        return;
02670     };
02671        
02672     /* like elsewhere */
02673     if (globals.pausing.flag) {
02674        XClearWindow(DISP, mane.win);
02675     }
02676 
02677     do_toggle_color(True);
02678 }
02679 
02680 #endif /* COLOR */
02681 
02682 #if PS
02683 
02684 void
02685 Act_set_ps(Widget w, XEvent *event,
02686           String *params, Cardinal *num_params)
02687 {
02688     int arg;
02689 
02690     if (block_this_mouse_event())
02691        return;
02692     
02693     UNUSED(event);
02694     UNUSED(w);
02695     
02696     if (!get_int_arg(params, num_params, &arg))
02697        resource.postscript++;
02698     else
02699        resource.postscript = arg;
02700 
02701     if (resource.postscript > 2)
02702        resource.postscript = 0;
02703 
02704 #ifdef PS_GS
02705     if (!resource.useGS) {
02706        if (resource.postscript > 0) {
02707            popup_message(globals.widgets.top_level,
02708                        MSG_WARN,
02709                        "This version of xdvi depends on ghostscript for rendering Postscript images. "
02710                        "Postscript rendering cannot be activated if the option ``-noghostscript'' is used "
02711                        "or if the resource ``Ghostscript'' is set to false.",
02712                        /* message */
02713                        "Option ``-noghostscript'' is active; "
02714                        "cannot enable Postscript rendering without ghostscript.");
02715        }
02716        resource.postscript = 0;
02717     }
02718 #endif
02719     if (resource.postscript > 0) {
02720        scanned_page_ps = scanned_page_ps_bak;
02721        if (scanned_page > scanned_page_ps)
02722            scanned_page = scanned_page_ps;
02723        if (resource.postscript == 1) {
02724            statusline_print(STATUS_SHORT, "Postscript rendering on");
02725        }
02726        else {
02727            statusline_print(STATUS_SHORT, "Postscript rendering on (with bounding box)");
02728        }
02729     }
02730     else {
02731        scanned_page_ps_bak = scanned_page_ps;
02732        scanned_page_ps = total_pages;
02733 #if COLOR
02734        if (ignore_papersize_specials || scanned_page_color <= total_pages) {
02735            scanned_page = scanned_page_color;
02736        }
02737 #endif
02738        statusline_print(STATUS_SHORT, "Postscript rendering off; displaying bounding box instead");
02739     }
02740 
02741     store_preference(NULL, "postscript", "%d", resource.postscript);
02742     
02743     psp.toggle(resource.postscript);
02744 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
02745     set_menu(&resource.postscript, Act_set_ps, check_int);
02746 #else
02747     toggle_menu(resource.postscript, Act_set_ps);
02748 #endif /* NEW_MENU_CREATION */
02749     /* like elsewhere */
02750     if (globals.pausing.flag) {
02751        XClearWindow(DISP, mane.win);
02752     }
02753     
02754     globals.ev.flags |= EV_PS_TOGGLE;
02755     XFlush(DISP);
02756 }
02757 
02758 #endif /* PS */
02759 
02760 void
02761 Act_htex_back(Widget w, XEvent *event,
02762              String *params, Cardinal *num_params)
02763 {
02764     UNUSED(w);
02765     UNUSED(event);
02766     UNUSED(params);
02767     UNUSED(num_params);
02768 
02769     if (block_this_mouse_event())
02770        return;
02771     
02772     htex_back();
02773 }
02774 
02775 void
02776 Act_htex_forward(Widget w, XEvent *event,
02777                String *params, Cardinal *num_params)
02778 {
02779     UNUSED(w);
02780     UNUSED(event);
02781     UNUSED(params);
02782     UNUSED(num_params);
02783 
02784     if (block_this_mouse_event())
02785        return;
02786     
02787     htex_forward();
02788 }
02789 
02790 static void
02791 Act_htex_anchorinfo(Widget w, XEvent *event,
02792                   String *params, Cardinal *num_params)
02793 {
02794     int x, y;
02795     Window dummy;
02796 
02797     if (block_this_mouse_event())
02798        return;
02799     
02800     UNUSED(w);
02801     UNUSED(params);
02802     UNUSED(num_params);
02803 
02804     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
02805                             event->xkey.x, event->xkey.y, &x, &y, &dummy);
02806     htex_displayanchor(x, y);
02807 }
02808 
02809 #ifdef PS_GS
02810 void
02811 Act_set_gs_alpha(Widget w, XEvent *event,
02812                String *params, Cardinal *num_params)
02813 {
02814     UNUSED(w);
02815     UNUSED(event);
02816 
02817     if (block_this_mouse_event())
02818        return;
02819     
02820     if (!toggle_arg(resource.gs_alpha, params, num_params)) {
02821        return;
02822     }
02823     resource.gs_alpha = !resource.gs_alpha;
02824 
02825     if (resource.gs_alpha) {
02826        statusline_print(STATUS_SHORT, "ghostscript alpha active");
02827     }
02828     else {
02829        statusline_print(STATUS_SHORT, "ghostscript alpha inactive");
02830     }
02831 
02832     store_preference(NULL, "gsAlpha", "%s", resource.gs_alpha ? "True" : "False");
02833     
02834 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
02835     set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle);
02836 #else
02837     toggle_menu(resource.gs_alpha, Act_set_gs_alpha);
02838 #endif /* NEW_MENU_CREATION */
02839 #if GS_PIXMAP_CLEARING_HACK
02840     had_ps_specials = False; /* else infinite loop and/or crash in redraw! */
02841 #endif
02842     /* like elsewhere */
02843     if (globals.pausing.flag) {
02844        XClearWindow(DISP, mane.win);
02845     }
02846     
02847     globals.ev.flags |= EV_PS_TOGGLE;
02848     XFlush(DISP);
02849 }
02850 #endif
02851 
02852 static void
02853 Act_reread_dvi_file(Widget w, XEvent *event,
02854                   String *params, Cardinal *num_params)
02855 {
02856     UNUSED(w);
02857     UNUSED(event);
02858     UNUSED(params);
02859     UNUSED(num_params);
02860 
02861     if (block_this_mouse_event())
02862        return;
02863     
02864 /*      fprintf(stderr, "reread file!\n"); */
02865     globals.ev.flags |= EV_RELOAD;
02866 }
02867 
02868 static void
02869 Act_discard_number(Widget w, XEvent *event,
02870                  String *params, Cardinal *num_params)
02871 {
02872     UNUSED(w);
02873     UNUSED(event);
02874     UNUSED(params);
02875     UNUSED(num_params);
02876 
02877     if (block_this_mouse_event())
02878        return;
02879     
02880     m_have_arg = False;
02881     m_number = 0;
02882     m_sign = 1;
02883     if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
02884        statusline_print(STATUS_SHORT, "numerical prefix discarded");
02885 }
02886 
02887 /*
02888  * Actions to support dragging the image.
02889  */
02890 
02891 static int drag_last_x, drag_last_y;      /* last position of cursor */
02892 
02893 static int text_last_x = -1;
02894 static int text_last_y = -1;
02895 static int text_last_page = -1;
02896 
02897 static void drag_motion(XEvent *);
02898 static void drag_release(XEvent *);
02899 
02900 /* access for hyperref.c */
02901 Boolean dragging_text_selection(void)
02902 {
02903     MYTRACE((stderr, "dragging selection? %d", mouse_motion == text_motion));
02904     return mouse_motion == text_motion;
02905 }
02906 
02907 static void
02908 text_release(XEvent * event)
02909 {
02910     int ulx, uly, w, h;
02911     static char *text;
02912     int text_len = 0;
02913     
02914     UNUSED(event);
02915 
02916     MYTRACE((stderr, "text_release!"));
02917     
02918     if (mouse_motion == null_mouse)
02919        return;
02920     mouse_motion = mouse_release = null_mouse;
02921 
02922     w = text_last_x - drag_last_x;
02923     h = text_last_y - drag_last_y;
02924 
02925     if (w == 0 || h == 0) {
02926        text_last_x = text_last_y = -1;
02927        drag_last_x = drag_last_y = -1;
02928        text_last_page = -1;
02929        return;
02930     }
02931 
02932     if (w < 0) {
02933        ulx = drag_last_x + w;
02934        w = -w;
02935     }
02936     else
02937        ulx = drag_last_x;
02938 
02939     if (h < 0) {
02940        uly = drag_last_y + h;
02941        h = -h;
02942     }
02943     else
02944        uly = drag_last_y;
02945 
02946     free(text);
02947     /* this allocates text */
02948     text = get_text_selection(&text_len,
02949                            ulx * currwin.shrinkfactor,
02950                            uly * currwin.shrinkfactor,
02951                            (ulx + w) * currwin.shrinkfactor,
02952                            (uly + h) * currwin.shrinkfactor);
02953     if (text[0] == '\0') {
02954        /* fprintf(stdout, "Text selection empty.\n"); */
02955        return;
02956     }
02957 
02958     TRACE_GUI((stderr, "Selected `%s'", text));
02959 
02960     if (text_len > 4 * XMaxRequestSize(DISP) - 32) {
02961        MYTRACE((stderr, "selection too large: %d bytes", text_len));
02962        XBell(DISP, 10);
02963        statusline_print(STATUS_MEDIUM, "Selection too large (%d bytes, maximum %d bytes)",
02964                       text_len, 4 * XMaxRequestSize(DISP) - 32);
02965        MYTRACE((stderr, "after statusline_print1"));
02966        return;
02967     }
02968     
02969     if (!set_selection(text, globals.widgets.top_level)) {
02970        MYTRACE((stderr, "setting selection failed"));
02971        XBell(DISP, 10);
02972        statusline_print(STATUS_MEDIUM, "Could not set primary selection!");
02973        MYTRACE((stderr, "after statusline_print2"));
02974        text_change_region(TEXT_SEL_CLEAR, NULL);
02975        MYTRACE((stderr, "after text_change_region"));
02976     }
02977     MYTRACE((stderr, "End of text_release"));
02978 }
02979 
02980 void
02981 text_motion(XEvent *event)
02982 {
02983     text_change_region(TEXT_SEL_MOVE, event);
02984 }
02985 
02986 static void
02987 get_rectangle(XRectangle *rect, int x, int y, int last_x, int last_y)
02988 {
02989     rect->width = ABS(x - last_x);
02990     rect->height = ABS(y - last_y);
02991     rect->x = (x < last_x) ? x : last_x;
02992     rect->y = (y < last_y) ? y : last_y;
02993 }
02994 
02995 static void
02996 crop_to_window(int *x, int *y)
02997 {
02998     if (*x < -currwin.base_x + 1)
02999        *x = -currwin.base_x + 1;
03000     else if (*x > -currwin.base_x + (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1)
03001        *x = -currwin.base_x + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1;
03002     
03003     if (*y < -currwin.base_y + 1)
03004        *y = -currwin.base_y + 1;
03005     else if (*y > -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1)
03006        *y = -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1;
03007 }
03008 
03009 void
03010 text_change_region(textSelectionT mode, XEvent *event)
03011 {
03012     static GC bboxGC = 0;
03013     static GC redrawGC = 0; /* needed since bboxGC is clipped to diff of old and new region */
03014 
03015     MYTRACE((stderr, "text_change_region: %d", mode));
03016     
03017     if (bboxGC == 0) {
03018        XGCValues values;
03019        unsigned long valuemask;
03020 
03021        values.function = GXinvert; /* as in search code */
03022        if (values.function == GXinvert) {
03023            valuemask = GCFunction;
03024        }
03025        else {
03026            values.foreground = WhitePixelOfScreen(SCRN) ^ BlackPixelOfScreen(SCRN);
03027            /*               fprintf(stderr, "foreground: 0x%lx, white pixel: 0x%lx, black pixel: 0x%lx\n", */
03028            /*                      values.foreground, WhitePixelOfScreen(SCRN), BlackPixelOfScreen(SCRN)); */
03029            valuemask = GCFunction | GCForeground;
03030        }
03031        values.line_width = 1;
03032        valuemask |= GCLineWidth;
03033        bboxGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values);
03034        redrawGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values);
03035     }
03036 
03037     MYTRACE((stderr, "text_change_region: after bboxGC"));
03038     switch (mode) {
03039     case TEXT_SEL_MOVE:
03040        {
03041            int x, y;
03042            
03043            Window dummy;
03044 
03045            XRectangle redraw = { -1, -1, 0, 0 };
03046 
03047            ASSERT(event != NULL, "event in text_change_region() musn't be NULL for TEXT_SEL_MOVE");
03048 
03049            (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
03050                                    event->xkey.x, event->xkey.y,
03051                                    &x, &y,
03052                                    &dummy);
03053 
03054            crop_to_window(&x, &y);
03055 
03056            get_rectangle(&redraw, x, y, drag_last_x, drag_last_y);
03057 
03058            /*
03059             * If we have an old region, we want to clip the GC to the area:
03060             *
03061             * (clip \cup redraw) - (clip \cap redraw)
03062             *
03063             * and redraw both rectangle areas; otherwise, just draw the redraw area.
03064             */
03065 
03066            if (text_last_x != -1 && text_last_y != -1) {
03067 
03068               XRectangle clip = { -1, -1, 0, 0 };
03069               
03070               Region clip_region = XCreateRegion();
03071               Region redraw_region = XCreateRegion();
03072               Region union_region = XCreateRegion();
03073               Region intersect_region = XCreateRegion();
03074 
03075               get_rectangle(&clip, text_last_x, text_last_y, drag_last_x, drag_last_y);
03076 
03077               XUnionRectWithRegion(&clip, clip_region, clip_region);
03078               XUnionRectWithRegion(&redraw, redraw_region, redraw_region);
03079 
03080               XUnionRegion(clip_region, redraw_region, union_region);
03081               XIntersectRegion(clip_region, redraw_region, intersect_region);
03082               
03083               XSubtractRegion(union_region, intersect_region, redraw_region);
03084 
03085               XSetRegion(DISP, bboxGC, redraw_region);
03086 
03087               XDestroyRegion(clip_region);
03088               XDestroyRegion(redraw_region); 
03089               XDestroyRegion(union_region); 
03090               XDestroyRegion(intersect_region); 
03091               
03092               XFillRectangle(DISP, mane.win, bboxGC, clip.x, clip.y, clip.width, clip.height);
03093            }
03094            
03095            XFillRectangle(DISP, mane.win, bboxGC, redraw.x, redraw.y, redraw.width, redraw.height);
03096 
03097            text_last_x = x;
03098            text_last_y = y;
03099            text_last_page = current_page;
03100        }
03101        break;
03102     case TEXT_SEL_CLEAR:
03103        unset_selection(globals.widgets.top_level);
03104     case TEXT_SEL_ERASE:
03105     case TEXT_SEL_REDRAW:
03106        if (text_last_page == current_page
03107            && text_last_x != -1 && text_last_y != -1
03108            && drag_last_x != -1 && drag_last_y != -1) {
03109 
03110            XRectangle clear = { -1, -1, 0, 0 };
03111 
03112            get_rectangle(&clear, text_last_x, text_last_y, drag_last_x, drag_last_y);
03113            
03114            if (mode == TEXT_SEL_CLEAR) {
03115               text_last_x = text_last_y = drag_last_x = drag_last_y = -1;
03116               text_last_page = -1;
03117               /* Note ZLB: the region is erased instead of inverted to avoid
03118                * multiple inverting problem. An exposure is generated to
03119                * make the region redrawn */
03120               clearexpose(&mane, clear.x, clear.y, clear.width, clear.height);
03121            }
03122            else if (clip_region_to_rect(&clear)) {
03123               if (mode == TEXT_SEL_ERASE) {
03124                   /* If width or height are 0, XClearArea will clear entire window
03125                    * from coordinates x, y, so check for that: */
03126                   if (clear.width > 0 && clear.height > 0)
03127                      XClearArea(DISP, mane.win, clear.x, clear.y, clear.width, clear.height, False);
03128               }
03129               else
03130                   XFillRectangle(DISP, mane.win, redrawGC, clear.x, clear.y, clear.width, clear.height);
03131            }
03132        }
03133        break;
03134     }
03135     MYTRACE((stderr, "text_change_region: done"));
03136 }
03137 
03138 void
03139 text_selection_start(XEvent *event)
03140 {
03141     int x, y;
03142     Window dummy;
03143 
03144     /* erase existing region */
03145     text_change_region(TEXT_SEL_CLEAR, NULL);
03146 
03147     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
03148                             event->xkey.x, event->xkey.y,
03149                             &x, &y,
03150                             &dummy);      /* throw away last argument */
03151 
03152     crop_to_window(&x, &y);
03153     
03154     drag_last_x = x;
03155     drag_last_y = y;
03156 
03157     text_last_x = text_last_y = -1;
03158 
03159     MYTRACE((stderr, "selection start; mouse_release: %p; null: %p!",
03160             (void *)mouse_release, (void *)null_mouse));
03161     if (mouse_release == null_mouse) {
03162        MYTRACE((stderr, "init text_motion!"));
03163        mouse_motion = text_motion;
03164        mouse_release = text_release;
03165     }
03166 }
03167 
03168 /*
03169  * If action list for <Btn1Down> does NOT contain a magnifier() binding,
03170  * return that action list; else return NULL.
03171  */
03172 static char *
03173 btn1_no_magnifier_binding(void)
03174 {
03175     static Boolean initialized = False;
03176     static char *btn1_binding = NULL;
03177 
03178     if (!initialized) { /* cache value of previous calls */
03179        if (resource.main_translations != NULL) {
03180            if ((btn1_binding = strstr(resource.main_translations, "<Btn1Down>")) != NULL) {
03181               char *eol, *test;
03182               btn1_binding = strchr(btn1_binding, ':');
03183               if (btn1_binding != NULL)
03184                   btn1_binding++;
03185               while (isspace((int)*btn1_binding))
03186                   btn1_binding++;
03187               eol = strchr(btn1_binding, '\n');
03188               if (eol == NULL)
03189                   eol = btn1_binding + strlen(btn1_binding);
03190               if ((test = strstr(btn1_binding, "magnifier(")) == NULL || test > eol) {
03191                   /* binding not found */
03192                   return btn1_binding;
03193               }
03194            }
03195        }
03196 /*     fprintf(stderr, "MAG: %d\n", btn1_has_magnifier); */
03197        initialized = True;
03198     }
03199     return btn1_binding;
03200 }
03201 
03202 void
03203 Act_switch_mode(Widget w, XEvent *event,
03204               String *params, Cardinal *num_params)
03205 {
03206     mouseModeT prev_mode = resource.mouse_mode;
03207     int arg = -1;
03208     char *ptr;
03209     
03210     if (block_this_mouse_event())
03211        return;
03212     
03213     UNUSED(w);
03214 
03215     if ((ptr = btn1_no_magnifier_binding()) != NULL) {
03216        XBell(DISP, 0);
03217        statusline_print(STATUS_MEDIUM,
03218                       "Cannot switch modes: Action list for Btn1Down (%s) "
03219                       "does not contain magnifier() action.", ptr);
03220        return;
03221     }
03222     
03223 /*      fprintf(stderr, "prev mode: %d\n", prev_mode); */
03224     
03225     if (get_int_arg(params, num_params, &arg)) {
03226        if (arg < MOUSE_MAGNIFIER_MODE || arg >= MOUSE_MAX_MODE) {
03227            statusline_print(STATUS_SHORT, "Argument for Act_switch_mode outside of range from %d to %d",
03228                           MOUSE_MAGNIFIER_MODE, MOUSE_MAX_MODE - 1);
03229            resource.mouse_mode++;
03230        }
03231        else
03232            resource.mouse_mode = (mouseModeT)arg;
03233     }
03234     else
03235        resource.mouse_mode++;
03236 
03237     /* check if wrapped */
03238     if (resource.mouse_mode >= MOUSE_MAX_MODE)
03239        resource.mouse_mode = MOUSE_MAGNIFIER_MODE;
03240 
03241     /* undo effects of previous mode */
03242     switch (prev_mode) {
03243     case MOUSE_RULER_MODE:
03244        clear_ruler();
03245        mouse_motion = mouse_release = null_mouse;
03246        break;
03247     case MOUSE_TEXT_MODE:
03248        text_change_region(TEXT_SEL_CLEAR, NULL);
03249        mouse_motion = mouse_release = null_mouse;
03250        break;
03251     default: /* magnifier, nothing to do */
03252            break;
03253     }
03254     
03255     if (resource.mouse_mode == MOUSE_RULER_MODE) {
03256        XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
03257        statusline_print(STATUS_SHORT, "Ruler mode; click Mouse-1 to set ruler");
03258        show_ruler(event);
03259        show_distance_from_ruler(event, False);
03260     }
03261     else if (resource.mouse_mode == MOUSE_TEXT_MODE) {
03262        statusline_print(STATUS_SHORT, "Text selection mode; click and drag Mouse-1 to select a region");
03263     }
03264     else { /* default: MOUSE_MAGNIFIER_MODE */
03265        statusline_print(STATUS_SHORT, "Magnifier mode; click Mouse-1 to magnify text");
03266        mouse_motion = mouse_release = null_mouse;
03267     }
03268     
03269     globals.ev.flags |= EV_CURSOR;
03270     XFlush(DISP);
03271     
03272 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
03273     set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
03274 #else
03275     toggle_menu(resource.mouse_mode, Act_switch_mode);
03276 #endif /* NEW_MENU_CREATION */
03277     store_preference(NULL, "mouseMode", "%d", resource.mouse_mode);
03278 }
03279 
03280 static void
03281 Act_text_mode(Widget w, XEvent *event,
03282              String *params, Cardinal *num_params)
03283 {
03284     char *ptr;
03285     
03286     UNUSED(w);
03287     UNUSED(event);
03288     UNUSED(params);
03289     UNUSED(num_params);
03290 
03291     if (block_this_mouse_event())
03292        return;
03293 
03294     if ((ptr = btn1_no_magnifier_binding()) != NULL) {
03295        XBell(DISP, 0);
03296        statusline_print(STATUS_MEDIUM,
03297                       "Cannot switch modes: Action list for Btn1Down (%s) "
03298                       "does not contain magnifier() action.", ptr);
03299        return;
03300     }
03301     
03302     if (resource.mouse_mode == MOUSE_TEXT_MODE) {
03303        resource.mouse_mode++;
03304        if (resource.mouse_mode >= MOUSE_MAX_MODE)
03305            resource.mouse_mode = MOUSE_MAGNIFIER_MODE;
03306     }
03307     else
03308        resource.mouse_mode = MOUSE_TEXT_MODE;
03309 
03310     if (resource.mouse_mode == MOUSE_TEXT_MODE) {
03311        if (mouse_release != null_mouse && mouse_release != text_release)
03312            return;
03313        
03314        globals.ev.flags |= EV_CURSOR;
03315        
03316         XFlush(DISP);
03317     }
03318     else {
03319        mouse_motion = mouse_release = null_mouse;
03320        globals.ev.flags |= EV_CURSOR;
03321 
03322        text_change_region(TEXT_SEL_CLEAR, NULL);
03323        
03324        XFlush(DISP);
03325     }
03326 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
03327     set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
03328 #else
03329     toggle_menu(resource.mouse_mode, Act_switch_mode);
03330 #endif /* NEW_MENU_CREATION || MOTIF */
03331 }
03332 
03333 static void
03334 Act_drag(Widget w, XEvent *event,
03335         String *params, Cardinal *num_params)
03336 {
03337     UNUSED(w);
03338 
03339     if (block_this_mouse_event())
03340        return;
03341     
03342     if (mouse_release != null_mouse && mouse_release != drag_release)
03343        return;
03344 
03345     if (resource.mouse_mode == MOUSE_TEXT_MODE && text_last_page != -1
03346        /* this was:
03347           && (text_last_x != drag_last_x || text_last_y != drag_last_y)
03348           but that isn't restrictive enough */
03349        ) { /* dragging would mess up region drawing */
03350        XBell(DISP, 0);
03351        statusline_print(STATUS_SHORT, "Dragging page not possible while selection is active");
03352        return;
03353     }
03354     
03355     if (*num_params != 1) {
03356        XDVI_WARNING((stderr, "drag() requires 1 argument (got %d)", *num_params));
03357        return;
03358     }
03359     switch (**params) {
03360     case '|':
03361        globals.cursor.flags |= CURSOR_DRAG_V;
03362        break;
03363     case '-':
03364        globals.cursor.flags |= CURSOR_DRAG_H;
03365        break;
03366     case '+':
03367        globals.cursor.flags |= CURSOR_DRAG_A;
03368        break;
03369     default:
03370        XDVI_WARNING((stderr, "drag(): Valid arguments are `+', `|' or `-'"));
03371     }
03372     
03373     globals.ev.flags |= EV_CURSOR;
03374     
03375     if (mouse_release == null_mouse) {
03376        mouse_motion = drag_motion;
03377        mouse_release = drag_release;
03378        drag_last_x = event->xbutton.x_root;
03379        drag_last_y = event->xbutton.y_root;
03380     }
03381     else
03382        drag_motion(event);
03383 
03384     XFlush(DISP);
03385 }
03386 
03387 
03388 static void
03389 drag_motion(XEvent * event)
03390 {
03391 #ifdef MOTIF
03392     get_xy();
03393 #endif
03394 
03395     if (globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_A)) { /* horizontal motion */
03396 #ifdef MOTIF
03397        (void)set_bar_value(globals.widgets.x_bar,
03398                          drag_last_x - event->xbutton.x_root - window_x,
03399                          (int)(globals.page.w - mane.width));
03400 #else
03401        if (globals.widgets.x_bar != NULL) {
03402            XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
03403                          (XtPointer) (drag_last_x - event->xbutton.x_root));
03404        }
03405 #endif
03406        drag_last_x = event->xbutton.x_root;
03407     }
03408 
03409     if (globals.cursor.flags & (CURSOR_DRAG_V | CURSOR_DRAG_A)) { /* vertical motion */
03410 #ifdef MOTIF
03411        (void)set_bar_value(globals.widgets.y_bar,
03412                          drag_last_y - event->xbutton.y_root - window_y,
03413                          (int)(globals.page.h - mane.height));
03414 #else
03415        if (globals.widgets.y_bar != NULL) {
03416            XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
03417                          (XtPointer) (drag_last_y - event->xbutton.y_root));
03418        }
03419 #endif
03420        drag_last_y = event->xbutton.y_root;
03421     }
03422     handle_x_scroll(NULL, NULL, NULL, NULL);
03423     handle_y_scroll(NULL, NULL, NULL, NULL);
03424 }
03425 
03426 
03427 static void
03428 drag_release(XEvent * event)
03429 {
03430     drag_motion(event);
03431     mouse_motion = mouse_release = null_mouse;
03432 
03433     globals.cursor.flags &= ~(CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A);
03434     globals.ev.flags |= EV_CURSOR;
03435     XFlush(DISP);
03436 }
03437 
03438 
03439 
03440 /* Wheel mouse support.  */
03441 
03442 static int wheel_button = -1;
03443 
03444 static void
03445 Act_wheel(Widget w, XEvent *event,
03446          String *params, Cardinal *num_params)
03447 {
03448     int dist;
03449 
03450     UNUSED(w);
03451 
03452     if (block_this_mouse_event())
03453        return;
03454 
03455     if (*num_params != 1) {
03456        XDVI_WARNING((stderr, "wheel() requires 1 argument (got %d)", *num_params));
03457        return;
03458     }
03459     dist = (strchr(*params, '.') == NULL) ? atoi(*params)
03460        : (int)(my_atof(*params) * resource.wheel_unit);
03461 #ifdef MOTIF
03462     get_xy();
03463     set_bar_value(globals.widgets.y_bar, dist - window_y, (int)(globals.page.h - mane.height));
03464 #else
03465     if (globals.widgets.y_bar != NULL)
03466        XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) dist);
03467 #endif
03468 
03469     wheel_button = event->xbutton.button;
03470 
03471     handle_y_scroll(NULL, NULL, NULL, NULL);
03472 }
03473 
03474 
03475 /* Internal mouse actions.  */
03476 
03477 static void
03478 Act_motion(Widget w, XEvent *event,
03479           String *params, Cardinal *num_params)
03480 {
03481     /* remember last position, to do this only when pointer is actually moved */
03482     static int old_x = -1, old_y = -1;
03483     int x, y;
03484 
03485     UNUSED(w);
03486     UNUSED(params);
03487     UNUSED(num_params);
03488 
03489     if (block_this_mouse_event()) {
03490        MYTRACE((stderr, "returning!"));
03491        return;
03492     }
03493 
03494     if (resource.mouse_mode == MOUSE_RULER_MODE) {
03495        show_distance_from_ruler(event, False);
03496     }
03497     /* This used to be:
03498           (abs(x - old_x) > x_threshold || abs(y - old_y) > y_threshold))
03499        but that didn't work too well either. Just change it whenever user
03500        moves the mouse. */       
03501     if (!MAGNIFIER_ACTIVE && resource.mouse_mode != MOUSE_RULER_MODE
03502        && pointerlocate(&x, &y) && (x != old_x || y != old_y)) {
03503        htex_displayanchor(x, y);
03504        old_x = x;
03505        old_y = y;
03506     }
03507 
03508     if ((int)(event->xbutton.button) != wheel_button) {
03509        mouse_motion(event);
03510     }
03511 }
03512 
03513 
03514 static void
03515 Act_release(Widget w, XEvent *event,
03516            String *params, Cardinal *num_params)
03517 {
03518     UNUSED(w);
03519     UNUSED(params);
03520     UNUSED(num_params);
03521 
03522     if (block_this_mouse_event())
03523        return;
03524     
03525     if ((int)(event->xbutton.button) == wheel_button) {
03526        wheel_button = -1;
03527        return;
03528     }
03529 
03530     mouse_release(event);
03531 }
03532 
03533 void
03534 Act_ruler_mode(Widget w, XEvent *event,
03535               String *params, Cardinal *num_params)
03536 {
03537     char *ptr;
03538     
03539     UNUSED(w);
03540     UNUSED(params);
03541     UNUSED(num_params);
03542 
03543     if (block_this_mouse_event())
03544        return;
03545 
03546     if ((ptr = btn1_no_magnifier_binding()) != NULL) {
03547        XBell(DISP, 0);
03548        statusline_print(STATUS_MEDIUM,
03549                       "Cannot switch modes: Action list for Btn1Down (%s) "
03550                       "does not contain magnifier() action.", ptr);
03551        return;
03552     }
03553     
03554     if (resource.mouse_mode == MOUSE_RULER_MODE) {
03555        resource.mouse_mode++;
03556        if (resource.mouse_mode >= MOUSE_MAX_MODE)
03557            resource.mouse_mode = MOUSE_MAGNIFIER_MODE;
03558     }
03559     else
03560        resource.mouse_mode = MOUSE_RULER_MODE;
03561     
03562     if (resource.mouse_mode == MOUSE_RULER_MODE) {
03563        XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
03564        statusline_print(STATUS_SHORT, "Ruler mode on; use Mouse-1 to set/drag ruler");
03565        show_ruler(event);
03566        show_distance_from_ruler(event, False);
03567     }
03568     else {
03569        if (globals.cursor.flags & CURSOR_LINK) {
03570            XDefineCursor(DISP, CURSORWIN, globals.cursor.link);
03571        }
03572        else {
03573            XDefineCursor(DISP, CURSORWIN, globals.cursor.ready);
03574        }
03575        statusline_print(STATUS_SHORT, "Ruler mode off");
03576        clear_ruler();
03577        mouse_motion = mouse_release = null_mouse;
03578     }
03579 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
03580     set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
03581 #else
03582     toggle_menu(resource.mouse_mode, Act_switch_mode);
03583 #endif /* NEW_MENU_CREATION || MOTIF */
03584 }
03585 
03586 
03587 /* Actions for source specials.  */
03588 
03589 void
03590 Act_source_special(Widget w, XEvent *event,
03591                  String *params, Cardinal *num_params)
03592 {
03593     Window dummy;
03594 
03595     UNUSED(w);
03596     UNUSED(params);
03597     UNUSED(num_params);
03598 
03599     if (block_this_mouse_event())
03600        return;
03601     
03602     if ((event->type == ButtonPress && mouse_release != null_mouse)
03603        || MAGNIFIER_ACTIVE) {
03604        XBell(DISP, 0);
03605        return;
03606     }
03607 
03608     source_reverse_x = event->xbutton.x;
03609     source_reverse_y = event->xbutton.y;
03610     if (event->xbutton.window != mane.win)
03611        (void)XTranslateCoordinates(DISP,
03612                                 RootWindowOfScreen(SCRN), mane.win,
03613                                 event->xbutton.x_root,
03614                                 event->xbutton.y_root,
03615                                 &source_reverse_x,
03616                                 &source_reverse_y,
03617                                 &dummy);  /* throw away last argument */
03618 
03619     source_reverse_x = (source_reverse_x + mane_base_x) * mane.shrinkfactor;
03620     source_reverse_y = (source_reverse_y + mane_base_y) * mane.shrinkfactor;
03621 
03622     globals.ev.flags |= EV_SRC;
03623 }
03624 
03625 void
03626 Act_show_source_specials(Widget w, XEvent *event,
03627                       String *params, Cardinal *num_params)
03628 {
03629     int arg;
03630     Boolean clear_statusline = False;
03631     
03632     if (block_this_mouse_event())
03633        return;
03634     
03635     UNUSED(w);
03636 
03637     if (!get_int_arg(params, num_params, &arg))
03638        arg = -1;
03639     else
03640        clear_statusline = True;
03641 
03642     if ((event->type == ButtonPress && mouse_release != null_mouse)
03643        || magnifier.win != (Window) 0) {
03644        XBell(DISP, 0);
03645        return;
03646     }
03647 
03648     if (!(globals.ev.flags & EV_SRC)) {
03649        source_reverse_x = -1;
03650        source_show_all = (arg == 1 ? True : False);
03651 
03652        globals.ev.flags |= EV_SRC;
03653     }
03654     if (clear_statusline)
03655        statusline_clear();
03656 }
03657 
03658 void
03659 Act_source_what_special(Widget w, XEvent *event,
03660                      String *params, Cardinal *num_params)
03661 {
03662     int my_x, my_y;
03663     Window dummy;
03664 
03665     UNUSED(w);
03666     UNUSED(params);
03667     UNUSED(num_params);
03668 
03669     if (block_this_mouse_event())
03670        return;
03671     
03672     (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
03673                             event->xkey.x, event->xkey.y, &my_x, &my_y, &dummy);    /* throw away last argument */
03674     my_x = (my_x + mane_base_x) * mane.shrinkfactor;
03675     my_y = (my_y + mane_base_y) * mane.shrinkfactor;
03676     source_reverse_search(my_x, my_y, False);
03677 }
03678 
03679 static void
03680 select_cb(const char *filename, void *data)
03681 {
03682     UNUSED(data);
03683     if (filename != NULL) {
03684        TRACE_FILES((stderr, "new filename: |%s|", filename));
03685               
03686        set_dvi_name_expand(filename);
03687        current_page = 0; /* switch to first page */
03688        close_old_filep();
03689        globals.ev.flags |= EV_NEWDOC;
03690        globals.ev.flags |= EV_PAGEHIST_INSERT;
03691     }
03692 }
03693 
03694 static void
03695 Act_select_dvi_file(Widget w, XEvent *event,
03696                   String *params, Cardinal *num_params)
03697 {
03698     static struct filesel_callback cb; /* static so that we can pass its address */
03699     int arg = -1;
03700     
03701     UNUSED(w);
03702     UNUSED(event);
03703 
03704     if (block_this_mouse_event())
03705        return;
03706 
03707     if (get_int_arg(params, num_params, &arg)) { /* try item from file history */
03708        char *fname;
03709        int dummy_page;
03710 
03711        if (arg < 1) {
03712            XBell(DISP, 0);
03713            statusline_print(STATUS_MEDIUM, "Error: File history number must be >= 1");
03714        }
03715        else if ((fname = file_history_get_elem(arg - 1, &dummy_page)) == NULL) {
03716            statusline_print(STATUS_MEDIUM, "No file number %d in history (history size: %lu)",
03717                           arg, (unsigned long)file_history_size());
03718        }
03719        else {
03720            file_history_open(fname);
03721        }
03722        return;
03723     }
03724     
03725     cb.title = "Xdvi: Open file";
03726     cb.prompt = "Open file:";
03727     cb.ok = "OK";
03728     cb.cancel = "Cancel";
03729     cb.init_path = NULL;
03730     cb.filemask = "*.dvi";
03731     cb.must_exist = True;
03732     cb.exit_on_cancel = False;
03733     cb.func_ptr = select_cb;
03734     cb.data = NULL;
03735 
03736     file_history_set_page(current_page);
03737     
03738     XsraSelFile(globals.widgets.top_level, &cb);
03739 }
03740 
03741 /*
03742  * If all GUI elements have been turned on/off, make this synonymous
03743  * with expert mode off/on, so that next `x' keystroke does something
03744  * reasonable.
03745  */
03746 void
03747 update_expert_mode(void)
03748 {
03749     if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE | XPRT_SHOW_SCROLLBARS
03750 #ifdef MOTIF
03751                              | XPRT_SHOW_PAGELIST | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR
03752 #else
03753                              | XPRT_SHOW_BUTTONS
03754 #endif
03755                              )) == XPRT_SHOW_ALL) {
03756        resource.expert = False;
03757     }
03758     else if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE
03759 #ifdef MOTIF
03760                                   | XPRT_SHOW_SCROLLBARS | XPRT_SHOW_PAGELIST
03761                                   | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR
03762 #else
03763                                   | BROKEN_RECONFIG ? 0 : XPRT_SHOW_SCROLLBARS | XPRT_SHOW_BUTTONS
03764 #endif
03765                                   )) == XPRT_SHOW_NONE) {
03766        resource.expert = True;
03767     }
03768 }
03769 
03770 void
03771 Act_set_expert_mode(Widget w, XEvent *event,
03772                   String *params, Cardinal *num_params)
03773 {
03774     int arg;
03775     Boolean clear_statusline = False;
03776 
03777     if (block_this_mouse_event())
03778        return;
03779     
03780     UNUSED(w);
03781     UNUSED(event);
03782 
03783     if (!get_int_arg(params, num_params, &arg))
03784        arg = -1;
03785     else
03786        clear_statusline = True;
03787     
03788     switch(arg) {
03789     case 1:
03790        resource.expert_mode ^= XPRT_SHOW_STATUSLINE;
03791        toggle_statusline();
03792        update_expert_mode();
03793        break;
03794     case 2:
03795 #ifndef MOTIF
03796        /* show this warning only when not toggling global expert mode
03797           (that's why it can't be inside toggle_scrollbars) */
03798        if (BROKEN_RECONFIG) {
03799            popup_message(globals.widgets.top_level,
03800                        MSG_WARN,
03801                        NULL,
03802                        "Sorry - cannot toggle scrollbars with this X Version.\n"
03803                        "This version of XFree has a broken implementation of the viewportWidget, "
03804                        "which would break the layout if the scrollbars are toggled. "
03805                        "Versions that are known to work have a VendorRelease version below 4000 or above 4002. "
03806                        "You will need to update your XFree server to fix this.");
03807            return;
03808        }
03809 #endif
03810        resource.expert_mode ^= XPRT_SHOW_SCROLLBARS;
03811        toggle_scrollbars();
03812        update_expert_mode();
03813        break;
03814        
03815 #ifdef MOTIF
03816     case 3:
03817        resource.expert_mode ^= XPRT_SHOW_PAGELIST;
03818        toggle_pagelist();
03819        update_expert_mode();
03820        break;
03821        
03822     case 4: /* toolbar */
03823        resource.expert_mode ^= XPRT_SHOW_TOOLBAR;
03824        toggle_toolbar();
03825        update_expert_mode();
03826        break;
03827        
03828     case 5:
03829        resource.expert_mode ^= XPRT_SHOW_MENUBAR;
03830        toggle_menubar();
03831        update_expert_mode();
03832        break;
03833 #else
03834     case 3:
03835 /*     fprintf(stderr, "sidebar\n"); */
03836        resource.expert_mode ^= XPRT_SHOW_BUTTONS;
03837        toggle_buttons();
03838        update_expert_mode();
03839        break;
03840 #endif
03841     default:
03842        /* warn 'em */
03843        if (
03844 #ifdef MOTIF
03845            arg > 5
03846 #else
03847            arg > 3
03848 #endif
03849            ) {
03850            statusline_print(STATUS_SHORT, "Number %d too large for `set-expert-mode', using 0 (= toggle) instead.",
03851                           arg);
03852        }
03853        /* toggle all items */
03854        resource.expert = !resource.expert;
03855        if (resource.expert)
03856            resource.expert_mode = XPRT_SHOW_NONE;
03857        else
03858            resource.expert_mode = XPRT_SHOW_ALL;
03859        
03860        toggle_statusline();
03861 #ifndef MOTIF
03862        if (!BROKEN_RECONFIG)
03863            toggle_scrollbars();
03864 #else
03865        toggle_scrollbars();
03866 #endif
03867        
03868 #ifdef MOTIF
03869        toggle_pagelist();
03870        toggle_toolbar();
03871        toggle_menubar();
03872 #else
03873        toggle_buttons();
03874 #endif
03875     }
03876 
03877 #ifdef MOTIF
03878     update_preferences_expert();
03879 #endif
03880 
03881     store_preference(NULL, "expertMode", "%d", resource.expert_mode);
03882     
03883     if (clear_statusline)
03884        statusline_clear();
03885 }
03886 
03887 Boolean have_src_specials = False;
03888 static Boolean do_update_property = False;
03889 
03890 void
03891 handle_x_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
03892 {
03893 #ifndef MOTIF
03894     Dimension get_x = 0;
03895 #endif
03896 
03897     UNUSED(w);
03898     UNUSED(closure);
03899     UNUSED(ev);
03900     UNUSED(cont);
03901 
03902     if (/* !resource.keep_flag || */ globals.widgets.x_bar == NULL)
03903        return;
03904 
03905 #ifdef MOTIF
03906     XtVaGetValues(globals.widgets.x_bar, XmNvalue, &m_x_scroll, NULL);
03907 #else
03908     get_xy();
03909     XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
03910     m_x_scroll = get_x - window_x;
03911     scroll_x_panner(m_x_scroll);
03912 #endif
03913 }
03914 
03915 void
03916 handle_y_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
03917 {
03918 #ifndef MOTIF
03919     Dimension get_y = 0;
03920 #endif
03921     
03922     UNUSED(w);
03923     UNUSED(closure);
03924     UNUSED(ev);
03925     UNUSED(cont);
03926     
03927     if (/* !resource.keep_flag || */ globals.widgets.y_bar == NULL)
03928        return;
03929     
03930 #ifdef MOTIF
03931     XtVaGetValues(globals.widgets.y_bar, XmNvalue, &m_y_scroll, NULL);
03932 #else
03933     get_xy();
03934     XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
03935     m_y_scroll = get_y - window_y;
03936     scroll_y_panner(m_y_scroll);
03937 #endif
03938 }
03939 
03940 void
03941 handle_expose(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
03942 {
03943     struct WindowRec *windowrec = (struct WindowRec *)closure;
03944 
03945     UNUSED(w);
03946     UNUSED(cont);
03947 
03948     if (windowrec == &magnifier) {
03949        if (magnifier_stat < 0) { /* destroy upon exposure */
03950            magnifier_stat = 0;
03951            mag_release(ev);
03952            return;
03953        }
03954        else
03955            magnifier_stat = 0;
03956     }
03957 
03958     do_update_property = False;
03959 
03960     if (have_src_specials && !MAGNIFIER_ACTIVE) {
03961        do_update_property = True;
03962     }
03963 
03964     MYTRACE((stderr, "Expose!"));
03965     expose(windowrec, (&(ev->xexpose))->x, (&(ev->xexpose))->y,
03966           (unsigned int)(&(ev->xexpose))->width, (unsigned int)(&(ev->xexpose))->height);
03967 }
03968 
03969 
03970 void
03971 handle_property_change(Widget w, XtPointer junk,
03972                      XEvent *ev, Boolean *cont)
03973 {
03974     char *prop_ret;
03975     size_t prop_len;
03976 
03977     UNUSED(w);
03978     UNUSED(junk);
03979     UNUSED(cont);
03980 
03981     if ((&(ev->xproperty))->window != XtWindow(globals.widgets.top_level)) /* if spurious event */
03982        return;
03983     
03984     if ((&(ev->xproperty))->atom == atom_src_goto()) {
03985        /* forward search requested */
03986        if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_src_goto(),
03987                                      &prop_ret,
03988                                      XGetWindowProperty)) == 0) {
03989            TRACE_CLIENT((stderr, "property_get_data() failed for atom_src_goto()!"));
03990            return;
03991        }
03992        TRACE_CLIENT((stderr, "got back atom_src_goto: |%s|", prop_ret));
03993        globals.src.fwd_string = prop_ret;
03994        globals.ev.flags |= EV_SRC;
03995     }
03996     else if ((&(ev->xproperty))->atom == atom_find_string()) {
03997        /* string search requested */
03998        if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_find_string(),
03999                                      &prop_ret,
04000                                      XGetWindowProperty)) == 0) {
04001            TRACE_CLIENT((stderr, "property_get_data() failed for atom_find_string()!"));
04002            return;
04003        }
04004        TRACE_FIND((stderr, "got back atom_find_string: |%s|", prop_ret));
04005        resource.find_string = prop_ret;
04006        globals.ev.flags |= EV_FIND;
04007     }
04008     else if ((&(ev->xproperty))->atom == atom_reload()) {
04009        /* like do_sigusr(); there's no data in this case. */
04010        TRACE_CLIENT((stderr, "atom_reload()"));
04011        globals.ev.flags |= EV_RELOAD;
04012     }
04013     else if ((&(ev->xproperty))->atom == atom_newdoc()) {
04014        /* loading a new file */
04015        FILE *new_fp;
04016        if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newdoc(),
04017                                      &prop_ret,
04018                                      XGetWindowProperty)) == 0) {
04019            TRACE_CLIENT((stderr, "property_get_data() returned zero length for atom_newdoc()"));
04020            /* just raise it in this case */
04021            XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
04022            raise_message_windows();
04023            return;
04024        }
04025        TRACE_CLIENT((stderr, "got back atom_newdoc: |%s|", prop_ret));
04026        if ((new_fp = XFOPEN(prop_ret, "r")) == NULL) {
04027            popup_message(globals.widgets.top_level,
04028                        MSG_ERR, NULL, "Loading %s failed: %s",
04029                        prop_ret, strerror(errno));
04030            return;
04031        }
04032        set_dvi_name_expand(prop_ret);
04033        /*FIXME: Here we would like to insert the first page of the new file
04034          into the page history:
04035          
04036          page_history_insert(0);
04037          
04038          However, if the commandline had been:
04039          xdvi -unique +5 file.dvi
04040          then the following property change will change to page 5 and insert
04041          page 5, so in this case two pages will be inserted by the xdvi invocation.
04042          I guess to fix this we'd need a bitmask instead of 2 atoms to set both
04043          features at the same time ...
04044        */
04045        globals.ev.flags |= EV_NEWDOC;
04046     }
04047     else if ((&(ev->xproperty))->atom == atom_newpage()) {
04048        /* jumping to a different page */
04049        int newpage;
04050        char *testptr;
04051        if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newpage(),
04052                                      &prop_ret,
04053                                      XGetWindowProperty)) == 0) {
04054            TRACE_CLIENT((stderr, "property_get_data() failed for atom_newpage(): |%s|", prop_ret));
04055            return;
04056        }
04057        TRACE_CLIENT((stderr, "got back atom_newpage: |%s|", prop_ret));
04058        if (strcmp(prop_ret, "+") == 0) { /* special case: treat `+' as last page */
04059            newpage = total_pages - 1;
04060        }
04061        else {
04062            newpage = strtol(prop_ret, &testptr, 10) - 1;
04063            if (*testptr != '\0') {
04064               XDVI_FATAL((stderr, "Invalid page number: `%s'.", prop_ret));
04065            }
04066        }
04067 
04068        if (newpage == total_pages - 1) { /* as in Act_goto_page() */
04069            goto_page(check_goto_page(newpage), resource.keep_flag ? NULL : home, False);
04070            search_signal_page_changed();
04071        }
04072        else {
04073            goto_page(check_goto_tex_page(newpage), resource.keep_flag ? NULL : home, False);
04074            search_signal_page_changed();
04075        }
04076     }
04077     else if ((&(ev->xproperty))->atom == atom_raise()) {
04078        XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
04079        raise_message_windows();
04080     }
04081     else if ((&(ev->xproperty))->atom == atom_reread_prefs()) {
04082        read_user_preferences(globals.widgets.top_level, ".xdvirc.tmp");
04083     }
04084 }
04085 
04086 
04087 /*
04088  *     Signal routines.  At the signal level, all we do is set flags.
04089  */
04090 
04091 #ifndef FLAKY_SIGPOLL
04092 static RETSIGTYPE
04093 handle_sigpoll(int signo)
04094 {
04095     UNUSED(signo);
04096     
04097     globals.ev.ctr = 1;
04098     event_freq = -1; /* forget Plan B */
04099     sig_flags |= SF_POLL;
04100 # ifndef HAVE_SIGACTION
04101     (void) signal(SIGPOLL, handle_sigpoll);      /* reset the signal */
04102 # endif
04103 }
04104 #endif
04105 
04106 static RETSIGTYPE
04107 handle_sigterm(int signo)
04108 {
04109     UNUSED(signo);
04110     
04111     sig_flags |= SF_TERM;
04112 }
04113 
04114 static RETSIGTYPE
04115 handle_sigchld(int signo)
04116 {
04117     UNUSED(signo);
04118 
04119     sig_flags |= SF_CHLD;
04120 }
04121 
04122 static RETSIGTYPE
04123 handle_sigalrm(int signo)
04124 {
04125     UNUSED(signo);
04126 
04127     sig_flags |= SF_ALRM;
04128 }
04129 
04130 static RETSIGTYPE
04131 handle_sigusr(int signo)
04132 {
04133     UNUSED(signo);
04134     
04135     globals.ev.ctr = 1;
04136     sig_flags |= SF_USR;
04137 }
04138 
04139 static RETSIGTYPE
04140 handle_sigsegv(int signo)
04141 {
04142     UNUSED(signo);
04143     
04144     XDVI_ABORT((stderr, "Segmentation fault - trying to clean up and aborting ..."));
04145 }
04146 
04147 static Boolean sigalarm_initialized = False;
04148 
04149 void
04150 setup_sigalarm(void)
04151 {
04152 #if HAVE_SIGACTION
04153     struct sigaction a;
04154 
04155     a.sa_handler = handle_sigalrm;
04156     (void) sigemptyset(&a.sa_mask);
04157     (void) sigaddset(&a.sa_mask, SIGALRM);
04158     a.sa_flags = 0;
04159     sigaction(SIGALRM, &a, NULL);
04160 #else /* not HAVE_SIGACTION */
04161     (void) signal(SIGALRM, handle_sigalrm);
04162 #endif /* not HAVE_SIGACTION */
04163 
04164     sigalarm_initialized = True;
04165 }
04166 
04167 /*
04168  * Called from main to set up the signal handlers.
04169  */
04170 void
04171 setup_signal_handlers(void)
04172 {
04173 #ifndef FLAKY_SIGPOLL
04174     int       sock_fd       = ConnectionNumber(DISP);
04175 #endif
04176 #if HAVE_SIGACTION
04177     struct sigaction a;
04178 #endif
04179 
04180 #ifndef FLAKY_SIGPOLL
04181 #if HAVE_SIGACTION
04182     /* Subprocess handling, e.g., MakeTeXPK, fails on the Alpha without
04183        this, because SIGPOLL interrupts the call of system(3), since OSF/1
04184        doesn't retry interrupted wait calls by default.  From code by
04185        maj@cl.cam.ac.uk.  */
04186     a.sa_handler = handle_sigpoll;
04187     (void) sigemptyset(&a.sa_mask);
04188     (void) sigaddset(&a.sa_mask, SIGPOLL);
04189     a.sa_flags = SA_RESTART;
04190     sigaction(SIGPOLL, &a, NULL);
04191 #else /* not HAVE_SIGACTION */
04192     (void) signal(SIGPOLL, handle_sigpoll);
04193 #endif /* not HAVE_SIGACTION */
04194 
04195     prep_fd(sock_fd, False);
04196 #endif /* not FLAKY_SIGPOLL */
04197 
04198 #if HAVE_SIGACTION
04199     a.sa_handler = handle_sigterm;
04200     (void) sigemptyset(&a.sa_mask);
04201     (void) sigaddset(&a.sa_mask, SIGINT);
04202     (void) sigaddset(&a.sa_mask, SIGQUIT);
04203     (void) sigaddset(&a.sa_mask, SIGTERM);
04204     (void) sigaddset(&a.sa_mask, SIGHUP);
04205     a.sa_flags = SA_RESETHAND;
04206     sigaction(SIGINT, &a, NULL);
04207     sigaction(SIGQUIT, &a, NULL);
04208     sigaction(SIGTERM, &a, NULL);
04209     sigaction(SIGHUP, &a, NULL);
04210     a.sa_handler = handle_sigsegv;
04211     (void)sigemptyset(&a.sa_mask);
04212     (void)sigaddset(&a.sa_mask, SIGSEGV);
04213     a.sa_flags = 0;
04214     sigaction(SIGSEGV, &a, NULL);
04215 #else /* not HAVE_SIGACTION */
04216     (void) signal(SIGINT, handle_sigterm);
04217     (void) signal(SIGQUIT, handle_sigterm);
04218     (void) signal(SIGTERM, handle_sigterm);
04219     (void) signal(SIGHUP, handle_sigterm);
04220     (void)signal(SIGSEGV, handle_sigsegv);
04221 #endif /* not HAVE_SIGACTION */
04222 
04223 #if HAVE_SIGACTION
04224     a.sa_handler = handle_sigchld;
04225     (void) sigemptyset(&a.sa_mask);
04226     (void) sigaddset(&a.sa_mask, SIGCHLD);
04227     a.sa_flags = 0;
04228     sigaction(SIGCHLD, &a, NULL);
04229 #else /* not HAVE_SIGACTION */
04230     (void) signal(SIGCHLD, handle_sigchld);
04231 #endif /* not HAVE_SIGACTION */
04232 
04233 #if HAVE_SIGACTION
04234     a.sa_handler = handle_sigusr;
04235     (void) sigemptyset(&a.sa_mask);
04236     (void) sigaddset(&a.sa_mask, SIGUSR1);
04237     a.sa_flags = 0;
04238     sigaction(SIGUSR1, &a, NULL);
04239 #else /* not HAVE_SIGACTION */
04240     (void) signal(SIGUSR1, handle_sigusr);
04241 #endif /* not HAVE_SIGACTION */
04242 
04243     (void)sigemptyset(&all_signals);
04244     (void)sigaddset(&all_signals, SIGPOLL);
04245     (void)sigaddset(&all_signals, SIGINT);
04246     (void)sigaddset(&all_signals, SIGQUIT);
04247     (void)sigaddset(&all_signals, SIGTERM);
04248     (void)sigaddset(&all_signals, SIGHUP);
04249     (void)sigaddset(&all_signals, SIGCHLD);
04250     (void)sigaddset(&all_signals, SIGALRM);
04251     (void)sigaddset(&all_signals, SIGUSR1);
04252     (void)sigaddset(&all_signals, SIGSEGV);
04253 
04254 }
04255 
04256 
04257 /*
04258  *     Mid-level signal handlers.  These are called from within read_events(),
04259  *     and do the actual work appropriate for the given signal.
04260  */
04261 
04262 /*
04263  *     Process-related routines.  Call set_chld(xchild *) to indicate
04264  *     that a given child process should be watched for when it
04265  *     terminates.  Call clear_chld() to remove the process from the
04266  *     list.  When the child terminates, the record is removed from
04267  *     the list and xchild->proc is called.
04268  *     The caller can set the `io' member in xchild to a pointer to
04269  *     an xio structure for reading from the file descriptor; see
04270  *     psgs.c and util.c for examples.
04271  */
04272 
04273 static struct xchild *child_recs = NULL;  /* head of child process list */
04274 
04275 void
04276 set_chld(struct xchild *cp)
04277 {
04278     cp->next = child_recs;
04279     child_recs = cp;
04280 }
04281 
04282 void
04283 clear_chld(struct xchild *cp)
04284 {
04285     struct xchild    **cpp;
04286     struct xchild    *cp2;
04287 
04288     if (child_recs == NULL) {
04289        if (globals.debug & DBG_EVENT)
04290            fprintf(stderr, "child_recs: %p\n", (void *)child_recs);
04291        return;
04292     }
04293     for (cpp = &child_recs;;) {
04294        cp2 = *cpp;
04295        if (cp2 == cp)
04296            break;
04297        cpp = &cp2->next;
04298     }
04299     *cpp = cp->next;
04300 }
04301 
04302 static void
04303 do_sigchld(void)
04304 {
04305     pid_t     pid;
04306     int       status;
04307 
04308     sig_flags &= ~SF_CHLD;
04309 
04310 #if ! HAVE_SIGACTION
04311     (void) signal(SIGCHLD, handle_sigchld);      /* reset the signal */
04312 #endif
04313     for (;;) {
04314 #if HAVE_WAITPID
04315        pid = waitpid(-1, &status, WNOHANG);
04316 #else
04317        pid = wait3(&status, WNOHANG, NULL);
04318 #endif
04319        if (pid == 0) break;
04320 
04321        if (pid != -1) {
04322            struct xchild    **cpp;
04323            struct xchild    *cp;
04324 
04325            for (cpp = &child_recs;;) {
04326               cp = *cpp;
04327               if (cp == NULL)
04328                   break;
04329               if (cp->pid == pid) {
04330                   *cpp = cp->next; /* unlink it */
04331                   /* call exit reporting procedure for this child */
04332                   (cp->proc)(status, cp);
04333                   break;
04334               }
04335               cpp = &cp->next;
04336            }
04337            break;
04338        }
04339 
04340        if (errno == EINTR) continue;
04341        if (errno == ECHILD) break;
04342 #if HAVE_WAITPID
04343        perror("xdvi: waitpid");
04344 #else
04345        perror("xdvi: wait3");
04346 #endif
04347        break;
04348     }
04349 }
04350 
04351 
04352 /*
04353  *     File-related routines.  Call set_io() to indicate that a given fd
04354  *     should be watched for ability to input or output data.  Call clear_io()
04355  *     to remove it from the list.  When poll()/select() indicates that the fd
04356  *     is available for the indicated type of i/o, the corresponding routine
04357  *     is called.  Call clear_io() to remove an fd from the list.
04358  *     Both set_io() and clear_io() can be called from within read_proc or
04359  *     write_proc (although turning an io descriptor on or off is better
04360  *     accomplished by setting the events flag in the xio structure, and
04361  *     in the corresponding pollfd structure if the pfd pointer is not NULL
04362  *     (it is always non-NULL when read_proc and write_proc are called)).
04363  *     We allocate space for one additional record in the pollfd array, to
04364  *     accommodate the fd for the X connection; this is done by initializing
04365  *     num_fds to 1 instead of zero.
04366  */
04367 
04368 static struct xio    *iorecs       = NULL;       /* head of xio list */
04369 
04370 #if HAVE_POLL
04371 static struct pollfd *fds   = NULL;
04372 static int           num_fds       = 1;   /* current number of fds */
04373 static int           max_fds       = 0;   /* max allocated number of fds */
04374 static Boolean              io_dirty= True;      /* need to recompute fds[] array */
04375 #else
04376 static int           numfds = 0;
04377 static fd_set        readfds;
04378 static fd_set        writefds;
04379 #endif
04380 
04381 void
04382 set_io(struct xio *ip)
04383 {
04384     ip->next = iorecs;
04385     iorecs = ip;
04386 
04387 #if HAVE_POLL
04388     ++num_fds;
04389     if (!io_dirty && num_fds <= max_fds) {
04390        fds[num_fds - 1].fd = ip->fd;
04391        fds[num_fds - 1].events = ip->xio_events;
04392        ip->pfd = &fds[num_fds - 1];
04393     }
04394     else {
04395        ip->pfd = NULL;
04396        io_dirty = True;
04397     }
04398 #else
04399     if (numfds <= ip->fd) numfds = ip->fd + 1;
04400 #endif
04401 }
04402 
04403 void
04404 clear_io(struct xio *ip)
04405 {
04406     struct xio       **ipp;
04407 
04408     for (ipp = &iorecs;;) {
04409        struct xio *ip2;
04410 
04411        ip2 = *ipp;
04412        if (ip2 == ip) break;
04413        ipp = &ip2->next;
04414     }
04415     *ipp = ip->next;
04416 
04417 #if HAVE_POLL
04418     --num_fds;
04419     io_dirty = True;
04420 #else
04421 # if FLAKY_SIGPOLL
04422     numfds = ConnectionNumber(DISP);
04423 # else
04424     numfds = (event_freq < 0 ? -1 : ConnectionNumber(DISP));
04425 # endif
04426     for (ip = iorecs; ip != NULL; ip = ip->next)
04427        if (ip->fd > numfds)
04428            numfds = ip->fd;
04429     ++numfds;
04430 #endif /* !HAVE_POLL */
04431 }
04432 
04433 static void
04434 do_sigpoll(void)
04435 {
04436 #if FLAKY_SIGPOLL
04437     sig_flags &= ~SF_POLL;
04438 #else
04439     struct xio       *ip;
04440 
04441     sig_flags &= ~SF_POLL;
04442 
04443 # if HAVE_POLL
04444 
04445     if (io_dirty) {
04446        struct pollfd *fp;
04447 
04448        if (num_fds > max_fds) {
04449            if (fds != NULL) free(fds);
04450            fds = xmalloc(num_fds * sizeof *fds);
04451            max_fds = num_fds;
04452            fds->fd = ConnectionNumber(DISP);
04453            fds->events = POLLIN;
04454        }
04455        fp = fds + 1;
04456        for (ip = iorecs; ip != NULL; ip = ip->next) {
04457            fp->fd = ip->fd;
04458            fp->events = ip->xio_events;
04459            ip->pfd = fp;
04460            ++fp;
04461        }
04462        io_dirty = False;
04463     }
04464 
04465     for (;;) {
04466        if (poll(fds + 1, num_fds - 1, 0) >= 0)
04467            break;
04468 
04469        if (errno != EAGAIN && errno != EINTR) {
04470            perror("xdvi: poll");
04471            return;
04472        }
04473     }
04474 
04475     for (ip = iorecs; ip != NULL; ip = ip->next) {
04476        int revents = ip->pfd->revents;
04477        if (revents & POLLIN && ip->read_proc != NULL)
04478            (void)(ip->read_proc)(ip->fd);
04479        if (revents & POLLOUT && ip->write_proc != NULL)
04480            (ip->write_proc)(ip->fd);
04481     }
04482 
04483 # else
04484 
04485     FD_ZERO(&readfds);
04486     FD_ZERO(&writefds);
04487     for (ip = iorecs; ip != NULL; ip = ip->next) {
04488        if (ip->xio_events & XIO_IN)
04489            FD_SET(ip->fd, &readfds);
04490        if (ip->xio_events & XIO_OUT)
04491            FD_SET(ip->fd, &writefds);
04492     }
04493 
04494     for (;;) {
04495        struct timeval tv;
04496 
04497        tv.tv_sec = tv.tv_usec = 0;
04498        if (select(numfds, &readfds, &writefds, (fd_set *) NULL, &tv) >= 0)
04499            break;
04500 
04501        if (errno != EAGAIN && errno != EINTR) {
04502            perror("select (xdvi read_events)");
04503            return;
04504        }
04505     }
04506 
04507     for (ip = iorecs; ip != NULL; ip = ip->next) {
04508        if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL)
04509            (void)(ip->read_proc)(ip->fd);
04510        if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL)
04511            (ip->write_proc)(ip->fd);
04512     }
04513 
04514 # endif
04515 #endif /* not FLAKY_SIGPOLL */
04516 }
04517 
04518 
04519 /*
04520  *     Timer-related routines.  Call set_timer() to set a timer a given number
04521  *     of milliseconds in the future.  At that time, the timer will be cleared
04522  *     and the given procedure will be called with argument set to the struct
04523  *     passed to set_timer().  The timer routine may call set_timer() or
04524  *     cancel_timer().
04525  */
04526 
04527 
04528 static struct xtimer        *timers       = NULL;       /* head of timer list */
04529 
04530 static struct itimerval     itv    = {{0, 0}, {0, 0}};
04531 
04532 #ifndef       timercmp
04533 #define       timercmp(a, b, OP)   (((a)->tv_sec OP (b)->tv_sec || \
04534        ((a)->tv_sec == (b)->tv_sec && (a)->tv_usec OP (b)->tv_usec)))
04535 #endif /* timercmp */
04536 
04537 
04538 static void
04539 show_timers(char *what)
04540 {
04541     struct xtimer *tp;
04542     fprintf(stderr, "=======%s; timers:\n", what);
04543     for (tp = timers; tp != NULL; tp = tp->next) {
04544        fprintf(stderr, "timer %p: %lu\n", (void *)tp, (unsigned long)tp->when.tv_sec);
04545     }
04546     fprintf(stderr, "=======\n");
04547 }
04548 
04549 void
04550 set_timer(struct xtimer *tp, int ms)
04551 {
04552     struct xtimer    **tpp;
04553     struct xtimer    *tp2;
04554 
04555     if (globals.debug & DBG_EVENT)
04556        fprintf(stderr, "%s:%d: set_timer\n", __FILE__, __LINE__);
04557 
04558     gettimeofday(&tp->when, NULL);
04559     itv.it_value.tv_sec = ms / 1000;
04560     itv.it_value.tv_usec = (ms % 1000) * 1000;
04561     tp->when.tv_sec += itv.it_value.tv_sec;
04562     tp->when.tv_usec += itv.it_value.tv_usec;
04563     if (tp->when.tv_usec >= 1000000) {
04564        tp->when.tv_usec -= 1000000;
04565        ++tp->when.tv_sec;
04566     }
04567 
04568     for (tpp = &timers;;) {        /* add timer to list */
04569        tp2 = *tpp;
04570        if (tp2 == NULL || timercmp(&tp->when, &tp2->when, <))
04571            break;
04572        tpp = &tp2->next;
04573     }
04574     tp->next = tp2;
04575     *tpp = tp;
04576 
04577     if (tpp == &timers) {
04578        setitimer(ITIMER_REAL, &itv, NULL);
04579        if (ms == 0)
04580            sig_flags |= SF_ALRM;
04581     }
04582     if (globals.debug & DBG_EVENT)
04583        show_timers("after set_timer");
04584 }
04585 
04586 void
04587 cancel_timer(struct xtimer *tp)
04588 {
04589     struct xtimer    **tpp;
04590 
04591     if (globals.debug & DBG_EVENT)
04592        show_timers("beginning of cancel_timer");
04593     
04594     if (timers == NULL) {
04595        fprintf(stderr, "%s:%d: BUG? timers == NULL!\n", __FILE__, __LINE__);
04596        return;
04597     }
04598 
04599     if (globals.debug & DBG_EVENT)
04600        fprintf(stderr, "%s:%d: cancel_timer %p from %p\n", __FILE__, __LINE__, (void *)&timers, (void *)tp);
04601 
04602     ASSERT(timers != NULL, "timers in cancel_timer() mustn't be NULL");
04603     for (tpp = &timers; ; ) {             /* remove from list */
04604        
04605        if (*tpp == tp)
04606            break;
04607        tpp = &(*tpp)->next;
04608     }
04609 
04610     *tpp = (*tpp)->next;    /* unlink it */
04611 
04612     if (timers == NULL) {   /* cancel SIGALRM */
04613        itv.it_value.tv_sec = itv.it_value.tv_usec = 0;
04614        setitimer(ITIMER_REAL, &itv, NULL);
04615     }
04616 }
04617 
04618 #if XDVI_XT_TIMER_HACK
04619 /*
04620  * Original comment by Paul:
04621  *     Newer versions of the Motif toolkit use the timer facility
04622  *     (XtAppAddTimeOut(), etc.) in the X Toolkit.  Proper functioning of
04623  *     this mechanism, however, requires that the X Toolkit be in charge of
04624  *     blocking.  Since xdvi does its own blocking, this means that we need
04625  *     to provide working alternatives to these X Toolkit routines (by
04626  *     redefining them ...).
04627  *     One symptom of the above-mentioned bug is that the printlog window
04628  *     eventually stops showing dvips progress until you move the mouse.
04629  *
04630  * Comment SU:
04631  *     Xdvik also uses XtAppAddTimeOut() in other places (hyperref/statusline/...),
04632  *     so we also need these redefinitions also in the Xaw version.
04633  *
04634  */
04635 
04636 static void xt_alarm (struct xtimer *);
04637 
04638 static struct xtimer *xt_free_timers = NULL;
04639 
04640 XtIntervalId
04641 XtAddTimeOut(unsigned long interval, XtTimerCallbackProc proc, XtPointer closure)
04642 {
04643     return XtAppAddTimeOut(NULL, interval, proc, closure);
04644 }
04645 
04646 XtIntervalId
04647 XtAppAddTimeOut(XtAppContext app, unsigned long interval, XtTimerCallbackProc proc, XtPointer closure)
04648 {
04649     struct xtimer *tp;
04650     
04651     UNUSED(app);
04652 
04653     /* FIXME: better way of checking this instead of static boolean sigalarm_initialized?
04654        The following doesn't work, even after
04655        sigaddset(&all_signals, SIGALRM);
04656        
04657           static sigset_t sig_set;
04658           (void)sigprocmask(0, NULL, &sig_set);
04659          if (sigismember(&sig_set, SIGALRM))
04660              ... OK ...
04661          else
04662              ... NOT OK ...
04663     */
04664     ASSERT(sigalarm_initialized, "Shouldn't invoke XtAppAddTimeOut() before setup_sigalarm()");
04665     
04666     if (globals.debug & DBG_EVENT)
04667        fprintf(stderr, "XtAppAddTimeOut: %lu msecs\n", interval);
04668     
04669     if (xt_free_timers == NULL)
04670        tp = xmalloc(sizeof *tp);
04671     else {
04672        tp = xt_free_timers;
04673        xt_free_timers = xt_free_timers->next;
04674     }
04675 
04676     tp->proc = xt_alarm;
04677     tp->xt_proc = proc;
04678     tp->closure = closure;
04679     
04680     set_timer(tp, interval);
04681 
04682     if (globals.debug & DBG_EVENT)
04683        show_timers("XtAppAddTimeOut");
04684 
04685     return (XtIntervalId)tp;
04686 }
04687 
04688 void
04689 XtRemoveTimeOut(XtIntervalId id)
04690 {
04691     struct xtimer *tp;
04692 
04693     ASSERT(id != 0, "XtIntervalId argument in XtRemoveTimeOut() mustn't be NULL");
04694     tp = (struct xtimer *)id;
04695 
04696     /* Motif (2.1 on Solaris 9, 2003) sometimes calls XtRemoveTimeOut() after
04697        the timer event has occurred, so we need to be sure not to remove
04698        the timer record twice.  */
04699     if (tp->proc == NULL)
04700        return;
04701     
04702     cancel_timer(tp);
04703     
04704     tp->next = xt_free_timers;
04705     xt_free_timers = tp;
04706 
04707     if (globals.debug & DBG_EVENT)
04708        show_timers("XtRemoveTimeOut");
04709 }
04710 
04711 void
04712 xt_alarm(struct xtimer *tp)
04713 {
04714     XtIntervalId id;
04715 
04716     tp->proc = NULL; /* flag timer as used-up */
04717     id = (XtIntervalId) tp;
04718     (tp->xt_proc)(tp->closure, &id);
04719     
04720     tp->next = xt_free_timers;
04721     xt_free_timers = tp;
04722 
04723     if (globals.debug & DBG_EVENT)
04724        show_timers("xt_alarm");
04725 }
04726 #endif /* XDVI_XT_TIMER_HACK */
04727 
04728 static void
04729 do_sigalrm(void)
04730 {
04731     struct timeval   now;
04732 
04733     sig_flags &= ~SF_ALRM;
04734 #ifndef HAVE_SIGACTION
04735     (void) signal(SIGALRM, handle_sigalrm);      /* reset the signal */
04736 #endif
04737 
04738     gettimeofday(&now, NULL);
04739 
04740     while (timers != NULL && timercmp(&timers->when, &now, <=)) {
04741        struct xtimer *tp = timers;
04742        timers = timers->next;      /* unlink it _first_ */
04743        (tp->proc)(tp);
04744     }
04745 
04746     if (timers != NULL) {          /* set next timer */
04747        int i;
04748        itv.it_value.tv_sec = timers->when.tv_sec - now.tv_sec;
04749        i = timers->when.tv_usec - now.tv_usec;
04750        if (i < 0) {
04751            --itv.it_value.tv_sec;
04752            i += 1000000;
04753        }
04754        itv.it_value.tv_usec = i;
04755 
04756        setitimer(ITIMER_REAL, &itv, NULL);
04757     }
04758 }
04759 
04760 
04761 
04762 /*
04763  *     Handle SIGUSR1 signal.  Pretty straightforward.
04764  */
04765 
04766 static void
04767 do_sigusr(void)
04768 {
04769     sig_flags &= ~SF_USR;
04770 #ifndef HAVE_SIGACTION
04771     (void) signal(SIGUSR1, handle_sigusr);       /* reset the signal */
04772 #endif
04773     globals.ev.flags |= EV_RELOAD;
04774 }
04775 
04776 
04777 static void
04778 do_sigsegv(void)
04779 {
04780     sig_flags &= ~SF_SEGV;
04781 #ifndef HAVE_SIGACTION
04782     (void) signal(SIGSEGV, handle_sigsegv);      /* reset the signal */
04783 #endif
04784     handle_sigsegv(SIGSEGV);
04785 }
04786 
04787 
04788 /*
04789  *     Handle termination signals.  Kill child processes, if permitted.
04790  *     Otherwise, leave it up to the caller.  This is the only place where
04791  *     the EV_TERM event flag is set, and it can only happen if there's an
04792  *     active non-killable process.  This should only happen if read_events()
04793  *     is called from one of a very few select locations.
04794  */
04795 
04796 static void
04797 do_sigterm(void)
04798 {
04799     struct xchild   *cp;
04800 
04801     sig_flags &= ~SF_TERM;
04802 
04803     /* loop through child processes */
04804     for (cp = child_recs; cp != NULL; cp = cp->next) {
04805        if (cp->killable)
04806            kill(cp->pid, SIGKILL);
04807     }
04808 
04809      /* SU: Unlike non-k xdvi, we don't care about whether all children have been
04810        killed, since processes forked via fork_process() (e.g. the web browser)
04811        may still continue running. So we just exit here.
04812      */
04813     xdvi_exit(EXIT_SUCCESS);
04814 
04815     /* since xdvi_exit() may return, we mustn't do the following which
04816        is in non-k xdvi, else we'll end up in a busy loop! I don't know
04817        what's meant by `caller' here anyway ...
04818 
04819        globals.ev.flags |= EV_TERM; /\* otherwise, let the caller handle it *\/
04820     */
04821 }
04822 
04823 /*
04824  *     Since redrawing the screen is (potentially) a slow task, xdvi checks
04825  *     for incoming events while this is occurring.  It does not register
04826  *     a work proc that draws and returns every so often, as the toolkit
04827  *     documentation suggests.  Instead, it checks for events periodically
04828  *     (or not, if SIGPOLL can be used instead) and processes them in
04829  *     a subroutine called by the page drawing routine.  This routine (below)
04830  *     checks to see if anything has happened and processes those events and
04831  *     signals.  (Or, if it is called when there is no redrawing that needs
04832  *     to be done, it blocks until something happens.)
04833  *
04834  *     Ultimately, the goal is to have this be the only place in xdvi where
04835  *     blocking occurs.
04836  *
04837  *     The argument to this function should be a mask of event types (EV_*)
04838  *     indicating which event types should cause read_events to return instead
04839  *     of waiting for more events.  This function will always process all
04840  *     pending events and signals before returning.
04841  *     The return value is the value of globals.ev.flags.
04842  */
04843 
04844 unsigned int
04845 read_events(unsigned int ret_mask)
04846 {
04847     XEvent event;
04848 
04849 #if !HAVE_POLL
04850     if (numfds == 0)
04851        numfds = ConnectionNumber(DISP) + 1;
04852 #endif
04853 
04854     if (globals.debug & DBG_EVENT)
04855        fprintf(stderr, "%s:%d: read_events %u\n", __FILE__, __LINE__, ret_mask);
04856     for (;;) {
04857        globals.ev.ctr = event_freq;
04858        /*
04859         * The above line clears the flag indicating that an event is
04860         * pending.  So if an event comes in right now, the flag will be
04861         * set again needlessly, but we just end up making an extra call.
04862         * Also, be careful about destroying the magnifying glass while
04863         * drawing on it.
04864         */
04865 
04866 #if !FLAKY_SIGPOLL
04867 
04868        if (event_freq < 0) {       /* if SIGPOLL works */
04869            if (!XtPending()) {
04870               sigset_t oldsig;
04871 
04872               (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig);
04873               for (;;) {
04874 #ifdef SHOW_SIG_FLAGS
04875                   /* this gives HUGE output ... */
04876                   if (globals.debug & DBG_EVENT)
04877                      fprintf(stderr, "%s:%d: sig_flags = %d\n",
04878                             __FILE__, __LINE__, sig_flags);
04879 #endif
04880                   while (sig_flags) {
04881                      flags_to_sigproc[sig_flags]();
04882                   }
04883 
04884                   if (XtPending())
04885                      break;
04886 
04887                   if (globals.ev.flags & ret_mask) {
04888                      (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL);
04889                      return globals.ev.flags;
04890                   }
04891                   (void) sigsuspend(&oldsig);
04892               }
04893               (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL);
04894            }
04895        }
04896        else
04897 
04898 #endif /* not FLAKY_SIGPOLL */
04899 
04900            {
04901               for (;;) {
04902                   struct xio       *ip;
04903 
04904                   if (globals.debug & DBG_EVENT)
04905                      fprintf(stderr, "%s:%d: (flaky) sig_flags = %d\n",
04906                             __FILE__, __LINE__, sig_flags);
04907                   while (sig_flags) {
04908                      sigset_t oldsig;
04909 
04910                      (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig);
04911 
04912                      while (sig_flags) {
04913                          flags_to_sigproc[sig_flags]();
04914                      }
04915 
04916                      (void) sigprocmask(SIG_SETMASK, &oldsig,
04917                                       (sigset_t *) NULL);
04918                   }
04919 
04920                   if (XtPending())
04921                      break;
04922 
04923                   if (globals.ev.flags & ret_mask)
04924                      return globals.ev.flags;
04925 
04926                   /* If a SIGUSR1 signal comes right now, then it will wait
04927                      until an X event or another SIGUSR1 signal arrives. */
04928 
04929 #if HAVE_POLL
04930                   if (globals.debug & DBG_EVENT)
04931                      fprintf(stderr, "%s:%d: have_poll!\n",
04932                             __FILE__, __LINE__);
04933                   if (io_dirty) {
04934                      struct pollfd *fp;
04935 
04936                      if (num_fds > max_fds) {
04937                          if (fds != NULL) free(fds);
04938                          fds = xmalloc(num_fds * sizeof *fds);
04939                          max_fds = num_fds;
04940                          fds->fd = ConnectionNumber(DISP);
04941                          fds->events = POLLIN;
04942                      }
04943                      fp = fds + 1;
04944                      for (ip = iorecs; ip != NULL; ip = ip->next) {
04945                          fp->fd = ip->fd;
04946                          fp->events = ip->xio_events;
04947                          ip->pfd = fp;
04948                          ++fp;
04949                      }
04950                      io_dirty = False;
04951                   }
04952 
04953                   for (;;) {
04954                      if (poll(fds, num_fds, -1) >= 0) {
04955                          for (ip = iorecs; ip != NULL; ip = ip->next) {
04956                             int revents = ip->pfd->revents;
04957 
04958                             if (revents & POLLIN && ip->read_proc != NULL)
04959                                 (ip->read_proc)(ip->fd);
04960                             if (revents & POLLOUT && ip->write_proc != NULL)
04961                                 (ip->write_proc)(ip->fd);
04962                          }
04963                          break;
04964                      }
04965 
04966                      if (errno == EINTR)
04967                          break;
04968 
04969                      if (errno != EAGAIN) {
04970                          perror("xdvi: poll");
04971                          break;
04972                      }
04973                   }
04974 #else /* HAVE_POLL */
04975                   if (globals.debug & DBG_EVENT)
04976                      fprintf(stderr, "%s:%d: NOT have_poll!\n",
04977                             __FILE__, __LINE__);
04978                   FD_ZERO(&readfds);
04979                   FD_ZERO(&writefds);
04980                   FD_SET(ConnectionNumber(DISP), &readfds);
04981                   for (ip = iorecs; ip != NULL; ip = ip->next) {
04982                      if (ip->xio_events & XIO_IN)
04983                          FD_SET(ip->fd, &readfds);
04984                      if (ip->xio_events & XIO_OUT)
04985                          FD_SET(ip->fd, &writefds);
04986                   }
04987 
04988                   for (;;) {
04989                      if (select(numfds, &readfds, &writefds, (fd_set *) NULL,
04990                                (struct timeval *) NULL) >= 0) {
04991                          for (ip = iorecs; ip != NULL; ip = ip->next) {
04992                             if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL) {
04993                                 if (globals.debug & DBG_EVENT)
04994                                    fprintf(stderr, "%s:%d: reading from %d\n",
04995                                           __FILE__, __LINE__, ip->fd);
04996                                 (ip->read_proc)(ip->fd);
04997                             }
04998                             if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL) {
04999                                 if (globals.debug & DBG_EVENT)
05000                                    fprintf(stderr, "%s:%d: writing to %d\n",
05001                                           __FILE__, __LINE__, ip->fd);
05002                                 (ip->write_proc)(ip->fd);
05003                             }
05004                          }
05005                          break;
05006                      }
05007 
05008                      if (errno == EINTR)
05009                          break;
05010 
05011                      if (errno != EAGAIN) {
05012                          perror("xdvi: select");
05013                          break;
05014                      }
05015                   }
05016 #endif /* HAVE_POLL */
05017               }
05018            }
05019        XtAppNextEvent(app, &event);
05020 #ifdef MOTIF
05021        if ((resource.expert_mode & XPRT_SHOW_TOOLBAR) != 0)
05022            TipAppHandle(app, &event);
05023 #endif
05024 
05025        if (resized)
05026            get_geom();
05027 
05028        if (event.xany.window == magnifier.win && event.type == Expose) {
05029            handle_expose((Widget) NULL, (XtPointer) &magnifier, &event,
05030                        (Boolean *) NULL);
05031            continue;
05032        }
05033        else if (globals.broken_motif_event_handling &&
05034                (resource.mouse_mode == MOUSE_RULER_MODE ||
05035                 resource.mouse_mode == MOUSE_TEXT_MODE)) {
05036            /* In this case, Act_motion() and Act_release() are not called properly
05037             * for updating the ruler/text selection (it works with the magnifier though),
05038             * so we need to invoke them ourselves here: */
05039            if (event.type == MotionNotify)
05040               Act_motion(NULL, &event, NULL, NULL);
05041            else if (event.type == ButtonRelease)
05042               Act_release(NULL, &event, NULL, NULL);
05043        }
05044 
05045 #ifdef MOTIF
05046        if (XtIsRealized(globals.widgets.top_level)
05047            && event.xany.window == XtWindow(globals.widgets.clip_widget)
05048            && event.type == KeyPress) { /* workaround for #610206 */
05049            motif_translations_hack();
05050        }
05051 #else
05052        if (resource.expert_mode & XPRT_SHOW_BUTTONS)
05053            SubMenuHandleEvent(app, &event);
05054 #endif
05055        XtDispatchEvent(&event);
05056     }
05057 }
05058 
05059 
05060 /*
05061  * Higher-level routines for managing events.
05062  */
05063 
05064 static void
05065 can_exposures(struct WindowRec *windowrec)
05066 {
05067     windowrec->min_x = windowrec->min_y = MAXDIM;
05068     windowrec->max_x = windowrec->max_y = 0;
05069 }
05070 
05071 void
05072 redraw(struct WindowRec *windowrec)
05073 {
05074     currwin = *windowrec;
05075     globals.win_expose.min_x = currwin.min_x + currwin.base_x;
05076     globals.win_expose.min_y = currwin.min_y + currwin.base_y;
05077     globals.win_expose.max_x = currwin.max_x + currwin.base_x;
05078     globals.win_expose.max_y = currwin.max_y + currwin.base_y;
05079     can_exposures(windowrec);
05080 
05081     /* fix for bug #619070 - the complicated flags (and do_update_property)
05082        are needed to avoid too many updates at exposures, especially for
05083        a window of another xdvi instance when the magnifier intersects with
05084        that window.
05085     */
05086     if (have_src_specials && do_update_property
05087        && globals.win_expose.min_x != 1 && globals.win_expose.max_y - globals.win_expose.min_y != 1
05088        && currwin.base_x == 0 && currwin.base_y == 0) {
05089        update_window_property(XtWindow(globals.widgets.top_level), True);
05090     }
05091 
05092     TRACE_EVENTS((stderr, "Redraw %d x %d at (%d, %d) (base=%d,%d)",
05093                 globals.win_expose.max_x - globals.win_expose.min_x,
05094                 globals.win_expose.max_y - globals.win_expose.min_y,
05095                 globals.win_expose.min_x, globals.win_expose.min_y,
05096                 currwin.base_x, currwin.base_y));
05097 
05098     /* can't use ev_cursor here, since the event loop might not see this change quick enough */
05099     if (!(globals.ev.flags & EV_CURSOR)) {
05100        TRACE_EVENTS((stderr, "Cursor: %ld", globals.cursor.flags));
05101        if (!(globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A))) {
05102            if (resource.mouse_mode == MOUSE_RULER_MODE)
05103               XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
05104            else
05105               XDefineCursor(DISP, CURSORWIN, globals.cursor.wait);
05106            XFlush(DISP);
05107        }
05108        globals.ev.flags |= EV_CURSOR;
05109     }
05110 
05111     /* No longer needed since windows are correctly transient now */
05112     /*      raise_message_windows(); */
05113     raise_file_selector();
05114     draw_page();
05115     globals.warn_spec_now = False;
05116 }
05117 
05118 
05119 void
05120 redraw_page(void)
05121 {
05122 #if COLOR
05123     const struct rgb *rgbp;
05124 #endif
05125     TRACE_FILES((stderr, "Redraw page on %p", (void *)globals.dvi_file.bak_fp));
05126 
05127     if (globals.dvi_file.bak_fp == NULL)
05128        return;
05129 
05130     if (scanned_page < current_page) {
05131        TRACE_FILES((stderr, "redraw_page: scanned_page = %d, current_page = %d, prescanning %p\n",
05132                    scanned_page, current_page, (void *)globals.dvi_file.bak_fp));
05133 
05134        prescan(globals.dvi_file.bak_fp);
05135 
05136        if (globals.ev.flags & EV_GE_NEWPAGE) {   /* if we need to re-prescan */
05137            return;
05138        }
05139     }
05140 
05141     TRACE_FILES((stderr, "redraw_page: current_page = %d", current_page));
05142     if (pageinfo_get_window_width(current_page) != globals.page.unshrunk_w
05143        || pageinfo_get_window_height(current_page) != globals.page.unshrunk_h) {
05144        TRACE_FILES((stderr, "NEW SIZE: %dx%d",
05145                    pageinfo_get_window_width(current_page), pageinfo_get_window_height(current_page)));
05146        init_page();
05147        reconfig();
05148     }
05149     
05150     /* We can't call home() without proper unshrunk_page_*, which requires
05151      * prescan(), which can't be done from within read_events() */
05152 
05153     /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
05154        There's some complicated interaction with Postscript specials
05155        here: if home_action check comes before the gs stuff, psp.drawfile
05156        might not get initialized correctly, resulting in empty PS figures
05157        (bounding box instead of figure). This is different in xdvi, due to
05158        different handling of the home_action stuff, but at the moment I can't
05159        remember the reason for this ...
05160        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
05161     */
05162 #if PS_GS
05163     if (gs_postpone_prescan) {
05164        if (!setjmp(globals.ev.canit)) {
05165            gs_resume_prescan();
05166        }
05167        else
05168            return;
05169     }
05170 #endif
05171     if (home_action != NULL) {
05172        home_action(False);
05173        home_action = NULL;
05174        /* This discards the expose event generated by home()
05175           (1 for each page) */
05176        if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) {
05177            return;
05178        }
05179 
05180        can_exposures(&mane);
05181     }
05182 
05183 #if COLOR
05184     rgbp = &bg_initial;
05185     if (page_colors.stack != NULL) {
05186        ASSERT(current_page < (int)page_colors.size, "page_colors.size too small");
05187        rgbp = &page_colors.stack[current_page].bg;
05188     }
05189 
05190     /* Set background color */
05191     if (bg_current == NULL
05192        || rgbp->r != bg_current->color.r
05193        || rgbp->g != bg_current->color.g
05194        || rgbp->b != bg_current->color.b) {
05195        struct bgrec **bgpp;
05196 
05197        for (bgpp = &bg_head;;) {
05198            bg_current = *bgpp;
05199            if (bg_current == NULL) {      /* if bg is not in list */
05200               bg_current = *bgpp = xmalloc(sizeof *bg_current);
05201               bg_current->next = NULL;
05202               bg_current->color = *rgbp;
05203               bg_current->fg_head = NULL;
05204               bg_current->pixel_good = False;
05205               break;
05206            }
05207            if (bg_current->color.r == rgbp->r
05208               && bg_current->color.g == rgbp->g
05209               && bg_current->color.b == rgbp->b)
05210               break;
05211            bgpp = &bg_current->next;
05212        }
05213        fg_current = NULL;   /* force change of foreground color */
05214        /* globals.gc.high is only used in XDrawRectangle, so its background color
05215           doesn't need to be changed.  */
05216        if (globals.debug & DBG_DVI)
05217            printf("Changing background color to %5d %5d %5d\n",
05218                  bg_current->color.r, bg_current->color.g,
05219                  bg_current->color.b);
05220 
05221        if (!bg_current->pixel_good) {
05222            bg_current->pixel = alloc_color(&bg_current->color,
05223                                        color_data[1].pixel);
05224            bg_current->pixel_good = True;
05225        }
05226        XSetWindowBackground(DISP, mane.win, bg_current->pixel);
05227 #if MOTIF && !FIXED_FLUSHING_PAGING
05228        XtVaSetValues(XtParent(globals.widgets.draw_widget), XtNbackground, bg_current->pixel, NULL);
05229 #endif
05230 /*     XSetWindowBackground(DISP, mane.win, bg_current->pixel); */
05231 /*     XClearWindow(DISP, mane.win); */
05232 #if 0 /* don't recolor the cursor - gives too low contrast on color backgrounds,
05233         and bad appearance when part of the background is white and cursor mask
05234         is colored */
05235        {
05236            XColor    bg_Color;
05237            bg_Color.pixel = bg_current->pixel;
05238            XQueryColor(DISP, G_colormap, &bg_Color);
05239            XRecolorCursor(DISP, globals.cursor.ready, &globals.cr_color, &bg_Color);
05240            XRecolorCursor(DISP, globals.cursor.wait, &globals.cr_color, &bg_Color);
05241        }
05242 #endif
05243     }
05244 #endif /* COLOR */
05245     
05246     if (!globals.pausing.flag) {
05247        XClearWindow(DISP, mane.win);
05248     }
05249 
05250     if (G_backing_store != NotUseful) {
05251        mane.min_x = mane.min_y = 0;
05252        mane.max_x = globals.page.w;
05253        mane.max_y = globals.page.h;
05254     }
05255     else {
05256        get_xy();
05257        mane.min_x = -window_x;
05258        mane.max_x = -window_x + mane.width;
05259        mane.min_y = -window_y;
05260        mane.max_y = -window_y + mane.height;
05261     }
05262 
05263 /*      update_TOC(); */
05264     redraw(&mane);
05265 }
05266 
05267 void
05268 do_pages(void)
05269 {
05270     if (globals.debug & DBG_BATCH) {
05271        
05272        (void)read_events(EV_GT_IDLE);
05273        for (current_page = 0; current_page < total_pages; ++current_page) {
05274            if (resource.keep_flag) {
05275               home_action = NULL;
05276            }
05277            else {
05278               home_action = home;
05279            }
05280            globals.warn_spec_now = resource.warn_spec;
05281 #if PS_GS
05282            for (;;) {
05283               redraw_page();
05284               (void) read_events(EV_NOWAIT);
05285               if (!(globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD)))
05286                   break;
05287               globals.ev.flags = EV_IDLE;
05288            }
05289 #else
05290            redraw_page();
05291 #endif
05292        }
05293        xdvi_exit(EXIT_SUCCESS);
05294     }
05295     else {
05296        for (;;) {    /* normal operation */
05297            (void) read_events(EV_GT_IDLE);
05298            TRACE_EVENTS((stderr, "globals.ev.flags: %d; ev_newpage: %d, ev_newdoc: %d, ev_reload: %d\n",
05299                        globals.ev.flags, EV_NEWPAGE, EV_NEWDOC, EV_RELOAD));
05300            /* NOTE: reloading must be checked first! */
05301            if (globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD | EV_PS_TOGGLE)) {
05302               TRACE_EVENTS((stderr, "EV_NEWPAGE | ..."));
05303               globals.ev.flags &= ~(EV_NEWPAGE | EV_EXPOSE | EV_PS_TOGGLE);
05304               if (globals.ev.flags & EV_RELOAD) {
05305                   dviErrFlagT errflag;
05306 
05307                   globals.ev.flags &= ~EV_RELOAD;
05308                   if (load_dvi_file(True, &errflag)) {
05309 #if PS
05310                      ps_clear_cache();
05311 #if PS_GS
05312                      if (resource.gs_alpha) {
05313                          /* restart gs so that user has a method for fixing GS artifacts with gs_alpha
05314                             by using `reload' (see also GS_PIXMAP_CLEARING_HACK) */
05315                          ps_destroy();
05316                      }
05317 #endif
05318 #endif
05319                      statusline_print(STATUS_SHORT, "File reloaded.");
05320                   }
05321 /*                else { */
05322 /*                   statusline_print(STATUS_MEDIUM, "File corrupted, not reloading."); */
05323 /*                } */
05324               }
05325               if (globals.ev.flags & EV_NEWDOC) {
05326                   dviErrFlagT errflag;
05327                   TRACE_EVENTS((stderr, "EV_NEWDOC!"));
05328 /*                fprintf(stderr, "newdoc!\n"); */
05329                   TRACE_FILES((stderr, "current page: %d", current_page));
05330 /*                file_history_set_page(current_page); */
05331                   globals.ev.flags &= ~EV_NEWDOC;
05332                   if (load_dvi_file(True, &errflag)) {
05333                      statusline_append(STATUS_SHORT, "Opened ", "Opened \"%s\"", globals.dvi_name);
05334 /*                   statusline_print(STATUS_SHORT, "Opened \"%s\"", globals.dvi_name); */
05335                      TRACE_FILES((stderr, "Adding to history: |%s|\n", globals.dvi_name));
05336                      if (file_history_push(globals.dvi_name)) { /* it's a new file, add to history */
05337                          TRACE_FILES((stderr, "New entry!"));
05338                          filehist_menu_add_entry(globals.dvi_name);
05339                      }
05340                      else { /* only need to move existing elements to new positions */
05341                          TRACE_FILES((stderr, "Existing entry!\n"));
05342                          filehist_menu_refresh();
05343                      }
05344                   }
05345               }
05346 
05347               can_exposures(&mane);
05348               can_exposures(&magnifier);
05349 
05350 #if PS && PS_GS && GS_PIXMAP_CLEARING_HACK
05351               if (had_ps_specials && !MAGNIFIER_ACTIVE) {
05352                   erasepage_gs();
05353                   had_ps_specials = False;
05354               }
05355 #endif /* PS && PS_GS && GS_PIXMAP_CLEARING_HACK */
05356 
05357               if (globals.dvi_file.bak_fp != NULL) {
05358                   TRACE_EVENTS((stderr, "redraw_page()"));
05359                   redraw_page();
05360               }
05361               else {
05362                   TRACE_EVENTS((stderr, "dvi_file_changed()"));
05363                   (void)dvi_file_changed();
05364               }
05365            }
05366            else if (globals.ev.flags & EV_FILEHIST_GOTO_PAGE) {
05367               int pageno;
05368               globals.ev.flags &= ~EV_FILEHIST_GOTO_PAGE;
05369               pageno = file_history_get_page();
05370               goto_page(pageno, resource.keep_flag ? NULL : home, False);
05371               TRACE_FILES((stderr, "got page: %d", pageno));
05372            }
05373            else if (globals.ev.flags & EV_PAGEHIST_INSERT) {
05374               globals.ev.flags &= ~EV_PAGEHIST_INSERT;
05375               page_history_insert(current_page);
05376            }
05377            else if (globals.ev.flags & EV_FIND_CANCEL) {
05378               /* NOTE: This must be done before checking for expose() */
05379               globals.ev.flags &= ~EV_FIND_CANCEL;
05380            }
05381            else if (globals.ev.flags & EV_ANCHOR) {
05382               /*
05383                * Similar to forward search: search for a htex anchor.
05384                * This needs to come before the next case which does the redraw_page(),
05385                * otherwise anchors for the current page might not be drawn at all:
05386                * anchor_search() sets the info later used by htex_draw_anchormarkers(),
05387                * which is invoked by redraw_page().
05388                */
05389               
05390               /* switch off the link cursor */
05391               globals.cursor.flags &= ~CURSOR_LINK;
05392 
05393               if (dvi_file_changed())
05394                   continue;
05395               
05396               anchor_search(g_anchor_pos);
05397               globals.ev.flags &= ~EV_ANCHOR;
05398            }
05399            else if (globals.ev.flags & EV_SRC) {
05400               /*
05401                * Source special operations are deferred to here because
05402                * they call geom_scan(), which may call define_font(),
05403                * which may call makefont(), which may call read_events()
05404                * recursively.
05405                */
05406               if (globals.src.fwd_string != NULL) {
05407                   const char *s = globals.src.fwd_string;
05408 
05409                   if (dvi_file_changed())
05410                      continue;
05411                   
05412                   source_forward_search(s);
05413                   globals.ev.flags &= ~EV_SRC;
05414                   globals.src.fwd_string = NULL;
05415 
05416                   /* de-iconify window if needed, and raise it */
05417                   XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
05418                   raise_message_windows();
05419               }
05420               else if (source_reverse_x != -1) {
05421                   if (dvi_file_changed())
05422                      continue;
05423                   
05424                   source_reverse_search(source_reverse_x, source_reverse_y, True);
05425                   globals.ev.flags &= ~EV_SRC;
05426               }
05427               else {
05428                   source_special_show(source_show_all);
05429                   globals.ev.flags &= ~EV_SRC;
05430               }
05431            }
05432            /* support for `-findstring' */
05433            else if (globals.ev.flags & EV_FIND) {
05434               if (dvi_file_changed())
05435                   continue;
05436 
05437               if (resource.find_string != NULL) { /* not first call */
05438                   dvi_find_string(resource.find_string, False);
05439                   resource.find_string = NULL;
05440               }
05441               else { /* actually should never arrive here?? */
05442                   dvi_find_string(NULL, True);
05443               }
05444               globals.ev.flags &= ~EV_FIND;
05445            }
05446            else if (globals.ev.flags & EV_MAG_MOVE) {
05447               MYTRACE((stderr, "moving mag!"));
05448               move_magnifier();
05449            }
05450            else if (globals.ev.flags & EV_EXPOSE) {
05451               if (magnifier.min_x < MAXDIM) {
05452 /*                fprintf(stderr, "magnifier < maxdim!\n"); */
05453                   if (mane.min_x >= MAXDIM) {
05454 /*                   fprintf(stderr, "mane >= maxdim!\n"); */
05455                      globals.ev.flags &= ~EV_EXPOSE;
05456                   }
05457                   redraw(&magnifier);
05458               }
05459               else {
05460                   /* see comment in mag.c */
05461                   globals.ev.flags &= ~EV_EXPOSE;
05462                   if (mane.min_x < MAXDIM) {
05463                      redraw(&mane);
05464                   }
05465               }
05466            }
05467            else if (globals.ev.flags & EV_CURSOR) {
05468               /*
05469                * This code eliminates unnecessary calls to XDefineCursor,
05470                * since this is a slow operation on some hardware (e.g., S3
05471                * chips).
05472                */
05473               XSync(DISP, False);
05474               if (!XtPending()) {
05475                   Cursor curr;
05476                      
05477                   if (globals.cursor.flags & CURSOR_DRAG_V)
05478                      curr = globals.cursor.drag_v;
05479                   else if (globals.cursor.flags & CURSOR_DRAG_H)
05480                      curr = globals.cursor.drag_h;
05481                   else if (globals.cursor.flags & CURSOR_DRAG_A)
05482                      curr = globals.cursor.drag_a;
05483                   else if (resource.mouse_mode == MOUSE_RULER_MODE)
05484                      curr = globals.cursor.rule;
05485                   else if (resource.mouse_mode == MOUSE_TEXT_MODE && !(globals.cursor.flags & CURSOR_LINK))
05486                      curr = globals.cursor.text;
05487                   else if (globals.cursor.flags & CURSOR_LINK)
05488                      curr = globals.cursor.link;
05489                   else if (globals.cursor.flags & CURSOR_MAG)
05490                      curr = globals.cursor.mag;
05491                   else if (globals.cursor.flags & CURSOR_CORRUPTED)
05492                      curr = globals.cursor.corrupted;
05493                   else if (globals.pausing.flag)
05494                      curr = globals.cursor.pause;
05495                   else
05496                      curr = globals.cursor.ready;
05497                   XDefineCursor(DISP, CURSORWIN, curr);
05498                   globals.ev.flags &= ~EV_CURSOR;
05499               }
05500            }
05501            XFlush(DISP);
05502        }
05503     }
05504 }
05505 
05506 static void
05507 Act_unpause_or_next(Widget w, XEvent *event,
05508                   String *params, Cardinal *num_params)
05509 {
05510     if (block_this_mouse_event())
05511        return;
05512     
05513     if (globals.pausing.flag) {
05514         globals.pausing.num++;
05515        if (globals.pausing.num_save)
05516            globals.pausing.num_save[current_page] = globals.pausing.num; 
05517        redraw_page();
05518     }
05519     else {
05520        Act_down_or_next(w, event, params, num_params);
05521     }
05522 }
05523 
05524 /*
05525  * timer callback for watching the DVI file.
05526  */
05527 void
05528 watch_file_cb(XtPointer client_data, XtIntervalId *id)
05529 {
05530     static XtIntervalId timer = 0;
05531     
05532     UNUSED(client_data);
05533     UNUSED(id);
05534 
05535     if (resource.watch_file > 0.0) {
05536        unsigned long watch_time_ms;
05537 
05538        (void)dvi_file_changed();
05539     
05540        if (timer) {
05541            XtRemoveTimeOut(timer);
05542            timer = (XtIntervalId)(XtIntervalId)0;
05543        }
05544 
05545        watch_time_ms = (unsigned long)(resource.watch_file * 1000);
05546        timer = XtAppAddTimeOut(app, watch_time_ms, watch_file_cb, (XtPointer)NULL);
05547     }
05548 }
05549 
05550 void Act_pagehistory_back(Widget w, XEvent *event, String *params, Cardinal *num_params)
05551 {
05552     int arg;
05553 
05554     if (block_this_mouse_event())
05555        return;
05556     
05557     UNUSED(w);
05558     UNUSED(event);
05559 
05560     if (!get_int_arg(params, num_params, &arg)) {
05561        arg = 1;
05562     }
05563     page_history_move(-arg);
05564 }
05565 
05566 void Act_pagehistory_forward(Widget w, XEvent *event, String *params, Cardinal *num_params)
05567 {
05568     int arg;
05569 
05570     if (block_this_mouse_event())
05571        return;
05572     
05573     UNUSED(w);
05574     UNUSED(event);
05575 
05576     if (!get_int_arg(params, num_params, &arg)) {
05577        arg = 1;
05578     }
05579     page_history_move(arg);
05580 }
05581 
05582 void Act_pagehistory_clear(Widget w, XEvent *event, String *params, Cardinal *num_params)
05583 {
05584     UNUSED(w);
05585     UNUSED(event);
05586     UNUSED(params);
05587     UNUSED(num_params);
05588 
05589     if (block_this_mouse_event())
05590        return;
05591     
05592     page_history_clear();
05593 }
05594 
05595 void Act_pagehistory_delete_backward(Widget w, XEvent *event, String *params, Cardinal *num_params)
05596 {
05597     int arg;
05598 
05599     if (block_this_mouse_event())
05600        return;
05601     
05602     UNUSED(w);
05603     UNUSED(event);
05604 
05605     if (!get_int_arg(params, num_params, &arg)) {
05606        arg = 1;
05607     }
05608     page_history_delete(-arg);
05609 }
05610 
05611 void Act_pagehistory_delete_forward(Widget w, XEvent *event, String *params, Cardinal *num_params)
05612 {
05613     int arg;
05614 
05615     if (block_this_mouse_event())
05616        return;
05617     
05618     UNUSED(w);
05619     UNUSED(event);
05620 
05621     if (!get_int_arg(params, num_params, &arg)) {
05622        arg = 1;
05623     }
05624     page_history_delete(arg);
05625 }
05626 
05627 #ifdef MOTIF
05628 void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params)
05629 {
05630     int arg;
05631     
05632     if (block_this_mouse_event())
05633        return;
05634     
05635     UNUSED(w);
05636     UNUSED(event);
05637 
05638     if (!get_int_arg(params, num_params, &arg)) {
05639        arg = -1;
05640     }
05641     
05642     popup_preferences_dialog(globals.widgets.top_level, arg);
05643 }
05644 #endif /* MOTIF */
05645