/*======================================================================*\ Copyright (c) 1990-2004 Paul Vojta and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. NOTE: xdvi is based on prior work, as noted in the modification history in xdvi.c. \*========================================================================*/ #include "xdvi-config.h" #ifdef STDC_HEADERS # include # include #endif #include #include /* this defines FASYNC */ #ifdef HAVE_SYS_FCNTL_H # include /* Or this might define FASYNC */ #endif #include /* this defines SIOCSPGRP and FIOASYNC */ #include /* ignore HAVE_SYS_WAIT_H -- we always need WNOHANG */ #include "xdvi.h" /* this includes Xlib and Xutil are already included */ #include "xdvi-debug.h" #ifndef MOTIF #include /* for XtNresizable */ #endif #include #include /* for gettimeofday */ #include #include #include #include /* needed for def. of XtNiconX(??) */ #include "pagesel.h" #include "filehist.h" #include "special.h" #include "psgs.h" #include #include #include "util.h" #include "x_util.h" #include "string-utils.h" #include "print-dialog.h" #include "search-dialog.h" #include "sfSelFile.h" #include "mag.h" #include "help-window.h" #include "message-window.h" #include "dvi-draw.h" #include "statusline.h" #include "hypertex.h" #include "dvi-init.h" #include "Tip.h" #include "browser.h" #include "search-internal.h" #include "my-snprintf.h" #include "events.h" #include "selection.h" #include "encodings.h" #include "pagehist.h" #include "xm_colorsel.h" #include "xm_toolbar.h" #include "xaw_menu.h" #include "xm_menu.h" #include "xm_prefs.h" #include "xm_prefs_appearance.h" /* for update_preferences_expert() */ #include "xm_prefs_fonts.h" /* for update_preferences_color() */ #include "xm_prefs_page.h" /* for update_preferences_shrink() */ #ifdef X_NOT_STDC_ENV extern int errno; #endif /* X_NOT_STDC_ENV */ /* Linux prefers O_ASYNC over FASYNC; SGI IRIX does the opposite. */ #if !defined(FASYNC) && defined(O_ASYNC) # define FASYNC O_ASYNC #endif #if !defined(FLAKY_SIGPOLL) && !HAVE_STREAMS && !defined(FASYNC) # if !defined(SIOCSPGRP) || !defined(FIOASYNC) # define FLAKY_SIGPOLL 1 # endif #endif #ifndef FLAKY_SIGPOLL # ifndef SIGPOLL # define SIGPOLL SIGIO # endif # ifndef SA_RESTART # define SA_RESTART 0 # endif # if HAVE_STREAMS # include # ifndef S_RDNORM # define S_RDNORM S_INPUT # endif # ifndef S_RDBAND # define S_RDBAND 0 # endif # ifndef S_HANGUP # define S_HANGUP 0 # endif # ifndef S_WRNORM # define S_WRNORM S_OUTPUT # endif # endif /* HAVE_STREAMS */ #endif /* not FLAKY_SIGPOLL */ #if HAVE_SIGACTION && !defined SA_RESETHAND # ifdef SA_ONESHOT # define SA_RESETHAND SA_ONESHOT # else # undef HAVE_SIGACTION /* Needed for Mac OS X < 10.2 (9/2002) */ # endif #endif #if HAVE_POLL # include #else # if HAVE_SYS_SELECT_H # include # else # if HAVE_SELECT_H # include # endif # endif # define XIO_IN 1 # define XIO_OUT 2 #endif /* HAVE_POLL */ static sigset_t all_signals; /* * Interrupt system for receiving events. The program sets a flag * whenever an event comes in, so that at the proper time (i.e., when * reading a new dvi item), we can check incoming events to see if we * still want to go on printing this page. This way, one can stop * displaying a page if it is about to be erased anyway. We try to read * as many events as possible before doing anything and base the next * action on all events read. * Note that the Xlib and Xt routines are not reentrant, so the most we * can do is set a flag in the interrupt routine and check it later. * Also, sometimes the interrupts are not generated (some systems only * guarantee that SIGIO is generated for terminal files, and on the system * I use, the interrupts are not generated if I use "(xdvi foo &)" instead * of "xdvi foo"). Therefore, there is also a mechanism to check the * event queue every 70 drawing operations or so. This mechanism is * disabled if it turns out that the interrupts do work. * For a fuller discussion of some of the above, see xlife in * comp.sources.x. */ /* * Signal flags */ /* This could be static volatile, but we want to avoid any optimizer bugs. */ VOLATILE unsigned int sig_flags = 0; #define SF_USR 1 #define SF_ALRM 2 #define SF_POLL 4 #define SF_CHLD 8 #define SF_TERM 16 #define SF_SEGV 32 static void do_sigusr(void); static void do_sigalrm(void); static void do_sigpoll(void); static void do_sigchld(void); static void do_sigterm(void); static void do_sigsegv(void); /* these must be in the same order as SF_*. The higher flags have higher priority, since these are checked first when resolving flags_to_sigproc[sig_flags]. Example: flag := SF_TERM | SF_CHLD = 24 flags_to_sigproc[24] => do_sigterm, which sets flag to 24 & ~SF_TERM == 8 then, in next check: flags_to_sigproc[8] => do_sigchld, which sets flag 8 & ~SF_CHLD == 0. */ #define SP0 do_sigusr #define SP1 do_sigalrm #define SP2 do_sigpoll #define SP3 do_sigchld #define SP4 do_sigterm #define SP5 do_sigsegv typedef void (*signalproc)(void); static const signalproc flags_to_sigproc[64] = { NULL, /* 1 */ SP0, /* 2 - 3 */ SP1, SP1, /* 4 - 7 */ SP2, SP2, SP2, SP2, /* 8 - 15 */ SP3, SP3, SP3, SP3, SP3, SP3, SP3, SP3, /* 16 - 31 */ SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4, /* 32 - 63 */ SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5 }; #undef SP0 #undef SP2 #undef SP3 #undef SP4 #undef SP5 /* file-static variables for prefix argument mechanism */ static Boolean m_have_arg = False; /* flag whether we have a possible prefix arg */ static int m_number = 0; /* prefix arg value, without the sign */ static int m_sign = 1; /* is prefix arg negative? */ /* to remember the scrollbar positions so that we can restore them when `keep position' is active and window is resized to full size (removing scrollbars) and then back (#810501) */ static int m_x_scroll = 0, m_y_scroll = 0; static int source_reverse_x, source_reverse_y; static int source_show_all; /* globals for color stuff */ Pixel plane_masks[4]; XColor color_data[2]; struct pagecolor_info page_colors = { 0, NULL }; struct rgb *color_bottom; unsigned int color_bot_size; /* number of entries */ struct colorframe *rcs_top; struct rgb fg_initial; /* Initial fg (from command line) */ struct rgb bg_initial; /* Initial bg */ struct bgrec *bg_head = NULL; /* head of list */ struct bgrec *bg_current = NULL; /* current bg value */ struct fgrec *fg_current; /* current fg value */ struct fgrec *fg_active = NULL; /* where the GCs are */ Pixel *color_list; /* list of colors */ unsigned int color_list_len = 0; /* current len of list*/ unsigned int color_list_max = 0; /* allocated size */ Boolean color_warned = False; /* FIXME: the following file-static flag is used to control whether a mouse-related Act_* should block further actions that have been registered for the same event. It is used for the Act_href_* routines. This used to be a longjmp(somewehre_in_the_event_loop); but that was pretty dreadful as well. Surely there must be some other (X(t)-specific?) way to do this?? (Apart from de-registering the action and re-registering it again.) */ static Boolean m_block_mouse_event = False; /* for calling it from mag.c */ void block_next_mouse_event(void) { m_block_mouse_event = True; } Boolean block_this_mouse_event(void) { if (m_block_mouse_event == True) { m_block_mouse_event = False; return True; } return False; } void null_mouse(XEvent *event) { UNUSED(event); } mouse_proc mouse_motion = null_mouse; mouse_proc mouse_release = null_mouse; static void Act_digit(Widget, XEvent *, String *, Cardinal *); static void Act_find(Widget, XEvent *, String *, Cardinal *); static void Act_find_next(Widget, XEvent *, String *, Cardinal *); static void Act_minus(Widget, XEvent *, String *, Cardinal *); static void Act_quit(Widget, XEvent *, String *, Cardinal *); static void Act_quit_confirm(Widget, XEvent *, String *, Cardinal *); static void Act_print(Widget, XEvent *, String *, Cardinal *); static void Act_save(Widget, XEvent *, String *, Cardinal *); static void Act_help(Widget, XEvent *, String *, Cardinal *); static void Act_goto_page(Widget, XEvent *, String *, Cardinal *); static void Act_declare_page_number(Widget, XEvent *, String *, Cardinal *); static void Act_toggle_mark(Widget, XEvent *, String *, Cardinal *); static void Act_home(Widget, XEvent *, String *, Cardinal *); static void Act_home_or_top(Widget, XEvent *, String *, Cardinal *); static void Act_end_or_bottom(Widget, XEvent *, String *, Cardinal *); static void Act_center(Widget, XEvent *, String *, Cardinal *); static void Act_left(Widget, XEvent *, String *, Cardinal *); static void Act_right(Widget, XEvent *, String *, Cardinal *); static void Act_up(Widget, XEvent *, String *, Cardinal *); static void Act_down(Widget, XEvent *, String *, Cardinal *); static void Act_up_or_previous(Widget, XEvent *, String *, Cardinal *); static void Act_down_or_next(Widget, XEvent *, String *, Cardinal *); static void Act_set_margins(Widget, XEvent *, String *, Cardinal *); static void Act_show_display_attributes(Widget, XEvent *, String *, Cardinal *); static void Act_set_density(Widget, XEvent *, String *, Cardinal *); static void Act_change_density(Widget, XEvent *, String *, Cardinal *); static void Act_fullscreen(Widget, XEvent *, String *, Cardinal *); #ifdef GREY static void Act_set_greyscaling(Widget, XEvent *, String *, Cardinal *); #endif #if COLOR static void Act_set_color(Widget, XEvent *, String *, Cardinal *); #endif static void Act_htex_anchorinfo(Widget, XEvent *, String *, Cardinal *); static void Act_reread_dvi_file(Widget, XEvent *, String *, Cardinal *); static void Act_select_dvi_file(Widget, XEvent *, String *, Cardinal *); static void Act_discard_number(Widget, XEvent *, String *, Cardinal *); static void Act_drag(Widget, XEvent *, String *, Cardinal *); static void Act_wheel(Widget, XEvent *, String *, Cardinal *); static void Act_motion(Widget, XEvent *, String *, Cardinal *); static void Act_release(Widget, XEvent *, String *, Cardinal *); static void Act_source_special(Widget, XEvent *, String *, Cardinal *); static void Act_show_source_specials(Widget, XEvent *, String *, Cardinal *); static void Act_source_what_special(Widget, XEvent *, String *, Cardinal *); static void Act_unpause_or_next(Widget, XEvent *, String *, Cardinal *); static void Act_ruler_snap_origin(Widget w, XEvent *event, String *params, Cardinal *num_params); static void Act_text_mode(Widget w, XEvent *event, String *params, Cardinal *num_params); static void Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params); #ifdef MOTIF static void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params); #endif static XtActionsRec m_actions[] = { {"digit", Act_digit}, {"switch-mode", Act_switch_mode}, {"ruler-mode", Act_ruler_mode}, {"text-mode", Act_text_mode}, {"minus", Act_minus}, {"recent-files", Act_recent_files}, {"quit", Act_quit}, {"quit-confirm", Act_quit_confirm}, {"print", Act_print}, {"save", Act_save}, {"find", Act_find}, {"find-next", Act_find_next}, {"help", Act_help}, {"goto-page", Act_goto_page}, {"use-tex-pages", Act_use_tex_pages}, {"forward-page", Act_forward_page}, {"back-page", Act_back_page}, {"toggle-mark", Act_toggle_mark}, {"declare-page-number", Act_declare_page_number}, {"home", Act_home}, {"home-or-top", Act_home_or_top}, {"end-or-bottom", Act_end_or_bottom}, {"center", Act_center}, {"set-keep-flag", Act_set_keep_flag}, {"ruler-snap-origin", Act_ruler_snap_origin}, {"left", Act_left}, {"right", Act_right}, {"up", Act_up}, {"down", Act_down}, {"up-or-previous", Act_up_or_previous}, {"down-or-next", Act_down_or_next}, {"set-margins", Act_set_margins}, {"show-display-attributes", Act_show_display_attributes}, {"set-shrink-factor", Act_set_shrink_factor}, {"shrink-to-dpi", Act_shrink_to_dpi}, {"set-density", Act_set_density}, {"change-density", Act_change_density}, {"fullscreen", Act_fullscreen}, #ifdef GREY {"set-greyscaling", Act_set_greyscaling}, #endif #if COLOR {"set-color", Act_set_color}, #endif #ifdef PS {"set-ps", Act_set_ps}, #endif {"htex-back", Act_htex_back}, {"htex-forward", Act_htex_forward}, {"htex-anchorinfo", Act_htex_anchorinfo}, #ifdef PS_GS {"set-gs-alpha", Act_set_gs_alpha}, #endif {"set-expert-mode", Act_set_expert_mode}, {"reread-dvi-file", Act_reread_dvi_file}, {"select-dvi-file", Act_select_dvi_file}, {"discard-number", Act_discard_number}, {"drag", Act_drag}, {"wheel", Act_wheel}, {"motion", Act_motion}, {"release", Act_release}, {"source-special", Act_source_special}, {"show-source-specials", Act_show_source_specials}, {"source-what-special", Act_source_what_special}, {"unpause-or-next", Act_unpause_or_next}, #if defined(NEW_MENU_CREATION) || defined(MOTIF) {"set-papersize", Act_set_papersize}, {"set-paper-landscape", Act_set_paper_landscape}, #endif /* NEW_MENU_CREATION */ {"load-url", Act_load_url}, {"pagehistory-clear", Act_pagehistory_clear}, {"pagehistory-back", Act_pagehistory_back}, {"pagehistory-forward", Act_pagehistory_forward}, {"pagehistory-delete-backward", Act_pagehistory_delete_backward}, {"pagehistory-delete-forward", Act_pagehistory_delete_forward}, #ifdef MOTIF {"prefs-dialog", Act_prefs_dialog}, #endif }; /* * Access to m_actions */ int get_num_actions(void) { return XtNumber(m_actions); } XtActionsRec *get_actions(void) { return m_actions; } /* * Data for buffered events. */ #ifndef FLAKY_SIGPOLL static VOLATILE int event_freq = 70; #else #define event_freq 70 #endif static void can_exposures(struct WindowRec *windowrec); /* * Set the flag so that termination occurs, via the above routine. * This should be used in place of xdvi_exit() when there may be a * non-killable process running (e.g., anytime within read_events()). */ static void xdvi_normal_exit(void) { sig_flags |= SF_TERM; } static void xdvi_normal_exit_cb(XtPointer arg) { UNUSED(arg); sig_flags |= SF_TERM; } void xdvi_exit_callback(Widget w, XtPointer client_data, XtPointer call_data) { UNUSED(w); UNUSED(client_data); UNUSED(call_data); sig_flags |= SF_TERM; } /* * Event-handling routines. */ void expose(struct WindowRec *windowrec, int x, int y, unsigned int w, unsigned int h) { if (windowrec->min_x > x) windowrec->min_x = x; if (windowrec->max_x < (int)(x + w)) windowrec->max_x = x + w; if (windowrec->min_y > y) windowrec->min_y = y; if (windowrec->max_y < (int)(y + h)) windowrec->max_y = y + h; globals.ev.flags |= EV_EXPOSE; } void clearexpose(struct WindowRec *windowrec, int x, int y, unsigned w, unsigned h) { XClearArea(DISP, windowrec->win, x, y, w, h, False); expose(windowrec, x, y, w, h); } /* * Routines for X11 toolkit. */ static Position window_x, window_y; static Arg arg_xy[] = { {XtNx, (XtArgVal) &window_x}, {XtNy, (XtArgVal) &window_y}, }; #define get_xy() XtGetValues(globals.widgets.draw_widget, arg_xy, XtNumber(arg_xy)) static void warn_num_params(const char *act_name, String *params, int num_params, int max_params) { if (num_params > max_params) { XDVI_WARNING((stderr, "Too many parameters (%d) for action \"%s\", ignoring all after \"%s\"", num_params, act_name, params[max_params - 1])); } } struct xdvi_action * compile_action(const char *str) { const char *p, *p1, *p2; XtActionsRec *actp; struct xdvi_action *ap; while (*str == ' ' || *str == '\t') ++str; if (*str == '\0' || *str == '\n') return NULL; p = str; /* find end of command name */ while (isalnum((int)*p) || *p == '-' || *p == '_') ++p; for (actp = m_actions; ; ++actp) { if (actp >= m_actions + XtNumber(m_actions)) { const char *tmp = strchr(str, '\0'); if (tmp == NULL) { tmp = p; } XDVI_WARNING((stderr, "Cannot compile action \"%.*s\".", (int)(tmp - str), str)); return NULL; } if (memcmp(str, actp->string, p - str) == 0 && actp->string[p - str] == '\0') break; } while (*p == ' ' || *p == '\t') ++p; if (*p != '(') { while (*p != '\0' && *p != '\n') ++p; XDVI_WARNING((stderr, "Syntax error in action %.*s.", (int)(p - str), str)); return NULL; } ++p; while (*p == ' ' || *p == '\t') ++p; for (p1 = p;; ++p1) { if (*p1 == '\0' || *p1 == '\n') { XDVI_WARNING((stderr, "Syntax error in action %.*s.", (int)(p1 - str), str)); return NULL; } if (*p1 == ')') break; } ap = xmalloc(sizeof *ap); ap->proc = actp->proc; for (p2 = p1;; --p2) { if (p2 <= p) { /* if no args */ ap->num_params = 0; ap->param = NULL; break; } else if (p2[-1] != ' ' && p2[-1] != '\t') { char *arg; arg = xmalloc(p2 - p + 1); memcpy(arg, p, p2 - p); arg[p2 - p] = '\0'; ap->num_params = 1; ap->param = arg; break; } } ap->next = compile_action(p1 + 1); return ap; } void handle_command(Widget widget, XtPointer client_data, XtPointer call_data) { struct xdvi_action *actp; UNUSED(call_data); /* call all actions registered for this event */ for (actp = (struct xdvi_action *)client_data; actp != NULL; actp = actp->next) { if (globals.debug & DBG_EVENT) fprintf(stderr, "calling action with param: %s\n", actp->param); (actp->proc) (widget, NULL, &actp->param, &actp->num_params); } } #ifdef MOTIF int set_bar_value(Widget bar, int value, int max) { XmScrollBarCallbackStruct call_data; #ifdef TEST_SCROLLING fprintf(stderr, "set_bar_value: val %d, max %d\n", value, max); #endif if (value > max) value = max; if (value < 0) value = 0; call_data.value = value; XtVaSetValues(bar, XmNvalue, value, NULL); XtCallCallbacks(bar, XmNvalueChangedCallback, &call_data); return value; } #endif void home(wide_bool scrl) { if (!scrl) XUnmapWindow(DISP, mane.win); # ifdef MOTIF { int value; value = (globals.page.w - mane.width) / 2; if (value > resource.sidemargin_int / mane.shrinkfactor) value = resource.sidemargin_int / mane.shrinkfactor; (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width)); value = (globals.page.h - mane.height) / 2; if (value > resource.topmargin_int / mane.shrinkfactor) value = resource.topmargin_int / mane.shrinkfactor; (void)set_bar_value(globals.widgets.y_bar, value, (int)(globals.page.h - mane.height)); } # else get_xy(); if (globals.widgets.x_bar != NULL) { int coord = (globals.page.w - mane.width) / 2; if (coord > resource.sidemargin_int / mane.shrinkfactor) coord = resource.sidemargin_int / mane.shrinkfactor; XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (window_x + coord)); } if (globals.widgets.y_bar != NULL) { int coord = (globals.page.h - mane.height) / 2; if (coord > resource.topmargin_int / mane.shrinkfactor) coord = resource.topmargin_int / mane.shrinkfactor; XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (window_y + coord)); } # endif /* MOTIF */ if (!scrl) { XMapWindow(DISP, mane.win); /* Wait for the server to catch up---this eliminates flicker. */ XSync(DISP, False); } handle_x_scroll(NULL, NULL, NULL, NULL); handle_y_scroll(NULL, NULL, NULL, NULL); } /* * Same as home(), except move to the bottom of the page. */ static void home_bottom(wide_bool scrl) { UNUSED(scrl); XUnmapWindow(DISP, mane.win); #ifdef MOTIF { int value; value = (globals.page.w - mane.width) / 2; if (value > resource.sidemargin_int / mane.shrinkfactor) value = resource.sidemargin_int / mane.shrinkfactor; (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width)); (void)set_bar_value(globals.widgets.y_bar, (int)(globals.page.h - mane.height), (int)(globals.page.h - mane.height)); } #else /* MOTIF */ get_xy(); if (globals.widgets.x_bar != NULL) { int coord = (globals.page.w - mane.width) / 2; if (coord > resource.sidemargin_int / mane.shrinkfactor) coord = resource.sidemargin_int / mane.shrinkfactor; XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (window_x + coord)); } if (globals.widgets.y_bar != NULL) XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(window_y + (globals.page.h - mane.height))); #endif /* MOTIF */ XMapWindow(DISP, mane.win); /* Wait for the server to catch up---this eliminates flicker. */ XSync(DISP, False); handle_x_scroll(NULL, NULL, NULL, NULL); handle_y_scroll(NULL, NULL, NULL, NULL); } #ifndef MOTIF static void handle_destroy_bar(Widget w, XtPointer client_data, XtPointer call_data) { UNUSED(w); UNUSED(call_data); *(Widget *) client_data = NULL; } #endif static Boolean resized = False; static void get_geom(void) { static Dimension new_clip_w, new_clip_h; static Arg arg_wh_clip[] = { {XtNwidth, (XtArgVal) &new_clip_w}, {XtNheight, (XtArgVal) &new_clip_h}, }; static Dimension window_w, window_h; static Arg arg_wh[] = { {XtNwidth, (XtArgVal) &window_w}, {XtNheight, (XtArgVal) &window_h}, }; int old_clip_w; #ifdef MOTIF /* event handlers for Motif scrollbars have already been added in create_initialize_widgets(), xdvi.c */ XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh)); #else XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh)); /* Note: widgets may be destroyed but not forgotten */ if (globals.widgets.x_bar == NULL) { globals.widgets.x_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal"); if (globals.widgets.x_bar != NULL) { XtAddCallback(globals.widgets.x_bar, XtNdestroyCallback, handle_destroy_bar, (XtPointer)&globals.widgets.x_bar); XtAddEventHandler(globals.widgets.x_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, False, handle_x_scroll, NULL); } } if (globals.widgets.y_bar == NULL) { globals.widgets.y_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical"); if (globals.widgets.y_bar != NULL) { XtAddCallback(globals.widgets.y_bar, XtNdestroyCallback, handle_destroy_bar, (XtPointer)&globals.widgets.y_bar); XtAddEventHandler(globals.widgets.y_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, False, handle_y_scroll, NULL); } } #endif XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip)); old_clip_w = mane.width; /* we need to do this because sizeof(Dimension) != sizeof(int) */ mane.width = new_clip_w; mane.height = new_clip_h; if (old_clip_w == 0) { globals.ev.flags |= EV_NEWPAGE; } if (resource.keep_flag) { #ifndef MOTIF Dimension d; int curr_scroll; if ((globals.widgets.x_bar != NULL && m_x_scroll != 0) || (globals.widgets.y_bar != NULL && m_y_scroll != 0)) { get_xy(); } #endif if (globals.widgets.x_bar != NULL && m_x_scroll != 0) { #ifdef MOTIF if (m_x_scroll > 0) (void)set_bar_value(globals.widgets.x_bar, m_x_scroll, (int)(globals.page.w - mane.width)); #else XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL); curr_scroll = d - window_x; if (m_x_scroll > curr_scroll) { TRACE_GUI((stderr, "======== diff: %d", m_x_scroll - curr_scroll)); XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(m_x_scroll - curr_scroll)); } #endif } if (globals.widgets.y_bar != NULL && m_y_scroll != 0) { #ifdef MOTIF if (m_y_scroll > 0) (void)set_bar_value(globals.widgets.y_bar, m_y_scroll, (int)(globals.page.h - mane.height)); #else XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL); curr_scroll = d - window_y; if (m_y_scroll > curr_scroll) { TRACE_GUI((stderr, "======== diff: %d", m_y_scroll - curr_scroll)); XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(m_y_scroll - curr_scroll)); } #endif } } /* home(False); */ resized = False; } /* * Callback routines */ void handle_resize(Widget widget, XtPointer junk, XEvent *event, Boolean *cont) { UNUSED(widget); UNUSED(junk); UNUSED(event); UNUSED(cont); resized = True; #ifndef MOTIF handle_statusline_resize(); handle_pagelist_resize(); #endif } void reconfig(void) { /* Dimension x, y; */ if (globals.dvi_file.bak_fp == NULL) return; #ifndef MOTIF XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)False, NULL); #endif TRACE_GUI((stderr, "globals.widgets.draw_widget: w %d, h %d", globals.page.w, globals.page.h)); XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL); #ifdef TEST_SCROLLING /* XtVaSetValues(globals.widgets.draw_background, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL); */ #endif #ifndef MOTIF handle_statusline_resize(); /* without this, statusline will disappear */ /* following not needed? */ /* handle_pagelist_resize(); */ #endif get_geom(); /* set_windowsize(&x, &y */ /* #ifndef MOTIF */ /* , get_panel_width() */ /* #endif */ /* ); */ /* XResizeWindow(DISP, XtWindow(globals.widgets.top_level), x, y); */ /* reconfigure_window(False, x, y, True); */ /* reconfig_window(); */ } int check_goto_page(int pageno) { int retval; if (pageno < 0) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1); retval = 0; } else if (pageno >= total_pages) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Can't go to page %d, going to last page (%d) instead", pageno + 1, total_pages); retval = total_pages - 1; } else retval = pageno; page_history_insert(retval); return retval; } static int check_goto_tex_page(int pageno) { /* Translate from TeX page number to `real' page number if needed. Note that pageno is a C-style 0-based number, hence we add 1 for the argument of pageinfo_get_index_of_number(). */ int retval; if (resource.use_tex_pages) { int res = pageinfo_get_index_of_number(pageno + 1); if (res >= 0) retval = res; else { XBell(DISP, 0); if (pageno < 1) { statusline_print(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1); retval = 0; } else { /* there is no quick way to determine the last page number in the TeX page index */ statusline_print(STATUS_SHORT, "Can't go to page %d, going to last page instead", pageno + 1); retval = total_pages - 1; } } page_history_insert(retval); } else { retval = check_goto_page(pageno); } return retval; } /* ||| * Currently the event handler does not coordinate XCopyArea requests * with GraphicsExpose events. This can lead to problems if the window * is partially obscured and one, for example, drags a scrollbar. */ /* * Actions for the translation mechanism. */ /* if there are global prefixes, return them in res and reset them to defaults */ static Boolean get_prefix_arg(int *res) { Boolean ret; *res = m_sign * m_number; ret = m_have_arg; /* reset global flags */ m_have_arg = False; m_number = 0; m_sign = 1; return ret; } Boolean get_int_arg(String *param, Cardinal *num_params, int *res) { if (*num_params > 0) { *res = atoi(*param); return True; } else { if (get_prefix_arg(res)) { /* prefix argument? */ return True; } } return False; } Boolean toggle_arg(int arg, String *param, Cardinal *num_params) { if (*num_params > 0) { if (**param != 't' && (atoi(*param) != 0) == arg) return False; } else { if (m_have_arg) { int tmparg = m_number; m_have_arg = False; m_number = 0; m_sign = 1; if ((tmparg != 0) == arg) return False; } } return True; } #if defined(NEW_MENU_CREATION) || defined(MOTIF) Boolean check_resource_expert(void *val, const char *param) { int j = strtol(param, (char **)NULL, 10); /* check if the j-1th bit is set: */ return (*(int *)val >> (j - 1)) & 1; } Boolean check_papersize(void *val, const char *param) { UNUSED(val); UNUSED(param); return False; /* TODO */ } Boolean check_paper_landscape(void *val, const char *param) { UNUSED(val); UNUSED(param); return False; /* TODO */ } /* comparison functions for the menu setting code */ Boolean check_toggle(void *val, const char *param) { Boolean *on = val; if (strcmp(param, "toggle") == 0) { return *on; } else { fprintf(stderr, "TODO: check_toggle: arg |%s|, curr: %d\n", param, *(int *)val); return *on; } } Boolean check_int(void *val, const char *param) { int i = strtol(param, (char **)NULL, 10); return i == *(int *)val; } #endif /* NEW_MENU_CREATION */ static void Act_digit(Widget w, XEvent *event, String *params, Cardinal *num_params) { int digit; /* for overflow checks */ static const int MAXINT_QUOT = INT_MAX / 10; static const int MAXINT_MOD = INT_MAX % 10; UNUSED(w); UNUSED(event); if (*num_params != 1 || (digit = **params - '0') > 9) { XBell(DISP, 0); return; } m_have_arg = True; /* don't increment m_number if it would overflow */ if (m_number < MAXINT_QUOT || (m_number == MAXINT_QUOT && digit <= MAXINT_MOD)) { m_number = m_number * 10 + digit; if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */ statusline_print(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number); } else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "numerical prefix: %s%d: no larger value possible", m_sign < 0 ? "-" : "", m_number); } } static void Act_minus(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); m_have_arg = True; m_sign = -m_sign; if (m_number > 0) { if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */ statusline_print(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number); } else { if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */ statusline_print(STATUS_SHORT, "numerical prefix: %s", m_sign < 0 ? "-" : ""); } } static void Act_quit(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); #ifndef FLAKY_SIGPOLL if (globals.debug & DBG_EVENT) puts(event_freq < 0 ? "SIGPOLL is working" : "no SIGPOLL signals received"); #endif xdvi_normal_exit(); } static void Act_quit_confirm(Widget w, XEvent *event, String *params, Cardinal *num_params) { static Widget dialog = 0; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); #ifndef FLAKY_SIGPOLL if (globals.debug & DBG_EVENT) puts(event_freq < 0 ? "SIGPOLL is working" : "no SIGPOLL signals received"); #endif /* already a quit dialog open? */ if (dialog != 0) { /* HACK ALERT: use brute force, since tests for XtIsRealized() or XtIsMapped() don't work?? Grabbing the server apparently has problems with Xaw, and it's not a nice solution anyway ... */ if (kill_message_window(dialog)) XBell(DISP, 0); } dialog = choice_dialog_sized(globals.widgets.top_level, MSG_QUESTION, SIZE_SMALL, NULL, #ifndef MOTIF "quit", #endif NULL, NULL, /* no pre_callbacks */ "OK", xdvi_normal_exit_cb, NULL, "Cancel", NULL, NULL, "Really quit xdvi?"); } static void Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(num_params); launch_browser(*params); } static void Act_print(Widget w, XEvent *event, String *params, Cardinal *num_params) { static struct save_or_print_info info; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); info.act = FILE_PRINT; save_or_print_callback(&info); } static void Act_save(Widget w, XEvent *event, String *params, Cardinal *num_params) { static struct save_or_print_info info; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); info.act = FILE_SAVE; save_or_print_callback(&info); } static void Act_find(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); dvi_find_string(NULL, False); } static void Act_find_next(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); dvi_find_string(NULL, True); } static void Act_help(Widget w, XEvent *event, String *params, Cardinal *num_params) { char *arg = NULL; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (*num_params > 0) arg = *params; show_help(globals.widgets.top_level, arg); } static home_proc home_action = NULL; void goto_page(int new_page, home_proc proc, Boolean force) { /* SU: added clearing the window here, else old window content will survive switching pages as long as globals.pausing.flag is active (i.e. when inadvertedly changing page instead of unpausing) */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } if (globals.dvi_file.bak_fp == NULL) { current_page = new_page; /* so that xdvi gets the page change */ return; } ASSERT(new_page >= 0 && new_page < total_pages, "new_page in goto_page() out of range"); if (current_page != new_page || force) { globals.cursor.flags &= ~CURSOR_LINK; /* disable link cursor if needed */ current_page = new_page; home_action = proc; globals.warn_spec_now = resource.warn_spec; /* this seems unneccessary */ /* if (!resource.keep_flag) */ /* home(False); */ #if defined(MOTIF) && HAVE_XPM tb_check_navigation_sensitivity(current_page); /* page_history_update_toolbar_navigation(); */ #endif maybe_scroll_pagelist(current_page, False); if (globals.pausing.num_save) globals.pausing.num = globals.pausing.num_save[new_page]; else globals.pausing.num = 0; /* Control-L (and changing the page) clears this box */ globals.src.fwd_box_page = -1; globals.ev.flags |= EV_NEWPAGE; XFlush(DISP); } } static void Act_goto_page(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; Boolean clear_statusline = False; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); warn_num_params("goto-page()", params, *num_params, 1); if (*num_params > 0) { if (**params == 'e') { arg = total_pages - 1; } else arg = atoi(*params) - globals.pageno_correct; } else { if (get_prefix_arg(&arg)) { clear_statusline = True; arg -= globals.pageno_correct; } else { arg = total_pages - 1; } } if (arg == total_pages - 1) { /* with TeX numbers, total_pages - 1 might not be found in the page list, so don't use check_goto_tex_page() in this case */ goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False); } else { goto_page(check_goto_tex_page(arg), resource.keep_flag ? NULL : home, False); } search_signal_page_changed(); if (clear_statusline) statusline_clear(); } void Act_recent_files(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); } void Act_use_tex_pages(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); if (block_this_mouse_event()) return; if (*num_params == 0) { if (m_have_arg) { resource.use_tex_pages = (m_number != 0); m_have_arg = False; m_number = 0; m_sign = 1; } else { resource.use_tex_pages = !resource.use_tex_pages; } } else resource.use_tex_pages = (**params == 't' ? !resource.use_tex_pages : atoi(*params)); if (resource.use_tex_pages) { statusline_print(STATUS_SHORT, "Using TeX page numbers for \"g\", goto-page()"); } else { statusline_print(STATUS_SHORT, "Using physical page numbers for \"g\", goto-page()"); } store_preference(NULL, "useTeXPages", "%s", resource.use_tex_pages ? "True" : "False"); #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle); #else #ifdef MOTIF set_use_tex_option(); #else toggle_menu(resource.use_tex_pages, Act_use_tex_pages); #endif #endif /* NEW_MENU_CREATION */ refresh_pagelist(total_pages, current_page); } void Act_forward_page(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; Boolean clear_statusline = False; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) arg = 1; else clear_statusline = True; arg += current_page; if (arg == current_page) { /* zero argument -> redraw page */ globals.src.fwd_box_page = -1; search_reset_info(); globals.ev.flags |= EV_NEWPAGE; XFlush(DISP); statusline_print(STATUS_SHORT, "Page redrawn."); return; } else if (current_page >= total_pages - 1) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Last page of DVI file"); return; } goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False); if (clear_statusline) statusline_clear(); search_signal_page_changed(); } void Act_back_page(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; Boolean clear_statusline = False; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) arg = 1; else clear_statusline = True; arg = current_page - arg; if (current_page == 0) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "First page of DVI file"); return; } goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False); if (clear_statusline) statusline_clear(); search_signal_page_changed(); } static void Act_declare_page_number(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (resource.mouse_mode == MOUSE_RULER_MODE) { show_distance_from_ruler(event, True); return; } if (!get_int_arg(params, num_params, &arg)) { arg = 0; } globals.pageno_correct = arg - current_page; statusline_print(STATUS_SHORT, "Current page number set to %d", arg); } static void Act_toggle_mark(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (globals.dvi_file.bak_fp == NULL) return; if ( #ifdef MOTIF (resource.expert_mode & XPRT_SHOW_PAGELIST) == 0 #else (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 #endif ) return; if (*num_params > 0) { arg = atoi(*params); if (arg < -1 || arg > 2) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Possible arguments: none (toggle current), " "-1 (mark all), 0 (unmark all), 1 (toggle odd), 2 (toggle even)"); } list_toggle_marks(arg); } else if (get_prefix_arg(&arg)) { list_toggle_marks(arg); statusline_clear(); } else { arg = current_page; list_toggle_current(arg); } } static void Act_home(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; home(True); } static void Act_home_or_top(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; if (resource.keep_flag) { String args[1]; args[0] = "10"; /* should be large enough ... */ XtCallActionProc(globals.widgets.top_level, "up", NULL, args, 1); } else { home(True); } } static void Act_end_or_bottom(Widget w, XEvent *event, String *params, Cardinal *num_params) { String args[1]; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); args[0] = "10"; /* should be large enough ... */ XtCallActionProc(globals.widgets.top_level, "down", NULL, args, 1); if (!resource.keep_flag #ifndef MOTIF && globals.widgets.x_bar != NULL #endif ) { XtCallActionProc(globals.widgets.top_level, "right", NULL, args, 1); } } static void Act_center(Widget w, XEvent *event, String *params, Cardinal *num_params) { int x, y; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(params); UNUSED(num_params); #ifdef MOTIF /* The clip widget gives a more exact value. */ x = event->xkey.x - mane.width / 2; y = event->xkey.y - mane.height / 2; x = set_bar_value(globals.widgets.x_bar, x, (int)(globals.page.w - mane.width)); y = set_bar_value(globals.widgets.y_bar, y, (int)(globals.page.h - mane.height)); get_xy(); XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x - window_x, -y - window_y); #else if (event == NULL) return; /* button actions do not provide events */ x = event->xkey.x - mane.width / 2; y = event->xkey.y - mane.height / 2; /* The clip widget gives a more exact value. */ if (globals.widgets.x_bar != NULL) XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) x); if (globals.widgets.y_bar != NULL) XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) y); XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x, -y); #endif handle_x_scroll(NULL, NULL, NULL, NULL); handle_y_scroll(NULL, NULL, NULL, NULL); } void Act_ruler_snap_origin(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(params); UNUSED(num_params); if (resource.mouse_mode == MOUSE_RULER_MODE) ruler_snap_origin(event); } #if defined(NEW_MENU_CREATION) || defined(MOTIF) void Act_set_papersize(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(num_params); fprintf(stderr, "set_paperformat: %s\n", *params); resource.paper = *params; set_menu((void *)resource.paper, Act_set_papersize, check_papersize); } void Act_set_paper_landscape(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(num_params); resource.paper_landscape = !resource.paper_landscape; fprintf(stderr, "set_paper_landscape: %s\n", *params); set_menu((char *)resource.paper, Act_set_paper_landscape, check_paper_landscape); } #endif /* NEW_MENU_CREATION */ void Act_set_keep_flag(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(event); UNUSED(w); if (*num_params == 0) { if (m_have_arg) { resource.keep_flag = (m_number != 0); m_have_arg = False; m_number = 0; m_sign = 1; } else { resource.keep_flag = !resource.keep_flag; } } else resource.keep_flag = (**params == 't' ? !resource.keep_flag : atoi(*params)); if (resource.keep_flag) { statusline_print(STATUS_SHORT, "Keeping position when switching pages"); } else { statusline_print(STATUS_SHORT, "Not keeping position when switching pages"); } store_preference(NULL, "keepPosition", "%s", resource.keep_flag ? "True" : "False"); #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle); #else toggle_menu(resource.keep_flag, Act_set_keep_flag); #endif /* NEW_MENU_CREATION */ } static void Act_left(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); do_autoscroll = False; warn_num_params("left()", params, *num_params, 1); #ifdef MOTIF get_xy(); #ifdef TEST_SCROLLING fprintf(stderr, "left for %p\n", (void *)w); fprintf(stderr, "window x: %d, y: %d\n", window_x, window_y); #endif (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (-2 * (int)mane.width / 3) : (int)(-my_atof(*params) * mane.width)) - window_x, (int)(globals.page.w - mane.width)); #else if (globals.widgets.x_bar != NULL) XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (*num_params == 0 ? (-2 * (int)mane.width / 3) : (int)(-my_atof(*params) * mane.width))); else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Horizontal scrolling not possible"); } #endif handle_x_scroll(NULL, NULL, NULL, NULL); } static void Act_right(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); do_autoscroll = False; warn_num_params("right()", params, *num_params, 1); #ifdef MOTIF get_xy(); #ifdef TEST_SCROLLING fprintf(stderr, "right for %p\n", (void *)w); fprintf(stderr, "window x: %d, y: %d\n", window_x, window_y); #endif (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (2 * (int)mane.width / 3) : (int)(my_atof(*params) * mane.width)) - window_x, (int)(globals.page.w - mane.width)); #else if (globals.widgets.x_bar != NULL) XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (*num_params == 0 ? (2 * (int)mane.width / 3) : (int)(my_atof(*params) * mane.width))); else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Horizontal scrolling not possible"); } #endif handle_x_scroll(NULL, NULL, NULL, NULL); } static void Act_up(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); do_autoscroll = False; warn_num_params("up()", params, *num_params, 1); #ifdef MOTIF #ifdef TEST_SCROLLING fprintf(stderr, "up for %p\n", (void *)w); #endif get_xy(); (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (-2 * (int)mane.height / 3) : (int)(-my_atof(*params) * mane.height)) - window_y, (int)(globals.page.h - mane.height)); #else if (globals.widgets.y_bar != NULL) XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (*num_params == 0 ? (-2 * (int)mane.height / 3) : (int)(-my_atof(*params) * mane.height))); else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Vertical scrolling not possible"); } #endif handle_y_scroll(NULL, NULL, NULL, NULL); } static void Act_down(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); do_autoscroll = False; warn_num_params("down()", params, *num_params, 1); #ifdef MOTIF #ifdef TEST_SCROLLING fprintf(stderr, "down for %p\n", (void *)w); #endif get_xy(); (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3) : (int)(my_atof(*params) * mane.height)) - window_y, (int)(globals.page.h - mane.height)); #else if (globals.widgets.y_bar != NULL) XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (*num_params == 0 ? (2 * (int)mane.height / 3) : (int)(my_atof(*params) * mane.height))); else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Vertical scrolling not possible"); } #endif handle_y_scroll(NULL, NULL, NULL, NULL); } static void Act_down_or_next(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); do_autoscroll = False; warn_num_params("down-or-next()", params, *num_params, 1); #ifdef TEST_SCROLLING fprintf(stderr, "down-or-next for %p\n", (void *)w); #endif #ifdef MOTIF get_xy(); if (window_y > (int)mane.height - (int)globals.page.h) { (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3) : (int)(my_atof(*params) * mane.height)) - window_y, (int)(globals.page.h - mane.height)); return; } #else if (globals.widgets.y_bar != NULL) { get_xy(); if (window_y > (int)mane.height - (int)globals.page.h) { XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (*num_params == 0 ? (2 * (int)mane.height / 3) : (int)(my_atof(*params) * mane.height))); return; } } #endif if (current_page < total_pages - 1) { goto_page(current_page + 1, home, False); search_signal_page_changed(); } else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "At bottom of last page"); } handle_y_scroll(NULL, NULL, NULL, NULL); } static void Act_up_or_previous(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); do_autoscroll = False; warn_num_params("up-or-previous()", params, *num_params, 1); #ifdef TEST_SCROLLING fprintf(stderr, "up-or-previous for %p\n", (void *)w); #endif #ifdef MOTIF get_xy(); if (window_y < 0) { (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (-2 * (int)mane.height / 3) : (int)(-my_atof(*params) * mane.height)) - window_y, (int)(globals.page.h - mane.height)); return; } #else if (globals.widgets.y_bar != NULL) { get_xy(); if (window_y < 0) { XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (*num_params == 0 ? (-2 * (int)mane.height / 3) : (int)(-my_atof(*params) * mane.height))); return; } } #endif if (current_page > 0) { goto_page(current_page - 1, home_bottom, False); search_signal_page_changed(); } else { XBell(DISP, 0); statusline_print(STATUS_SHORT, "At top of first page"); } handle_y_scroll(NULL, NULL, NULL, NULL); } static void Act_set_margins(Widget w, XEvent *event, String *params, Cardinal *num_params) { Window dummy; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(params); UNUSED(num_params); #ifndef MOTIF if (event == NULL) return; /* button actions do not provide events */ #endif (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, event->xkey.x, event->xkey.y, &resource.sidemargin_int, &resource.topmargin_int, &dummy); /* throw away last argument */ statusline_print(STATUS_SHORT, "Margins set to cursor position (%d, %d)", resource.sidemargin_int, resource.topmargin_int); resource.sidemargin_int *= mane.shrinkfactor; resource.topmargin_int *= mane.shrinkfactor; } static void Act_show_display_attributes(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); statusline_print(STATUS_SHORT, "Unit = %d, bitord = %d, byteord = %d", BitmapUnit(DISP), BitmapBitOrder(DISP), ImageByteOrder(DISP)); } int shrink_to_fit(void) { int value1; int value2; static Dimension window_w; #ifndef MOTIF static Dimension window_h; #endif static Arg arg_wh[] = { {XtNwidth, (XtArgVal) &window_w} #ifndef MOTIF , {XtNheight, (XtArgVal) &window_h} #endif }; #ifdef MOTIF XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh)); #else XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh)); #endif value1 = ROUNDUP(globals.page.unshrunk_w, window_w - 2); #ifdef MOTIF { /* account for menubar */ static Dimension new_h; /* get rid of scrollbar */ XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)1, XtNheight, (XtArgVal)1, NULL); XtVaGetValues(globals.widgets.clip_widget, XtNheight, &new_h, NULL); value2 = ROUNDUP(globals.page.unshrunk_h, new_h - 2); } #else /* FIXME: value seems too small here! */ value2 = ROUNDUP(globals.page.unshrunk_h, window_h - 2); #endif return value1 > value2 ? value1 : value2; } void do_set_shrinkfactor(int arg, Boolean set_resource) { static int shrink_bak = -1; /* We don't store this as preference since it may affect initial window geometry. */ /* store_preference(NULL, "shrinkFactor", "%d", arg); */ if (resource.mouse_mode == MOUSE_TEXT_MODE) { text_change_region(TEXT_SEL_CLEAR, NULL); } mane.shrinkfactor = arg; if (set_resource) resource.shrinkfactor = mane.shrinkfactor; #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&arg, Act_set_shrink_factor, check_int); #if HAVE_XMP tb_set_zoom_sensitivity(arg != 1); #endif #else toggle_menu(arg, Act_set_shrink_factor); #endif /* NEW_MENU_CREATION */ if (arg != 1 && arg != shrink_bak) { shrink_bak = arg; #if GREY #if COLOR if (resource.use_grey) fg_active = NULL; #else if (resource.use_grey) init_pix(); #endif #endif /* GREY */ reset_fonts(); } init_page(); reconfig(); htex_resize_page(); /* this seems unneccessary */ /* if (!resource.keep_flag) */ /* home(False); */ globals.ev.flags |= EV_NEWPAGE; XFlush(DISP); } void Act_set_shrink_factor(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; /* Set a limit to the shrink factors allowed; otherwise xdvi may consume a lot of memory (even run out of memory in malloc) in the allocation for the subpixel lookup table pixeltbl, dvi-draw.c. The `correct' solution would be to recognize that at a certain shrink, the only pixel for a character will be always set to `on' anyway, so that we could do without the lookup table for very large shrink factors ... */ static const int SHRINK_MAX = 999; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); warn_num_params("set-shrink-factor()", params, *num_params, 1); if (*num_params > 0) { if (**params == 'a') arg = shrink_to_fit(); else if (**params == '-') arg = mane.shrinkfactor + 1; else if (**params == '+') arg = mane.shrinkfactor - 1; else arg = atoi(*params); } else if (!get_prefix_arg(&arg)) { arg = shrink_to_fit(); } if (arg <= 0) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "No more enlarging possible"); return; } if (arg > SHRINK_MAX) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "Shrink factor %d too large (maximum: %d)", arg, SHRINK_MAX); return; } /* SU: added clearing the window here, else old window content will survive changing shrink */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } statusline_print(STATUS_SHORT, "shrink factor: %d", arg); #if 0 /* Uncommented this, otherwise selecting `shrink to fit' with same value as current mane.shrinkfactor will clear the canvas (only fixed by changing shrink factor again). That bug is also present in xdvik-22.48-alpha3. */ if (arg == mane.shrinkfactor) { return; } #endif do_set_shrinkfactor(arg, True); #if MOTIF /* note: mustn't do that inside do_set_shrinkfactor(), or we get into an endless loop! */ update_preferences_shrink(); #endif } void Act_shrink_to_dpi(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg, arg_bak; Boolean clear_statusline = False; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); warn_num_params("shrink-to-dpi()", params, *num_params, 1); if (!get_int_arg(params, num_params, &arg)) arg = 0; else clear_statusline = True; arg_bak = arg; if (arg > 0) arg = (double)resource.pixels_per_inch / arg + 0.5; if (arg <= 0) { XBell(DISP, 0); statusline_print(STATUS_SHORT, "shrink-to-dpi requires a positive argument"); return; } /* like elsewhere */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } #if 0 /* Uncommented this, otherwise selecting `shrink to fit' with same value as current mane.shrinkfactor will clear the canvas (only fixed by changing shrink factor again). That bug is also present in xdvik-22.48-alpha3. */ if (arg == mane.shrinkfactor) return; #endif do_set_shrinkfactor(arg, True); #if MOTIF /* note: mustn't do that inside do_set_shrinkfactor(), or we get into an endless loop! */ update_preferences_shrink(); #endif } void do_set_density(double newgamma, Boolean force, Boolean update_resource) { /* like elsewhere */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } #ifdef GREY if (resource.use_grey) { if (newgamma == resource.gamma && !force) { statusline_print(STATUS_SHORT, "density value: %.3f", newgamma); return; } resource.gamma = newgamma; if (update_resource) globals.curr_gamma = resource.gamma; #if COLOR fg_active = NULL; reset_colors(); #else init_pix(); if (G_visual->class != TrueColor) { return; } reset_fonts(); #endif /* COLOR */ statusline_print(STATUS_SHORT, "density value: %.3f", newgamma); } else #endif /* GREY */ { reset_fonts(); if (mane.shrinkfactor == 1) { statusline_print(STATUS_SHORT, "set-density ignored at magnification 1"); return; } statusline_print(STATUS_SHORT, "density value: %.3f", newgamma); } store_preference(NULL, "gamma", "%f", resource.gamma); globals.ev.flags |= EV_NEWPAGE; XFlush(DISP); } static void Act_set_density(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); warn_num_params("set-density()", params, *num_params, 1); if (!get_int_arg(params, num_params, &arg)) { XBell(DISP, 0); return; } /* if (arg < 0) { */ /* XBell(DISP, 0); */ /* statusline_print(STATUS_SHORT, */ /* "set-density requires a positive value"); */ /* return; */ /* } */ do_set_density(arg != 0 ? arg / 100.0 : 1.0, False, True); #if MOTIF update_preferences_darkness(); #else store_preference(NULL, "gamma", "%f", resource.gamma); #endif } static void Act_change_density(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; double diff; double oldgamma = resource.gamma; UNUSED(w); UNUSED(event); if (block_this_mouse_event()) return; warn_num_params("change-density()", params, *num_params, 1); if (!get_int_arg(params, num_params, &arg)) { XBell(DISP, 0); return; } diff = oldgamma / arg; if (oldgamma + diff <= 0.0) oldgamma = 0.0; else oldgamma += diff; do_set_density(oldgamma, False, True); #if MOTIF update_preferences_darkness(); #endif } static void Act_fullscreen(Widget w, XEvent *event, String *params, Cardinal *num_params) { Dimension main_win_w, main_win_h; static int orig_x = 0, orig_y = 0; #ifndef MOTIF static Dimension get_x, get_y; #endif static Dimension old_w = 0, old_h = 0; Dimension panel_width = 0; #if 0 static Dimension w_old = 0, h_old = 0; #endif if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!toggle_arg(resource.fullscreen, params, num_params)) { return; } resource.fullscreen = !resource.fullscreen; #ifndef MOTIF panel_width = get_panel_width(); #endif if (resource.fullscreen) { /* when toggling to fullscreen, save current scrollbar values and window geometry */ XtVaGetValues(globals.widgets.top_level, XtNwidth, &old_w, XtNheight, &old_h, NULL); /* fprintf(stderr, "saved geometry: %dx%d\n", old_w, old_h); */ #ifdef MOTIF if (globals.widgets.x_bar != NULL) XtVaGetValues(globals.widgets.x_bar, XmNvalue, &orig_x, NULL); if (globals.widgets.y_bar != NULL) XtVaGetValues(globals.widgets.y_bar, XmNvalue, &orig_y, NULL); #else get_xy(); if (globals.widgets.x_bar != NULL) XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL); if (globals.widgets.y_bar != NULL) XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL); orig_x = -(get_x - window_x); orig_y = -(get_y - window_y); #endif /* MOTIF */ /* fprintf(stderr, "Current offsets: %d, %d, %d, %d\n", orig_x, window_x, orig_y, window_y); */ } #if 0 /* TODO: don't hardcode w/h values - what's wrong with the set_windowsize() call below? */ if (resource.fullscreen) { XWindowAttributes attr; if (XGetWindowAttributes(DISP, XtWindow(globals.widgets.top_level), &attr)) { w_old = attr.width; h_old = attr.height; } main_win_w = WidthOfScreen(SCRN); main_win_h = HeightOfScreen(SCRN); } else if (w_old > 0 && h_old > 0) { main_win_w = w_old; main_win_h = h_old; } else { main_win_w = 800; main_win_h = 600; } #else if (resource.fullscreen) { set_windowsize(&main_win_w, &main_win_h, panel_width, 0, False); } else { /* re-use old window sizes, or initialize them if started in fullscreen mode */ if (old_w == 0 || old_h == 0) /* started in fullscreen mode */ set_windowsize(&old_w, &old_h, panel_width, 0, False); else set_windowsize(&old_w, &old_h, panel_width, 0, True); } #endif /* fprintf(stderr, "reconfigure window!\n"); */ if (resource.fullscreen) reconfigure_window(resource.fullscreen, main_win_w, main_win_h, True); else { reconfigure_window(resource.fullscreen, old_w, old_h, True); } /* fprintf(stderr, "orig values: %d, %d\n", orig_x, orig_y); */ /* restore horizontal scroll position */ if (resource.keep_flag && orig_x != 0 && globals.widgets.x_bar != NULL) { #ifdef MOTIF if (!resource.fullscreen) { /* only scroll back in non-fullscreen mode. This was: if (resource.fullscreen) (void)set_bar_value(globals.widgets.y_bar, orig_y, (int)(globals.page.h - mane.height)); else ... but that was broken (bogus values in globals.page.h - mane.height). */ (void)set_bar_value(globals.widgets.x_bar, orig_x, INT_MAX); } #else int curr_x; get_xy(); XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL); curr_x = -(get_x - window_x); if (curr_x - orig_x > 0) XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(curr_x - orig_x)); #endif /* MOTIF */ } /* restore vertical scroll position */ if (resource.keep_flag && orig_y != 0 && globals.widgets.y_bar != NULL) { #ifdef MOTIF if (!resource.fullscreen) { /* only scroll back in non-fullscreen mode. This was: if (resource.fullscreen) (void)set_bar_value(globals.widgets.y_bar, orig_y, (int)(globals.page.h - mane.height)); else ... but that was broken (bogus values in globals.page.h - mane.height). */ (void)set_bar_value(globals.widgets.y_bar, orig_y, INT_MAX); } #else int curr_y; get_xy(); XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL); curr_y = -(get_y - window_y); if (curr_y - orig_y > 0) XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(curr_y - orig_y)); #endif /* MOTIF */ } handle_x_scroll(NULL, NULL, NULL, NULL); handle_y_scroll(NULL, NULL, NULL, NULL); } #ifdef GREY static void Act_set_greyscaling(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); warn_num_params("set-greyscaling()", params, *num_params, 1); if (!get_int_arg(params, num_params, &arg)) { /* no arg, toggle */ if (!toggle_arg(resource.use_grey, params, num_params)) { return; } resource.use_grey = !resource.use_grey; if (resource.use_grey) { statusline_print(STATUS_SHORT, "greyscaling on"); } else { statusline_print(STATUS_SHORT, "greyscaling off"); } globals.ev.flags |= EV_NEWPAGE; XFlush(DISP); return; } switch (arg) { case 0: resource.use_grey = False; statusline_print(STATUS_SHORT, "greyscaling off"); break; case 1: resource.use_grey = True; statusline_print(STATUS_SHORT, "greyscaling on"); break; default: { float newgamma = arg != 0 ? arg / 100.0 : 1.0; resource.use_grey = newgamma; statusline_print(STATUS_SHORT, "greyscale value: %.1f", newgamma); } } /* like elsewhere */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } if (resource.use_grey) { if (G_visual->class != TrueColor) init_plane_masks(); #if COLOR fg_active = NULL; #else init_pix(); #endif } reset_fonts(); globals.ev.flags |= EV_NEWPAGE; XFlush(DISP); } #endif #if COLOR void do_toggle_color(Boolean update_resource) { if (resource.use_color) { resource.use_color = False; full_reset_colors(); scanned_page_color = total_pages; #if PS if (ignore_papersize_specials || scanned_page_ps <= total_pages) { scanned_page = scanned_page_ps; } #endif statusline_print(STATUS_SHORT, "color specials off"); } else { resource.use_color = True; scanned_page = scanned_page_color = scanned_page_reset; statusline_print(STATUS_SHORT, "color specials on"); } if (update_resource) globals.curr_use_color = resource.use_color; #ifdef MOTIF update_preferences_color(); #endif globals.ev.flags |= EV_NEWPAGE; } static void Act_set_color(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); if (block_this_mouse_event()) return; if (!toggle_arg(resource.use_color, params, num_params)) { return; }; /* like elsewhere */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } do_toggle_color(True); } #endif /* COLOR */ #if PS void Act_set_ps(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(event); UNUSED(w); if (!get_int_arg(params, num_params, &arg)) resource.postscript++; else resource.postscript = arg; if (resource.postscript > 2) resource.postscript = 0; #ifdef PS_GS if (!resource.useGS) { if (resource.postscript > 0) { popup_message(globals.widgets.top_level, MSG_WARN, "This version of xdvi depends on ghostscript for rendering Postscript images. " "Postscript rendering cannot be activated if the option ``-noghostscript'' is used " "or if the resource ``Ghostscript'' is set to false.", /* message */ "Option ``-noghostscript'' is active; " "cannot enable Postscript rendering without ghostscript."); } resource.postscript = 0; } #endif if (resource.postscript > 0) { scanned_page_ps = scanned_page_ps_bak; if (scanned_page > scanned_page_ps) scanned_page = scanned_page_ps; if (resource.postscript == 1) { statusline_print(STATUS_SHORT, "Postscript rendering on"); } else { statusline_print(STATUS_SHORT, "Postscript rendering on (with bounding box)"); } } else { scanned_page_ps_bak = scanned_page_ps; scanned_page_ps = total_pages; #if COLOR if (ignore_papersize_specials || scanned_page_color <= total_pages) { scanned_page = scanned_page_color; } #endif statusline_print(STATUS_SHORT, "Postscript rendering off; displaying bounding box instead"); } store_preference(NULL, "postscript", "%d", resource.postscript); psp.toggle(resource.postscript); #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.postscript, Act_set_ps, check_int); #else toggle_menu(resource.postscript, Act_set_ps); #endif /* NEW_MENU_CREATION */ /* like elsewhere */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } globals.ev.flags |= EV_PS_TOGGLE; XFlush(DISP); } #endif /* PS */ void Act_htex_back(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; htex_back(); } void Act_htex_forward(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; htex_forward(); } static void Act_htex_anchorinfo(Widget w, XEvent *event, String *params, Cardinal *num_params) { int x, y; Window dummy; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(params); UNUSED(num_params); (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, event->xkey.x, event->xkey.y, &x, &y, &dummy); htex_displayanchor(x, y); } #ifdef PS_GS void Act_set_gs_alpha(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); if (block_this_mouse_event()) return; if (!toggle_arg(resource.gs_alpha, params, num_params)) { return; } resource.gs_alpha = !resource.gs_alpha; if (resource.gs_alpha) { statusline_print(STATUS_SHORT, "ghostscript alpha active"); } else { statusline_print(STATUS_SHORT, "ghostscript alpha inactive"); } store_preference(NULL, "gsAlpha", "%s", resource.gs_alpha ? "True" : "False"); #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle); #else toggle_menu(resource.gs_alpha, Act_set_gs_alpha); #endif /* NEW_MENU_CREATION */ #if GS_PIXMAP_CLEARING_HACK had_ps_specials = False; /* else infinite loop and/or crash in redraw! */ #endif /* like elsewhere */ if (globals.pausing.flag) { XClearWindow(DISP, mane.win); } globals.ev.flags |= EV_PS_TOGGLE; XFlush(DISP); } #endif static void Act_reread_dvi_file(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; /* fprintf(stderr, "reread file!\n"); */ globals.ev.flags |= EV_RELOAD; } static void Act_discard_number(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; m_have_arg = False; m_number = 0; m_sign = 1; if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */ statusline_print(STATUS_SHORT, "numerical prefix discarded"); } /* * Actions to support dragging the image. */ static int drag_last_x, drag_last_y; /* last position of cursor */ static int text_last_x = -1; static int text_last_y = -1; static int text_last_page = -1; static void drag_motion(XEvent *); static void drag_release(XEvent *); /* access for hyperref.c */ Boolean dragging_text_selection(void) { MYTRACE((stderr, "dragging selection? %d", mouse_motion == text_motion)); return mouse_motion == text_motion; } static void text_release(XEvent * event) { int ulx, uly, w, h; static char *text; int text_len = 0; UNUSED(event); MYTRACE((stderr, "text_release!")); if (mouse_motion == null_mouse) return; mouse_motion = mouse_release = null_mouse; w = text_last_x - drag_last_x; h = text_last_y - drag_last_y; if (w == 0 || h == 0) { text_last_x = text_last_y = -1; drag_last_x = drag_last_y = -1; text_last_page = -1; return; } if (w < 0) { ulx = drag_last_x + w; w = -w; } else ulx = drag_last_x; if (h < 0) { uly = drag_last_y + h; h = -h; } else uly = drag_last_y; free(text); /* this allocates text */ text = get_text_selection(&text_len, ulx * currwin.shrinkfactor, uly * currwin.shrinkfactor, (ulx + w) * currwin.shrinkfactor, (uly + h) * currwin.shrinkfactor); if (text[0] == '\0') { /* fprintf(stdout, "Text selection empty.\n"); */ return; } TRACE_GUI((stderr, "Selected `%s'", text)); if (text_len > 4 * XMaxRequestSize(DISP) - 32) { MYTRACE((stderr, "selection too large: %d bytes", text_len)); XBell(DISP, 10); statusline_print(STATUS_MEDIUM, "Selection too large (%d bytes, maximum %d bytes)", text_len, 4 * XMaxRequestSize(DISP) - 32); MYTRACE((stderr, "after statusline_print1")); return; } if (!set_selection(text, globals.widgets.top_level)) { MYTRACE((stderr, "setting selection failed")); XBell(DISP, 10); statusline_print(STATUS_MEDIUM, "Could not set primary selection!"); MYTRACE((stderr, "after statusline_print2")); text_change_region(TEXT_SEL_CLEAR, NULL); MYTRACE((stderr, "after text_change_region")); } MYTRACE((stderr, "End of text_release")); } void text_motion(XEvent *event) { text_change_region(TEXT_SEL_MOVE, event); } static void get_rectangle(XRectangle *rect, int x, int y, int last_x, int last_y) { rect->width = ABS(x - last_x); rect->height = ABS(y - last_y); rect->x = (x < last_x) ? x : last_x; rect->y = (y < last_y) ? y : last_y; } static void crop_to_window(int *x, int *y) { if (*x < -currwin.base_x + 1) *x = -currwin.base_x + 1; else if (*x > -currwin.base_x + (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1) *x = -currwin.base_x + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1; if (*y < -currwin.base_y + 1) *y = -currwin.base_y + 1; else if (*y > -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1) *y = -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1; } void text_change_region(textSelectionT mode, XEvent *event) { static GC bboxGC = 0; static GC redrawGC = 0; /* needed since bboxGC is clipped to diff of old and new region */ MYTRACE((stderr, "text_change_region: %d", mode)); if (bboxGC == 0) { XGCValues values; unsigned long valuemask; values.function = GXinvert; /* as in search code */ if (values.function == GXinvert) { valuemask = GCFunction; } else { values.foreground = WhitePixelOfScreen(SCRN) ^ BlackPixelOfScreen(SCRN); /* fprintf(stderr, "foreground: 0x%lx, white pixel: 0x%lx, black pixel: 0x%lx\n", */ /* values.foreground, WhitePixelOfScreen(SCRN), BlackPixelOfScreen(SCRN)); */ valuemask = GCFunction | GCForeground; } values.line_width = 1; valuemask |= GCLineWidth; bboxGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values); redrawGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values); } MYTRACE((stderr, "text_change_region: after bboxGC")); switch (mode) { case TEXT_SEL_MOVE: { int x, y; Window dummy; XRectangle redraw = { -1, -1, 0, 0 }; ASSERT(event != NULL, "event in text_change_region() musn't be NULL for TEXT_SEL_MOVE"); (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, event->xkey.x, event->xkey.y, &x, &y, &dummy); crop_to_window(&x, &y); get_rectangle(&redraw, x, y, drag_last_x, drag_last_y); /* * If we have an old region, we want to clip the GC to the area: * * (clip \cup redraw) - (clip \cap redraw) * * and redraw both rectangle areas; otherwise, just draw the redraw area. */ if (text_last_x != -1 && text_last_y != -1) { XRectangle clip = { -1, -1, 0, 0 }; Region clip_region = XCreateRegion(); Region redraw_region = XCreateRegion(); Region union_region = XCreateRegion(); Region intersect_region = XCreateRegion(); get_rectangle(&clip, text_last_x, text_last_y, drag_last_x, drag_last_y); XUnionRectWithRegion(&clip, clip_region, clip_region); XUnionRectWithRegion(&redraw, redraw_region, redraw_region); XUnionRegion(clip_region, redraw_region, union_region); XIntersectRegion(clip_region, redraw_region, intersect_region); XSubtractRegion(union_region, intersect_region, redraw_region); XSetRegion(DISP, bboxGC, redraw_region); XDestroyRegion(clip_region); XDestroyRegion(redraw_region); XDestroyRegion(union_region); XDestroyRegion(intersect_region); XFillRectangle(DISP, mane.win, bboxGC, clip.x, clip.y, clip.width, clip.height); } XFillRectangle(DISP, mane.win, bboxGC, redraw.x, redraw.y, redraw.width, redraw.height); text_last_x = x; text_last_y = y; text_last_page = current_page; } break; case TEXT_SEL_CLEAR: unset_selection(globals.widgets.top_level); case TEXT_SEL_ERASE: case TEXT_SEL_REDRAW: if (text_last_page == current_page && text_last_x != -1 && text_last_y != -1 && drag_last_x != -1 && drag_last_y != -1) { XRectangle clear = { -1, -1, 0, 0 }; get_rectangle(&clear, text_last_x, text_last_y, drag_last_x, drag_last_y); if (mode == TEXT_SEL_CLEAR) { text_last_x = text_last_y = drag_last_x = drag_last_y = -1; text_last_page = -1; /* Note ZLB: the region is erased instead of inverted to avoid * multiple inverting problem. An exposure is generated to * make the region redrawn */ clearexpose(&mane, clear.x, clear.y, clear.width, clear.height); } else if (clip_region_to_rect(&clear)) { if (mode == TEXT_SEL_ERASE) { /* If width or height are 0, XClearArea will clear entire window * from coordinates x, y, so check for that: */ if (clear.width > 0 && clear.height > 0) XClearArea(DISP, mane.win, clear.x, clear.y, clear.width, clear.height, False); } else XFillRectangle(DISP, mane.win, redrawGC, clear.x, clear.y, clear.width, clear.height); } } break; } MYTRACE((stderr, "text_change_region: done")); } void text_selection_start(XEvent *event) { int x, y; Window dummy; /* erase existing region */ text_change_region(TEXT_SEL_CLEAR, NULL); (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, event->xkey.x, event->xkey.y, &x, &y, &dummy); /* throw away last argument */ crop_to_window(&x, &y); drag_last_x = x; drag_last_y = y; text_last_x = text_last_y = -1; MYTRACE((stderr, "selection start; mouse_release: %p; null: %p!", (void *)mouse_release, (void *)null_mouse)); if (mouse_release == null_mouse) { MYTRACE((stderr, "init text_motion!")); mouse_motion = text_motion; mouse_release = text_release; } } /* * If action list for does NOT contain a magnifier() binding, * return that action list; else return NULL. */ static char * btn1_no_magnifier_binding(void) { static Boolean initialized = False; static char *btn1_binding = NULL; if (!initialized) { /* cache value of previous calls */ if (resource.main_translations != NULL) { if ((btn1_binding = strstr(resource.main_translations, "")) != NULL) { char *eol, *test; btn1_binding = strchr(btn1_binding, ':'); if (btn1_binding != NULL) btn1_binding++; while (isspace((int)*btn1_binding)) btn1_binding++; eol = strchr(btn1_binding, '\n'); if (eol == NULL) eol = btn1_binding + strlen(btn1_binding); if ((test = strstr(btn1_binding, "magnifier(")) == NULL || test > eol) { /* binding not found */ return btn1_binding; } } } /* fprintf(stderr, "MAG: %d\n", btn1_has_magnifier); */ initialized = True; } return btn1_binding; } void Act_switch_mode(Widget w, XEvent *event, String *params, Cardinal *num_params) { mouseModeT prev_mode = resource.mouse_mode; int arg = -1; char *ptr; if (block_this_mouse_event()) return; UNUSED(w); if ((ptr = btn1_no_magnifier_binding()) != NULL) { XBell(DISP, 0); statusline_print(STATUS_MEDIUM, "Cannot switch modes: Action list for Btn1Down (%s) " "does not contain magnifier() action.", ptr); return; } /* fprintf(stderr, "prev mode: %d\n", prev_mode); */ if (get_int_arg(params, num_params, &arg)) { if (arg < MOUSE_MAGNIFIER_MODE || arg >= MOUSE_MAX_MODE) { statusline_print(STATUS_SHORT, "Argument for Act_switch_mode outside of range from %d to %d", MOUSE_MAGNIFIER_MODE, MOUSE_MAX_MODE - 1); resource.mouse_mode++; } else resource.mouse_mode = (mouseModeT)arg; } else resource.mouse_mode++; /* check if wrapped */ if (resource.mouse_mode >= MOUSE_MAX_MODE) resource.mouse_mode = MOUSE_MAGNIFIER_MODE; /* undo effects of previous mode */ switch (prev_mode) { case MOUSE_RULER_MODE: clear_ruler(); mouse_motion = mouse_release = null_mouse; break; case MOUSE_TEXT_MODE: text_change_region(TEXT_SEL_CLEAR, NULL); mouse_motion = mouse_release = null_mouse; break; default: /* magnifier, nothing to do */ break; } if (resource.mouse_mode == MOUSE_RULER_MODE) { XDefineCursor(DISP, CURSORWIN, globals.cursor.rule); statusline_print(STATUS_SHORT, "Ruler mode; click Mouse-1 to set ruler"); show_ruler(event); show_distance_from_ruler(event, False); } else if (resource.mouse_mode == MOUSE_TEXT_MODE) { statusline_print(STATUS_SHORT, "Text selection mode; click and drag Mouse-1 to select a region"); } else { /* default: MOUSE_MAGNIFIER_MODE */ statusline_print(STATUS_SHORT, "Magnifier mode; click Mouse-1 to magnify text"); mouse_motion = mouse_release = null_mouse; } globals.ev.flags |= EV_CURSOR; XFlush(DISP); #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.mouse_mode, Act_switch_mode, check_int); #else toggle_menu(resource.mouse_mode, Act_switch_mode); #endif /* NEW_MENU_CREATION */ store_preference(NULL, "mouseMode", "%d", resource.mouse_mode); } static void Act_text_mode(Widget w, XEvent *event, String *params, Cardinal *num_params) { char *ptr; UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; if ((ptr = btn1_no_magnifier_binding()) != NULL) { XBell(DISP, 0); statusline_print(STATUS_MEDIUM, "Cannot switch modes: Action list for Btn1Down (%s) " "does not contain magnifier() action.", ptr); return; } if (resource.mouse_mode == MOUSE_TEXT_MODE) { resource.mouse_mode++; if (resource.mouse_mode >= MOUSE_MAX_MODE) resource.mouse_mode = MOUSE_MAGNIFIER_MODE; } else resource.mouse_mode = MOUSE_TEXT_MODE; if (resource.mouse_mode == MOUSE_TEXT_MODE) { if (mouse_release != null_mouse && mouse_release != text_release) return; globals.ev.flags |= EV_CURSOR; XFlush(DISP); } else { mouse_motion = mouse_release = null_mouse; globals.ev.flags |= EV_CURSOR; text_change_region(TEXT_SEL_CLEAR, NULL); XFlush(DISP); } #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.mouse_mode, Act_switch_mode, check_int); #else toggle_menu(resource.mouse_mode, Act_switch_mode); #endif /* NEW_MENU_CREATION || MOTIF */ } static void Act_drag(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); if (block_this_mouse_event()) return; if (mouse_release != null_mouse && mouse_release != drag_release) return; if (resource.mouse_mode == MOUSE_TEXT_MODE && text_last_page != -1 /* this was: && (text_last_x != drag_last_x || text_last_y != drag_last_y) but that isn't restrictive enough */ ) { /* dragging would mess up region drawing */ XBell(DISP, 0); statusline_print(STATUS_SHORT, "Dragging page not possible while selection is active"); return; } if (*num_params != 1) { XDVI_WARNING((stderr, "drag() requires 1 argument (got %d)", *num_params)); return; } switch (**params) { case '|': globals.cursor.flags |= CURSOR_DRAG_V; break; case '-': globals.cursor.flags |= CURSOR_DRAG_H; break; case '+': globals.cursor.flags |= CURSOR_DRAG_A; break; default: XDVI_WARNING((stderr, "drag(): Valid arguments are `+', `|' or `-'")); } globals.ev.flags |= EV_CURSOR; if (mouse_release == null_mouse) { mouse_motion = drag_motion; mouse_release = drag_release; drag_last_x = event->xbutton.x_root; drag_last_y = event->xbutton.y_root; } else drag_motion(event); XFlush(DISP); } static void drag_motion(XEvent * event) { #ifdef MOTIF get_xy(); #endif if (globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_A)) { /* horizontal motion */ #ifdef MOTIF (void)set_bar_value(globals.widgets.x_bar, drag_last_x - event->xbutton.x_root - window_x, (int)(globals.page.w - mane.width)); #else if (globals.widgets.x_bar != NULL) { XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (drag_last_x - event->xbutton.x_root)); } #endif drag_last_x = event->xbutton.x_root; } if (globals.cursor.flags & (CURSOR_DRAG_V | CURSOR_DRAG_A)) { /* vertical motion */ #ifdef MOTIF (void)set_bar_value(globals.widgets.y_bar, drag_last_y - event->xbutton.y_root - window_y, (int)(globals.page.h - mane.height)); #else if (globals.widgets.y_bar != NULL) { XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (drag_last_y - event->xbutton.y_root)); } #endif drag_last_y = event->xbutton.y_root; } handle_x_scroll(NULL, NULL, NULL, NULL); handle_y_scroll(NULL, NULL, NULL, NULL); } static void drag_release(XEvent * event) { drag_motion(event); mouse_motion = mouse_release = null_mouse; globals.cursor.flags &= ~(CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A); globals.ev.flags |= EV_CURSOR; XFlush(DISP); } /* Wheel mouse support. */ static int wheel_button = -1; static void Act_wheel(Widget w, XEvent *event, String *params, Cardinal *num_params) { int dist; UNUSED(w); if (block_this_mouse_event()) return; if (*num_params != 1) { XDVI_WARNING((stderr, "wheel() requires 1 argument (got %d)", *num_params)); return; } dist = (strchr(*params, '.') == NULL) ? atoi(*params) : (int)(my_atof(*params) * resource.wheel_unit); #ifdef MOTIF get_xy(); set_bar_value(globals.widgets.y_bar, dist - window_y, (int)(globals.page.h - mane.height)); #else if (globals.widgets.y_bar != NULL) XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) dist); #endif wheel_button = event->xbutton.button; handle_y_scroll(NULL, NULL, NULL, NULL); } /* Internal mouse actions. */ static void Act_motion(Widget w, XEvent *event, String *params, Cardinal *num_params) { /* remember last position, to do this only when pointer is actually moved */ static int old_x = -1, old_y = -1; int x, y; UNUSED(w); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) { MYTRACE((stderr, "returning!")); return; } if (resource.mouse_mode == MOUSE_RULER_MODE) { show_distance_from_ruler(event, False); } /* This used to be: (abs(x - old_x) > x_threshold || abs(y - old_y) > y_threshold)) but that didn't work too well either. Just change it whenever user moves the mouse. */ if (!MAGNIFIER_ACTIVE && resource.mouse_mode != MOUSE_RULER_MODE && pointerlocate(&x, &y) && (x != old_x || y != old_y)) { htex_displayanchor(x, y); old_x = x; old_y = y; } if ((int)(event->xbutton.button) != wheel_button) { mouse_motion(event); } } static void Act_release(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; if ((int)(event->xbutton.button) == wheel_button) { wheel_button = -1; return; } mouse_release(event); } void Act_ruler_mode(Widget w, XEvent *event, String *params, Cardinal *num_params) { char *ptr; UNUSED(w); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; if ((ptr = btn1_no_magnifier_binding()) != NULL) { XBell(DISP, 0); statusline_print(STATUS_MEDIUM, "Cannot switch modes: Action list for Btn1Down (%s) " "does not contain magnifier() action.", ptr); return; } if (resource.mouse_mode == MOUSE_RULER_MODE) { resource.mouse_mode++; if (resource.mouse_mode >= MOUSE_MAX_MODE) resource.mouse_mode = MOUSE_MAGNIFIER_MODE; } else resource.mouse_mode = MOUSE_RULER_MODE; if (resource.mouse_mode == MOUSE_RULER_MODE) { XDefineCursor(DISP, CURSORWIN, globals.cursor.rule); statusline_print(STATUS_SHORT, "Ruler mode on; use Mouse-1 to set/drag ruler"); show_ruler(event); show_distance_from_ruler(event, False); } else { if (globals.cursor.flags & CURSOR_LINK) { XDefineCursor(DISP, CURSORWIN, globals.cursor.link); } else { XDefineCursor(DISP, CURSORWIN, globals.cursor.ready); } statusline_print(STATUS_SHORT, "Ruler mode off"); clear_ruler(); mouse_motion = mouse_release = null_mouse; } #if defined(NEW_MENU_CREATION) || defined(MOTIF) set_menu(&resource.mouse_mode, Act_switch_mode, check_int); #else toggle_menu(resource.mouse_mode, Act_switch_mode); #endif /* NEW_MENU_CREATION || MOTIF */ } /* Actions for source specials. */ void Act_source_special(Widget w, XEvent *event, String *params, Cardinal *num_params) { Window dummy; UNUSED(w); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; if ((event->type == ButtonPress && mouse_release != null_mouse) || MAGNIFIER_ACTIVE) { XBell(DISP, 0); return; } source_reverse_x = event->xbutton.x; source_reverse_y = event->xbutton.y; if (event->xbutton.window != mane.win) (void)XTranslateCoordinates(DISP, RootWindowOfScreen(SCRN), mane.win, event->xbutton.x_root, event->xbutton.y_root, &source_reverse_x, &source_reverse_y, &dummy); /* throw away last argument */ source_reverse_x = (source_reverse_x + mane_base_x) * mane.shrinkfactor; source_reverse_y = (source_reverse_y + mane_base_y) * mane.shrinkfactor; globals.ev.flags |= EV_SRC; } void Act_show_source_specials(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; Boolean clear_statusline = False; if (block_this_mouse_event()) return; UNUSED(w); if (!get_int_arg(params, num_params, &arg)) arg = -1; else clear_statusline = True; if ((event->type == ButtonPress && mouse_release != null_mouse) || magnifier.win != (Window) 0) { XBell(DISP, 0); return; } if (!(globals.ev.flags & EV_SRC)) { source_reverse_x = -1; source_show_all = (arg == 1 ? True : False); globals.ev.flags |= EV_SRC; } if (clear_statusline) statusline_clear(); } void Act_source_what_special(Widget w, XEvent *event, String *params, Cardinal *num_params) { int my_x, my_y; Window dummy; UNUSED(w); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win, event->xkey.x, event->xkey.y, &my_x, &my_y, &dummy); /* throw away last argument */ my_x = (my_x + mane_base_x) * mane.shrinkfactor; my_y = (my_y + mane_base_y) * mane.shrinkfactor; source_reverse_search(my_x, my_y, False); } static void select_cb(const char *filename, void *data) { UNUSED(data); if (filename != NULL) { TRACE_FILES((stderr, "new filename: |%s|", filename)); set_dvi_name_expand(filename); current_page = 0; /* switch to first page */ close_old_filep(); globals.ev.flags |= EV_NEWDOC; globals.ev.flags |= EV_PAGEHIST_INSERT; } } static void Act_select_dvi_file(Widget w, XEvent *event, String *params, Cardinal *num_params) { static struct filesel_callback cb; /* static so that we can pass its address */ int arg = -1; UNUSED(w); UNUSED(event); if (block_this_mouse_event()) return; if (get_int_arg(params, num_params, &arg)) { /* try item from file history */ char *fname; int dummy_page; if (arg < 1) { XBell(DISP, 0); statusline_print(STATUS_MEDIUM, "Error: File history number must be >= 1"); } else if ((fname = file_history_get_elem(arg - 1, &dummy_page)) == NULL) { statusline_print(STATUS_MEDIUM, "No file number %d in history (history size: %lu)", arg, (unsigned long)file_history_size()); } else { file_history_open(fname); } return; } cb.title = "Xdvi: Open file"; cb.prompt = "Open file:"; cb.ok = "OK"; cb.cancel = "Cancel"; cb.init_path = NULL; cb.filemask = "*.dvi"; cb.must_exist = True; cb.exit_on_cancel = False; cb.func_ptr = select_cb; cb.data = NULL; file_history_set_page(current_page); XsraSelFile(globals.widgets.top_level, &cb); } /* * If all GUI elements have been turned on/off, make this synonymous * with expert mode off/on, so that next `x' keystroke does something * reasonable. */ void update_expert_mode(void) { if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE | XPRT_SHOW_SCROLLBARS #ifdef MOTIF | XPRT_SHOW_PAGELIST | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR #else | XPRT_SHOW_BUTTONS #endif )) == XPRT_SHOW_ALL) { resource.expert = False; } else if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE #ifdef MOTIF | XPRT_SHOW_SCROLLBARS | XPRT_SHOW_PAGELIST | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR #else | BROKEN_RECONFIG ? 0 : XPRT_SHOW_SCROLLBARS | XPRT_SHOW_BUTTONS #endif )) == XPRT_SHOW_NONE) { resource.expert = True; } } void Act_set_expert_mode(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; Boolean clear_statusline = False; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) arg = -1; else clear_statusline = True; switch(arg) { case 1: resource.expert_mode ^= XPRT_SHOW_STATUSLINE; toggle_statusline(); update_expert_mode(); break; case 2: #ifndef MOTIF /* show this warning only when not toggling global expert mode (that's why it can't be inside toggle_scrollbars) */ if (BROKEN_RECONFIG) { popup_message(globals.widgets.top_level, MSG_WARN, NULL, "Sorry - cannot toggle scrollbars with this X Version.\n" "This version of XFree has a broken implementation of the viewportWidget, " "which would break the layout if the scrollbars are toggled. " "Versions that are known to work have a VendorRelease version below 4000 or above 4002. " "You will need to update your XFree server to fix this."); return; } #endif resource.expert_mode ^= XPRT_SHOW_SCROLLBARS; toggle_scrollbars(); update_expert_mode(); break; #ifdef MOTIF case 3: resource.expert_mode ^= XPRT_SHOW_PAGELIST; toggle_pagelist(); update_expert_mode(); break; case 4: /* toolbar */ resource.expert_mode ^= XPRT_SHOW_TOOLBAR; toggle_toolbar(); update_expert_mode(); break; case 5: resource.expert_mode ^= XPRT_SHOW_MENUBAR; toggle_menubar(); update_expert_mode(); break; #else case 3: /* fprintf(stderr, "sidebar\n"); */ resource.expert_mode ^= XPRT_SHOW_BUTTONS; toggle_buttons(); update_expert_mode(); break; #endif default: /* warn 'em */ if ( #ifdef MOTIF arg > 5 #else arg > 3 #endif ) { statusline_print(STATUS_SHORT, "Number %d too large for `set-expert-mode', using 0 (= toggle) instead.", arg); } /* toggle all items */ resource.expert = !resource.expert; if (resource.expert) resource.expert_mode = XPRT_SHOW_NONE; else resource.expert_mode = XPRT_SHOW_ALL; toggle_statusline(); #ifndef MOTIF if (!BROKEN_RECONFIG) toggle_scrollbars(); #else toggle_scrollbars(); #endif #ifdef MOTIF toggle_pagelist(); toggle_toolbar(); toggle_menubar(); #else toggle_buttons(); #endif } #ifdef MOTIF update_preferences_expert(); #endif store_preference(NULL, "expertMode", "%d", resource.expert_mode); if (clear_statusline) statusline_clear(); } Boolean have_src_specials = False; static Boolean do_update_property = False; void handle_x_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont) { #ifndef MOTIF Dimension get_x = 0; #endif UNUSED(w); UNUSED(closure); UNUSED(ev); UNUSED(cont); if (/* !resource.keep_flag || */ globals.widgets.x_bar == NULL) return; #ifdef MOTIF XtVaGetValues(globals.widgets.x_bar, XmNvalue, &m_x_scroll, NULL); #else get_xy(); XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL); m_x_scroll = get_x - window_x; scroll_x_panner(m_x_scroll); #endif } void handle_y_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont) { #ifndef MOTIF Dimension get_y = 0; #endif UNUSED(w); UNUSED(closure); UNUSED(ev); UNUSED(cont); if (/* !resource.keep_flag || */ globals.widgets.y_bar == NULL) return; #ifdef MOTIF XtVaGetValues(globals.widgets.y_bar, XmNvalue, &m_y_scroll, NULL); #else get_xy(); XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL); m_y_scroll = get_y - window_y; scroll_y_panner(m_y_scroll); #endif } void handle_expose(Widget w, XtPointer closure, XEvent *ev, Boolean *cont) { struct WindowRec *windowrec = (struct WindowRec *)closure; UNUSED(w); UNUSED(cont); if (windowrec == &magnifier) { if (magnifier_stat < 0) { /* destroy upon exposure */ magnifier_stat = 0; mag_release(ev); return; } else magnifier_stat = 0; } do_update_property = False; if (have_src_specials && !MAGNIFIER_ACTIVE) { do_update_property = True; } MYTRACE((stderr, "Expose!")); expose(windowrec, (&(ev->xexpose))->x, (&(ev->xexpose))->y, (unsigned int)(&(ev->xexpose))->width, (unsigned int)(&(ev->xexpose))->height); } void handle_property_change(Widget w, XtPointer junk, XEvent *ev, Boolean *cont) { char *prop_ret; size_t prop_len; UNUSED(w); UNUSED(junk); UNUSED(cont); if ((&(ev->xproperty))->window != XtWindow(globals.widgets.top_level)) /* if spurious event */ return; if ((&(ev->xproperty))->atom == atom_src_goto()) { /* forward search requested */ if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_src_goto(), &prop_ret, XGetWindowProperty)) == 0) { TRACE_CLIENT((stderr, "property_get_data() failed for atom_src_goto()!")); return; } TRACE_CLIENT((stderr, "got back atom_src_goto: |%s|", prop_ret)); globals.src.fwd_string = prop_ret; globals.ev.flags |= EV_SRC; } else if ((&(ev->xproperty))->atom == atom_find_string()) { /* string search requested */ if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_find_string(), &prop_ret, XGetWindowProperty)) == 0) { TRACE_CLIENT((stderr, "property_get_data() failed for atom_find_string()!")); return; } TRACE_FIND((stderr, "got back atom_find_string: |%s|", prop_ret)); resource.find_string = prop_ret; globals.ev.flags |= EV_FIND; } else if ((&(ev->xproperty))->atom == atom_reload()) { /* like do_sigusr(); there's no data in this case. */ TRACE_CLIENT((stderr, "atom_reload()")); globals.ev.flags |= EV_RELOAD; } else if ((&(ev->xproperty))->atom == atom_newdoc()) { /* loading a new file */ FILE *new_fp; if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newdoc(), &prop_ret, XGetWindowProperty)) == 0) { TRACE_CLIENT((stderr, "property_get_data() returned zero length for atom_newdoc()")); /* just raise it in this case */ XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level)); raise_message_windows(); return; } TRACE_CLIENT((stderr, "got back atom_newdoc: |%s|", prop_ret)); if ((new_fp = XFOPEN(prop_ret, "r")) == NULL) { popup_message(globals.widgets.top_level, MSG_ERR, NULL, "Loading %s failed: %s", prop_ret, strerror(errno)); return; } set_dvi_name_expand(prop_ret); /*FIXME: Here we would like to insert the first page of the new file into the page history: page_history_insert(0); However, if the commandline had been: xdvi -unique +5 file.dvi then the following property change will change to page 5 and insert page 5, so in this case two pages will be inserted by the xdvi invocation. I guess to fix this we'd need a bitmask instead of 2 atoms to set both features at the same time ... */ globals.ev.flags |= EV_NEWDOC; } else if ((&(ev->xproperty))->atom == atom_newpage()) { /* jumping to a different page */ int newpage; char *testptr; if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newpage(), &prop_ret, XGetWindowProperty)) == 0) { TRACE_CLIENT((stderr, "property_get_data() failed for atom_newpage(): |%s|", prop_ret)); return; } TRACE_CLIENT((stderr, "got back atom_newpage: |%s|", prop_ret)); if (strcmp(prop_ret, "+") == 0) { /* special case: treat `+' as last page */ newpage = total_pages - 1; } else { newpage = strtol(prop_ret, &testptr, 10) - 1; if (*testptr != '\0') { XDVI_FATAL((stderr, "Invalid page number: `%s'.", prop_ret)); } } if (newpage == total_pages - 1) { /* as in Act_goto_page() */ goto_page(check_goto_page(newpage), resource.keep_flag ? NULL : home, False); search_signal_page_changed(); } else { goto_page(check_goto_tex_page(newpage), resource.keep_flag ? NULL : home, False); search_signal_page_changed(); } } else if ((&(ev->xproperty))->atom == atom_raise()) { XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level)); raise_message_windows(); } else if ((&(ev->xproperty))->atom == atom_reread_prefs()) { read_user_preferences(globals.widgets.top_level, ".xdvirc.tmp"); } } /* * Signal routines. At the signal level, all we do is set flags. */ #ifndef FLAKY_SIGPOLL static RETSIGTYPE handle_sigpoll(int signo) { UNUSED(signo); globals.ev.ctr = 1; event_freq = -1; /* forget Plan B */ sig_flags |= SF_POLL; # ifndef HAVE_SIGACTION (void) signal(SIGPOLL, handle_sigpoll); /* reset the signal */ # endif } #endif static RETSIGTYPE handle_sigterm(int signo) { UNUSED(signo); sig_flags |= SF_TERM; } static RETSIGTYPE handle_sigchld(int signo) { UNUSED(signo); sig_flags |= SF_CHLD; } static RETSIGTYPE handle_sigalrm(int signo) { UNUSED(signo); sig_flags |= SF_ALRM; } static RETSIGTYPE handle_sigusr(int signo) { UNUSED(signo); globals.ev.ctr = 1; sig_flags |= SF_USR; } static RETSIGTYPE handle_sigsegv(int signo) { UNUSED(signo); XDVI_ABORT((stderr, "Segmentation fault - trying to clean up and aborting ...")); } static Boolean sigalarm_initialized = False; void setup_sigalarm(void) { #if HAVE_SIGACTION struct sigaction a; a.sa_handler = handle_sigalrm; (void) sigemptyset(&a.sa_mask); (void) sigaddset(&a.sa_mask, SIGALRM); a.sa_flags = 0; sigaction(SIGALRM, &a, NULL); #else /* not HAVE_SIGACTION */ (void) signal(SIGALRM, handle_sigalrm); #endif /* not HAVE_SIGACTION */ sigalarm_initialized = True; } /* * Called from main to set up the signal handlers. */ void setup_signal_handlers(void) { #ifndef FLAKY_SIGPOLL int sock_fd = ConnectionNumber(DISP); #endif #if HAVE_SIGACTION struct sigaction a; #endif #ifndef FLAKY_SIGPOLL #if HAVE_SIGACTION /* Subprocess handling, e.g., MakeTeXPK, fails on the Alpha without this, because SIGPOLL interrupts the call of system(3), since OSF/1 doesn't retry interrupted wait calls by default. From code by maj@cl.cam.ac.uk. */ a.sa_handler = handle_sigpoll; (void) sigemptyset(&a.sa_mask); (void) sigaddset(&a.sa_mask, SIGPOLL); a.sa_flags = SA_RESTART; sigaction(SIGPOLL, &a, NULL); #else /* not HAVE_SIGACTION */ (void) signal(SIGPOLL, handle_sigpoll); #endif /* not HAVE_SIGACTION */ prep_fd(sock_fd, False); #endif /* not FLAKY_SIGPOLL */ #if HAVE_SIGACTION a.sa_handler = handle_sigterm; (void) sigemptyset(&a.sa_mask); (void) sigaddset(&a.sa_mask, SIGINT); (void) sigaddset(&a.sa_mask, SIGQUIT); (void) sigaddset(&a.sa_mask, SIGTERM); (void) sigaddset(&a.sa_mask, SIGHUP); a.sa_flags = SA_RESETHAND; sigaction(SIGINT, &a, NULL); sigaction(SIGQUIT, &a, NULL); sigaction(SIGTERM, &a, NULL); sigaction(SIGHUP, &a, NULL); a.sa_handler = handle_sigsegv; (void)sigemptyset(&a.sa_mask); (void)sigaddset(&a.sa_mask, SIGSEGV); a.sa_flags = 0; sigaction(SIGSEGV, &a, NULL); #else /* not HAVE_SIGACTION */ (void) signal(SIGINT, handle_sigterm); (void) signal(SIGQUIT, handle_sigterm); (void) signal(SIGTERM, handle_sigterm); (void) signal(SIGHUP, handle_sigterm); (void)signal(SIGSEGV, handle_sigsegv); #endif /* not HAVE_SIGACTION */ #if HAVE_SIGACTION a.sa_handler = handle_sigchld; (void) sigemptyset(&a.sa_mask); (void) sigaddset(&a.sa_mask, SIGCHLD); a.sa_flags = 0; sigaction(SIGCHLD, &a, NULL); #else /* not HAVE_SIGACTION */ (void) signal(SIGCHLD, handle_sigchld); #endif /* not HAVE_SIGACTION */ #if HAVE_SIGACTION a.sa_handler = handle_sigusr; (void) sigemptyset(&a.sa_mask); (void) sigaddset(&a.sa_mask, SIGUSR1); a.sa_flags = 0; sigaction(SIGUSR1, &a, NULL); #else /* not HAVE_SIGACTION */ (void) signal(SIGUSR1, handle_sigusr); #endif /* not HAVE_SIGACTION */ (void)sigemptyset(&all_signals); (void)sigaddset(&all_signals, SIGPOLL); (void)sigaddset(&all_signals, SIGINT); (void)sigaddset(&all_signals, SIGQUIT); (void)sigaddset(&all_signals, SIGTERM); (void)sigaddset(&all_signals, SIGHUP); (void)sigaddset(&all_signals, SIGCHLD); (void)sigaddset(&all_signals, SIGALRM); (void)sigaddset(&all_signals, SIGUSR1); (void)sigaddset(&all_signals, SIGSEGV); } /* * Mid-level signal handlers. These are called from within read_events(), * and do the actual work appropriate for the given signal. */ /* * Process-related routines. Call set_chld(xchild *) to indicate * that a given child process should be watched for when it * terminates. Call clear_chld() to remove the process from the * list. When the child terminates, the record is removed from * the list and xchild->proc is called. * The caller can set the `io' member in xchild to a pointer to * an xio structure for reading from the file descriptor; see * psgs.c and util.c for examples. */ static struct xchild *child_recs = NULL; /* head of child process list */ void set_chld(struct xchild *cp) { cp->next = child_recs; child_recs = cp; } void clear_chld(struct xchild *cp) { struct xchild **cpp; struct xchild *cp2; if (child_recs == NULL) { if (globals.debug & DBG_EVENT) fprintf(stderr, "child_recs: %p\n", (void *)child_recs); return; } for (cpp = &child_recs;;) { cp2 = *cpp; if (cp2 == cp) break; cpp = &cp2->next; } *cpp = cp->next; } static void do_sigchld(void) { pid_t pid; int status; sig_flags &= ~SF_CHLD; #if ! HAVE_SIGACTION (void) signal(SIGCHLD, handle_sigchld); /* reset the signal */ #endif for (;;) { #if HAVE_WAITPID pid = waitpid(-1, &status, WNOHANG); #else pid = wait3(&status, WNOHANG, NULL); #endif if (pid == 0) break; if (pid != -1) { struct xchild **cpp; struct xchild *cp; for (cpp = &child_recs;;) { cp = *cpp; if (cp == NULL) break; if (cp->pid == pid) { *cpp = cp->next; /* unlink it */ /* call exit reporting procedure for this child */ (cp->proc)(status, cp); break; } cpp = &cp->next; } break; } if (errno == EINTR) continue; if (errno == ECHILD) break; #if HAVE_WAITPID perror("xdvi: waitpid"); #else perror("xdvi: wait3"); #endif break; } } /* * File-related routines. Call set_io() to indicate that a given fd * should be watched for ability to input or output data. Call clear_io() * to remove it from the list. When poll()/select() indicates that the fd * is available for the indicated type of i/o, the corresponding routine * is called. Call clear_io() to remove an fd from the list. * Both set_io() and clear_io() can be called from within read_proc or * write_proc (although turning an io descriptor on or off is better * accomplished by setting the events flag in the xio structure, and * in the corresponding pollfd structure if the pfd pointer is not NULL * (it is always non-NULL when read_proc and write_proc are called)). * We allocate space for one additional record in the pollfd array, to * accommodate the fd for the X connection; this is done by initializing * num_fds to 1 instead of zero. */ static struct xio *iorecs = NULL; /* head of xio list */ #if HAVE_POLL static struct pollfd *fds = NULL; static int num_fds = 1; /* current number of fds */ static int max_fds = 0; /* max allocated number of fds */ static Boolean io_dirty= True; /* need to recompute fds[] array */ #else static int numfds = 0; static fd_set readfds; static fd_set writefds; #endif void set_io(struct xio *ip) { ip->next = iorecs; iorecs = ip; #if HAVE_POLL ++num_fds; if (!io_dirty && num_fds <= max_fds) { fds[num_fds - 1].fd = ip->fd; fds[num_fds - 1].events = ip->xio_events; ip->pfd = &fds[num_fds - 1]; } else { ip->pfd = NULL; io_dirty = True; } #else if (numfds <= ip->fd) numfds = ip->fd + 1; #endif } void clear_io(struct xio *ip) { struct xio **ipp; for (ipp = &iorecs;;) { struct xio *ip2; ip2 = *ipp; if (ip2 == ip) break; ipp = &ip2->next; } *ipp = ip->next; #if HAVE_POLL --num_fds; io_dirty = True; #else # if FLAKY_SIGPOLL numfds = ConnectionNumber(DISP); # else numfds = (event_freq < 0 ? -1 : ConnectionNumber(DISP)); # endif for (ip = iorecs; ip != NULL; ip = ip->next) if (ip->fd > numfds) numfds = ip->fd; ++numfds; #endif /* !HAVE_POLL */ } static void do_sigpoll(void) { #if FLAKY_SIGPOLL sig_flags &= ~SF_POLL; #else struct xio *ip; sig_flags &= ~SF_POLL; # if HAVE_POLL if (io_dirty) { struct pollfd *fp; if (num_fds > max_fds) { if (fds != NULL) free(fds); fds = xmalloc(num_fds * sizeof *fds); max_fds = num_fds; fds->fd = ConnectionNumber(DISP); fds->events = POLLIN; } fp = fds + 1; for (ip = iorecs; ip != NULL; ip = ip->next) { fp->fd = ip->fd; fp->events = ip->xio_events; ip->pfd = fp; ++fp; } io_dirty = False; } for (;;) { if (poll(fds + 1, num_fds - 1, 0) >= 0) break; if (errno != EAGAIN && errno != EINTR) { perror("xdvi: poll"); return; } } for (ip = iorecs; ip != NULL; ip = ip->next) { int revents = ip->pfd->revents; if (revents & POLLIN && ip->read_proc != NULL) (void)(ip->read_proc)(ip->fd); if (revents & POLLOUT && ip->write_proc != NULL) (ip->write_proc)(ip->fd); } # else FD_ZERO(&readfds); FD_ZERO(&writefds); for (ip = iorecs; ip != NULL; ip = ip->next) { if (ip->xio_events & XIO_IN) FD_SET(ip->fd, &readfds); if (ip->xio_events & XIO_OUT) FD_SET(ip->fd, &writefds); } for (;;) { struct timeval tv; tv.tv_sec = tv.tv_usec = 0; if (select(numfds, &readfds, &writefds, (fd_set *) NULL, &tv) >= 0) break; if (errno != EAGAIN && errno != EINTR) { perror("select (xdvi read_events)"); return; } } for (ip = iorecs; ip != NULL; ip = ip->next) { if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL) (void)(ip->read_proc)(ip->fd); if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL) (ip->write_proc)(ip->fd); } # endif #endif /* not FLAKY_SIGPOLL */ } /* * Timer-related routines. Call set_timer() to set a timer a given number * of milliseconds in the future. At that time, the timer will be cleared * and the given procedure will be called with argument set to the struct * passed to set_timer(). The timer routine may call set_timer() or * cancel_timer(). */ static struct xtimer *timers = NULL; /* head of timer list */ static struct itimerval itv = {{0, 0}, {0, 0}}; #ifndef timercmp #define timercmp(a, b, OP) (((a)->tv_sec OP (b)->tv_sec || \ ((a)->tv_sec == (b)->tv_sec && (a)->tv_usec OP (b)->tv_usec))) #endif /* timercmp */ static void show_timers(char *what) { struct xtimer *tp; fprintf(stderr, "=======%s; timers:\n", what); for (tp = timers; tp != NULL; tp = tp->next) { fprintf(stderr, "timer %p: %lu\n", (void *)tp, (unsigned long)tp->when.tv_sec); } fprintf(stderr, "=======\n"); } void set_timer(struct xtimer *tp, int ms) { struct xtimer **tpp; struct xtimer *tp2; if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: set_timer\n", __FILE__, __LINE__); gettimeofday(&tp->when, NULL); itv.it_value.tv_sec = ms / 1000; itv.it_value.tv_usec = (ms % 1000) * 1000; tp->when.tv_sec += itv.it_value.tv_sec; tp->when.tv_usec += itv.it_value.tv_usec; if (tp->when.tv_usec >= 1000000) { tp->when.tv_usec -= 1000000; ++tp->when.tv_sec; } for (tpp = &timers;;) { /* add timer to list */ tp2 = *tpp; if (tp2 == NULL || timercmp(&tp->when, &tp2->when, <)) break; tpp = &tp2->next; } tp->next = tp2; *tpp = tp; if (tpp == &timers) { setitimer(ITIMER_REAL, &itv, NULL); if (ms == 0) sig_flags |= SF_ALRM; } if (globals.debug & DBG_EVENT) show_timers("after set_timer"); } void cancel_timer(struct xtimer *tp) { struct xtimer **tpp; if (globals.debug & DBG_EVENT) show_timers("beginning of cancel_timer"); if (timers == NULL) { fprintf(stderr, "%s:%d: BUG? timers == NULL!\n", __FILE__, __LINE__); return; } if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: cancel_timer %p from %p\n", __FILE__, __LINE__, (void *)&timers, (void *)tp); ASSERT(timers != NULL, "timers in cancel_timer() mustn't be NULL"); for (tpp = &timers; ; ) { /* remove from list */ if (*tpp == tp) break; tpp = &(*tpp)->next; } *tpp = (*tpp)->next; /* unlink it */ if (timers == NULL) { /* cancel SIGALRM */ itv.it_value.tv_sec = itv.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itv, NULL); } } #if XDVI_XT_TIMER_HACK /* * Original comment by Paul: * Newer versions of the Motif toolkit use the timer facility * (XtAppAddTimeOut(), etc.) in the X Toolkit. Proper functioning of * this mechanism, however, requires that the X Toolkit be in charge of * blocking. Since xdvi does its own blocking, this means that we need * to provide working alternatives to these X Toolkit routines (by * redefining them ...). * One symptom of the above-mentioned bug is that the printlog window * eventually stops showing dvips progress until you move the mouse. * * Comment SU: * Xdvik also uses XtAppAddTimeOut() in other places (hyperref/statusline/...), * so we also need these redefinitions also in the Xaw version. * */ static void xt_alarm (struct xtimer *); static struct xtimer *xt_free_timers = NULL; XtIntervalId XtAddTimeOut(unsigned long interval, XtTimerCallbackProc proc, XtPointer closure) { return XtAppAddTimeOut(NULL, interval, proc, closure); } XtIntervalId XtAppAddTimeOut(XtAppContext app, unsigned long interval, XtTimerCallbackProc proc, XtPointer closure) { struct xtimer *tp; UNUSED(app); /* FIXME: better way of checking this instead of static boolean sigalarm_initialized? The following doesn't work, even after sigaddset(&all_signals, SIGALRM); static sigset_t sig_set; (void)sigprocmask(0, NULL, &sig_set); if (sigismember(&sig_set, SIGALRM)) ... OK ... else ... NOT OK ... */ ASSERT(sigalarm_initialized, "Shouldn't invoke XtAppAddTimeOut() before setup_sigalarm()"); if (globals.debug & DBG_EVENT) fprintf(stderr, "XtAppAddTimeOut: %lu msecs\n", interval); if (xt_free_timers == NULL) tp = xmalloc(sizeof *tp); else { tp = xt_free_timers; xt_free_timers = xt_free_timers->next; } tp->proc = xt_alarm; tp->xt_proc = proc; tp->closure = closure; set_timer(tp, interval); if (globals.debug & DBG_EVENT) show_timers("XtAppAddTimeOut"); return (XtIntervalId)tp; } void XtRemoveTimeOut(XtIntervalId id) { struct xtimer *tp; ASSERT(id != 0, "XtIntervalId argument in XtRemoveTimeOut() mustn't be NULL"); tp = (struct xtimer *)id; /* Motif (2.1 on Solaris 9, 2003) sometimes calls XtRemoveTimeOut() after the timer event has occurred, so we need to be sure not to remove the timer record twice. */ if (tp->proc == NULL) return; cancel_timer(tp); tp->next = xt_free_timers; xt_free_timers = tp; if (globals.debug & DBG_EVENT) show_timers("XtRemoveTimeOut"); } void xt_alarm(struct xtimer *tp) { XtIntervalId id; tp->proc = NULL; /* flag timer as used-up */ id = (XtIntervalId) tp; (tp->xt_proc)(tp->closure, &id); tp->next = xt_free_timers; xt_free_timers = tp; if (globals.debug & DBG_EVENT) show_timers("xt_alarm"); } #endif /* XDVI_XT_TIMER_HACK */ static void do_sigalrm(void) { struct timeval now; sig_flags &= ~SF_ALRM; #ifndef HAVE_SIGACTION (void) signal(SIGALRM, handle_sigalrm); /* reset the signal */ #endif gettimeofday(&now, NULL); while (timers != NULL && timercmp(&timers->when, &now, <=)) { struct xtimer *tp = timers; timers = timers->next; /* unlink it _first_ */ (tp->proc)(tp); } if (timers != NULL) { /* set next timer */ int i; itv.it_value.tv_sec = timers->when.tv_sec - now.tv_sec; i = timers->when.tv_usec - now.tv_usec; if (i < 0) { --itv.it_value.tv_sec; i += 1000000; } itv.it_value.tv_usec = i; setitimer(ITIMER_REAL, &itv, NULL); } } /* * Handle SIGUSR1 signal. Pretty straightforward. */ static void do_sigusr(void) { sig_flags &= ~SF_USR; #ifndef HAVE_SIGACTION (void) signal(SIGUSR1, handle_sigusr); /* reset the signal */ #endif globals.ev.flags |= EV_RELOAD; } static void do_sigsegv(void) { sig_flags &= ~SF_SEGV; #ifndef HAVE_SIGACTION (void) signal(SIGSEGV, handle_sigsegv); /* reset the signal */ #endif handle_sigsegv(SIGSEGV); } /* * Handle termination signals. Kill child processes, if permitted. * Otherwise, leave it up to the caller. This is the only place where * the EV_TERM event flag is set, and it can only happen if there's an * active non-killable process. This should only happen if read_events() * is called from one of a very few select locations. */ static void do_sigterm(void) { struct xchild *cp; sig_flags &= ~SF_TERM; /* loop through child processes */ for (cp = child_recs; cp != NULL; cp = cp->next) { if (cp->killable) kill(cp->pid, SIGKILL); } /* SU: Unlike non-k xdvi, we don't care about whether all children have been killed, since processes forked via fork_process() (e.g. the web browser) may still continue running. So we just exit here. */ xdvi_exit(EXIT_SUCCESS); /* since xdvi_exit() may return, we mustn't do the following which is in non-k xdvi, else we'll end up in a busy loop! I don't know what's meant by `caller' here anyway ... globals.ev.flags |= EV_TERM; /\* otherwise, let the caller handle it *\/ */ } /* * Since redrawing the screen is (potentially) a slow task, xdvi checks * for incoming events while this is occurring. It does not register * a work proc that draws and returns every so often, as the toolkit * documentation suggests. Instead, it checks for events periodically * (or not, if SIGPOLL can be used instead) and processes them in * a subroutine called by the page drawing routine. This routine (below) * checks to see if anything has happened and processes those events and * signals. (Or, if it is called when there is no redrawing that needs * to be done, it blocks until something happens.) * * Ultimately, the goal is to have this be the only place in xdvi where * blocking occurs. * * The argument to this function should be a mask of event types (EV_*) * indicating which event types should cause read_events to return instead * of waiting for more events. This function will always process all * pending events and signals before returning. * The return value is the value of globals.ev.flags. */ unsigned int read_events(unsigned int ret_mask) { XEvent event; #if !HAVE_POLL if (numfds == 0) numfds = ConnectionNumber(DISP) + 1; #endif if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: read_events %u\n", __FILE__, __LINE__, ret_mask); for (;;) { globals.ev.ctr = event_freq; /* * The above line clears the flag indicating that an event is * pending. So if an event comes in right now, the flag will be * set again needlessly, but we just end up making an extra call. * Also, be careful about destroying the magnifying glass while * drawing on it. */ #if !FLAKY_SIGPOLL if (event_freq < 0) { /* if SIGPOLL works */ if (!XtPending()) { sigset_t oldsig; (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig); for (;;) { #ifdef SHOW_SIG_FLAGS /* this gives HUGE output ... */ if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: sig_flags = %d\n", __FILE__, __LINE__, sig_flags); #endif while (sig_flags) { flags_to_sigproc[sig_flags](); } if (XtPending()) break; if (globals.ev.flags & ret_mask) { (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL); return globals.ev.flags; } (void) sigsuspend(&oldsig); } (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL); } } else #endif /* not FLAKY_SIGPOLL */ { for (;;) { struct xio *ip; if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: (flaky) sig_flags = %d\n", __FILE__, __LINE__, sig_flags); while (sig_flags) { sigset_t oldsig; (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig); while (sig_flags) { flags_to_sigproc[sig_flags](); } (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL); } if (XtPending()) break; if (globals.ev.flags & ret_mask) return globals.ev.flags; /* If a SIGUSR1 signal comes right now, then it will wait until an X event or another SIGUSR1 signal arrives. */ #if HAVE_POLL if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: have_poll!\n", __FILE__, __LINE__); if (io_dirty) { struct pollfd *fp; if (num_fds > max_fds) { if (fds != NULL) free(fds); fds = xmalloc(num_fds * sizeof *fds); max_fds = num_fds; fds->fd = ConnectionNumber(DISP); fds->events = POLLIN; } fp = fds + 1; for (ip = iorecs; ip != NULL; ip = ip->next) { fp->fd = ip->fd; fp->events = ip->xio_events; ip->pfd = fp; ++fp; } io_dirty = False; } for (;;) { if (poll(fds, num_fds, -1) >= 0) { for (ip = iorecs; ip != NULL; ip = ip->next) { int revents = ip->pfd->revents; if (revents & POLLIN && ip->read_proc != NULL) (ip->read_proc)(ip->fd); if (revents & POLLOUT && ip->write_proc != NULL) (ip->write_proc)(ip->fd); } break; } if (errno == EINTR) break; if (errno != EAGAIN) { perror("xdvi: poll"); break; } } #else /* HAVE_POLL */ if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: NOT have_poll!\n", __FILE__, __LINE__); FD_ZERO(&readfds); FD_ZERO(&writefds); FD_SET(ConnectionNumber(DISP), &readfds); for (ip = iorecs; ip != NULL; ip = ip->next) { if (ip->xio_events & XIO_IN) FD_SET(ip->fd, &readfds); if (ip->xio_events & XIO_OUT) FD_SET(ip->fd, &writefds); } for (;;) { if (select(numfds, &readfds, &writefds, (fd_set *) NULL, (struct timeval *) NULL) >= 0) { for (ip = iorecs; ip != NULL; ip = ip->next) { if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL) { if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: reading from %d\n", __FILE__, __LINE__, ip->fd); (ip->read_proc)(ip->fd); } if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL) { if (globals.debug & DBG_EVENT) fprintf(stderr, "%s:%d: writing to %d\n", __FILE__, __LINE__, ip->fd); (ip->write_proc)(ip->fd); } } break; } if (errno == EINTR) break; if (errno != EAGAIN) { perror("xdvi: select"); break; } } #endif /* HAVE_POLL */ } } XtAppNextEvent(app, &event); #ifdef MOTIF if ((resource.expert_mode & XPRT_SHOW_TOOLBAR) != 0) TipAppHandle(app, &event); #endif if (resized) get_geom(); if (event.xany.window == magnifier.win && event.type == Expose) { handle_expose((Widget) NULL, (XtPointer) &magnifier, &event, (Boolean *) NULL); continue; } else if (globals.broken_motif_event_handling && (resource.mouse_mode == MOUSE_RULER_MODE || resource.mouse_mode == MOUSE_TEXT_MODE)) { /* In this case, Act_motion() and Act_release() are not called properly * for updating the ruler/text selection (it works with the magnifier though), * so we need to invoke them ourselves here: */ if (event.type == MotionNotify) Act_motion(NULL, &event, NULL, NULL); else if (event.type == ButtonRelease) Act_release(NULL, &event, NULL, NULL); } #ifdef MOTIF if (XtIsRealized(globals.widgets.top_level) && event.xany.window == XtWindow(globals.widgets.clip_widget) && event.type == KeyPress) { /* workaround for #610206 */ motif_translations_hack(); } #else if (resource.expert_mode & XPRT_SHOW_BUTTONS) SubMenuHandleEvent(app, &event); #endif XtDispatchEvent(&event); } } /* * Higher-level routines for managing events. */ static void can_exposures(struct WindowRec *windowrec) { windowrec->min_x = windowrec->min_y = MAXDIM; windowrec->max_x = windowrec->max_y = 0; } void redraw(struct WindowRec *windowrec) { currwin = *windowrec; globals.win_expose.min_x = currwin.min_x + currwin.base_x; globals.win_expose.min_y = currwin.min_y + currwin.base_y; globals.win_expose.max_x = currwin.max_x + currwin.base_x; globals.win_expose.max_y = currwin.max_y + currwin.base_y; can_exposures(windowrec); /* fix for bug #619070 - the complicated flags (and do_update_property) are needed to avoid too many updates at exposures, especially for a window of another xdvi instance when the magnifier intersects with that window. */ if (have_src_specials && do_update_property && globals.win_expose.min_x != 1 && globals.win_expose.max_y - globals.win_expose.min_y != 1 && currwin.base_x == 0 && currwin.base_y == 0) { update_window_property(XtWindow(globals.widgets.top_level), True); } TRACE_EVENTS((stderr, "Redraw %d x %d at (%d, %d) (base=%d,%d)", globals.win_expose.max_x - globals.win_expose.min_x, globals.win_expose.max_y - globals.win_expose.min_y, globals.win_expose.min_x, globals.win_expose.min_y, currwin.base_x, currwin.base_y)); /* can't use ev_cursor here, since the event loop might not see this change quick enough */ if (!(globals.ev.flags & EV_CURSOR)) { TRACE_EVENTS((stderr, "Cursor: %ld", globals.cursor.flags)); if (!(globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A))) { if (resource.mouse_mode == MOUSE_RULER_MODE) XDefineCursor(DISP, CURSORWIN, globals.cursor.rule); else XDefineCursor(DISP, CURSORWIN, globals.cursor.wait); XFlush(DISP); } globals.ev.flags |= EV_CURSOR; } /* No longer needed since windows are correctly transient now */ /* raise_message_windows(); */ raise_file_selector(); draw_page(); globals.warn_spec_now = False; } void redraw_page(void) { #if COLOR const struct rgb *rgbp; #endif TRACE_FILES((stderr, "Redraw page on %p", (void *)globals.dvi_file.bak_fp)); if (globals.dvi_file.bak_fp == NULL) return; if (scanned_page < current_page) { TRACE_FILES((stderr, "redraw_page: scanned_page = %d, current_page = %d, prescanning %p\n", scanned_page, current_page, (void *)globals.dvi_file.bak_fp)); prescan(globals.dvi_file.bak_fp); if (globals.ev.flags & EV_GE_NEWPAGE) { /* if we need to re-prescan */ return; } } TRACE_FILES((stderr, "redraw_page: current_page = %d", current_page)); if (pageinfo_get_window_width(current_page) != globals.page.unshrunk_w || pageinfo_get_window_height(current_page) != globals.page.unshrunk_h) { TRACE_FILES((stderr, "NEW SIZE: %dx%d", pageinfo_get_window_width(current_page), pageinfo_get_window_height(current_page))); init_page(); reconfig(); } /* We can't call home() without proper unshrunk_page_*, which requires * prescan(), which can't be done from within read_events() */ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! There's some complicated interaction with Postscript specials here: if home_action check comes before the gs stuff, psp.drawfile might not get initialized correctly, resulting in empty PS figures (bounding box instead of figure). This is different in xdvi, due to different handling of the home_action stuff, but at the moment I can't remember the reason for this ... !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ #if PS_GS if (gs_postpone_prescan) { if (!setjmp(globals.ev.canit)) { gs_resume_prescan(); } else return; } #endif if (home_action != NULL) { home_action(False); home_action = NULL; /* This discards the expose event generated by home() (1 for each page) */ if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) { return; } can_exposures(&mane); } #if COLOR rgbp = &bg_initial; if (page_colors.stack != NULL) { ASSERT(current_page < (int)page_colors.size, "page_colors.size too small"); rgbp = &page_colors.stack[current_page].bg; } /* Set background color */ if (bg_current == NULL || rgbp->r != bg_current->color.r || rgbp->g != bg_current->color.g || rgbp->b != bg_current->color.b) { struct bgrec **bgpp; for (bgpp = &bg_head;;) { bg_current = *bgpp; if (bg_current == NULL) { /* if bg is not in list */ bg_current = *bgpp = xmalloc(sizeof *bg_current); bg_current->next = NULL; bg_current->color = *rgbp; bg_current->fg_head = NULL; bg_current->pixel_good = False; break; } if (bg_current->color.r == rgbp->r && bg_current->color.g == rgbp->g && bg_current->color.b == rgbp->b) break; bgpp = &bg_current->next; } fg_current = NULL; /* force change of foreground color */ /* globals.gc.high is only used in XDrawRectangle, so its background color doesn't need to be changed. */ if (globals.debug & DBG_DVI) printf("Changing background color to %5d %5d %5d\n", bg_current->color.r, bg_current->color.g, bg_current->color.b); if (!bg_current->pixel_good) { bg_current->pixel = alloc_color(&bg_current->color, color_data[1].pixel); bg_current->pixel_good = True; } XSetWindowBackground(DISP, mane.win, bg_current->pixel); #if MOTIF && !FIXED_FLUSHING_PAGING XtVaSetValues(XtParent(globals.widgets.draw_widget), XtNbackground, bg_current->pixel, NULL); #endif /* XSetWindowBackground(DISP, mane.win, bg_current->pixel); */ /* XClearWindow(DISP, mane.win); */ #if 0 /* don't recolor the cursor - gives too low contrast on color backgrounds, and bad appearance when part of the background is white and cursor mask is colored */ { XColor bg_Color; bg_Color.pixel = bg_current->pixel; XQueryColor(DISP, G_colormap, &bg_Color); XRecolorCursor(DISP, globals.cursor.ready, &globals.cr_color, &bg_Color); XRecolorCursor(DISP, globals.cursor.wait, &globals.cr_color, &bg_Color); } #endif } #endif /* COLOR */ if (!globals.pausing.flag) { XClearWindow(DISP, mane.win); } if (G_backing_store != NotUseful) { mane.min_x = mane.min_y = 0; mane.max_x = globals.page.w; mane.max_y = globals.page.h; } else { get_xy(); mane.min_x = -window_x; mane.max_x = -window_x + mane.width; mane.min_y = -window_y; mane.max_y = -window_y + mane.height; } /* update_TOC(); */ redraw(&mane); } void do_pages(void) { if (globals.debug & DBG_BATCH) { (void)read_events(EV_GT_IDLE); for (current_page = 0; current_page < total_pages; ++current_page) { if (resource.keep_flag) { home_action = NULL; } else { home_action = home; } globals.warn_spec_now = resource.warn_spec; #if PS_GS for (;;) { redraw_page(); (void) read_events(EV_NOWAIT); if (!(globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD))) break; globals.ev.flags = EV_IDLE; } #else redraw_page(); #endif } xdvi_exit(EXIT_SUCCESS); } else { for (;;) { /* normal operation */ (void) read_events(EV_GT_IDLE); TRACE_EVENTS((stderr, "globals.ev.flags: %d; ev_newpage: %d, ev_newdoc: %d, ev_reload: %d\n", globals.ev.flags, EV_NEWPAGE, EV_NEWDOC, EV_RELOAD)); /* NOTE: reloading must be checked first! */ if (globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD | EV_PS_TOGGLE)) { TRACE_EVENTS((stderr, "EV_NEWPAGE | ...")); globals.ev.flags &= ~(EV_NEWPAGE | EV_EXPOSE | EV_PS_TOGGLE); if (globals.ev.flags & EV_RELOAD) { dviErrFlagT errflag; globals.ev.flags &= ~EV_RELOAD; if (load_dvi_file(True, &errflag)) { #if PS ps_clear_cache(); #if PS_GS if (resource.gs_alpha) { /* restart gs so that user has a method for fixing GS artifacts with gs_alpha by using `reload' (see also GS_PIXMAP_CLEARING_HACK) */ ps_destroy(); } #endif #endif statusline_print(STATUS_SHORT, "File reloaded."); } /* else { */ /* statusline_print(STATUS_MEDIUM, "File corrupted, not reloading."); */ /* } */ } if (globals.ev.flags & EV_NEWDOC) { dviErrFlagT errflag; TRACE_EVENTS((stderr, "EV_NEWDOC!")); /* fprintf(stderr, "newdoc!\n"); */ TRACE_FILES((stderr, "current page: %d", current_page)); /* file_history_set_page(current_page); */ globals.ev.flags &= ~EV_NEWDOC; if (load_dvi_file(True, &errflag)) { statusline_append(STATUS_SHORT, "Opened ", "Opened \"%s\"", globals.dvi_name); /* statusline_print(STATUS_SHORT, "Opened \"%s\"", globals.dvi_name); */ TRACE_FILES((stderr, "Adding to history: |%s|\n", globals.dvi_name)); if (file_history_push(globals.dvi_name)) { /* it's a new file, add to history */ TRACE_FILES((stderr, "New entry!")); filehist_menu_add_entry(globals.dvi_name); } else { /* only need to move existing elements to new positions */ TRACE_FILES((stderr, "Existing entry!\n")); filehist_menu_refresh(); } } } can_exposures(&mane); can_exposures(&magnifier); #if PS && PS_GS && GS_PIXMAP_CLEARING_HACK if (had_ps_specials && !MAGNIFIER_ACTIVE) { erasepage_gs(); had_ps_specials = False; } #endif /* PS && PS_GS && GS_PIXMAP_CLEARING_HACK */ if (globals.dvi_file.bak_fp != NULL) { TRACE_EVENTS((stderr, "redraw_page()")); redraw_page(); } else { TRACE_EVENTS((stderr, "dvi_file_changed()")); (void)dvi_file_changed(); } } else if (globals.ev.flags & EV_FILEHIST_GOTO_PAGE) { int pageno; globals.ev.flags &= ~EV_FILEHIST_GOTO_PAGE; pageno = check_goto_page(file_history_get_page()); goto_page(pageno, resource.keep_flag ? NULL : home, False); TRACE_FILES((stderr, "got page: %d", pageno)); } else if (globals.ev.flags & EV_PAGEHIST_INSERT) { globals.ev.flags &= ~EV_PAGEHIST_INSERT; page_history_insert(current_page); } else if (globals.ev.flags & EV_FIND_CANCEL) { /* NOTE: This must be done before checking for expose() */ globals.ev.flags &= ~EV_FIND_CANCEL; } else if (globals.ev.flags & EV_ANCHOR) { /* * Similar to forward search: search for a htex anchor. * This needs to come before the next case which does the redraw_page(), * otherwise anchors for the current page might not be drawn at all: * anchor_search() sets the info later used by htex_draw_anchormarkers(), * which is invoked by redraw_page(). */ /* switch off the link cursor */ globals.cursor.flags &= ~CURSOR_LINK; if (dvi_file_changed()) continue; anchor_search(g_anchor_pos); globals.ev.flags &= ~EV_ANCHOR; } else if (globals.ev.flags & EV_SRC) { /* * Source special operations are deferred to here because * they call geom_scan(), which may call define_font(), * which may call makefont(), which may call read_events() * recursively. */ if (globals.src.fwd_string != NULL) { const char *s = globals.src.fwd_string; if (dvi_file_changed()) continue; source_forward_search(s); globals.ev.flags &= ~EV_SRC; globals.src.fwd_string = NULL; /* de-iconify window if needed, and raise it */ XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level)); raise_message_windows(); } else if (source_reverse_x != -1) { if (dvi_file_changed()) continue; source_reverse_search(source_reverse_x, source_reverse_y, True); globals.ev.flags &= ~EV_SRC; } else { source_special_show(source_show_all); globals.ev.flags &= ~EV_SRC; } } /* support for `-findstring' */ else if (globals.ev.flags & EV_FIND) { if (dvi_file_changed()) continue; if (resource.find_string != NULL) { /* not first call */ dvi_find_string(resource.find_string, False); resource.find_string = NULL; } else { /* actually should never arrive here?? */ dvi_find_string(NULL, True); } globals.ev.flags &= ~EV_FIND; } else if (globals.ev.flags & EV_MAG_MOVE) { MYTRACE((stderr, "moving mag!")); move_magnifier(); } else if (globals.ev.flags & EV_EXPOSE) { if (magnifier.min_x < MAXDIM) { /* fprintf(stderr, "magnifier < maxdim!\n"); */ if (mane.min_x >= MAXDIM) { /* fprintf(stderr, "mane >= maxdim!\n"); */ globals.ev.flags &= ~EV_EXPOSE; } redraw(&magnifier); } else { /* see comment in mag.c */ globals.ev.flags &= ~EV_EXPOSE; if (mane.min_x < MAXDIM) { redraw(&mane); } } } else if (globals.ev.flags & EV_CURSOR) { /* * This code eliminates unnecessary calls to XDefineCursor, * since this is a slow operation on some hardware (e.g., S3 * chips). */ XSync(DISP, False); if (!XtPending()) { Cursor curr; if (globals.cursor.flags & CURSOR_DRAG_V) curr = globals.cursor.drag_v; else if (globals.cursor.flags & CURSOR_DRAG_H) curr = globals.cursor.drag_h; else if (globals.cursor.flags & CURSOR_DRAG_A) curr = globals.cursor.drag_a; else if (resource.mouse_mode == MOUSE_RULER_MODE) curr = globals.cursor.rule; else if (resource.mouse_mode == MOUSE_TEXT_MODE && !(globals.cursor.flags & CURSOR_LINK)) curr = globals.cursor.text; else if (globals.cursor.flags & CURSOR_LINK) curr = globals.cursor.link; else if (globals.cursor.flags & CURSOR_MAG) curr = globals.cursor.mag; else if (globals.cursor.flags & CURSOR_CORRUPTED) curr = globals.cursor.corrupted; else if (globals.pausing.flag) curr = globals.cursor.pause; else curr = globals.cursor.ready; XDefineCursor(DISP, CURSORWIN, curr); globals.ev.flags &= ~EV_CURSOR; } } XFlush(DISP); } } } static void Act_unpause_or_next(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (block_this_mouse_event()) return; if (globals.pausing.flag) { globals.pausing.num++; if (globals.pausing.num_save) globals.pausing.num_save[current_page] = globals.pausing.num; redraw_page(); } else { Act_down_or_next(w, event, params, num_params); } } /* * timer callback for watching the DVI file. */ void watch_file_cb(XtPointer client_data, XtIntervalId *id) { static XtIntervalId timer = 0; UNUSED(client_data); UNUSED(id); if (resource.watch_file > 0.0) { unsigned long watch_time_ms; (void)dvi_file_changed(); if (timer) { XtRemoveTimeOut(timer); timer = (XtIntervalId)(XtIntervalId)0; } watch_time_ms = (unsigned long)(resource.watch_file * 1000); timer = XtAppAddTimeOut(app, watch_time_ms, watch_file_cb, (XtPointer)NULL); } } void Act_pagehistory_back(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) { arg = 1; } page_history_move(-arg); } void Act_pagehistory_forward(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) { arg = 1; } page_history_move(arg); } void Act_pagehistory_clear(Widget w, XEvent *event, String *params, Cardinal *num_params) { UNUSED(w); UNUSED(event); UNUSED(params); UNUSED(num_params); if (block_this_mouse_event()) return; page_history_clear(); } void Act_pagehistory_delete_backward(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) { arg = 1; } page_history_delete(-arg); } void Act_pagehistory_delete_forward(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) { arg = 1; } page_history_delete(arg); } #ifdef MOTIF void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params) { int arg; if (block_this_mouse_event()) return; UNUSED(w); UNUSED(event); if (!get_int_arg(params, num_params, &arg)) { arg = -1; } popup_preferences_dialog(globals.widgets.top_level, arg); } #endif /* MOTIF */