diff options
Diffstat (limited to 'Build/source/texk/windvi/events.c')
-rw-r--r-- | Build/source/texk/windvi/events.c | 3172 |
1 files changed, 3172 insertions, 0 deletions
diff --git a/Build/source/texk/windvi/events.c b/Build/source/texk/windvi/events.c new file mode 100644 index 00000000000..5898edeb821 --- /dev/null +++ b/Build/source/texk/windvi/events.c @@ -0,0 +1,3172 @@ +/*========================================================================*\ + +Copyright (c) 1990-1999 Paul Vojta + +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 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" + + +/* Xlib and Xutil are already included */ + +#ifdef TOOLKIT + +#ifdef OLD_X11_TOOLKIT +#include <X11/Atoms.h> +#else /* not OLD_X11_TOOLKIT */ +#include <X11/Xatom.h> +#include <X11/StringDefs.h> +#endif /* not OLD_X11_TOOLKIT */ + +#include <X11/Shell.h> /* needed for def. of XtNiconX */ + +#ifndef XtSpecificationRelease +#define XtSpecificationRelease 0 +#endif +#if XtSpecificationRelease >= 4 +#ifndef MOTIF +#include <X11/Xaw/Viewport.h> +#ifdef HTEX +#include <X11/Xaw/Dialog.h> +#include <X11/Xaw/Text.h> +#endif +#else /* MOTIF */ +#include <Xm/MainW.h> +#include <Xm/ToggleB.h> +#include <Xm/RowColumn.h> +#include <Xm/MenuShell.h> +#endif /* MOTIF */ + +#ifdef BUTTONS +#ifndef MOTIF +#include <X11/Xaw/Command.h> +#define PANEL_WIDGET_CLASS compositeWidgetClass +#define BUTTON_WIDGET_CLASS commandWidgetClass +#else /* MOTIF */ +#include <Xm/Form.h> +#include <Xm/BulletinB.h> +#include <Xm/PushB.h> +#define PANEL_WIDGET_CLASS xmBulletinBoardWidgetClass +#define BUTTON_WIDGET_CLASS xmPushButtonWidgetClass +#endif /* MOTIF */ +#endif /* BUTTONS */ + +#else /* XtSpecificationRelease < 4 */ + +#define XtPointer caddr_t +#include <X11/Viewport.h> +#ifdef BUTTONS +#include <X11/Command.h> +#define PANEL_WIDGET_CLASS compositeWidgetClass +#define BUTTON_WIDGET_CLASS commandWidgetClass +#endif + +#endif /* XtSpecificationRelease */ + +#else /* not TOOLKIT */ + +typedef int Position; +#define XtPending() XPending(DISP) + +#endif /* not TOOLKIT */ + +#include <signal.h> +#include <sys/file.h> /* this defines FASYNC */ + +#if !defined(FLAKY_SIGPOLL) && !HAVE_STREAMS && !defined(FASYNC) +#define FLAKY_SIGPOLL 1 +#endif + +#ifndef FLAKY_SIGPOLL + +#ifndef SIGPOLL +#define SIGPOLL SIGIO +#endif + +#ifndef SA_RESTART +#define SA_RESTART 0 +#endif + +#if HAVE_STREAMS +#include <stropts.h> + +#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_POLL +# include <poll.h> +#else +# if HAVE_SYS_SELECT_H +# include <sys/select.h> +# else +# if HAVE_SELECT_H +# include <select.h> +# endif +# endif +#endif + +#include <errno.h> + +#ifdef X_NOT_STDC_ENV +extern int errno; +#endif /* X_NOT_STDC_ENV */ + +#ifndef X11HEIGHT +#define X11HEIGHT 8 /* Height of server default font */ +#endif + +#define MAGBORD 1 /* border size for magnifier */ + +/* + * Command line flags. + */ + +#define delay_rulers resource._delay_rulers +#define tick_units resource._tick_units +#define tick_length resource._tick_length +#define fore_Pixel resource._fore_Pixel +#define back_Pixel resource._back_Pixel +#define high_Pixel resource._high_Pixel +#ifdef TOOLKIT +extern struct _resource resource; +#define brdr_Pixel resource._brdr_Pixel +#endif /* TOOLKIT */ + +#define clip_w mane.width +#define clip_h mane.height +static Position main_x, main_y; +static Position mag_x, mag_y, new_mag_x, new_mag_y; +static Boolean mag_moved = False; +static Boolean busycurs = False; + +#ifndef FLAKY_SIGPOLL +sigset_t sigpollusrterm; +#endif + +#if HAVE_POLL +static struct pollfd fds[1] = {{0, POLLIN, 0}}; +#else +static fd_set readfds; +#endif + +#ifdef TOOLKIT +#ifdef BUTTONS +static Widget line_widget; +static Widget panel_widget; +static int destroy_count = 0; +#endif /* BUTTONS */ + +#ifndef MOTIF +static Widget x_bar, y_bar; /* horizontal and vertical scroll bars */ +#endif + +static Arg resize_args[] = { + {XtNwidth, (XtArgVal) 0}, + {XtNheight, (XtArgVal) 0}, +}; + +#define XdviResizeWidget(widget, w, h) \ + (resize_args[0].value = (XtArgVal) (w), \ + resize_args[1].value = (XtArgVal) (h), \ + XtSetValues(widget, resize_args, XtNumber(resize_args)) ) + +#ifdef BUTTONS + +#ifndef MOTIF +static Arg resizable_on[] = { + {XtNresizable, (XtArgVal) True}, +}; + +static Arg resizable_off[] = { + {XtNresizable, (XtArgVal) False}, +}; +#endif + +static Arg line_args[] = { + {XtNbackground, (XtArgVal) 0}, + {XtNwidth, (XtArgVal) 1}, +#ifndef MOTIF + {XtNheight, (XtArgVal) 0}, + {XtNfromHoriz, (XtArgVal) NULL}, + {XtNborderWidth, (XtArgVal) 0}, + {XtNtop, (XtArgVal) XtChainTop}, + {XtNbottom, (XtArgVal) XtChainBottom}, + {XtNleft, (XtArgVal) XtChainRight}, + {XtNright, (XtArgVal) XtChainRight}, +#else /* MOTIF */ + {XmNleftWidget, (XtArgVal) NULL}, + {XmNleftAttachment, (XtArgVal) XmATTACH_WIDGET}, + {XmNtopAttachment, (XtArgVal) XmATTACH_FORM}, + {XmNbottomAttachment, (XtArgVal) XmATTACH_FORM}, +#endif /* MOTIF */ +}; + +static Arg panel_args[] = { + {XtNwidth, (XtArgVal) (XTRA_WID - 1)}, +#ifndef MOTIF + {XtNheight, (XtArgVal) 0}, + {XtNborderWidth, (XtArgVal) 0}, + {XtNfromHoriz, (XtArgVal) NULL}, + {XtNtop, (XtArgVal) XtChainTop}, + {XtNbottom, (XtArgVal) XtChainBottom}, + {XtNleft, (XtArgVal) XtChainRight}, + {XtNright, (XtArgVal) XtChainRight}, +#else /* MOTIF */ + {XmNleftAttachment, (XtArgVal) XmATTACH_FORM}, + {XmNtopAttachment, (XtArgVal) XmATTACH_FORM}, + {XmNbottomAttachment, (XtArgVal) XmATTACH_FORM}, +#endif /* MOTIF */ +}; + +static struct { + _Xconst char *label; + _Xconst char *name; + int closure; + int y_pos; + } +#define BPOS(n) (10 + (n)*28) + command_table[] = { + {"Quit", "quit", 'q', BPOS(1)}, + {"Abort", "abort", 'Q', BPOS(2)}, + {"Again", "again", '\001', BPOS(4)}, + {"Help", "help", '?', BPOS(4)}, + {"Reread", "reread", 'R', BPOS(5)}, + {"100%", "sh1", 1 << 8 | 's', BPOS(7)}, +#define LOWRES_s_COMMANDS 6 + /* Good for 300dpi */ + {"50%", "sh2", 2 << 8 | 's', BPOS(8)}, + {"33%", "sh3", 3 << 8 | 's', BPOS(9)}, + {"25%", "sh4", 4 << 8 | 's', BPOS(10)}, +#define HIRES_s_COMMANDS 9 + /* Good for 600dpi */ + {"33%", "sh2", 3 << 8 | 's', BPOS(8)}, + {"25%", "sh3", 4 << 8 | 's', BPOS(9)}, + {"17%", "sh4", 6 << 8 | 's', BPOS(10)}, + + /* What's good for 1200dpi? */ + +#define PAGE_BUTTONS 12 + {"First", "next0", 1 << 8 | 'g', BPOS(12)}, + {"Page-10", "prev10", 10 << 8 | 'p', BPOS(13)}, + {"Page-5", "prev5", 5 << 8 | 'p', BPOS(14)}, + {"Prev", "prev", 'p', BPOS(15)}, + {"Next", "next", 'n', BPOS(16)}, + {"Page+5", "next5", 5 << 8 | 'n', BPOS(17)}, + {"Page+10", "next10", 10 << 8 | 'n', BPOS(18)}, + {"Last", "next999", 'g', BPOS(19)}, +#if PS + {"View PS", "postscript", 'v', BPOS(21)}, +#endif +#ifdef HTEX + {"Back", "back", 'B', BPOS(23)}, +#endif +#ifdef SELFILE + {"File", "file", '\006', BPOS(24)}, +#endif /* SELFILE */ +}; + +static void handle_command(); + +static XtCallbackRec command_call[] = { + {handle_command, NULL}, + {NULL, NULL}, +}; + +static Arg command_args[] = { +#ifndef MOTIF + {XtNlabel, (XtArgVal) NULL}, +#else + {XmNlabelString, (XtArgVal) NULL}, +#endif + {XtNx, (XtArgVal) 6}, + {XtNy, (XtArgVal) 0}, +#ifndef MOTIF + {XtNwidth, (XtArgVal) 64}, +#else + {XtNwidth, (XtArgVal) 90}, +#endif + {XtNheight, (XtArgVal) 30}, +#ifndef MOTIF + {XtNcallback, (XtArgVal) command_call}, +#else + {XmNactivateCallback, (XtArgVal) command_call}, +#endif +}; + +void +create_buttons(h) + XtArgVal h; +{ + int i; + + line_args[0].value = (XtArgVal) resource._hl_Pixel; +#ifndef MOTIF + line_args[2].value = h; + line_args[3].value = (XtArgVal) vport_widget; + line_widget = XtCreateManagedWidget("line", widgetClass, + form_widget, line_args, + XtNumber(line_args)); + panel_args[1].value = h; + panel_args[3].value = (XtArgVal) line_widget; +#endif + panel_widget = XtCreateManagedWidget("panel", PANEL_WIDGET_CLASS, + form_widget, panel_args, + XtNumber(panel_args)); + + /* This adjusts the command table according to any shrinkbutton + resources */ + for (i = 0; i <= XtNumber(command_table) - 4; ++i) { + if (strncmp(command_table[i].name, "sh1",2) == 0) { + /* It's a shrink command. Check if the resources tell us to + modify it, and how */ + int j,k; + + j = atoi(&command_table[i].name[2]); + k = resource.shrinkbutton[j-1]; + + if (k != 0) { + char *s = xmalloc(9); + + if (k < 1) + k = 1; + else if (k > 99) + k = 99; + + if (k<16) { + Sprintf(s, "%d%%", (int) 100/k); + } else { + /* Need more precition */ + Sprintf(s, "%.2f%%", (double) 100/k); + } + command_table[i].label = s; + command_table[i].closure = k << 8 | 's'; + } /* if k != 0 */ + } /* if strncmp */ + } /* For */ + + command_args[2].value = (XtArgVal) vport_widget; + + /* This loop sets up the buttons */ + for (i = 0; i < XtNumber(command_table); ++i) { + if ( ((i==HIRES_s_COMMANDS)&&(pixels_per_inch<600)) || + ((i==LOWRES_s_COMMANDS)&&(pixels_per_inch>=600)) ) i+=3; +#ifndef MOTIF + command_args[0].value = (XtArgVal) command_table[i].label; +#else /* MOTIF */ + command_args[0].value = (XtArgVal) + XmCvtCTToXmString((char *) command_table[i].label); +#endif /* MOTIF */ + command_args[2].value = (XtArgVal) command_table[i].y_pos; + command_call[0].closure = (XtPointer) &command_table[i].closure; + (void) XtCreateManagedWidget(command_table[i].name, + BUTTON_WIDGET_CLASS, panel_widget, + command_args, XtNumber(command_args)); + } +#ifdef MOTIF + line_args[2].value = (XtArgVal) panel_widget; + line_widget = XtCreateManagedWidget("line", widgetClass, form_widget, + line_args, XtNumber(line_args)); + XtVaSetValues(vport_widget, XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, line_widget, NULL); +#endif +} +#endif /* BUTTONS */ + +#else /* !TOOLKIT */ +static Window x_bar, y_bar; +static int x_bgn, x_end, y_bgn, y_end; /* scrollbar positions */ +#endif /* TOOLKIT */ + +/* We split this help string into smaller pieces, because some C +implementations have been known to croak on large strings. */ + +static char* help_string[] = +{ + "", + "----------------------------Help and Exiting----------------------------", + "", + "h or H or ? or Help", + " Displays this help text.", + "", + "q or Control-C or Control-D or Cancel or Stop or Control-Z (VAX VMS)", + " Quits the program.", + "", + "------------------------------Page Motion-------------------------------", + "", + "n or f or Space or Return or LineFeed or PgDn", + " Moves to the next page (or to the nth next page if a", + " number is given).", + "", + "p or b or Control-H or BackSpace or DELete or PgUp", + " Moves to the previous page (or back n pages).", + "", + "u or Up-arrow", + " Moves up two thirds of a window-full.", + "", + "d or Down-arrow", + " Moves down two thirds of a window-full.", + "", + "l or Left-arrow", + " Moves left two thirds of a window-full.", + "", + "r or Right-arrow", + " Moves right two thirds of a window-full.", + "", + "g or j or End", + " Moves to the page with the given number. Initially,", + " the first page is assumed to be page number 1, but this", + " can be changed with the `P' keystroke, below. If no", + " page number is given, then it goes to the last page.", + "", + "< Move to first page in document.", + "> Move to last page in document.", + "", + "-----------------------------Other Commands-----------------------------", + "", + "Control-A or Again", + " Repeat last command (including its argument).", + "", + "Control-L or Clear", + " Redisplays the current page.", + "", + "Control-P", + " Prints bitmap unit, bit order, and byte order.", + "", + "^ or Home", + " Move to the ``home'' position of the page. This is", + " normally the upper left-hand corner of the page,", + " depending on the margins as described in the -margins", + " option, above.", + "", + "c Moves the page so that the point currently beneath the", + " cursor is moved to the middle of the window. It also", + " (gasp!) warps the cursor to the same place.", + "", + "G This key toggles the use of greyscale anti-aliasing for", + " displaying shrunken bitmaps. In addition, the key", + " sequences `0G' and `1G' clear and set this flag,", + " respectively. See also the -nogrey option.", + "", + "k Normally when xdvi switches pages, it moves to the home", + " position as well. The `k' keystroke toggles a `keep-", + " position' flag which, when set, will keep the same", + " position when moving between pages. Also `0k' and `1k'", + " clear and set this flag, respectively. See also the", + " -keep option.", + "", + "M Sets the margins so that the point currently under the", + " cursor is the upper left-hand corner of the text in the", + " page. Note that this command itself does not move the", + " image at all. For details on how the margins are used,", + " see the -margins option.", + "", + "P ``This is page number n.'' This can be used to make", + " the `g' keystroke refer to actual page numbers instead", + " of absolute page numbers.", + "", + "R Forces the dvi file to be reread. This allows you to", + " preview many versions of the same file while running", + " xdvi only once.", + "", + "s Changes the shrink factor to the given number. If no", + " number is given, the smallest factor that makes the", + " entire page fit in the window will be used. (Margins", + " are ignored in this computation.)", + "", + "S Sets the density factor to be used when shrinking", + " bitmaps. This should be a number between 0 and 100;", + " higher numbers produce lighter characters.", + "", + "t Toggles to the next unit in a sorted list of TeX dimension", + " units for the popup magnifier ruler.", + "", + "x Toggles expert mode (in which the buttons do not appear).", + " Also `0x' and `1x' clear and enable this mode,", + " respectively. See also the -expert option.", + "", + "V Toggles Ghostscript anti-aliasing. Also `0V' and `1V' clear", + " and enables this mode, respectively. See also the the -gsalpha", + " option.", + "", +#ifdef SELFILE + "Control-F Find another DVI file.", + "", +#endif +#ifdef HTEX + "B Go back to the previous anchor.", + "F Follow hyperlink forward.", + "", +#endif + "------------------------------------------------------------------------", +}; + +static void +show_help() +{ + int k; + + for (k = 0; k < (sizeof(help_string)/sizeof(help_string[0])); ++k) + (void)puts(help_string[k]); +} + +/* + * Mechanism to keep track of the magnifier window. The problems are, + * (a) if the button is released while the window is being drawn, this + * could cause an X error if we continue drawing in it after it is + * destroyed, and + * (b) creating and destroying the window too quickly confuses the window + * manager, which is avoided by waiting for an expose event before + * destroying it. + */ +static short alt_stat; /* 1 = wait for expose, */ + /* -1 = destroy upon expose */ +static Boolean alt_canit; /* stop drawing this window */ + +/* + * Data for buffered events. + */ + +#ifndef FLAKY_SIGPOLL +static VOLATILE short event_freq = 70; +#else +#define event_freq 70 +#endif + +static void can_exposures(), keystroke(); + +#ifdef GREY +#define gamma resource._gamma + +static void +mask_shifts(mask, pshift1, pshift2) + Pixel mask; + int *pshift1, *pshift2; +{ + int k, l; + + for (k = 0; (mask & 1) == 0; ++k) + mask >>= 1; + for (l = 0; (mask & 1) == 1; ++l) + mask >>= 1; + *pshift1 = sizeof(short) * 8 - l; + *pshift2 = k; +} + +/* + * Try to allocate 4 color planes for 16 colors (for GXor drawing) + */ + +void +init_plane_masks() +{ + Pixel pixel; + + if (copy || plane_masks[0] != 0) + return; + + if (XAllocColorCells(DISP, our_colormap, False, plane_masks, 4, + &pixel, 1)) { + /* Make sure fore_Pixel and back_Pixel are a part of the palette */ + back_Pixel = pixel; + fore_Pixel = pixel | plane_masks[0] | plane_masks[1] + | plane_masks[2] | plane_masks[3]; + if (mane.win != (Window) 0) + XSetWindowBackground(DISP, mane.win, back_Pixel); + } + else { + copy = True; + puts("\ +Greyscaling is running in copy mode: your display can only display\n\ +a limited number of colors at a time (typically 256), and other applications\n\ +(such as netscape) are using many of them. Running in copy mode will\n\ +cause overstrike characters to appear incorrectly, and may result in\n\ +poor display quality.\n\ +\n\ +See the section ``GREYSCALING AND COLORMAPS'' in the xdvi manual page\n\ +for more details."); + fflush(stdout); /* useful if called by netscape */ + } +} + +extern double pow(); + +#ifndef TOOLKIT +#define XtWindow(win) win +#endif + +#define MakeGC(fcn, fg, bg) (values.function = fcn, \ + values.foreground=fg, values.background=bg, \ + XCreateGC(DISP, XtWindow(top_level), \ + GCFunction | GCForeground | GCBackground, &values)) + +void +init_pix() +{ + static int shrink_allocated_for = 0; + static float oldgamma = 0.0; + static Pixel palette[17]; + XGCValues values; + int i; + + if (fore_color_data.pixel == back_color_data.pixel) { + /* get foreground and background RGB values for interpolating */ + fore_color_data.pixel = fore_Pixel; + XQueryColor(DISP, our_colormap, &fore_color_data); + back_color_data.pixel = back_Pixel; + XQueryColor(DISP, our_colormap, &back_color_data); + } + + if (our_visual->class == TrueColor) { + /* This mirrors the non-grey code in xdvi.c */ + static int shift1_r, shift1_g, shift1_b; + static int shift2_r, shift2_g, shift2_b; + static Pixel set_bits; + static Pixel clr_bits; + unsigned int sf_squared; + + if (oldgamma == 0.0) { + mask_shifts(our_visual->red_mask, &shift1_r, &shift2_r); + mask_shifts(our_visual->green_mask, &shift1_g, &shift2_g); + mask_shifts(our_visual->blue_mask, &shift1_b, &shift2_b); + + set_bits = fore_color_data.pixel & ~back_color_data.pixel; + clr_bits = back_color_data.pixel & ~fore_color_data.pixel; + + if (set_bits & our_visual->red_mask) + set_bits |= our_visual->red_mask; + if (clr_bits & our_visual->red_mask) + clr_bits |= our_visual->red_mask; + if (set_bits & our_visual->green_mask) + set_bits |= our_visual->green_mask; + if (clr_bits & our_visual->green_mask) + clr_bits |= our_visual->green_mask; + if (set_bits & our_visual->blue_mask) + set_bits |= our_visual->blue_mask; + if (clr_bits & our_visual->blue_mask) + clr_bits |= our_visual->blue_mask; + + /* + * Make the GCs + */ + + foreGC = foreGC2 = ruleGC = 0; + copyGC = MakeGC(GXcopy, fore_Pixel, back_Pixel); + if (copy || (set_bits && clr_bits)) { + ruleGC = copyGC; + if (!resource.thorough) copy = True; + } + if (copy) { + foreGC = ruleGC; + if (!resource.copy) + Puts("Note: overstrike characters may be incorrect."); + } + else { + if (set_bits) + foreGC = MakeGC(GXor, + set_bits & fore_color_data.pixel, 0); + if (clr_bits || !set_bits) + *(foreGC ? &foreGC2 : &foreGC) = MakeGC(GXandInverted, + clr_bits & ~fore_color_data.pixel, 0); + if (!ruleGC) ruleGC = foreGC; + } + + oldgamma = gamma; + } + + if (mane.shrinkfactor == 1) return; + sf_squared = mane.shrinkfactor * mane.shrinkfactor; + + if (shrink_allocated_for < mane.shrinkfactor) { + if (pixeltbl != NULL) { + free((char *) pixeltbl); + if (pixeltbl_t != NULL) + free((char *) pixeltbl_t); + } + pixeltbl = xmalloc((sf_squared + 1) * sizeof(Pixel)); + if (foreGC2 != NULL) + pixeltbl_t = xmalloc((sf_squared + 1) * sizeof(Pixel)); + shrink_allocated_for = mane.shrinkfactor; + } + + /* + * Compute pixel values directly. + */ + +#define SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2)) + + for (i = 0; i <= sf_squared; ++i) { + double frac = gamma > 0 + ? pow((double) i / sf_squared, 1 / gamma) + : 1 - pow((double) (sf_squared - i) / sf_squared, -gamma); + unsigned int red, green, blue; + Pixel pixel; + + red = frac + * ((double) fore_color_data.red - back_color_data.red) + + back_color_data.red; + green = frac + * ((double) fore_color_data.green - back_color_data.green) + + back_color_data.green; + blue = frac + * ((double) fore_color_data.blue - back_color_data.blue) + + back_color_data.blue; + + pixel = SHIFTIFY(red, shift1_r, shift2_r) | + SHIFTIFY(green, shift1_g, shift2_g) | + SHIFTIFY(blue, shift1_b, shift2_b); + + if (copy) pixeltbl[i] = pixel; + else { + pixeltbl[i] = set_bits ? pixel & set_bits + : ~pixel & clr_bits; + if (pixeltbl_t != NULL) + pixeltbl_t[i] = ~pixel & clr_bits; + } + } + +#undef SHIFTIFY + + return; + } + + /* if not TrueColor ... */ + + if (gamma != oldgamma) { + XColor color; + + if (oldgamma == 0.0) + init_plane_masks(); + + for (i = 0; i < 16; ++i) { + double frac = gamma > 0 ? pow((double) i / 15, 1 / gamma) + : 1 - pow((double) (15 - i) / 15, -gamma); + + color.red = frac + * ((double) fore_color_data.red - back_color_data.red) + + back_color_data.red; + color.green = frac + * ((double) fore_color_data.green - back_color_data.green) + + back_color_data.green; + color.blue = frac + * ((double) fore_color_data.blue - back_color_data.blue) + + back_color_data.blue; + + color.pixel = back_Pixel; + color.flags = DoRed | DoGreen | DoBlue; + + if (!copy) { + if (i & 1) color.pixel |= plane_masks[0]; + if (i & 2) color.pixel |= plane_masks[1]; + if (i & 4) color.pixel |= plane_masks[2]; + if (i & 8) color.pixel |= plane_masks[3]; + XStoreColor(DISP, our_colormap, &color); + palette[i] = color.pixel; + } + else { + if (XAllocColor(DISP, our_colormap, &color)) + palette[i] = color.pixel; + else + palette[i] = (i * 100 >= density * 15) + ? fore_Pixel : back_Pixel; + } + } + + copyGC = MakeGC(GXcopy, fore_Pixel, back_Pixel); + foreGC = ruleGC = copy ? copyGC + : MakeGC(GXor, fore_Pixel, back_Pixel); + foreGC2 = 0; + + oldgamma = gamma; + } + + if (mane.shrinkfactor == 1) return; + + if (shrink_allocated_for < mane.shrinkfactor) { + if (pixeltbl != NULL) free((char *) pixeltbl); + pixeltbl = xmalloc((unsigned) + (mane.shrinkfactor * mane.shrinkfactor + 1) * sizeof(Pixel)); + shrink_allocated_for = mane.shrinkfactor; + } + + for (i = 0; i <= mane.shrinkfactor * mane.shrinkfactor; ++i) + pixeltbl[i] = + palette[(i * 30 + mane.shrinkfactor * mane.shrinkfactor) + / (2 * mane.shrinkfactor * mane.shrinkfactor)]; +} + +#undef MakeGC + +void +init_colors() +{ + static Boolean warn_done = False; + Boolean warn = False; + + copy = resource.copy; + +#ifdef GREY + if (use_grey) + init_pix(); + else +#endif + { + XGCValues values; + Pixel set_bits = (Pixel) (fore_Pixel & ~back_Pixel); + Pixel clr_bits = (Pixel) (back_Pixel & ~fore_Pixel); + + if (copy == Maybe) + copy = (!resource.thorough + && DefaultDepthOfScreen(SCRN) > 1); + +#define MakeGC(fcn, fg, bg) (values.function = fcn, values.foreground=fg,\ + values.background=bg,\ + XCreateGC(DISP, RootWindowOfScreen(SCRN),\ + GCFunction|GCForeground|GCBackground, &values)) + + foreGC = foreGC2 = ruleGC = NULL; + if (copy || (set_bits && clr_bits)) + ruleGC = MakeGC(GXcopy, fore_Pixel, back_Pixel); + if (copy) foreGC = ruleGC; + else if (!resource.thorough && ruleGC) { + foreGC = ruleGC; + warn = True; + } + else { + if (set_bits) foreGC = MakeGC(GXor, set_bits, 0); + if (clr_bits || !set_bits) + *(foreGC ? &foreGC2 : &foreGC) = + MakeGC(GXandInverted, clr_bits, 0); + if (!ruleGC) ruleGC = foreGC; + } + } + + if (warn && !warn_done) { + Puts("Note: overstrike characters may be incorrect."); + warn_done = True; /* Print warning only once. */ + } +} + +/* + * Ruler routines + */ + +static int +tick_scale(k) + int k; +{ + if (k == 0) + return 3; + else if ((k % 1000) == 0) + return 7; + else if ((k % 500) == 0) + return 6; + else if ((k % 100) == 0) + return 5; + else if ((k % 50) == 0) + return 4; + else if ((k % 10) == 0) + return 3; + else if ((k % 5) == 0) + return 2; + else + return 1; +} + + +static void +draw_rulers(width, height, ourGC) + unsigned int width, height; + GC ourGC; +{ + int k; /* tick counter */ + double old_pixels_per_tick; + double pixels_per_tick; + int scale; + int tick_offset; /* offset along axes */ + int x; /* coordinates of top-left popup */ + int y; /* window corner */ + double xx; /* coordinates of tick */ + double yy; /* coordinates of tick */ + static char *last_tick_units = ""; /* memory of last tick units */ + + if (tick_length <= 0) /* user doesn't want tick marks */ + return; + + x = 0; /* the pop-up window always has origin (0,0) */ + y = 0; + + /* We need to clear the existing window to remove old rulers. I think + that this could be avoided if draw_rulers() could be invoked earlier. + The expose argument in XClearArea() must be True to force redrawing + of the text inside the popup window. Also, it would be better to draw + the rulers before painting the text, so that rulers would not + overwrite the text, but I haven't figured out yet how to arrange + that. */ + + XClearArea(DISP, alt.win, x, y, width, height, True); + + /* The global resource._pixels_per_inch tells us how to find the ruler + scale. For example, 300dpi corresponds to these TeX units: + + 1 TeX point (pt) = 4.151 pixels + 1 big point (bp) = 4.167 pixels + 1 pica (pc) = 49.813 pixels + 1 cicero (cc) = 53.501 pixels + 1 didot point (dd) = 4.442 pixels + 1 millimeter (mm) = 11.811 pixels + 1 centimeter (cm) = 118.110 pixels + 1 inch (in) = 300.000 pixels + 1 scaled point (sp) = 0.00006334 pixels + + The user can select the units via a resource (e.g. XDvi*tickUnits: bp), + or a command-line option (e.g. -xrm '*tickUnits: cm'). The length of + the ticks can be controlled by a resource (e.g. XDvi*tickLength: 10), or + a command-line option (e.g. -xrm '*tickLength: 10000'). If the tick + length exceeds the popup window size, then a graph-paper grid is drawn + over the whole window. Zero, or negative, tick length completely + suppresses rulers. */ + + pixels_per_tick = (double)resource._pixels_per_inch; + if (strcmp(tick_units,"pt") == 0) + pixels_per_tick /= 72.27; + else if (strcmp(tick_units,"bp") == 0) + pixels_per_tick /= 72.0; + else if (strcmp(tick_units,"in") == 0) + /* NO-OP */; + else if (strcmp(tick_units,"cm") == 0) + pixels_per_tick /= 2.54; + else if (strcmp(tick_units,"mm") == 0) + pixels_per_tick /= 25.4; + else if (strcmp(tick_units,"dd") == 0) + pixels_per_tick *= (1238.0 / 1157.0) / 72.27; + else if (strcmp(tick_units,"cc") == 0) + pixels_per_tick *= 12.0 * (1238.0 / 1157.0) / 72.27; + else if (strcmp(tick_units,"pc") == 0) + pixels_per_tick *= 12.0 /72.27; + else if (strcmp(tick_units,"sp") == 0) + pixels_per_tick /= (65536.0 * 72.27); + else + { + Printf("Unrecognized tickUnits [%s]: defaulting to TeX points [pt]\n", + tick_units); + tick_units = "pt"; + pixels_per_tick /= 72.27; + } + + /* To permit accurate measurement in the popup window, we can reasonably + place tick marks about 3 to 10 pixels apart, so we scale the computed + pixels_per_tick by a power of ten to bring it into that range. */ + + old_pixels_per_tick = pixels_per_tick; /* remember the original scale */ + while (pixels_per_tick < 3.0) + pixels_per_tick *= 10.0; + while (pixels_per_tick > 30.0) + pixels_per_tick /= 10.0; + if (strcmp(last_tick_units, tick_units) != 0) + { /* tell user what the ruler scale is, but only when it changes */ + if (old_pixels_per_tick != pixels_per_tick) + Printf("Ruler tick interval adjusted to represent %.2f%s\n", + pixels_per_tick/old_pixels_per_tick, tick_units); + else if (debug & DBG_EVENT) + Printf("Ruler tick interval represents 1%s\n", tick_units); + } + + /* In order to make the ruler as accurate as possible, given the coarse + screen resolution, we compute tick positions in floating-point + arithmetic, then round to nearest integer values. */ + + for (k = 0, xx = 0.0; xx < (double)width; k++, xx += pixels_per_tick) + { /* draw vertical ticks on top and bottom */ + tick_offset = (int)(0.5 + xx); /* round to nearest pixel */ + scale = tick_scale(k); + XDrawLine(DISP, alt.win, ourGC, + x + tick_offset, y, + x + tick_offset, y + scale*tick_length); + XDrawLine(DISP, alt.win, ourGC, + x + tick_offset, y + height, + x + tick_offset, y + height - scale*tick_length); + } + + for (k = 0, yy = 0.0; yy < (double)height; k++, yy += pixels_per_tick) + { /* draw horizontal ticks on left and right */ + tick_offset = (int)(0.5 + yy); /* round to nearest pixel */ + scale = tick_scale(k); + XDrawLine(DISP, alt.win, ourGC, + x, y + tick_offset, + x + scale*tick_length, y + tick_offset); + XDrawLine(DISP, alt.win, ourGC, + x + width, y + tick_offset, + x + width - scale*tick_length, y + tick_offset); + } + + last_tick_units = tick_units; + + XFlush(DISP); /* bring window up-to-date */ +} + +#undef MakeGC + +#endif /* GREY */ + +/* + * Event-handling routines + */ + +static void +expose(windowrec, x, y, w, h) + struct WindowRec *windowrec; + int x, y; + unsigned int w, h; +{ + if (windowrec->min_x > x) windowrec->min_x = x; + if (windowrec->max_x < x + w) + windowrec->max_x = x + w; + if (windowrec->min_y > y) windowrec->min_y = y; + if (windowrec->max_y < y + h) + windowrec->max_y = y + h; +} + +static void +clearexpose(windowrec, x, y, w, h) + struct WindowRec *windowrec; + int x, y; + unsigned int w, h; +{ + XClearArea(DISP, windowrec->win, x, y, w, h, False); + expose(windowrec, x, y, w, h); +} + +static void +scrollwindow(windowrec, x0, y0) + struct WindowRec *windowrec; + int x0, y0; +{ + int x, y; + int x2 = 0, y2 = 0; + int ww, hh; + + x = x0 - windowrec->base_x; + y = y0 - windowrec->base_y; + ww = windowrec->width - x; + hh = windowrec->height - y; + windowrec->base_x = x0; + windowrec->base_y = y0; + if (currwin.win == windowrec->win) { + currwin.base_x = x0; + currwin.base_y = y0; + } + windowrec->min_x -= x; + if (windowrec->min_x < 0) windowrec->min_x = 0; + windowrec->max_x -= x; + if (windowrec->max_x > windowrec->width) + windowrec->max_x = windowrec->width; + windowrec->min_y -= y; + if (windowrec->min_y < 0) windowrec->min_y = 0; + windowrec->max_y -= y; + if (windowrec->max_y > windowrec->height) + windowrec->max_y = windowrec->height; + if (x < 0) { + x2 = -x; + x = 0; + ww = windowrec->width - x2; + } + if (y < 0) { + y2 = -y; + y = 0; + hh = windowrec->height - y2; + } + if (ww <= 0 || hh <= 0) { + XClearWindow(DISP, windowrec->win); + windowrec->min_x = windowrec->min_y = 0; + windowrec->max_x = windowrec->width; + windowrec->max_y = windowrec->height; + } + else { + XCopyArea(DISP, windowrec->win, windowrec->win, copyGC, + x, y, (unsigned int) ww, (unsigned int) hh, x2, y2); + if (x > 0) + clearexpose(windowrec, ww, 0, + (unsigned int) x, windowrec->height); + if (x2 > 0) + clearexpose(windowrec, 0, 0, + (unsigned int) x2, windowrec->height); + if (y > 0) + clearexpose(windowrec, 0, hh, + windowrec->width, (unsigned int) y); + if (y2 > 0) + clearexpose(windowrec, 0, 0, + windowrec->width, (unsigned int) y2); + } +} + +#ifdef TOOLKIT + +/* + * routines for X11 toolkit + */ + +static Arg arg_wh[] = { + {XtNwidth, (XtArgVal) &window_w}, + {XtNheight, (XtArgVal) &window_h}, +}; + +static Position window_x, window_y; +static Arg arg_xy[] = { + {XtNx, (XtArgVal) &window_x}, + {XtNy, (XtArgVal) &window_y}, +}; + +#define get_xy() XtGetValues(draw_widget, arg_xy, XtNumber(arg_xy)) + +#define mane_base_x 0 +#define mane_base_y 0 + + +#ifdef MOTIF + +static int +set_bar_value(bar, value, max) + Widget bar; + int value; + int max; +{ + XmScrollBarCallbackStruct call_data; + + 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 /* MOTIF */ + +/* not static because SELFILE stuff in dvi_init.c needs it */ +void +home(scrl) + Boolean scrl; +{ +#if PS + psp.interrupt(); +#endif + if (!scrl) XUnmapWindow(DISP, mane.win); +#ifndef MOTIF + get_xy(); + if (x_bar != NULL) { + int coord = (page_w - clip_w) / 2; + + if (coord > home_x / mane.shrinkfactor) + coord = home_x / mane.shrinkfactor; + XtCallCallbacks(x_bar, XtNscrollProc, + (XtPointer) (window_x + coord)); + } + if (y_bar != NULL) { + int coord = (page_h - clip_h) / 2; + + if (coord > home_y / mane.shrinkfactor) + coord = home_y / mane.shrinkfactor; + XtCallCallbacks(y_bar, XtNscrollProc, + (XtPointer) (window_y + coord)); + } +#else /* MOTIF */ + { + int value; + + value = (page_w - clip_w) / 2; + if (value > home_x / mane.shrinkfactor) + value = home_x / mane.shrinkfactor; + (void) set_bar_value(x_bar, value, (int) (page_w - clip_w)); + + value = (page_h - clip_h) / 2; + if (value > home_y / mane.shrinkfactor) + value = home_y / mane.shrinkfactor; + (void) set_bar_value(y_bar, value, (int) (page_h - clip_h)); + } +#endif /* MOTIF */ + if (!scrl) { + XMapWindow(DISP, mane.win); + /* Wait for the server to catch up---this eliminates flicker. */ + XSync(DISP, False); + } +} + + +#ifndef MOTIF + /*ARGSUSED*/ +static void +handle_destroy_bar(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + * (Widget *) client_data = NULL; +} +#endif + + +static Boolean resized = False; + +static void +get_geom() +{ + static Dimension new_clip_w, new_clip_h; + static Arg arg_wh_clip[] = { + {XtNwidth, (XtArgVal) &new_clip_w}, + {XtNheight, (XtArgVal) &new_clip_h}, + }; + int old_clip_w; + + XtGetValues(vport_widget, arg_wh, XtNumber(arg_wh)); + XtGetValues(clip_widget, arg_wh_clip, XtNumber(arg_wh_clip)); +#ifndef MOTIF + /* Note: widgets may be destroyed but not forgotten */ + if (x_bar == NULL) { + x_bar = XtNameToWidget(vport_widget, "horizontal"); + if (x_bar != NULL) + XtAddCallback(x_bar, XtNdestroyCallback, handle_destroy_bar, + (XtPointer) &x_bar); + } + if (y_bar == NULL) { + y_bar = XtNameToWidget(vport_widget, "vertical"); + if (y_bar != NULL) + XtAddCallback(y_bar, XtNdestroyCallback, handle_destroy_bar, + (XtPointer) &y_bar); + } +#endif + old_clip_w = clip_w; + /* we need to do this because */ + /* sizeof(Dimension) != sizeof(int) */ + clip_w = new_clip_w; + clip_h = new_clip_h; + if (old_clip_w == 0) home(False); + resized = False; +} + +/* + * callback routines + */ + + /*ARGSUSED*/ +void +handle_resize(widget, junk, event, cont) + Widget widget; + XtPointer junk; + XEvent *event; + Boolean *cont; /* unused */ +{ + resized = True; +} + +#ifdef BUTTONS + /*ARGSUSED*/ +static void +handle_command(widget, client_data_p, call_data) + Widget widget; + XtPointer client_data_p; + XtPointer call_data; +{ + int client_data = * (int *) client_data_p; + + keystroke((client_data) & 0xff, (client_data) >> 8, + ((client_data) >> 8) != 0, (XEvent *) NULL); +} + + /*ARGSUSED*/ +static void +handle_destroy_buttons(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + if (--destroy_count != 0) return; +#ifndef MOTIF + XtSetValues(vport_widget, resizable_on, XtNumber(resizable_on)); + if (resource.expert) { + XtGetValues(form_widget, arg_wh, XtNumber(arg_wh)); + XdviResizeWidget(vport_widget, window_w, window_h); + } + else { + XdviResizeWidget(vport_widget, window_w -= XTRA_WID, window_h); + create_buttons((XtArgVal) window_h); + } +#else /* MOTIF */ + if (resource.expert) + XtVaSetValues(vport_widget, + XmNleftAttachment, XmATTACH_FORM, + XmNleftOffset, 0, + NULL); + else { + create_buttons((XtArgVal) 0); /* the argument is ignored */ + window_w -= XTRA_WID; + } +#endif /* MOTIF */ +} + +#endif /* BUTTONS */ + +void +reconfig() +{ +#ifdef BUTTONS +#ifndef MOTIF + XtSetValues(vport_widget, resizable_off, XtNumber(resizable_off)); +#endif +#endif + XdviResizeWidget(draw_widget, page_w, page_h); + get_geom(); +} + + +#ifdef HTEX + +#define BIGBUF 32768 +char anchorpaintstring[BIGBUF]; +static Arg new_paint_args[] = { + {XtNstring, (XtArgVal) anchorpaintstring}, +}; + +/* Using Text widget ??? - see if this works... */ +void paint_anchor(text) +char *text; +{ + static long pos = 0; /* Keep track of how many characters we've added */ + int len; + char *firstnl; + + if (text == NULL) { /* Not on anchor any more */ + if ((pos > 0) && (pos < BIGBUF) && (anchorpaintstring[pos-1] != '\n')) { + anchorpaintstring[pos]= '\n'; /* But we were */ + anchorpaintstring[pos+1]= '\0'; + pos += 1; + XtSetValues(anchor_info, new_paint_args, + XtNumber(new_paint_args)); + XawTextSetInsertionPoint(anchor_info, pos); + } + return; + } /* There IS some anchor text under the mouse */ + len = strlen(text); + if (!strcmp(text, anchorpaintstring+pos-len)) { /* Still on same anchor */ + return; + } + if (len > BIGBUF) { /* Try it anyway: */ + text[BIGBUF-1] = '\0'; + len = BIGBUF; + } + if ((pos + len) > BIGBUF) { /* Fix in case of overflows... */ + firstnl = strchr(anchorpaintstring+pos+len-BIGBUF, '\n'); + if (firstnl == NULL) { + pos = 0; + } else { + strcpy(anchorpaintstring,firstnl+1); + pos -= 1 + firstnl - anchorpaintstring; + } + } + if (pos < 0) pos = 0; /* Shouldn't happen but... */ + sprintf(anchorpaintstring+pos,"\n%s", text); + pos += len+1; + + XtSetValues(anchor_info, new_paint_args, XtNumber(new_paint_args)); + XawTextSetInsertionPoint(anchor_info, pos); + XawTextDisplay(anchor_info); +} + +extern char anchorsearchstring[]; /* not being used? */ + +/* Callback for anchor search: */ +void search_callback(w, c_p, call_data) +Widget w; +XtPointer c_p, call_data; +{ + int len; + char *cp; + + cp = XawDialogGetValueString(anchor_search); + if (cp == NULL) return; + len = strlen(cp); + strcpy(anchorsearchstring,cp); + if (strchr(cp,'\n') == NULL) { + anchorsearchstring[len] = '\n'; + anchorsearchstring[len+1] = '\0'; + } + len = strchr(anchorsearchstring,'\n') - anchorsearchstring + 1; + add_search(anchorsearchstring, len); +} +#endif + +#else /* not TOOLKIT */ + +#ifdef HTEX +/* The anchor window stuff: */ + +void paint_anchor(text) +char *text; +{ + ClearArea(anchor_info, 1, 1, clip_w/2, BAR_THICK); + if (text != NULL) XDrawString(DISP, anchor_info, foreGC, + 1 , BAR_WID, text, strlen(text)); +} + +#endif + +/* + * brute force scrollbar routines + */ + +static void +paint_x_bar() +{ + int new_x_bgn = mane.base_x * clip_w / page_w; + int new_x_end = (mane.base_x + clip_w) * clip_w / page_w; + + if (new_x_bgn >= x_end || x_bgn >= new_x_end) { /* no overlap */ + XClearArea(DISP, x_bar, x_bgn, 1, x_end - x_bgn, BAR_WID, False); + XFillRectangle(DISP, x_bar, ruleGC, + new_x_bgn, 1, new_x_end - new_x_bgn, BAR_WID); + } + else { /* this stuff avoids flicker */ + if (x_bgn < new_x_bgn) + XClearArea(DISP, x_bar, x_bgn, 1, new_x_bgn - x_bgn, + BAR_WID, False); + else + XFillRectangle(DISP, x_bar, ruleGC, + new_x_bgn, 1, x_bgn - new_x_bgn, BAR_WID); + if (new_x_end < x_end) + XClearArea(DISP, x_bar, new_x_end, 1, x_end - new_x_end, + BAR_WID, False); + else + XFillRectangle(DISP, x_bar, ruleGC, + x_end, 1, new_x_end - x_end, BAR_WID); + } + x_bgn = new_x_bgn; + x_end = new_x_end; +} + +static void +paint_y_bar() +{ + int new_y_bgn = mane.base_y * clip_h / page_h; + int new_y_end = (mane.base_y + clip_h) * clip_h / page_h; + + if (new_y_bgn >= y_end || y_bgn >= new_y_end) { /* no overlap */ + XClearArea(DISP, y_bar, 1, y_bgn, BAR_WID, y_end - y_bgn, False); + XFillRectangle(DISP, y_bar, ruleGC, + 1, new_y_bgn, BAR_WID, new_y_end - new_y_bgn); + } + else { /* this stuff avoids flicker */ + if (y_bgn < new_y_bgn) + XClearArea(DISP, y_bar, 1, y_bgn, BAR_WID, new_y_bgn - y_bgn, + False); + else + XFillRectangle(DISP, y_bar, ruleGC, + 1, new_y_bgn, BAR_WID, y_bgn - new_y_bgn); + if (new_y_end < y_end) + XClearArea(DISP, y_bar, 1, new_y_end, + BAR_WID, y_end - new_y_end, False); + else + XFillRectangle(DISP, y_bar, ruleGC, + 1, y_end, BAR_WID, new_y_end - y_end); + } + y_bgn = new_y_bgn; + y_end = new_y_end; +} + +static void +scrollmane(x, y) + int x, y; +{ + int old_base_x = mane.base_x; + int old_base_y = mane.base_y; + +#if PS + psp.interrupt(); +#endif + if (x > (int) (page_w - clip_w)) x = page_w - clip_w; + if (x < 0) x = 0; + if (y > (int) (page_h - clip_h)) y = page_h - clip_h; + if (y < 0) y = 0; + scrollwindow(&mane, x, y); + if (old_base_x != mane.base_x && x_bar) paint_x_bar(); + if (old_base_y != mane.base_y && y_bar) paint_y_bar(); +} + +void +reconfig() +{ + int x_thick = 0; + int y_thick = 0; + + /* determine existence of scrollbars */ + if (window_w < page_w) x_thick = BAR_THICK; + if (window_h - x_thick < page_h) y_thick = BAR_THICK; + clip_w = window_w - y_thick; + if (clip_w < page_w) x_thick = BAR_THICK; + clip_h = window_h - x_thick; + + /* process drawing (clip) window */ + if (mane.win == (Window) 0) { /* initial creation */ + XWindowAttributes attrs; + + mane.win = XCreateSimpleWindow(DISP, top_level, y_thick, x_thick, + (unsigned int) clip_w, (unsigned int) clip_h, 0, + brdr_Pixel, back_Pixel); + XSelectInput(DISP, mane.win, ExposureMask | +#ifdef HTEX + PointerMotionMask | PointerMotionHintMask | + ButtonPressMask | ButtonReleaseMask); +#else + ButtonPressMask | ButtonMotionMask | ButtonReleaseMask); +#endif + (void) XGetWindowAttributes(DISP, mane.win, &attrs); + backing_store = attrs.backing_store; + XMapWindow(DISP, mane.win); + } + else + XMoveResizeWindow(DISP, mane.win, y_thick, x_thick, clip_w, clip_h); + +#ifdef HTEX + if (anchor_info) { + XMoveResizeWindow(DPY anchor_info, + y_thick - 1, x_thick - BAR_THICK - 1, + clip_w/2, BAR_THICK - 1); + paint_anchor(NULL); + XMoveResizeWindow(DPY anchor_search, + y_thick + clip_w/2 - 1, x_thick - BAR_THICK - 1, + clip_w/2, BAR_THICK - 1); + } else { + anchor_info = XCreateSimpleWindow(DISP, top_level, y_thick - 1, + x_thick - BAR_THICK -1, + (unsigned int) clip_w/2, BAR_THICK - 1, 1, + brdr_Pixel, back_Pixel); + XSelectInput(DISP, anchor_info, ExposureMask); + anchor_search = XCreateSimpleWindow(DISP, top_level, + y_thick + clip_w/2 - 1, + x_thick - BAR_THICK -1, + (unsigned int) clip_w/2, BAR_THICK - 1, 1, + brdr_Pixel, back_Pixel); + XSelectInput(DISP, anchor_search, ExposureMask|KeyPressMask); + XMapWindow(DPY anchor_info); + XMapWindow(DPY anchor_search); + } +#endif + /* process scroll bars */ + if (x_thick) { + if (x_bar) { + XMoveResizeWindow(DISP, x_bar, + y_thick - 1, -1, clip_w, BAR_THICK - 1); + paint_x_bar(); + } + else { + x_bar = XCreateSimpleWindow(DISP, top_level, y_thick - 1, -1, + (unsigned int) clip_w, BAR_THICK - 1, 1, + brdr_Pixel, back_Pixel); + XSelectInput(DISP, x_bar, + ExposureMask | ButtonPressMask | Button2MotionMask); + XMapWindow(DISP, x_bar); + } + x_bgn = mane.base_x * clip_w / page_w; + x_end = (mane.base_x + clip_w) * clip_w / page_w; + } + else + if (x_bar) { + XDestroyWindow(DISP, x_bar); + x_bar = (Window) 0; + } + + if (y_thick) { + if (y_bar) { + XMoveResizeWindow(DISP, y_bar, + -1, x_thick - 1, BAR_THICK - 1, clip_h); + paint_y_bar(); + } + else { + y_bar = XCreateSimpleWindow(DISP, top_level, -1, x_thick - 1, + BAR_THICK - 1, (unsigned int) clip_h, 1, + brdr_Pixel, back_Pixel); + XSelectInput(DISP, y_bar, + ExposureMask | ButtonPressMask | Button2MotionMask); + XMapWindow(DISP, y_bar); + } + y_bgn = mane.base_y * clip_h / page_h; + y_end = (mane.base_y + clip_h) * clip_h / page_h; + } + else + if (y_bar) { + XDestroyWindow(DISP, y_bar); + y_bar = (Window) 0; + } +} + +static void +home(scrl) + Boolean scrl; +{ + int x = 0, y = 0; + + if (page_w > clip_w) { + x = (page_w - clip_w) / 2; + if (x > home_x / mane.shrinkfactor) + x = home_x / mane.shrinkfactor; + } + if (page_h > clip_h) { + y = (page_h - clip_h) / 2; + if (y > home_y / mane.shrinkfactor) + y = home_y / mane.shrinkfactor; + } + if (scrl) + scrollmane(x, y); + else { + mane.base_x = x; + mane.base_y = y; + if (currwin.win == mane.win) { + currwin.base_x = x; + currwin.base_y = y; + } + if (x_bar) paint_x_bar(); + if (y_bar) paint_y_bar(); + } +} + +#define get_xy() +#define window_x 0 +#define window_y 0 +#define mane_base_x mane.base_x +#define mane_base_y mane.base_y +#endif /* not TOOLKIT */ + +static void +compute_mag_pos(xp, yp) + int *xp, *yp; +{ + int t; + + t = mag_x + main_x - alt.width/2; + if (t > WidthOfScreen(SCRN) - (int) alt.width - 2*MAGBORD) + t = WidthOfScreen(SCRN) - (int) alt.width - 2*MAGBORD; + if (t < 0) t = 0; + *xp = t; + t = mag_y + main_y - alt.height/2; + if (t > HeightOfScreen(SCRN) - (int) alt.height - 2*MAGBORD) + t = HeightOfScreen(SCRN) - (int) alt.height - 2*MAGBORD; + if (t < 0) t = 0; + *yp = t; +} + + +#define TRSIZE 100 + +/* BEGIN CHUNK events.c 1 */ +#ifdef SRC_SPECIALS +#define src_jumpButton resource._src_jumpButton +#endif +/* END CHUNK events.c 1 */ +#ifdef TOOLKIT + /*ARGSUSED*/ +void +handle_key(widget, junk, eventp, cont) + Widget widget; + XtPointer junk; + XEvent *eventp; + Boolean *cont; /* unused */ +#else /* !TOOLKIT */ +void +handle_key(eventp) + XEvent *eventp; +#endif /* TOOLKIT */ +{ + static Boolean has_arg = False; + static int number = 0; + static int sign = 1; + char ch; + Boolean arg0; + int number0; + char trbuf[TRSIZE]; + int nbytes; + + nbytes = XLookupString(&eventp->xkey, trbuf, TRSIZE, (KeySym *) NULL, + (XComposeStatus *) NULL); + if (nbytes == 0) return; + ch = '\0'; + if (nbytes == 1) ch = *trbuf; + if (ch >= '0' && ch <= '9') { + has_arg = True; + number = number * 10 + sign * (ch - '0'); + return; + } + else if (ch == '-') { + has_arg = True; + sign = -1; + number = 0; + return; + } + number0 = number; + number = 0; + sign = 1; + arg0 = has_arg; + has_arg = False; + keystroke(ch, number0, arg0, eventp); +} + +#ifdef TOOLKIT + /*ARGSUSED*/ +void +handle_button(widget, junk, ev, cont) + Widget widget; + XtPointer junk; + XEvent *ev; +#define event (&(ev->xbutton)) + Boolean *cont; /* unused */ +#else /* !TOOLKIT */ +void +handle_button(event) + XButtonEvent *event; +#endif /* TOOLKIT */ +{ + int x, y; + struct mg_size_rec *size_ptr = mg_size + event->button - 1; + XSetWindowAttributes attr; + +/* BEGIN CHUNK events.c 2 */ +#ifdef SRC_SPECIALS + /* If src specials are evaluated, make src_JumpButton jump to the next special. */ + if (src_evalMode) { + if (event->button == src_jumpButton) { + src_find_special(1, event->x, event->y); + return; + } + } +#endif +/* END CHUNK events.c 2 */ +#ifdef HTEX + if ((event->button == 1)||(event->button == 2)) { + if (pointerlocate(&x, &y)) { + /* Only do this if there's actually an href right there */ + int ret; + /* screen_to_page(&mane,x,y,&page,&px,&py); */ + if (event->button == 2) HTeXnext_extern = 1; + ret = htex_handleref(current_page, x, y); + HTeXnext_extern = 0; + if (ret == 1) return; + } + } +#endif + if (alt.win != (Window) 0 || mane.shrinkfactor == 1 || size_ptr->w <= 0) + XBell(DISP, 20); + else { + mag_x = event->x; + mag_y = event->y; + alt.width = size_ptr->w; + alt.height = size_ptr->h; + main_x = event->x_root - mag_x; + main_y = event->y_root - mag_y; + compute_mag_pos(&x, &y); + alt.base_x = (event->x + mane_base_x) * mane.shrinkfactor - + alt.width/2; + alt.base_y = (event->y + mane_base_y) * mane.shrinkfactor - + alt.height/2; + attr.save_under = True; + attr.border_pixel = brdr_Pixel; + attr.background_pixel = back_Pixel; + attr.override_redirect = True; +#ifdef GREY + attr.colormap = our_colormap; +#endif + alt.win = XCreateWindow(DISP, RootWindowOfScreen(SCRN), + x, y, alt.width, alt.height, MAGBORD, + our_depth, InputOutput, our_visual, + CWSaveUnder | CWBorderPixel | CWBackPixel | +#ifdef GREY + CWColormap | +#endif + CWOverrideRedirect, &attr); + XSelectInput(DISP, alt.win, ExposureMask); + XMapWindow(DISP, alt.win); + + /***************************************************************** + This call will draw the point rulers when the magnifier first + pops up, if the XDvi*delayRulers resource is false. Some users + may prefer rulers to remain invisible until the magnifier is + moved, so the default is true. Rulers can be suppressed entirely + by setting the XDvi*tickLength resource to zero or negative. + + It is better to use highGC for draw_rulers() than foreGC, + because this allows the ruler to be drawn in a different color, + helping to distinguish ruler ticks from normal typeset text. + *****************************************************************/ + if (!delay_rulers) + draw_rulers(alt.width, alt.height, highGC); + + alt_stat = 1; /* waiting for exposure */ + } +} + +#ifdef TOOLKIT +#undef event + + /*ARGSUSED*/ +void +handle_motion(widget, junk, ev, cont) + Widget widget; + XtPointer junk; + XEvent *ev; +#define event (&(ev->xmotion)) + Boolean *cont; /* unused */ +{ +#ifdef HTEX + int x, y; + +/* APS Modification required because of changes in motion events selected: */ + /* We asked only for hints, so check where pointer is: */ + if (pointerlocate(&x, &y)) { + if ((event->state&(Button1Mask|Button2Mask|Button3Mask| + Button4Mask|Button5Mask)) == 0) { + htex_displayanchor(current_page, x, y); + return; + } + } +#endif + new_mag_x = event->x; + main_x = event->x_root - new_mag_x; + new_mag_y = event->y; + main_y = event->y_root - new_mag_y; + mag_moved = (new_mag_x != mag_x || new_mag_y != mag_y); +} + +#undef event +#endif /* TOOLKIT */ + +static void +movemag(x, y) + int x, y; +{ + int xx, yy; + + mag_x = x; + mag_y = y; + if (mag_x == new_mag_x && mag_y == new_mag_y) mag_moved = False; + compute_mag_pos(&xx, &yy); + XMoveWindow(DISP, alt.win, xx, yy); + scrollwindow(&alt, + (x + mane_base_x) * mane.shrinkfactor - (int) alt.width/2, + (y + mane_base_y) * mane.shrinkfactor - (int) alt.height/2); + draw_rulers(alt.width, alt.height, highGC); +} + +#ifdef TOOLKIT + /*ARGSUSED*/ +void +handle_release(widget, junk, ev, cont) + Widget widget; + XtPointer junk; + XEvent *ev; +#define event (&(ev->xbutton)) + Boolean *cont; /* unused */ +#else /* !TOOLKIT */ +void +handle_release() +#endif /* TOOLKIT */ +{ + if (alt.win != (Window) 0) + if (alt_stat) alt_stat = -1; /* destroy upon expose */ + else { + XDestroyWindow(DISP, alt.win); + if (currwin.win == alt.win) alt_canit = True; + alt.win = (Window) 0; + mag_moved = False; + can_exposures(&alt); + } +} + +#ifdef TOOLKIT +#undef event + + /*ARGSUSED*/ +void +handle_exp(widget, closure, ev, cont) + Widget widget; + XtPointer closure; + XEvent *ev; +#define event (&(ev->xexpose)) + Boolean *cont; /* unused */ +{ + struct WindowRec *windowrec = (struct WindowRec *) closure; + + if (windowrec == &alt) + if (alt_stat < 0) { /* destroy upon exposure */ + alt_stat = 0; + handle_release(widget, (caddr_t) NULL, ev, (Boolean *) NULL); + return; + } + else + alt_stat = 0; + expose(windowrec, event->x, event->y, + (unsigned int) event->width, (unsigned int) event->height); +} + +#undef event +#endif /* TOOLKIT */ + +#ifdef MOTIF + +/* ARGSUSED */ +void +file_pulldown_callback(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + switch ((int) client_data) { + case 0: + keystroke('R', 0, False, (XEvent *) NULL); + break; + case 1: + keystroke('q', 0, False, (XEvent *) NULL); + break; + } +} + +/* ARGSUSED */ +void +navigate_pulldown_callback(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + switch ((int) client_data) { + case 0: + keystroke('p', 10, True, (XEvent *) NULL); + break; + case 1: + keystroke('p', 5, True, (XEvent *) NULL); + break; + case 2: + keystroke('p', 0, False, (XEvent *) NULL); + break; + case 3: + keystroke('n', 0, False, (XEvent *) NULL); + break; + case 4: + keystroke('n', 5, True, (XEvent *) NULL); + break; + case 5: + keystroke('n', 10, True, (XEvent *) NULL); + break; + } +} + +/* ARGSUSED */ +void +scale_pulldown_callback(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + int data = (int) client_data; + + if (data >= 0 && data < 4) + keystroke('s', data + 1, True, (XEvent *) NULL); +} + +void +set_shrink_factor(shrink) + int shrink; +{ + static Widget active_shrink_button = NULL; + Widget new_shrink_button; + + mane.shrinkfactor = shrink; + new_shrink_button = (shrink > 0 && shrink <= XtNumber(shrink_button) + ? shrink_button[shrink - 1] : NULL); + if (new_shrink_button != active_shrink_button) { + if (active_shrink_button != NULL) + XmToggleButtonSetState(active_shrink_button, False, False); + if (new_shrink_button != NULL) + XmToggleButtonSetState(new_shrink_button, True, False); + active_shrink_button = new_shrink_button; + } +} + +#endif /* MOTIF */ + +void +showmessage(message) + _Xconst char *message; +{ + get_xy(); + XDrawImageString(DISP, mane.win, copyGC, + 5 - window_x, 5 + X11HEIGHT - window_y, message, strlen(message)); +} + +/* ||| + * 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. + */ + +#ifdef SRC_SPECIALS +// TEST !!!!! +extern void test_print_out_list ARGS((void)); +extern void test_print_out_reversed_list ARGS((void)); +#endif +static void +keystroke(ch, number0, arg0, eventp) + char ch; + int number0; + Boolean arg0; + XEvent *eventp; +{ + static int last_number = 0; /* memory for Again command */ + static char last_ch = '\0'; /* ditto */ + static Boolean last_arg = False; /* ditto */ + +/* BEGIN CHUNK events.c 3 */ + int src_x, src_y; +/* END CHUNK events.c 3 */ + + int next_page; +#ifdef TOOLKIT + Window ww; +#endif + + next_page = current_page; + +#if DEBUG + if (isprint(ch)) + fprintf(stderr,"<%c>",(int)ch); + else + fprintf(stderr,"<%03o>",(int)ch); +#endif + + if (ch == '\001') /* Ctl-A or Again */ + { + ch = last_ch; + number0 = last_number; + arg0 = last_arg; + } + else /* remember key and arg for Again command */ + { + last_ch = ch; + last_number = number0; + last_arg = arg0; + } + + switch (ch) { + case 'q': + case 'Q': + case '\003': /* control-C */ + case '\004': /* control-D */ +#ifdef VMS + case '\032': /* control-Z */ +#endif +#if PS + ps_destroy(); +#endif +#ifndef FLAKY_SIGPOLL + if (debug & DBG_EVENT) + puts(event_freq < 0 + ? "SIGPOLL is working" : "no SIGPOLL signals received"); +#endif + exit((ch == 'Q') ? 2 : 0); + + case 'n': + case 'f': + case ' ': + case '\r': + case '\n': + /* scroll forward; i.e. go to relative page */ + next_page = current_page + (arg0 ? number0 : 1); + break; +#ifdef HTEX + case 'F': /* Follow link forward! */ + { + int x, y; + if (pointerlocate(&x, &y)) { + /* screen_to_page(&mane,s_x,s_y,&page,&px,&py); */ + (void) htex_handleref(current_page, x, y); + } + } + return; /* Should goto bad if problem arises? */ + case 'B': /* Go back to previous anchor. */ + htex_goback(); /* Should goto bad if problem arises? */ + return; +#endif + case 'p': + case 'b': + case '\b': + case '\177': /* Del */ + /* scroll backward */ + next_page = current_page - (arg0 ? number0 : 1); + break; + case '<': + next_page = 0; + break; + case 'g': + case 'j': + case '>': + /* go to absolute page (last by default) */ + next_page = (arg0 ? number0 - pageno_correct : + total_pages - 1); + break; + case '?': + case 'h': + case 'H': /* Help */ + show_help(); + return; + case 'P': /* declare current page */ + pageno_correct = arg0 * number0 - current_page; + return; + case 'k': /* toggle keep-position flag */ + resource.keep_flag = (arg0 ? number0 : !resource.keep_flag); + return; + case '\f': + /* redisplay current page */ + break; + case '^': + home(True); + return; +#ifdef TOOLKIT +#ifndef MOTIF + case 'l': + if (!x_bar) goto bad; + XtCallCallbacks(x_bar, XtNscrollProc, + (XtPointer) (-2 * (int) clip_w / 3)); + return; + case 'r': + if (!x_bar) goto bad; + XtCallCallbacks(x_bar, XtNscrollProc, + (XtPointer) (2 * (int) clip_w / 3)); + return; + case 'u': + if (!y_bar) goto bad; + XtCallCallbacks(y_bar, XtNscrollProc, + (XtPointer) (-2 * (int) clip_h / 3)); + return; + case 'd': + if (!y_bar) goto bad; + XtCallCallbacks(y_bar, XtNscrollProc, + (XtPointer) (2 * (int) clip_h / 3)); + return; + case 'c': + { + int x, y; + + x = eventp->xkey.x - clip_w / 2; + y = eventp->xkey.y - clip_h / 2; + /* The clip widget gives a more exact value. */ + if (x_bar != NULL) + XtCallCallbacks(x_bar, XtNscrollProc, (XtPointer) x); + if (y_bar != NULL) + XtCallCallbacks(y_bar, XtNscrollProc, (XtPointer) y); + XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x, -y); + } + return; +#else /* MOTIF */ + case 'c': + { + int x, y; + + get_xy(); + /* The clip widget gives a more exact value. */ + x = eventp->xkey.x - clip_w / 2; + y = eventp->xkey.y - clip_h / 2; + + x = set_bar_value(x_bar, x, (int) (page_w - clip_w)); + y = set_bar_value(y_bar, y, (int) (page_h - clip_h)); + XWarpPointer(DISP, None, None, 0, 0, 0, 0, + -x - window_x, -y - window_y); + } + return; + /* These four won't be needed once translations are put in. */ + case 'l': + get_xy(); + (void) set_bar_value(x_bar, -2 * (int) clip_w / 3 - window_x, + (int) (page_w - clip_w)); + return; + case 'r': + get_xy(); + (void) set_bar_value(x_bar, 2 * (int) clip_w / 3 - window_x, + (int) (page_w - clip_w)); + return; + case 'u': + get_xy(); + (void) set_bar_value(y_bar, -2 * (int) clip_h / 3 - window_y, + (int) (page_h - clip_h)); + return; + case 'd': + get_xy(); + (void) set_bar_value(y_bar, 2 * (int) clip_h / 3 - window_y, + (int) (page_h - clip_h)); + return; +#endif /* MOTIF */ + case 'M': + (void) XTranslateCoordinates(DISP, eventp->xkey.window, + mane.win, eventp->xkey.x, eventp->xkey.y, + &home_x, &home_y, &ww); /* throw away last argument */ + home_x *= mane.shrinkfactor; + home_y *= mane.shrinkfactor; + return; +/* BEGIN CHUNK events.c 4 */ +#ifdef SRC_SPECIALS + case 'X': + (void) XTranslateCoordinates(DISP, eventp->xkey.window, + mane.win, eventp->xkey.x, eventp->xkey.y, + &src_x, &src_y, &ww); /* throw away last argument */ + /* just highlight next special without calling editor for it */ + src_find_special(0, src_x, src_y); + return; /* or `break'? */ +#endif +/* END CHUNK events.c 4 */ +#ifdef BUTTONS + case 'x': + if (arg0 && resource.expert == (number0 != 0)) return; + if (resource.expert) { /* create buttons */ + resource.expert = False; + if (destroy_count != 0) return; +#ifndef MOTIF + XtSetValues(vport_widget, resizable_on, + XtNumber(resizable_on)); + XdviResizeWidget(vport_widget, + window_w -= XTRA_WID, window_h); + create_buttons((XtArgVal) window_h); +#else /* MOTIF */ + create_buttons((XtArgVal) 0); /* the argument is ignored */ + window_w -= XTRA_WID; +#endif /* MOTIF */ + } + else { /* destroy buttons */ + resource.expert = True; + if (destroy_count != 0) return; + destroy_count = 2; + XtAddCallback(panel_widget, XtNdestroyCallback, + handle_destroy_buttons, (XtPointer) 0); + XtAddCallback(line_widget, XtNdestroyCallback, + handle_destroy_buttons, (XtPointer) 0); + XtDestroyWidget(panel_widget); + XtDestroyWidget(line_widget); + window_w += XTRA_WID; + } + return; +#endif /* BUTTONS */ +#else /* not TOOLKIT */ + case 'l': + if (mane.base_x <= 0) goto bad; + scrollmane(mane.base_x - 2 * (int) clip_w / 3, mane.base_y); + return; + case 'r': + if (mane.base_x >= page_w - clip_w) goto bad; + scrollmane(mane.base_x + 2 * (int) clip_w / 3, mane.base_y); + return; + case 'u': + if (mane.base_y <= 0) goto bad; + scrollmane(mane.base_x, mane.base_y - 2 * (int) clip_h / 3); + return; + case 'd': + if (mane.base_y >= page_h - clip_h) goto bad; + scrollmane(mane.base_x, mane.base_y + 2 * (int) clip_h / 3); + return; + case 'c': /* unchecked scrollmane() */ + scrollwindow(&mane, mane.base_x + eventp->xkey.x - clip_w/2, + mane.base_y + eventp->xkey.y - clip_h/2); + if (x_bar) paint_x_bar(); + if (y_bar) paint_y_bar(); + XWarpPointer(DISP, None, None, 0, 0, 0, 0, + clip_w/2 - eventp->xkey.x, clip_h/2 - eventp->xkey.y); + return; + case 'M': + home_x = (eventp->xkey.x - (y_bar ? BAR_THICK : 0) + + mane.base_x) * mane.shrinkfactor; + home_y = (eventp->xkey.y - (x_bar ? BAR_THICK : 0) + + mane.base_y) * mane.shrinkfactor; + return; +/* BEGIN CHUNK events.c 5 */ +#ifdef SRC_SPECIALS + case 'X': + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * !!! WARNING: THIS IS UNTESTED !!! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + src_x = (eventp->xkey.x - (y_bar ? BAR_THICK : 0) + + mane.base_x); + src_y = (eventp->xkey.y - (x_bar ? BAR_THICK : 0) + + mane.base_y); + src_find_special(0, src_x, src_y); + return; +#endif /* SRC_SPECIALS */ +/* END CHUNK events.c 5 */ +#endif /* not TOOLKIT */ + +/* BEGIN CHUNK events.c 6 */ +#ifdef SRC_SPECIALS + /* + * Control-S toggles visibility of src specials + * (mnemonic for isearch in Emacs ;-) + * Also changes the cursor to emphasize the new mode. + */ + case '\023': /* Control-S */ + if (src_evalMode) { + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "SRC specials OFF\n"); + } + /* free src_arr */ + src_cleanup(); + src_evalMode = False; + XDefineCursor(DISP, mane.win, ready_cursor); + } + else { + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "SRC specials ON\n"); + } + src_evalMode = True; + /* used to have this in addition to emphasize the mode, + * but usage seems more coherent without it: */ + /* src_tickVisibility = True; */ + XDefineCursor(DISP, mane.win, src_cursor); + } + redraw_page(); + break; + case 'T': + /* + * change shape of specials, but only when they're visible; + * this makes the key usable for other purposes in ordinary mode. + * However, it seems that in ordinary mode `T' already does the same + * as Ctrl-p: print the Unit/bitord/byteord stuff; what's + * the reason for this ??? + */ + if (src_evalMode) { + src_tickShape++; + if (src_tickShape > SPECIAL_SHAPE_MAX_NUM) { + src_tickShape = 0; + } + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "changing shape to \"%d\"\n", src_tickShape); + } + if (src_tickVisibility) { + redraw_page(); + } + break; + } +#ifndef PS_GS + case 'V': + if (src_evalMode) { + /* + * toggle visibility of src specials + */ + if (src_tickVisibility) { + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "SRC special visibility OFF\n"); + } + src_tickVisibility = False; + } + else { + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "SRC special visibility ON\n"); + } + src_tickVisibility = True; + } + redraw_page(); + return; + } +#endif /* PS_GS */ +#endif /* SRC_SPECIALS */ +/* END CHUNK events.c 6 */ + case '\020': /* Control P */ + Printf("Unit = %d, bitord = %d, byteord = %d\n", + BitmapUnit(DISP), BitmapBitOrder(DISP), + ImageByteOrder(DISP)); + return; + case 's': + if (!arg0) { + int temp; + + number0 = ROUNDUP(unshrunk_page_w, window_w - 2); +#ifndef MOTIF + temp = ROUNDUP(unshrunk_page_h, window_h - 2); +#else /* MOTIF */ + { /* account for menubar */ + static Dimension new_h; + + /* get rid of scrollbar */ + XdviResizeWidget(draw_widget, 1, 1); + XtVaGetValues(clip_widget, XtNheight, &new_h, NULL); + temp = ROUNDUP(unshrunk_page_h, new_h - 2); + } +#endif /* MOTIF */ + if (number0 < temp) number0 = temp; + } + if (number0 <= 0) goto bad; + if (number0 == mane.shrinkfactor) return; +#ifndef MOTIF + mane.shrinkfactor = number0; +#else + set_shrink_factor(number0); +#endif + init_page(); + if (number0 != 1 && number0 != bak_shrink) { + bak_shrink = number0; +#ifdef GREY + if (use_grey) init_colors(); +#endif + reset_fonts(); + } + reconfig(); + home(False); + break; + case 'S': + if (!arg0) goto bad; +#ifdef GREY + if (use_grey) { + float newgamma = number0 != 0 ? number0 / 100.0 : 1.0; + + if (newgamma == gamma) return; + gamma = newgamma; + init_colors(); + return; + } +#endif + if (number0 < 0) goto bad; + if (number0 == density) return; + density = number0; + reset_fonts(); + if (mane.shrinkfactor == 1) return; + break; + + case 't': + { /* toggle through magnifier ruler tick units */ + int k = 0; + static char *TeX_units[] = { + "bp", "cc", "cm", "dd", "in", "mm", "pc", "pt", "sp", + }; + + for (k = 0; k < sizeof(TeX_units)/sizeof(TeX_units[0]); ++k) + if (strcmp(tick_units,TeX_units[k]) == 0) + break; + k++; + if (k >= sizeof(TeX_units)/sizeof(TeX_units[0])) + k = 0; + tick_units = TeX_units[k]; + Printf("Ruler units = %s\n", tick_units); + } + return; + +#ifdef GREY + case 'G': + use_grey = (arg0 ? number0 : !use_grey); + if (use_grey) init_colors(); + reset_fonts(); + break; +#endif +#ifdef GRID + case 'D': + grid_mode = (arg0 ? number0 : !grid_mode ); + init_page(); + reconfig(); + break; +#endif /* GRID */ + +#if PS + case 'v': + if (!arg0 || resource._postscript != !number0) { + resource._postscript = !resource._postscript; + if (resource._postscript) scanned_page = scanned_page_bak; + psp.toggle(); + } + break; +#endif + +#ifdef SELFILE + case '\006': /* control-f */ + ++dvi_time ; /* notice we want a new file in check_dvi_file */ + break ; +#endif /* SELFILE */ + +#if PS_GS + case 'V': +/* BEGIN CHUNK events.c 7 */ +#ifdef SRC_SPECIALS + if (src_evalMode) { + /* + * toggle visibility of src specials + */ + if (src_tickVisibility) { + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "SRC special visibility OFF\n"); + } + src_tickVisibility = False; + } + else { + if (src_warn_verbosity >= SRC_WARNINGS_MEDIUM) { + Fprintf(stdout, "SRC special visibility ON\n"); + } + src_tickVisibility = True; + } + redraw_page(); + return; + } + else { +#endif +/* END CHUNK events.c 7 */ + if (!arg0 || resource.gs_alpha != !number0) + resource.gs_alpha = !resource.gs_alpha; + break; +/* BEGIN CHUNK events.c 8 */ +#ifdef SRC_SPECIALS + } +#endif +/* END CHUNK events.c 8 */ +#endif + + case 'R': + /* reread DVI file */ + --dvi_time; /* then it will notice a change */ + break; + default: + goto bad; + } + if (0 <= next_page && next_page < total_pages) { + if (current_page != next_page) { + /* BEGIN CHUNK events.c 0 */ +#ifdef SRC_SPECIALS + src_delete_all_specials(); +#endif +/* END CHUNK events.c 0 */ + current_page = next_page; + warn_spec_now = warn_spec; + if (!resource.keep_flag) home(False); + } + canit = True; + XFlush(DISP); + return; /* Don't use longjmp here: it might be called from + * within the toolkit, and we don't want to longjmp out + * of Xt routines. */ + } + bad: XBell(DISP, 10); +} + +/* + * 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.) + */ + +void +#if PS +ps_read_events(wait, allow_can) + wide_bool wait; + wide_bool allow_can; +#else +read_events(wait) + wide_bool wait; +#define allow_can True +#endif +{ + XEvent event; + + alt_canit = False; + for (;;) { + event_counter = 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. + */ + +#ifndef FLAKY_SIGPOLL + + if (event_freq < 0) { /* if SIGPOLL works */ + + if (!XtPending()) { + sigset_t oldsig; + + (void) sigprocmask(SIG_BLOCK, &sigpollusrterm, &oldsig); + if (terminate_flag) { +#if PS + ps_destroy(); +#endif + exit(0); + } + while (!XtPending()) + { + /* + * The following code eliminates unnecessary calls to + * XDefineCursor, since this is a slow operation on + * some hardware (e.g., S3 chips). + */ + if (busycurs && wait && !canit && !mag_moved + && alt.min_x == MAXDIM && mane.min_x == MAXDIM) { + XSync(DISP, False); + if (XtPending()) break; +/* BEGIN CHUNK events.c 9 */ +#ifdef SRC_SPECIALS + if (src_evalMode) { + XDefineCursor(DISP, mane.win, src_cursor); + } + else { +#endif +/* END CHUNK events.c 9 */ + XDefineCursor(DISP, mane.win, ready_cursor); +/* BEGIN CHUNK events.c 10 */ +#ifdef SRC_SPECIALS + } +#endif +/* END CHUNK events.c 10 */ + XFlush(DISP); + busycurs = False; + } + if (!wait && (canit | alt_canit)) { +#if PS + psp.interrupt(); +#endif + if (allow_can) { + (void) sigprocmask(SIG_SETMASK, &oldsig, + (sigset_t *)NULL); + longjmp(canit_env, 1); + } + } + if (!wait || canit || mane.min_x < MAXDIM + || alt.min_x < MAXDIM || mag_moved) { + (void) sigprocmask(SIG_SETMASK, &oldsig, + (sigset_t *) NULL); + return; + } + (void) sigsuspend(&oldsig); + if (terminate_flag) { +#if PS + ps_destroy(); +#endif + exit(0); + } + + } + (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL); + } + } + else + +#endif /* not FLAKY_SIGPOLL */ + + { + for (;;) { +#if HAVE_POLL + int retval; +#endif + + if (terminate_flag) { +#if PS + ps_destroy(); +#endif + exit(0); + } + + if (XtPending()) + break; + + /* + * The following code eliminates unnecessary calls to + * XDefineCursor, since this is a slow operation on some + * hardware (e.g., S3 chips). + */ + if (busycurs && wait && !canit && !mag_moved + && alt.min_x == MAXDIM && mane.min_x == MAXDIM) { + XSync(DISP, False); + if (XtPending()) break; +/* BEGIN CHUNK events.c 11 */ +#ifdef SRC_SPECIALS + if (src_evalMode) { + XDefineCursor(DISP, mane.win, src_cursor); + } + else { +#endif +/* END CHUNK events.c 11 */ + XDefineCursor(DISP, mane.win, ready_cursor); +/* BEGIN CHUNK events.c 12 */ +#ifdef SRC_SPECIALS + } +#endif +/* END CHUNK events.c 12 */ + XFlush(DISP); + busycurs = False; + } + if (!wait && (canit | alt_canit)) { +#if PS + psp.interrupt(); +#endif + if (allow_can) longjmp(canit_env, 1); + } + if (!wait || canit + || mane.min_x < MAXDIM || alt.min_x < MAXDIM || mag_moved) + return; + /* If a SIGUSR1 signal comes right now, then it will wait + until an X event or another SIGUSR1 signal arrives. */ + +#if HAVE_POLL + do { + retval = poll(fds, XtNumber(fds), -1); + } while (retval < 0 && errno == EAGAIN); + + if (retval < 0 && errno != EINTR) + perror("poll (xdvi read_events)"); +#else + FD_SET(ConnectionNumber(DISP), &readfds); + if (select(ConnectionNumber(DISP) + 1, &readfds, + (fd_set *) NULL, (fd_set *) NULL, + (struct timeval *) NULL) < 0 && errno != EINTR) + perror("select (xdvi read_events)"); +#endif + } + } + +#ifdef TOOLKIT + + XtNextEvent(&event); + if (resized) get_geom(); + if (event.xany.window == alt.win && event.type == Expose) { + handle_exp((Widget) NULL, (XtPointer) &alt, &event, + (Boolean *) NULL); + continue; + } + (void) XtDispatchEvent(&event); + +#else /* not TOOLKIT */ + + XNextEvent(DISP, &event); + if (event.xany.window == mane.win || event.xany.window == alt.win) { + struct WindowRec *wr = &mane; + + if (event.xany.window == alt.win) { + wr = &alt; + /* check in case we already destroyed the window */ + if (alt_stat < 0) { /* destroy upon exposure */ + alt_stat = 0; + handle_release(); + continue; + } + else + alt_stat = 0; + } + switch (event.type) { + case GraphicsExpose: + case Expose: + expose(wr, event.xexpose.x, event.xexpose.y, + event.xexpose.width, event.xexpose.height); + break; + + case MotionNotify: +#ifdef HTEX +/* APS Only using motion hints, so get the last pointer location: */ + while (XCheckMaskEvent(DISP, PointerMotionMask, &event)); + if ((keys_buttons&(Button1Mask|Button2Mask|Button3Mask| + Button4Mask|Button5Mask)) == 0) { + /* None of the buttons was down, so this isn't a mag motion event: */ + int x, y; + if (!pointerlocate(&x, &y)) break; + htex_displayanchor(current_page, x, y); + break; + } +#endif + new_mag_x = event.xmotion.x; + new_mag_y = event.xmotion.y; + mag_moved = (new_mag_x != mag_x || new_mag_y != mag_y); + break; + + case ButtonPress: + handle_button(&event.xbutton); + break; + + case ButtonRelease: + handle_release(); + break; + } /* end switch */ + } /* end if window == {mane,alt}.win */ + + else if (event.xany.window == x_bar) { + if (event.type == Expose) + XFillRectangle(DISP, x_bar, ruleGC, + x_bgn, 1, x_end - x_bgn, BAR_WID); + else if (event.type == MotionNotify) + scrollmane(event.xmotion.x * page_w / clip_w, + mane.base_y); + else switch (event.xbutton.button) + { + case 1: + scrollmane(mane.base_x + event.xbutton.x, mane.base_y); + break; + case 2: + scrollmane(event.xbutton.x * page_w / clip_w, + mane.base_y); + break; + case 3: + scrollmane(mane.base_x - event.xbutton.x, mane.base_y); + } + } + + else if (event.xany.window == y_bar) { + if (event.type == Expose) + XFillRectangle(DISP, y_bar, ruleGC, + 1, y_bgn, BAR_WID, y_end - y_bgn); + else if (event.type == MotionNotify) + scrollmane(mane.base_x, + event.xmotion.y * page_h / clip_h); + else switch (event.xbutton.button) + { + case 1: + scrollmane(mane.base_x, mane.base_y + event.xbutton.y); + break; + case 2: + scrollmane(mane.base_x, + event.xbutton.y * page_h / clip_h); + break; + case 3: + scrollmane(mane.base_x, mane.base_y - event.xbutton.y); + } + } + + else if (event.xany.window == top_level) + switch (event.type) { + case ConfigureNotify: + if (event.xany.window == top_level && + (event.xconfigure.width != window_w || + event.xconfigure.height != window_h)) { + Window old_mane_win = mane.win; + + window_w = event.xconfigure.width; + window_h = event.xconfigure.height; + reconfig(); + if (old_mane_win == (Window) 0) home(False); + } + break; + + case MapNotify: /* if running w/o WM */ + if (mane.win == (Window) 0) { + reconfig(); + home(False); + } + break; + + case KeyPress: + handle_key(&event); + break; + } + +#endif /* not TOOLKIT */ + + } +} + +static void +redraw(windowrec) + struct WindowRec *windowrec; +{ + + currwin = *windowrec; + min_x = currwin.min_x + currwin.base_x; + min_y = currwin.min_y + currwin.base_y; + max_x = currwin.max_x + currwin.base_x; + max_y = currwin.max_y + currwin.base_y; + can_exposures(windowrec); + + if (debug & DBG_EVENT) + Printf("Redraw %d x %d at (%d, %d) (base=%d,%d)\n", max_x - min_x, + max_y - min_y, min_x, min_y, currwin.base_x, currwin.base_y); + if (!busycurs) { + XDefineCursor(DISP, mane.win, redraw_cursor); + XFlush(DISP); + busycurs = True; + } + if (setjmp(dvi_env)) { + XClearWindow(DISP, mane.win); + showmessage(dvi_oops_msg); + if (dvi_file) { + Fclose(dvi_file); + dvi_file = NULL; + } + } + else { + draw_page(); + // fixme: kpse_pathname is sometimes corrupt when calling this function from here: + // src_warn_outdated(); + warn_spec_now = False; + } +} + +void +redraw_page() +{ + if (debug & DBG_EVENT) Fputs("Redraw page: ", stdout); + XClearWindow(DISP, mane.win); + if (backing_store != NotUseful) { + mane.min_x = mane.min_y = 0; + mane.max_x = page_w; + mane.max_y = page_h; + } + else { + get_xy(); + mane.min_x = -window_x; + mane.max_x = -window_x + clip_w; + mane.min_y = -window_y; + mane.max_y = -window_y + clip_h; + } + redraw(&mane); +} + +/* + * 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. + */ + +static void +can_exposures(windowrec) + struct WindowRec *windowrec; +{ + windowrec->min_x = windowrec->min_y = MAXDIM; + windowrec->max_x = windowrec->max_y = 0; +} + +#ifndef FLAKY_SIGPOLL +/* ARGSUSED */ +static RETSIGTYPE +handle_sigpoll(signo) + int signo; +{ + event_counter = 1; + event_freq = -1; /* forget Plan B */ +#if !HAVE_SIGACTION + (void) signal(SIGPOLL, handle_sigpoll); /* reset the signal */ +#endif +} +#endif /* not FLAKY_SIGPOLL */ + +/* ARGSUSED */ +static RETSIGTYPE +handle_sigusr(signo) + int signo; +{ + event_counter = 1; + canit = True; + dvi_time = 0; +#if ! HAVE_SIGACTION + (void) signal(SIGUSR1, handle_sigusr); /* reset the signal */ +#endif +} + +/* ARGSUSED */ +static RETSIGTYPE +handle_sigterm(signo) + int signo; +{ + terminate_flag = True; +} + +static void +enable_intr() { +#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., mktexpk, 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 */ + +#if HAVE_STREAMS + if (isastream(sock_fd) > 0) { + if (ioctl(sock_fd, I_SETSIG, + S_RDNORM | S_RDBAND | S_HANGUP | S_WRNORM) == -1) + perror("ioctl I_SETSIG (xdvi)"); + } + else +#endif + { +#ifdef FASYNC + if (fcntl(sock_fd, F_SETOWN, getpid()) == -1) + perror("fcntl F_SETOWN (xdvi)"); + if (fcntl(sock_fd, F_SETFL, fcntl(sock_fd, F_GETFL, 0) | FASYNC) + == -1) + perror("fcntl F_SETFL (xdvi)"); +#elif defined(SIOCSPGRP) && defined(FIOASYNC) + /* For HP-UX B.10.10 and maybe others. See "man 7 socket". */ + int arg; + + arg = getpid(); + if (ioctl(sock_fd, SIOCSPGRP, &arg) == -1) + perror("ioctl SIOCSPGRP (xdvi)"); + arg = 1; + if (ioctl(sock_fd, FIOASYNC, &arg) == -1) + perror("ioctl FIOASYNC (xdvi)"); +#endif + } +#endif /* not FLAKY_SIGPOLL */ + +#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 */ + +#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); + a.sa_flags = 0; + sigaction(SIGINT, &a, NULL); + sigaction(SIGQUIT, &a, NULL); + sigaction(SIGTERM, &a, NULL); +#else /* not HAVE_SIGACTION */ + (void) signal(SIGINT, handle_sigterm); + (void) signal(SIGQUIT, handle_sigterm); + (void) signal(SIGTERM, handle_sigterm); +#endif /* not HAVE_SIGACTION */ + +#ifndef FLAKY_SIGPOLL + + (void) sigemptyset(&sigpollusrterm); + (void) sigaddset(&sigpollusrterm, SIGPOLL); + (void) sigaddset(&sigpollusrterm, SIGUSR1); + (void) sigaddset(&sigpollusrterm, SIGINT); + (void) sigaddset(&sigpollusrterm, SIGQUIT); + (void) sigaddset(&sigpollusrterm, SIGTERM); + +#endif + +#if HAVE_POLL + fds[0].fd = ConnectionNumber(DISP); +#else + FD_ZERO(&readfds); +#endif + +} + +void +do_pages() +{ + enable_intr(); + if (debug & DBG_BATCH) { +#ifdef TOOLKIT + while (mane.min_x == MAXDIM) read_events(True); +#else /* !TOOLKIT */ + while (mane.min_x == MAXDIM) + if (setjmp(canit_env)) break; + else read_events(True); +#endif /* TOOLKIT */ + for (current_page = 0; current_page < total_pages; ++current_page) { +#ifdef __convex__ + /* convex C turns off optimization for the entire function + if setjmp return value is discarded.*/ + if (setjmp(canit_env)) /*optimize me*/; +#else + (void) setjmp(canit_env); +#endif + canit = False; + redraw_page(); + } + } + else { /* normal operation */ +#ifdef __convex__ + /* convex C turns off optimization for the entire function + if setjmp return value is discarded.*/ + if (setjmp(canit_env)) /*optimize me*/; +#else + (void) setjmp(canit_env); +#endif + for (;;) { + read_events(True); + if (canit) { + canit = False; + can_exposures(&mane); + can_exposures(&alt); + redraw_page(); + } + else if (mag_moved) { + if (alt.win == (Window) 0) mag_moved = False; + else if (abs(new_mag_x - mag_x) > + 2 * abs(new_mag_y - mag_y)) + movemag(new_mag_x, mag_y); + else if (abs(new_mag_y - mag_y) > + 2 * abs(new_mag_x - mag_x)) + movemag(mag_x, new_mag_y); + else movemag(new_mag_x, new_mag_y); + } + else if (alt.min_x < MAXDIM) redraw(&alt); + else if (mane.min_x < MAXDIM) redraw(&mane); + XFlush(DISP); + } + } +} |