diff options
Diffstat (limited to 'support/hypertex/tanmoy/ghostview-1.5-hacked/Ghostview.c')
-rw-r--r-- | support/hypertex/tanmoy/ghostview-1.5-hacked/Ghostview.c | 2029 |
1 files changed, 2029 insertions, 0 deletions
diff --git a/support/hypertex/tanmoy/ghostview-1.5-hacked/Ghostview.c b/support/hypertex/tanmoy/ghostview-1.5-hacked/Ghostview.c new file mode 100644 index 0000000000..4d427ac80d --- /dev/null +++ b/support/hypertex/tanmoy/ghostview-1.5-hacked/Ghostview.c @@ -0,0 +1,2029 @@ +/* + * Ghostview.c -- Ghostview widget. + * Copyright (C) 1992 Timothy O. Theisen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Tim Theisen Systems Programmer + * Internet: tim@cs.wisc.edu Department of Computer Sciences + * UUCP: uwvax!tim University of Wisconsin-Madison + * Phone: (608)262-0438 1210 West Dayton Street + * FAX: (608)262-9777 Madison, WI 53706 + */ + +/* This file is part of the hacked version of the ghostview package */ +/* which is distributed under the terms of the gnu license. The */ +/* modification referred to above is by Tanmoy Bhattacharya, */ +/* <tanmoy@qcd.lanl.gov> on Nov 17, 1994. Neither the modification, */ +/* nor the original program provides any warranty. */ + +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Xatom.h> +#include <X11/Xproto.h> +#include <X11/Xos.h> +#include "GhostviewP.h" +#include "pdf.h" +#include <ctype.h> + +#ifndef XlibSpecificationRelease +typedef char *XPointer; +#endif + +#include <signal.h> +#ifdef SIGNALRETURNSINT +#define SIGVAL int +#else +#define SIGVAL void +#endif + +#ifdef NON_BLOCKING_IO +#include <fcntl.h> +/* if POSIX O_NONBLOCK is not available, use O_NDELAY */ +#if !defined(O_NONBLOCK) && defined(O_NDELAY) +#define O_NONBLOCK O_NDELAY +#endif +#endif + +#include <errno.h> +/* BSD 4.3 errno.h does not declare errno */ +extern int errno; +/* Both error returns are checked for non-blocking I/O. */ +/* Manufacture the other error code if only one exists. */ +#if !defined(EWOULDBLOCK) && defined(EAGAIN) +#define EWOULDBLOCK EAGAIN +#endif +#if !defined(EAGAIN) && defined(EWOULDBLOCK) +#define EAGAIN EWOULDBLOCK +#endif + +#ifndef VMS +/* GV_BUFSIZ is set to the minimum POSIX PIPE_BUF to ensure that + * nonblocking writes to ghostscript will work properly. + */ +#define GV_BUFSIZ 1024*1024 +#else /* VMS */ +/* +** GV_BUFSIZ is the maximum length line we can handle, so we up it to 1024 +*/ +#define GV_BUFSIZ 1024*1024 +#endif /* VMS */ + +static void ComputeXdpi(); +static void ComputeYdpi(); + +static XtResource resources[] = { +#define offset(field) XtOffsetOf(GhostviewRec, ghostview.field) + { XtNarguments, XtCArguments, XtRString, sizeof(String), + offset(arguments), XtRString, (XtPointer)NULL }, + { XtNbottomMargin, XtCMargin, XtRInt, sizeof(int), + offset(bottom_margin), XtRImmediate, (XtPointer)0 }, + { XtNbusyCursor, XtCCursor, XtRCursor, sizeof(XtPointer), + offset(busy_cursor), XtRString, "watch" }, + { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(callback), XtRCallback, (XtPointer)NULL }, + { XtNcursor, XtCCursor, XtRCursor, sizeof(XtPointer), + offset(cursor), XtRString, "crosshair" }, + { XtNfilename, XtCFilename, XtRString, sizeof(String), + offset(filename), XtRString, (XtPointer)NULL }, + { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + offset(foreground), XtRString, XtDefaultForeground}, + { XtNinterpreter, XtCInterpreter, XtRString, sizeof(String), + offset(interpreter), XtRString, "gs" }, + { XtNleftMargin, XtCMargin, XtRInt, sizeof(int), + offset(left_margin), XtRImmediate, (XtPointer)0 }, + { XtNllx, XtCBoundingBox, XtRInt, sizeof(int), + offset(llx), XtRImmediate, (XtPointer)0 }, + { XtNlly, XtCBoundingBox, XtRInt, sizeof(int), + offset(lly), XtRImmediate, (XtPointer)0 }, + { XtNmessageCallback, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(message_callback), XtRCallback, (XtPointer)NULL }, + { XtNorientation, XtCOrientation, XtRPageOrientation, + sizeof(XtPageOrientation), offset(orientation), XtRImmediate, + (XtPointer)XtPageOrientationPortrait }, + { XtNoutputCallback, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(output_callback), XtRCallback, (XtPointer)NULL }, + { XtNpalette, XtCPalette, XtRPalette, sizeof(XtPalette), + offset(palette), XtRImmediate, (XtPointer)XtPaletteColor }, + { XtNquiet, XtCQuiet, XtRBoolean, sizeof(Boolean), + offset(quiet), XtRImmediate, (XtPointer)True }, + { XtNrightMargin, XtCMargin, XtRInt, sizeof(int), + offset(right_margin), XtRImmediate, (XtPointer)0 }, + { XtNsafer, XtCSafer, XtRBoolean, sizeof(Boolean), + offset(safer), XtRImmediate, (XtPointer)True }, + { XtNtopMargin, XtCMargin, XtRInt, sizeof(int), + offset(top_margin), XtRImmediate, (XtPointer)0 }, + { XtNuseBackingPixmap, XtCUseBackingPixmap, XtRBoolean, sizeof(Boolean), + offset(use_bpixmap), XtRImmediate, (XtPointer)True }, + { XtNurx, XtCBoundingBox, XtRInt, sizeof(int), + offset(urx), XtRImmediate, (XtPointer)612 }, + { XtNury, XtCBoundingBox, XtRInt, sizeof(int), + offset(ury), XtRImmediate, (XtPointer)792 }, + { XtNxdpi, XtCResolution, XtRFloat, sizeof(float), + offset(xdpi), XtRCallProc, (XtPointer)ComputeXdpi }, + { XtNydpi, XtCResolution, XtRFloat, sizeof(float), + offset(ydpi), XtRCallProc, (XtPointer)ComputeYdpi }, +#undef offset +}; + +static void Message(); +static void Notify(); +static void Input(); +static void Output(); + +static void ClassInitialize(); +static void ClassPartInitialize(); +static void Initialize(); +static void Realize(); +static void Destroy(); +static void Resize(); +static Boolean SetValues(); +static XtGeometryResult QueryGeometry(); + +static void Layout(); +static Boolean ComputeSize(); +static void ChangeSize(); +static Boolean Setup(); +static void StartInterpreter(); +static void StopInterpreter(); +static void InterpreterFailed(); + +static XtActionsRec actions[] = +{ + {"message", Message}, + {"notify", Notify}, +}; + +/* notify takes zero to four parameters. The first two give the width and + * height of the zoom requested in the default user coordinate system. + * If they are omitted, a default value of 72 is provided. If the second + * parameter is omitted, the zoom area is assumed to be a square. + * The next two parameters give the desired resolution of the zoom window. + * If they are omitted, a default value of 300 is provided. If the four + * parameter is omitted, the y resolution is assumed to be equal to the + * x resolution. + */ +static char translations[] = +"<Message>: message() \n\ +<Btn1Down>: notify(72) \n\ +<Btn2Down>: notify(108) \n\ +<Btn3Down>: notify(144) \n\ +"; + +GhostviewClassRec ghostviewClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &coreClassRec, + /* class_name */ "Ghostview", + /* widget_size */ sizeof(GhostviewRec), + /* class_initialize */ ClassInitialize, + /* class_part_initialize */ ClassPartInitialize, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ Realize, + /* actions */ actions, + /* num_actions */ XtNumber(actions), + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ Destroy, + /* resize */ Resize, + /* expose */ NULL, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ translations, + /* query_geometry */ QueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* ghostview fields */ + /* ghostview */ NULL, + /* gv_colors */ NULL, + /* next */ NULL, + /* page */ NULL, + /* done */ NULL + } +}; + +WidgetClass ghostviewWidgetClass = (WidgetClass)&ghostviewClassRec; + +/* Procedures that compute the default xdpi and ydpi from display parameters */ + +static void +ComputeXdpi(w, offset, value) + Widget w; + int offset; + XrmValue *value; +{ + static float xdpi; + xdpi = 25.4 * WidthOfScreen(XtScreen(w)) / WidthMMOfScreen(XtScreen(w)); + value->addr = (XtPointer) &xdpi; +} + +static void +ComputeYdpi(w, offset, value) + Widget w; + int offset; + XrmValue *value; +{ + static float ydpi; + ydpi = 25.4 * HeightOfScreen(XtScreen(w)) / HeightMMOfScreen(XtScreen(w)); + value->addr = (XtPointer) &ydpi; +} + +/* Message action routine. + * Passes ghostscript message events back to application via + * the message callback. It also marks the interpreter as + * being not busy at the end of page, and stops the interpreter + * when it send a "done" message. + */ +static void +Message(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; /* unused */ + Cardinal *num_params; /* unused */ +{ + GhostviewWidget gvw = (GhostviewWidget) w; + GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w); + + gvw->ghostview.mwin = event->xclient.data.l[0]; + if (event->xclient.message_type == + XmuInternAtom(XtDisplay(w), gvc->ghostview_class.page)) { + gvw->ghostview.busy = False; + XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor); + XtCallCallbackList(w, gvw->ghostview.message_callback, "Page"); + } else if (event->xclient.message_type == + XmuInternAtom(XtDisplay(w), gvc->ghostview_class.done)) { + StopInterpreter(w); + XtCallCallbackList(w, gvw->ghostview.message_callback, "Done"); + } +} + +/* Notify action routine. + * Calculates where the user clicked in the default user coordinate system. + * Call the callbacks with the point of click and size of zoom window + * requested. + */ +static void +Notify(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + GhostviewReturnStruct ret_val; + + /* notify takes zero to four parameters. The first two give the width and + * height of the zoom requested in the default user coordinate system. + * If they are omitted, a default value of 72 is provided. If the second + * parameter is omitted, the zoom area is assumed to be a square. + * The next two parameters give the desired resolution of the zoom window. + * If they are omitted, a default value of 300 is provided. If the four + * parameter is omitted, the y resolution is assumed to be equal to the + * x resolution. + */ + switch (*num_params) { + case 0: + ret_val.width = ret_val.height = 72; + ret_val.xdpi = ret_val.ydpi = 300; + break; + case 1: + ret_val.width = ret_val.height = atoi(params[0]); + ret_val.xdpi = ret_val.ydpi = 300; + break; + case 2: + ret_val.width = atoi(params[0]); + ret_val.height = atoi(params[1]); + ret_val.xdpi = ret_val.ydpi = 300; + break; + case 3: + ret_val.width = atoi(params[0]); + ret_val.height = atoi(params[1]); + ret_val.xdpi = ret_val.ydpi = atoi(params[2]); + break; + default: + ret_val.width = atoi(params[0]); + ret_val.height = atoi(params[1]); + ret_val.xdpi = atoi(params[2]); + ret_val.ydpi = atoi(params[3]); + break; + } + + switch (gvw->ghostview.orientation) { + case XtPageOrientationPortrait: + ret_val.psx = gvw->ghostview.llx + + event->xbutton.x * 72.0 / gvw->ghostview.xdpi; + ret_val.psy = gvw->ghostview.ury - + event->xbutton.y * 72.0 / gvw->ghostview.ydpi; + break; + case XtPageOrientationLandscape: + ret_val.psx = gvw->ghostview.llx + + event->xbutton.y * 72.0 / gvw->ghostview.ydpi; + ret_val.psy = gvw->ghostview.lly + + event->xbutton.x * 72.0 / gvw->ghostview.xdpi; + break; + case XtPageOrientationUpsideDown: + ret_val.psx = gvw->ghostview.urx - + event->xbutton.x * 72.0 / gvw->ghostview.xdpi; + ret_val.psy = gvw->ghostview.lly + + event->xbutton.y * 72.0 / gvw->ghostview.ydpi; + break; + case XtPageOrientationSeascape: + ret_val.psx = gvw->ghostview.urx - + event->xbutton.y * 72.0 / gvw->ghostview.ydpi; + ret_val.psy = gvw->ghostview.ury - + event->xbutton.x * 72.0 / gvw->ghostview.xdpi; + break; + } + XtCallCallbackList(w, gvw->ghostview.callback, (XtPointer) &ret_val); +} + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +static Boolean broken_pipe = False; + +static SIGVAL +CatchPipe(i) + int i; +{ + broken_pipe = True; +#ifdef SIGNALRETURNSINT + return 0; +#endif +} + +#ifndef VMS + +/* Input - Feed data to ghostscript's stdin. + * Write bytes to ghostscript using non-blocking I/O. + * Also, pipe signals are caught during writing. The return + * values are checked and the appropriate action is taken. I do + * this at this low level, because it may not be appropriate for + * SIGPIPE to be caught for the overall application. + */ + +static void +Input(client_data, source, id) + XtPointer client_data; + int *source; + XtInputId *id; +{ + Widget w = (Widget) client_data; + GhostviewWidget gvw = (GhostviewWidget) w; + int bytes_written; + SIGVAL (*oldsig)(); + + oldsig = signal(SIGPIPE, CatchPipe); + +#ifdef NON_BLOCKING_IO + do { +#endif + + if (gvw->ghostview.buffer_bytes_left == 0) { + + /* Get a new section if required */ + if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) { + struct record_list *ps_old = gvw->ghostview.ps_input; + gvw->ghostview.ps_input = ps_old->next; + if (ps_old->close) fclose(ps_old->fp); + XtFree((char *)ps_old); + } + + /* Have to seek at the beginning of each section */ + if (gvw->ghostview.ps_input && + gvw->ghostview.ps_input->seek_needed) { + if (gvw->ghostview.ps_input->len > 0) + fseek(gvw->ghostview.ps_input->fp, + gvw->ghostview.ps_input->begin, SEEK_SET); + gvw->ghostview.ps_input->seek_needed = False; + gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len; + } + + if (gvw->ghostview.bytes_left > GV_BUFSIZ) { + gvw->ghostview.buffer_bytes_left = + fread(gvw->ghostview.input_buffer, + sizeof (char), GV_BUFSIZ, + gvw->ghostview.ps_input->fp); + } else if (gvw->ghostview.bytes_left > 0) { + gvw->ghostview.buffer_bytes_left = + fread(gvw->ghostview.input_buffer, + sizeof (char), gvw->ghostview.bytes_left, + gvw->ghostview.ps_input->fp); + } else { + gvw->ghostview.buffer_bytes_left = 0; + } + if (gvw->ghostview.bytes_left > 0 && + gvw->ghostview.buffer_bytes_left == 0) { + InterpreterFailed(w); /* Error occurred */ + } + gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer; + gvw->ghostview.bytes_left -= gvw->ghostview.buffer_bytes_left; + } + + if (gvw->ghostview.buffer_bytes_left > 0) { + bytes_written = write(gvw->ghostview.interpreter_input, + gvw->ghostview.input_buffer_ptr, + gvw->ghostview.buffer_bytes_left); + + if (broken_pipe) { + broken_pipe = False; + InterpreterFailed(w); /* Something bad happened */ + } else if (bytes_written == -1) { + if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) { + InterpreterFailed(w); /* Something bad happened */ + } + } else { + gvw->ghostview.buffer_bytes_left -= bytes_written; + gvw->ghostview.input_buffer_ptr += bytes_written; + } + } +#ifdef NON_BLOCKING_IO + } while(gvw->ghostview.ps_input && + gvw->ghostview.buffer_bytes_left == 0); +#endif + signal(SIGPIPE, oldsig); + if (gvw->ghostview.ps_input == NULL && + gvw->ghostview.buffer_bytes_left == 0) { + if (gvw->ghostview.interpreter_input_id != None) { + XtRemoveInput(gvw->ghostview.interpreter_input_id); + gvw->ghostview.interpreter_input_id = None; + } + } +} + +/* Output - receive I/O from ghostscript's stdout and stderr. + * Pass this to the application via the output_callback. */ +static void +Output(client_data, source, id) + XtPointer client_data; + int *source; + XtInputId *id; +{ + Widget w = (Widget) client_data; + GhostviewWidget gvw = (GhostviewWidget) w; + char buf[GV_BUFSIZ+1]; + int bytes = 0; + + if (*source == gvw->ghostview.interpreter_output) { + bytes = read(gvw->ghostview.interpreter_output, buf, GV_BUFSIZ); + if (bytes == 0) { /* EOF occurred */ + close(gvw->ghostview.interpreter_output); + gvw->ghostview.interpreter_output = -1; + XtRemoveInput(gvw->ghostview.interpreter_output_id); + return; + } else if (bytes == -1) { + InterpreterFailed(w); /* Something bad happened */ + return; + } + } else if (*source == gvw->ghostview.interpreter_error) { + bytes = read(gvw->ghostview.interpreter_error, buf, GV_BUFSIZ); + if (bytes == 0) { /* EOF occurred */ + close(gvw->ghostview.interpreter_error); + gvw->ghostview.interpreter_error = -1; + XtRemoveInput(gvw->ghostview.interpreter_error_id); + return; + } else if (bytes == -1) { + InterpreterFailed(w); /* Something bad happened */ + return; + } + } + if (bytes > 0) { + buf[bytes] = '\0'; + pdf_process(buf); + if (*buf) { + XtCallCallbackList(w, gvw->ghostview.output_callback, (XtPointer) buf); + } + else { + bytes = 0; + } + } +} + +#endif /* VMS */ + +/* Register the type converter required for the PageOrientation. */ +/* Register the type converter required for the Palette. */ +/* This routine is called exactly once. */ +static void +ClassInitialize() +{ + XtSetTypeConverter(XtRString, XtRPageOrientation, + XmuCvtStringToPageOrientation, NULL, 0, + XtCacheAll, NULL); + XtSetTypeConverter(XtRString, XtRPalette, + XmuCvtStringToPalette, NULL, 0, + XtCacheAll, NULL); +} + +/* Get atoms needed to communicate with ghostscript. */ +/* This routine is called once per display. */ +static void +ClassPartInitialize(class) + WidgetClass class; +{ + GhostviewWidgetClass gvc = (GhostviewWidgetClass)class; + gvc->ghostview_class.ghostview = XmuMakeAtom("GHOSTVIEW"); + gvc->ghostview_class.gv_colors = XmuMakeAtom("GHOSTVIEW_COLORS"); + gvc->ghostview_class.next = XmuMakeAtom("NEXT"); + gvc->ghostview_class.page = XmuMakeAtom("PAGE"); + gvc->ghostview_class.done = XmuMakeAtom("DONE"); +} + +/* Initialize private state. */ + +static void +Initialize(request, new, args, num_args) + Widget request, new; + ArgList args; /* unused */ + Cardinal *num_args; /* unused */ +{ + XGCValues values; + XtGCMask mask; + GhostviewWidget ngvw = (GhostviewWidget) new; + GhostviewWidget rgvw = (GhostviewWidget) request; + + values.foreground = new->core.background_pixel; + mask = GCForeground; + ngvw->ghostview.gc = XtGetGC(new, mask, &values); + ngvw->ghostview.mwin = None; + ngvw->ghostview.disable_start = False; + ngvw->ghostview.interpreter_pid = -1; + ngvw->ghostview.input_buffer = NULL; + ngvw->ghostview.bytes_left = 0; +#ifndef VMS + ngvw->ghostview.input_buffer_ptr = NULL; + ngvw->ghostview.buffer_bytes_left = 0; +#endif + ngvw->ghostview.ps_input = NULL; + ngvw->ghostview.interpreter_input = -1; + ngvw->ghostview.interpreter_output = -1; +#ifndef VMS + ngvw->ghostview.interpreter_error = -1; + ngvw->ghostview.interpreter_input_id = None; + ngvw->ghostview.interpreter_output_id = None; + ngvw->ghostview.interpreter_error_id = None; +#else /* VMS */ + memset(ngvw->ghostview.interpreter_input_iosb, 0, 8); + memset(ngvw->ghostview.interpreter_output_iosb, 0, 8); + ngvw->ghostview.output_buffer = NULL; +#endif /* VMS */ + ngvw->ghostview.gs_width = 0; + ngvw->ghostview.gs_height = 0; + ngvw->ghostview.changed = False; + ngvw->ghostview.busy = False; + + /* Compute window size */ + Layout(new, (rgvw->core.width == 0), (rgvw->core.height == 0)); +} + +/* Create Window and start interpreter if needed */ +static void +Realize(w, valueMask, attributes) + Widget w; + Mask *valueMask; + XSetWindowAttributes *attributes; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + + if (gvw->ghostview.cursor != None) { + attributes->cursor = gvw->ghostview.cursor; + *valueMask |= CWCursor; + } + + XtCreateWindow(w, (unsigned int) InputOutput, (Visual *) CopyFromParent, + *valueMask, attributes); + + Setup(w); +} + +/* Destroy routine: kill the interpreter and release the GC */ +static void +Destroy(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + + StopInterpreter(w); + XtReleaseGC(w, gvw->ghostview.gc); + if (gvw->ghostview.input_buffer) XtFree(gvw->ghostview.input_buffer); +#ifdef VMS + if (gvw->ghostview.output_buffer) XtFree(gvw->ghostview.output_buffer); +#endif /* VMS */ + if (gvw->core.background_pixmap != XtUnspecifiedPixmap) + XFreePixmap(XtDisplay(w), gvw->core.background_pixmap); +} + +/* Process resize request. Requested size cannot be changed. + * NOTE: This routine may be called before the widget is realized. + * (It was a surprise to me.) + * If the widget is realized, start a new interpreter by calling Setup(). + * If Setup() actually started a new interpreter and it is taking input + * from stdin, send a refresh message to the application. This is the + * only way that the application can be notified that it needs to resend + * the input because someone forced a new window size on the widget. + */ +static void +Resize(w) + Widget w; +{ + Layout(w, False, False); + if (!XtIsRealized(w)) return; + if (Setup(w)) { + GhostviewWidget gvw = (GhostviewWidget) w; + if (gvw->ghostview.filename == NULL) { + XtCallCallbackList(w, gvw->ghostview.message_callback, "Refresh"); + } + } +} + +/* SetValues routine. Set new private state, based on changed values + * in the widget. Always returns False, because redisplay is never needed. + */ +static Boolean +SetValues(current, request, new) + Widget current, request, new; +{ + GhostviewWidget cgvw = (GhostviewWidget) current; + GhostviewWidget rgvw = (GhostviewWidget) request; + GhostviewWidget ngvw = (GhostviewWidget) new; + String cfilename; + String rfilename; + String carguments; + String rarguments; + + cfilename = cgvw->ghostview.filename; + if (cfilename == NULL) cfilename = "(null)"; + rfilename = rgvw->ghostview.filename; + if (rfilename == NULL) rfilename = "(null)"; + carguments = cgvw->ghostview.arguments; + if (carguments == NULL) carguments = "(null)"; + rarguments = rgvw->ghostview.arguments; + if (rarguments == NULL) rarguments = "(null)"; + + if (XtIsRealized(new) && !ngvw->ghostview.busy && + (cgvw->ghostview.cursor != ngvw->ghostview.cursor)) { + XDefineCursor(XtDisplay(new), XtWindow(new), ngvw->ghostview.cursor); + } + if (XtIsRealized(new) && ngvw->ghostview.busy && + (cgvw->ghostview.busy_cursor != ngvw->ghostview.busy_cursor)) { + XDefineCursor(XtDisplay(new), XtWindow(new), + ngvw->ghostview.busy_cursor); + } + if (cgvw->core.background_pixel != rgvw->core.background_pixel) { + XGCValues values; + XtGCMask mask; + + XtReleaseGC(current, cgvw->ghostview.gc); + values.foreground = new->core.background_pixel; + mask = GCForeground; + ngvw->ghostview.gc = XtGetGC(new, mask, &values); + } + if ((cgvw->core.width != rgvw->core.width) || + (cgvw->core.height != rgvw->core.height) || + (cgvw->core.background_pixel != rgvw->core.background_pixel) || + (cgvw->ghostview.foreground != rgvw->ghostview.foreground) || + (cgvw->ghostview.palette != rgvw->ghostview.palette) || + strcmp(cgvw->ghostview.interpreter, rgvw->ghostview.interpreter) || + strcmp(carguments, rarguments) || + (cgvw->ghostview.quiet != rgvw->ghostview.quiet) || + (cgvw->ghostview.safer != rgvw->ghostview.safer) || + strcmp(cfilename, rfilename) || + (cgvw->ghostview.orientation != rgvw->ghostview.orientation) || + (cgvw->ghostview.use_bpixmap != rgvw->ghostview.use_bpixmap) || + (cgvw->ghostview.xdpi != rgvw->ghostview.xdpi) || + (cgvw->ghostview.ydpi != rgvw->ghostview.ydpi) || + (cgvw->ghostview.bottom_margin != rgvw->ghostview.bottom_margin) || + (cgvw->ghostview.left_margin != rgvw->ghostview.left_margin) || + (cgvw->ghostview.right_margin != rgvw->ghostview.right_margin) || + (cgvw->ghostview.top_margin != rgvw->ghostview.top_margin) || + (cgvw->ghostview.llx != rgvw->ghostview.llx) || + (cgvw->ghostview.lly != rgvw->ghostview.lly) || + (cgvw->ghostview.urx != rgvw->ghostview.urx) || + (cgvw->ghostview.ury != rgvw->ghostview.ury)) { + + ngvw->ghostview.changed = True; + Layout(new, True, True); + } + + if (ngvw->ghostview.changed && XtIsRealized(current)) Setup(new); + return(False); +} + +/* Function Name: QueryGeometry + * Description: This tells the parent what size we would like to be + * given certain constraints. + * Arguments: w - the widget. + * intended - what the parent intends to do with us. + * requested - what we want to happen. + */ + +static XtGeometryResult +QueryGeometry(w, intended, requested) +Widget w; +XtWidgetGeometry *intended, *requested; +{ + Dimension new_width, new_height; + Boolean change, width_req, height_req; + + width_req = intended->request_mode & CWWidth; + height_req = intended->request_mode & CWHeight; + + if (width_req) + new_width = intended->width; + else + new_width = w->core.width; + + if (height_req) + new_height = intended->height; + else + new_height = w->core.height; + + requested->request_mode = 0; + +/* + * We only care about our height and width. + */ + + if (!width_req && !height_req) + return(XtGeometryYes); + + change = ComputeSize(w, !width_req, !height_req, &new_width, &new_height); + + requested->request_mode |= CWWidth; + requested->width = new_width; + requested->request_mode |= CWHeight; + requested->height = new_height; + + if (change) + return(XtGeometryAlmost); + return(XtGeometryYes); +} + +/* Layout the widget. */ + +static void +Layout(w, xfree, yfree) + Widget w; + Boolean xfree, yfree; +{ + Dimension width = w->core.width; + Dimension height = w->core.height; + Boolean different_size = ComputeSize(w, xfree, yfree, &width, &height); + if (different_size) ChangeSize(w, width, height); +} + +/* Compute new size of window, sets xdpi and ydpi if necessary. + * returns True if new window size is different */ +static Boolean +ComputeSize(w, xfree, yfree, width, height) + Widget w; + Boolean xfree, yfree; /* Am I allowed to change width or height */ + Dimension *width, *height; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + Dimension new_width = *width; + Dimension new_height = *height; + float newxdpi, newydpi; + Boolean change; + + if (xfree && yfree) { + /* width and height can be changed, calculate window size according */ + /* to xpdi and ydpi */ + switch (gvw->ghostview.orientation) { + case XtPageOrientationPortrait: + case XtPageOrientationUpsideDown: + new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 * + gvw->ghostview.xdpi + 0.5; + new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 * + gvw->ghostview.ydpi + 0.5; + break; + case XtPageOrientationLandscape: + case XtPageOrientationSeascape: + new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 * + gvw->ghostview.xdpi + 0.5; + new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 * + gvw->ghostview.ydpi + 0.5; + break; + } + } else if (xfree) { + /* height is fixed. Preserve aspect ratio by recomputing */ + /* ydpi and xdpi */ + switch (gvw->ghostview.orientation) { + case XtPageOrientationPortrait: + case XtPageOrientationUpsideDown: + newydpi = gvw->core.height * 72.0 / + (gvw->ghostview.ury - gvw->ghostview.lly); + newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi; + gvw->ghostview.xdpi = newxdpi; + gvw->ghostview.ydpi = newydpi; + new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 * + gvw->ghostview.xdpi + 0.5; + break; + case XtPageOrientationLandscape: + case XtPageOrientationSeascape: + newydpi = gvw->core.height * 72.0 / + (gvw->ghostview.urx - gvw->ghostview.llx); + newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi; + gvw->ghostview.xdpi = newxdpi; + gvw->ghostview.ydpi = newydpi; + new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 * + gvw->ghostview.xdpi + 0.5; + break; + } + } else if (yfree) { + /* width is fixed. Preserve aspect ratio by recomputing */ + /* xdpi and ydpi */ + switch (gvw->ghostview.orientation) { + case XtPageOrientationPortrait: + case XtPageOrientationUpsideDown: + newxdpi = gvw->core.width * 72.0 / + (gvw->ghostview.urx - gvw->ghostview.llx); + newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi; + gvw->ghostview.xdpi = newxdpi; + gvw->ghostview.ydpi = newydpi; + new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 * + gvw->ghostview.ydpi + 0.5; + break; + case XtPageOrientationLandscape: + case XtPageOrientationSeascape: + newxdpi = gvw->core.width * 72.0 / + (gvw->ghostview.ury - gvw->ghostview.lly); + newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi; + gvw->ghostview.xdpi = newxdpi; + gvw->ghostview.ydpi = newydpi; + new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 * + gvw->ghostview.ydpi + 0.5; + break; + } + } else { + /* height and width are fixed. Just have to live with it. */ + switch (gvw->ghostview.orientation) { + case XtPageOrientationPortrait: + case XtPageOrientationUpsideDown: + gvw->ghostview.xdpi = gvw->core.width * 72.0 / + (gvw->ghostview.urx - gvw->ghostview.llx); + gvw->ghostview.ydpi = gvw->core.height * 72.0 / + (gvw->ghostview.ury - gvw->ghostview.lly); + break; + case XtPageOrientationLandscape: + case XtPageOrientationSeascape: + gvw->ghostview.xdpi = gvw->core.width * 72.0 / + (gvw->ghostview.ury - gvw->ghostview.lly); + gvw->ghostview.ydpi = gvw->core.height * 72.0 / + (gvw->ghostview.urx - gvw->ghostview.llx); + break; + } + } + + change = (new_width != *width) || (new_height != *height); + *width = new_width; + *height = new_height; + return (change); +} + +/* Function Name: ChangeSize. + * Description: Request a size change. + * Arguments: w - the widget to try change the size of. + */ + +static void +ChangeSize(w, width, height) +Widget w; +Dimension width, height; +{ + XtWidgetGeometry request, reply; + Boolean changed = False; + + request.request_mode = CWWidth | CWHeight; + request.width = width; + request.height = height; + + switch ( XtMakeGeometryRequest(w, &request, &reply) ) { + case XtGeometryYes: + changed = True; + break; + case XtGeometryNo: + break; + case XtGeometryAlmost: + ComputeSize(w, (request.height != reply.height), + (request.width != reply.width), + &(reply.width), &(reply.height)); + request = reply; + switch (XtMakeGeometryRequest(w, &request, &reply) ) { + case XtGeometryYes: + changed = True; + break; + case XtGeometryNo: + break; + case XtGeometryAlmost: + request = reply; + ComputeSize(w, FALSE, FALSE, &(request.width), &(request.height)); + request.request_mode = CWWidth | CWHeight; + XtMakeGeometryRequest(w, &request, &reply); + changed = True; + break; + } + break; + } + + /* If success, setup the widet for the new size. */ + if (changed && XtIsRealized(w)) Setup(w); +} + +/* Catch the alloc error when there is not enough resources for the + * backing pixmap. Automatically shut off backing pixmap and let the + * user know when this happens. + */ +static Boolean alloc_error; +static XErrorHandler oldhandler; + +static int +catch_alloc (dpy, err) +Display *dpy; +XErrorEvent *err; +{ + if (err->error_code == BadAlloc) { + alloc_error = True; + } + if (alloc_error) return 0; + return oldhandler(dpy, err); +} + +/* Setup - sets up the backing pixmap, and GHOSTVIEW property and + * starts interpreter if needed. + * NOTE: the widget must be realized before calling Setup(). + * Returns True if a new interpreter was started, False otherwise. + */ + +static Boolean +Setup(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w); + char buf[GV_BUFSIZ]; + Pixmap bpixmap; + XSetWindowAttributes xswa; + + if (!gvw->ghostview.changed && + (gvw->core.width == gvw->ghostview.gs_width) && + (gvw->core.height == gvw->ghostview.gs_height)) return False; + + StopInterpreter(w); + + if ((gvw->core.width != gvw->ghostview.gs_width) || + (gvw->core.height != gvw->ghostview.gs_height) || + (!gvw->ghostview.use_bpixmap)) { + if (gvw->core.background_pixmap != XtUnspecifiedPixmap) { + XFreePixmap(XtDisplay(w), gvw->core.background_pixmap); + gvw->core.background_pixmap = XtUnspecifiedPixmap; + XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None); + } + } + + if (gvw->ghostview.use_bpixmap) { + if (gvw->core.background_pixmap == XtUnspecifiedPixmap) { + /* Get a Backing Pixmap, but be ready for the BadAlloc. */ + XSync(XtDisplay(w), False); /* Get to known state */ + oldhandler = XSetErrorHandler(catch_alloc); + alloc_error = False; + bpixmap = XCreatePixmap(XtDisplay(w), XtWindow(w), + gvw->core.width, gvw->core.height, + gvw->core.depth); + XSync(XtDisplay(w), False); /* Force the error */ + if (alloc_error) { + XtCallCallbackList(w, gvw->ghostview.message_callback, + "BadAlloc"); + if (bpixmap != None) { + XFreePixmap(XtDisplay(w), bpixmap); + XSync(XtDisplay(w), False); /* Force the error */ + bpixmap = None; + } + } + oldhandler = XSetErrorHandler(oldhandler); + if (bpixmap != None) { + gvw->core.background_pixmap = bpixmap; + XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), + gvw->core.background_pixmap); + } + } else { + bpixmap = gvw->core.background_pixmap; + } + } else { + if (gvw->core.background_pixmap != XtUnspecifiedPixmap) { + XFreePixmap(XtDisplay(w), gvw->core.background_pixmap); + gvw->core.background_pixmap = XtUnspecifiedPixmap; + XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None); + } + bpixmap = None; + } + + if (bpixmap != None) { + xswa.backing_store = NotUseful; + XChangeWindowAttributes(XtDisplay(w), XtWindow(w), + CWBackingStore, &xswa); + } else { + xswa.backing_store = Always; + XChangeWindowAttributes(XtDisplay(w), XtWindow(w), + CWBackingStore, &xswa); + } + + gvw->ghostview.gs_width = gvw->core.width; + gvw->ghostview.gs_height = gvw->core.height; + + sprintf(buf, "%d %d %d %d %d %d %g %g %d %d %d %d", + bpixmap, gvw->ghostview.orientation, + gvw->ghostview.llx, gvw->ghostview.lly, + gvw->ghostview.urx, gvw->ghostview.ury, + gvw->ghostview.xdpi, gvw->ghostview.ydpi, + gvw->ghostview.left_margin, gvw->ghostview.bottom_margin, + gvw->ghostview.right_margin, gvw->ghostview.top_margin); + XChangeProperty(XtDisplay(w), XtWindow(w), + XmuInternAtom(XtDisplay(w), gvc->ghostview_class.ghostview), + XA_STRING, 8, PropModeReplace, + (unsigned char *)buf, strlen(buf)); + + sprintf(buf, "%s %d %d", + gvw->ghostview.palette == XtPaletteMonochrome ? "Monochrome" : + gvw->ghostview.palette == XtPaletteGrayscale ? "Grayscale" : + gvw->ghostview.palette == XtPaletteColor ? "Color" : "?", + gvw->ghostview.foreground, gvw->core.background_pixel); + XChangeProperty(XtDisplay(w), XtWindow(w), + XmuInternAtom(XtDisplay(w), gvc->ghostview_class.gv_colors), + XA_STRING, 8, PropModeReplace, + (unsigned char *)buf, strlen(buf)); + + XSync(XtDisplay(w), False); /* Be sure to update properties */ + StartInterpreter(w); + return True; +} + +#ifndef VMS + +/* This routine starts the interpreter. It sets the DISPLAY and + * GHOSTVIEW environment variables. The GHOSTVIEW environment variable + * contains the Window that ghostscript should write on. + * + * This routine also opens pipes for stdout and stderr and initializes + * application input events for them. If input to ghostscript is not + * from a file, a pipe for stdin is created. This pipe is setup for + * non-blocking I/O so that the user interface never "hangs" because of + * a write to ghostscript. + */ +static void +StartInterpreter(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + int std_in[2]; + int std_out[2]; + int std_err[2]; + char buf[GV_BUFSIZ]; +#define NUM_ARGS 100 + char *argv[NUM_ARGS]; + char *arguments = NULL; + char *cptr; + int argc = 0; + int ret; + + StopInterpreter(w); + + /* Clear the window before starting a new interpreter. */ + if (gvw->core.background_pixmap != XtUnspecifiedPixmap) { + XFillRectangle(XtDisplay(w), gvw->core.background_pixmap, + gvw->ghostview.gc, + 0, 0, gvw->core.width, gvw->core.height); + } + XClearArea(XtDisplay(w), XtWindow(w), + 0, 0, gvw->core.width, gvw->core.height, False); + + if (gvw->ghostview.disable_start) return; + + argv[argc++] = gvw->ghostview.interpreter; + argv[argc++] = "-sDEVICE=x11"; + argv[argc++] = "-dNOPAUSE"; + if (gvw->ghostview.quiet) argv[argc++] = "-dQUIET"; + if (gvw->ghostview.safer) argv[argc++] = "-dSAFER"; + argv[argc++] = "gvpdf.pro"; + if (gvw->ghostview.arguments) { + cptr = arguments = XtNewString(gvw->ghostview.arguments); + while (isspace(*cptr)) cptr++; + while (*cptr) { + argv[argc++] = cptr; + while (*cptr && !isspace(*cptr)) cptr++; + if (*cptr) *cptr++ = '\0'; + if (argc + 2 >= NUM_ARGS) { + fprintf(stderr, "Too many arguments to interpreter.\n"); + exit(1); + } + while (isspace(*cptr)) cptr++; + } + } + argv[argc++] = "-"; + argv[argc++] = NULL; + + if (gvw->ghostview.filename == NULL) { + ret = pipe(std_in); + if (ret == -1) { + perror("Could not create pipe"); + exit(1); + } + } else if (strcmp(gvw->ghostview.filename, "-")) { + std_in[0] = open(gvw->ghostview.filename, O_RDONLY, 0); + } + ret = pipe(std_out); + if (ret == -1) { + perror("Could not create pipe"); + exit(1); + } + ret = pipe(std_err); + if (ret == -1) { + perror("Could not create pipe"); + exit(1); + } + + gvw->ghostview.changed = False; + gvw->ghostview.busy = True; + XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor); +#if defined(SYSV) || defined(USG) +#define vfork fork +#endif + gvw->ghostview.interpreter_pid = vfork(); + + if (gvw->ghostview.interpreter_pid == 0) { /* child */ + close(std_out[0]); + close(std_err[0]); + dup2(std_out[1], 1); + close(std_out[1]); + dup2(std_err[1], 2); + close(std_err[1]); + sprintf(buf, "%d", XtWindow(w)); + setenv("GHOSTVIEW", buf, True); + setenv("DISPLAY", XDisplayString(XtDisplay(w)), True); + if (gvw->ghostview.filename == NULL) { + close(std_in[1]); + dup2(std_in[0], 0); + close(std_in[0]); + } else if (strcmp(gvw->ghostview.filename, "-")) { + dup2(std_in[0], 0); + close(std_in[0]); + } + execvp(argv[0], argv); + sprintf(buf, "Exec of %s failed", argv[0]); + perror(buf); + _exit(1); + } else { + if (gvw->ghostview.filename == NULL) { +#ifdef NON_BLOCKING_IO + int result; +#endif + close(std_in[0]); + +#ifdef NON_BLOCKING_IO + result = fcntl(std_in[1], F_GETFL, 0); + result = result | O_NONBLOCK; + result = fcntl(std_in[1], F_SETFL, result); +#endif + gvw->ghostview.interpreter_input = std_in[1]; + gvw->ghostview.interpreter_input_id = None; + } else if (strcmp(gvw->ghostview.filename, "-")) { + close(std_in[0]); + } + close(std_out[1]); + gvw->ghostview.interpreter_output = std_out[0]; + gvw->ghostview.interpreter_output_id = + XtAppAddInput(XtWidgetToApplicationContext(w), std_out[0], + (XtPointer)XtInputReadMask, Output, (XtPointer)w); + close(std_err[1]); + gvw->ghostview.interpreter_error = std_err[0]; + gvw->ghostview.interpreter_error_id = + XtAppAddInput(XtWidgetToApplicationContext(w), std_err[0], + (XtPointer)XtInputReadMask, Output, (XtPointer)w); + } + if (arguments) XtFree(arguments); +} + +/* Stop the interperter, if present, and remove any Input sources. */ +/* Also reset the busy state. */ +static void +StopInterpreter(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + if (gvw->ghostview.interpreter_pid >= 0) { + kill(gvw->ghostview.interpreter_pid, SIGTERM); + wait(0); + gvw->ghostview.interpreter_pid = -1; + } + if (gvw->ghostview.interpreter_input >= 0) { + close(gvw->ghostview.interpreter_input); + gvw->ghostview.interpreter_input = -1; + if (gvw->ghostview.interpreter_input_id != None) { + XtRemoveInput(gvw->ghostview.interpreter_input_id); + gvw->ghostview.interpreter_input_id = None; + } + while (gvw->ghostview.ps_input) { + struct record_list *ps_old = gvw->ghostview.ps_input; + gvw->ghostview.ps_input = ps_old->next; + if (ps_old->close) fclose(ps_old->fp); + XtFree((char *)ps_old); + } + } + if (gvw->ghostview.interpreter_output >= 0) { + close(gvw->ghostview.interpreter_output); + gvw->ghostview.interpreter_output = -1; + XtRemoveInput(gvw->ghostview.interpreter_output_id); + } + if (gvw->ghostview.interpreter_error >= 0) { + close(gvw->ghostview.interpreter_error); + gvw->ghostview.interpreter_error = -1; + XtRemoveInput(gvw->ghostview.interpreter_error_id); + } + gvw->ghostview.busy = False; + XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor); +} + +#endif /* VMS */ + +/* The interpeter failed, Stop what's left and notify application */ +static void +InterpreterFailed(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + StopInterpreter(w); + XtCallCallbackList(w, gvw->ghostview.message_callback, "Failed"); +} + +/* + * Public Routines + */ + +/* GhostviewDisableInterpreter: + * Stop any interpreter and disable new ones from starting. + */ +void +GhostviewDisableInterpreter(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + gvw->ghostview.disable_start = True; + if (XtIsRealized(w)) StopInterpreter(w); +} + +/* GhostviewDisableInterpreter: + * Allow an interpreter to start and start one if the widget is + * currently realized. + */ +void +GhostviewEnableInterpreter(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + gvw->ghostview.disable_start = False; + if (XtIsRealized(w)) StartInterpreter(w); +} + +/* GhostviewIsInterpreterReady: + * Returns True if the interpreter is ready for new input. + */ +Boolean +GhostviewIsInterpreterReady(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + return gvw->ghostview.interpreter_pid != -1 && + !gvw->ghostview.busy && + gvw->ghostview.ps_input == NULL; +} + +/* GhostviewIsInterpreterRunning: + * Returns True if the interpreter is running. + */ +Boolean +GhostviewIsInterpreterRunning(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + return gvw->ghostview.interpreter_pid != -1; +} + +/* GhostviewGetBackingPixmap: + * Returns the current backing pixmap. + */ +Pixmap +GhostviewGetBackingPixmap(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + if (gvw->core.background_pixmap != XtUnspecifiedPixmap) + return(gvw->core.background_pixmap); + else + return(None); +} + +#ifndef VMS + +/* GhostviewSendPS: + * Queue a portion of a PostScript file for output to ghostscript. + * fp: FILE * of the file in question. NOTE: if you have several + * Ghostview widgets reading from the same file. You must open + * a unique FILE * for each widget. + * SendPS does not actually send the PostScript, it merely queues it + * for output. + * begin: position in file (returned from ftell()) to start. + * len: number of bytes to write. + * + * If an interpreter is not running, nothing is queued and + * False is returned. + */ +Boolean +GhostviewSendPS(w, fp, begin, len, close) + Widget w; + FILE *fp; + long begin; + unsigned int len; + Bool close; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + struct record_list *ps_new; + + if (gvw->ghostview.interpreter_input < 0) return False; + ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list)); + ps_new->fp = fp; + ps_new->begin = begin; + ps_new->len = len; + ps_new->seek_needed = True; + ps_new->close = close; + ps_new->next = NULL; + + if (gvw->ghostview.input_buffer == NULL) { + gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ); + } + + if (gvw->ghostview.ps_input == NULL) { + gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer; + gvw->ghostview.bytes_left = len; + gvw->ghostview.buffer_bytes_left = 0; + gvw->ghostview.ps_input = ps_new; + gvw->ghostview.interpreter_input_id = + XtAppAddInput(XtWidgetToApplicationContext(w), + gvw->ghostview.interpreter_input, + (XtPointer)XtInputWriteMask, Input, (XtPointer)w); + } else { + struct record_list *p = gvw->ghostview.ps_input; + while (p->next != NULL) { + p = p->next; + } + p->next = ps_new; + } + return True; +} + +#endif /* VMS */ + +/* GhostviewNextPage: + * Tell ghostscript to start the next page. + * Returns False if ghostscript is not running, or not ready to start + * another page. + * If another page is started. Sets the busy flag and cursor. + */ +Boolean +GhostviewNextPage(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w); + XEvent event; + + if (gvw->ghostview.interpreter_pid < 0) return False; + if (gvw->ghostview.mwin == None) return False; + + if (!gvw->ghostview.busy) { + gvw->ghostview.busy = True; + XDefineCursor(XtDisplay(gvw), XtWindow(gvw), + gvw->ghostview.busy_cursor); + + event.xclient.type = ClientMessage; + event.xclient.display = XtDisplay(w); + event.xclient.window = gvw->ghostview.mwin; + event.xclient.message_type = + XmuInternAtom(XtDisplay(w), gvc->ghostview_class.next); + event.xclient.format = 32; + XSendEvent(XtDisplay(w), gvw->ghostview.mwin, False, 0, &event); + XFlush(XtDisplay(w)); /* And push it out */ + return True; + } else { + return False; + } +} + +#define done(type, value) \ + { \ + if (toVal->addr != NULL) { \ + if (toVal->size < sizeof(type)) { \ + toVal->size = sizeof(type); \ + return False; \ + } \ + *(type*)(toVal->addr) = (value); \ + } \ + else { \ + static type static_val; \ + static_val = (value); \ + toVal->addr = (XPointer)&static_val; \ + } \ + toVal->size = sizeof(type); \ + return True; \ + } + +/* PageOrienation Conversion Routine. + * Returns True if Conversion is successful. + */ +Boolean +XmuCvtStringToPageOrientation(dpy, args, num_args, fromVal, toVal, data) + Display *dpy; + XrmValue *args; /* unused */ + Cardinal *num_args; /* unused */ + XrmValue *fromVal; + XrmValue *toVal; + XtPointer *data; /* unused */ +{ + static XrmQuark XrmQEportrait; + static XrmQuark XrmQElandscape; + static XrmQuark XrmQEupsideDown; + static XrmQuark XrmQEseascape; + static int haveQuarks; + XrmQuark q; + char *str = (XPointer) fromVal->addr; + char lowerName[1000]; + + if (str == NULL) return False; + + if (!haveQuarks) { + XrmQEportrait = XrmStringToQuark(XtEportrait); + XrmQElandscape = XrmStringToQuark(XtElandscape); + XrmQEupsideDown = XrmStringToQuark(XtEupsideDown); + XrmQEseascape = XrmStringToQuark(XtEseascape); + haveQuarks = 1; + } + + XmuCopyISOLatin1Lowered(lowerName, str); + + q = XrmStringToQuark(lowerName); + + if (q == XrmQEportrait) + done(XtPageOrientation, XtPageOrientationPortrait); + if (q == XrmQElandscape) + done(XtPageOrientation, XtPageOrientationLandscape); + if (q == XrmQEupsideDown) + done(XtPageOrientation, XtPageOrientationUpsideDown); + if (q == XrmQEseascape) + done(XtPageOrientation, XtPageOrientationSeascape); + + XtDisplayStringConversionWarning(dpy, str, XtRPageOrientation); + return False; +} + +/* Palette Conversion Routine. + * Returns True if Conversion is successful. + */ +Boolean +XmuCvtStringToPalette(dpy, args, num_args, fromVal, toVal, data) + Display *dpy; + XrmValue *args; /* unused */ + Cardinal *num_args; /* unused */ + XrmValue *fromVal; + XrmValue *toVal; + XtPointer *data; /* unused */ +{ + static XrmQuark XrmQEmonochrome; + static XrmQuark XrmQEgrayscale; + static XrmQuark XrmQEcolor; + static int haveQuarks; + XrmQuark q; + char *str = (XPointer) fromVal->addr; + char lowerName[1000]; + + if (str == NULL) return False; + + if (!haveQuarks) { + XrmQEmonochrome = XrmStringToQuark(XtEmonochrome); + XrmQEgrayscale = XrmStringToQuark(XtEgrayscale); + XrmQEcolor = XrmStringToQuark(XtEcolor); + haveQuarks = 1; + } + + XmuCopyISOLatin1Lowered(lowerName, str); + + q = XrmStringToQuark(lowerName); + + if (q == XrmQEmonochrome) + done(XtPalette, XtPaletteMonochrome); + if (q == XrmQEgrayscale) + done(XtPalette, XtPaletteGrayscale); + if (q == XrmQEcolor) + done(XtPalette, XtPaletteColor); + + XtDisplayStringConversionWarning(dpy, str, XtRPalette); + return False; +} + +#ifdef VMS + +/* +** VMS specific include files +*/ +#include <descrip.h> +#include <ssdef.h> +#include <clidef.h> +#include <lnmdef.h> +#include <iodef.h> +#include <dvidef.h> +#include "vms_types.h" + +#define ERR_SIGNAL(s) if(!((s) & 1))lib$signal((s), 0, 0) +#define XtEFN 23 + +struct g_l_i +{ + GhostviewWidget w; + struct g_l_i *next; +}; + +typedef struct g_l_i GhostListItem, *GLI_p; + +static GhostListItem glhead = {(GhostviewWidget) -1, NULL}; +static GLI_p GL = &glhead; +static size_t GLI_Size = sizeof(GhostListItem); +static XtInputId EventId; + +/* +** This routine is passed to XtAppAddInput(). It is called whenever the event +** flag number XtEFN is set and the Xt main loop becomes idle. It clears the +** event flag and then scans all the ghostview widgets for completed I/O +** requests, processing each as they are found. We have to do them all because +** there is no way to have Xt dispatch them individually without a window of +** vulnerability that can cause missed events, or by using a separate event +** flag for each I/O stream. Event flags are, unfortunately, a limited +** resource. +*/ +static Boolean +IOProcess() +{ + GhostviewWidget gvw; + GLI_p cur; + + /* + ** Before we process any I/O's, clear the event flag. + */ + sys$clref(XtEFN); + /* + ** Scan all the ghostview widgets and check for completed I/O's + */ + for(cur = GL->next; cur; cur = cur->next){ + /* + ** Get the widget and check for I/O complete on either mailbox. + */ + gvw = cur->w; + if(gvw->ghostview.interpreter_input_iosb[0])Input(gvw); + if(gvw->ghostview.interpreter_output_iosb[0])Output(gvw); + } +} + +/* +** This is an AST routine. It is called asynchronously whenever one of our +** mailbox I/O's completes. +*/ +static void +IOComplete(client_data) + XtPointer client_data; +{ + /* + ** Set the event flag to tell Xt to call IOProcess. + */ + sys$setef(XtEFN); +} + +static void +GLInsert(w) + GhostviewWidget w; +{ + GLI_p new; + int first; + + /* + ** Insert this widget after the list head + */ + first = (GL->next == NULL); + new = XtMalloc(GLI_Size); + new->w = w; + new->next = GL->next; + GL->next = new; + /* + ** If this is the first item on the list, call XtAppAddInput() + */ + if(first)EventId = XtAppAddInput(XtWidgetToApplicationContext(w), XtEFN, 0, + IOProcess, 0); +} + +static void +GLRemove(w) + GhostviewWidget w; +{ + GLI_p prev, cur; + int last = 0; + + /* + ** Find and remove this widget from the list. + */ + prev = GL; + cur = prev->next; + while(cur && cur->w != w){ + prev = cur; + cur = cur->next; + } + if(cur){ + prev->next = cur->next; + XtFree(cur); + last = (GL->next == NULL); + } + /* + ** If this was the last item on the list, call XtRemoveInput() + */ + if(last)XtRemoveInput(EventId); +} + +/* Input - Feed data to ghostscript's stdin. + * Write bytes to ghostscript using non-blocking I/O. + * Also, pipe signals are caught during writing. The return + * values are checked and the appropriate action is taken. I do + * this at this low level, because it may not be appropriate for + * SIGPIPE to be caught for the overall application. + */ + +static void +Input(gvw) + GhostviewWidget gvw; +{ + int stat, bbytes; + char *ch; + + /* + ** Check for error on previous I/O. + */ + stat = gvw->ghostview.interpreter_input_iosb[0]; + if(stat != SS$_NORMAL){ + InterpreterFailed(gvw); + } else { + + /* Get a new section if required */ + + if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) { + struct record_list *ps_old = gvw->ghostview.ps_input; + gvw->ghostview.ps_input = ps_old->next; + if (ps_old->close) fclose(ps_old->fp); + XtFree((char *)ps_old); + } + if(gvw->ghostview.ps_input){ + /* Have to seek at the beginning of each section */ + if (gvw->ghostview.ps_input->seek_needed) { + if (gvw->ghostview.ps_input->len > 0) + fseek(gvw->ghostview.ps_input->fp, + gvw->ghostview.ps_input->begin, SEEK_SET); + gvw->ghostview.ps_input->seek_needed = False; + gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len; + } + /* + ** Read a line from the file. + */ + ch = fgets(gvw->ghostview.input_buffer, GV_BUFSIZ, + gvw->ghostview.ps_input->fp); + if(!ch){ + /* + ** Error, EOF when there's supposed to be data left. + */ + InterpreterFailed(gvw); + } else { + /* + ** Write it to the mailbox. + */ + bbytes = strlen(gvw->ghostview.input_buffer); + gvw->ghostview.bytes_left -= bbytes; + stat = sys$qio(0, (short)gvw->ghostview.interpreter_input, + IO$_WRITEVBLK, &gvw->ghostview.interpreter_input_iosb, + IOComplete, 0, gvw->ghostview.input_buffer, bbytes, + 0, 0, 0, 0); + ERR_SIGNAL(stat); + } + } + } +} + +/* Output - receive I/O from ghostscript's stdout and stderr. + * Pass this to the application via the output_callback. */ +static void +Output(gvw) + GhostviewWidget gvw; +{ + char buf[GV_BUFSIZ+1]; + int bytes, stat; + + stat = gvw->ghostview.interpreter_output_iosb[0]; + bytes = gvw->ghostview.interpreter_output_iosb[1]; + if (stat == SS$_NORMAL) { + /* + ** Got a message. If line complete, pass to the output_callback. + ** + ** HACK ALERT, if bytes is -1 nothing happens, but an I/O is queued. + ** This is our first time code, since Xt doesn't queue the I/O for us + ** under VMS, just watches for completion. In StartInterpreter We setup + ** an IOSB with a success status and -1 bytes so Xt will call us the + ** first time to get the I/O queued. + */ + if (bytes == 0) { + strcpy(buf, "\n"); + } else if (bytes == 1) { + buf[0] = gvw->ghostview.output_buffer[0]; + buf[1] = '\0'; + } else if (bytes > 1) { + /* + ** Copy the message to a local buffer and pass it to the callback. + */ + memcpy(buf, gvw->ghostview.output_buffer, bytes); + buf[bytes] = '\0'; + } + if(bytes >= 0)XtCallCallbackList(gvw, gvw->ghostview.output_callback, + (XtPointer) buf); + /* + ** Queue a new read to the mailbox + */ + stat = sys$qio(0, (short)gvw->ghostview.interpreter_output, + IO$_READVBLK, &gvw->ghostview.interpreter_output_iosb, IOComplete, + 0, gvw->ghostview.output_buffer, GV_BUFSIZ, 0, 0, 0, 0); + ERR_SIGNAL(stat); + } else { + InterpreterFailed(gvw); /* Something bad happened */ + } +} + +/* This routine starts the interpreter. It sets the DISPLAY and + * GHOSTVIEW environment variables. The GHOSTVIEW environment variable + * contains the Window that ghostscript should write on. + * + * This routine also opens pipes for stdout and stderr and initializes + * application input events for them. If input to ghostscript is not + * from a file, a pipe for stdin is created. This pipe is setup for + * non-blocking I/O so that the user interface never "hangs" because of + * a write to ghostscript. + */ +static void +StartInterpreter(w) + Widget w; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + char buf[GV_BUFSIZ]; + char cmd[512]; + int ret; + short ch1, ch2; + char in_mbx_name[65], out_mbx_name[65]; + long pid, nowait = CLI$M_NOWAIT; + const $DESCRIPTOR(ghostview_desc, "GHOSTVIEW"); + const $DESCRIPTOR(display_desc, "DECW$DISPLAY"); + const $DESCRIPTOR(lnt_desc, "LNM$PROCESS"); + $DESCRIPTOR(in_desc, ""); + $DESCRIPTOR(out_desc, ""); + $DESCRIPTOR(lnm_desc, ""); + $DESCRIPTOR(cmd_desc, cmd); + ITEM_LIST_3_T(gv_list, 1) = {{{0, LNM$_STRING, buf, NULL}}, 0}; + ITEM_LIST_3_T(dis_list, 1) = {{{0, LNM$_STRING, NULL, NULL}}, 0}; + ITEM_LIST_3_T(dvi_list, 1) = {{{64, DVI$_DEVNAM, NULL, NULL}}, 0}; + IOSB_GET_T dvi_iosb; + + /* + ** Stop interpreter if running + */ + StopInterpreter(w); + /* + ** Clear the window before starting a new interpreter. + */ + if (gvw->core.background_pixmap != XtUnspecifiedPixmap) { + XFillRectangle(XtDisplay(w), gvw->core.background_pixmap, + gvw->ghostview.gc, + 0, 0, gvw->core.width, gvw->core.height); + } + XClearArea(XtDisplay(w), XtWindow(w), + 0, 0, gvw->core.width, gvw->core.height, False); + /* + ** Check for disabled. + */ + if (gvw->ghostview.disable_start) return; + /* + ** Build Ghostscript startup command + */ + strcpy(cmd, gvw->ghostview.interpreter); + strcat(cmd, " \"-sDEVICE=x11\" \"-dNOPAUSE\" "); + if (gvw->ghostview.quiet) strcat(cmd, "\"-dQUIET\" "); + if (gvw->ghostview.safer) strcat(cmd, "\"-dSAFER\" "); + if (gvw->ghostview.arguments) { + strcat(cmd, gvw->ghostview.arguments); + strcat(cmd, " "); + } + strcat(cmd, "\"-\" "); + + /* + ** Determine input source. + */ + if (gvw->ghostview.filename == NULL) { + /* + ** Create a mailbox to feed input to Ghostscript and get its name. + */ + ret = sys$crembx(0, &ch1, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0); + ERR_SIGNAL(ret); + dvi_list.item[0].buffer_p = in_mbx_name; + ret = sys$getdvi(0, ch1, 0, &dvi_list, &dvi_iosb, 0, 0, 0); + ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status); + in_mbx_name[64] = '\0'; + in_desc.dsc$a_pointer = in_mbx_name; + in_desc.dsc$w_length = strlen(in_mbx_name); + } else { + /* + ** Set up file name to give Ghostscript as standard input. + */ + in_desc.dsc$a_pointer = gvw->ghostview.filename; + in_desc.dsc$w_length = strlen(gvw->ghostview.filename); + } + /* + ** Create mailbox to receive Ghostscript's output + */ + ret = sys$crembx(0, &ch2, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0); + ERR_SIGNAL(ret); + dvi_list.item[0].buffer_p = out_mbx_name; + ret = sys$getdvi(0, ch2, 0, &dvi_list, &dvi_iosb, 0, 0, 0); + ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status); + out_mbx_name[64] = '\0'; + out_desc.dsc$a_pointer = out_mbx_name; + out_desc.dsc$w_length = strlen(out_mbx_name); + /* + ** Create GHOSTVIEW and DECW$DISPLAY logical names. + ** + ** We use CRELNM rather than LIB$SET_LOGICAL because we want these to be + ** user mode and go away when the program exits. It doesn't matter that we + ** may set them multiple times, as with the mailbox logicals, since once + ** Ghostscript starts we don't need them any more. + */ + sprintf(buf, "%d", XtWindow(w)); + gv_list.item[0].buffer_size = strlen(buf); + ret = sys$crelnm(0, &lnt_desc, &ghostview_desc, 0, &gv_list); + ERR_SIGNAL(ret); + dis_list.item[0].buffer_p = XDisplayString(XtDisplay(w)); + dis_list.item[0].buffer_size = strlen(dis_list.item[0].buffer_p); + ret = sys$crelnm(0, &lnt_desc, &display_desc, 0, &dis_list); + ERR_SIGNAL(ret); + /* + ** Spawn Ghostscript process + */ + gvw->ghostview.changed = False; + gvw->ghostview.busy = True; + cmd_desc.dsc$w_length = strlen(cmd); + ret = lib$spawn(&cmd_desc, &in_desc, &out_desc, &nowait, 0, &pid, 0, 0, + 0, 0, 0, 0, 0); + ERR_SIGNAL(ret); + XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor); + /* + ** Everything worked, initialize IOSBs and save info about interpretter. + */ + gvw->ghostview.interpreter_pid = pid; + if (gvw->ghostview.filename == NULL) { + gvw->ghostview.interpreter_input = ch1; + gvw->ghostview.interpreter_input_iosb[0] = 0; + } + gvw->ghostview.interpreter_output = ch2; + if (gvw->ghostview.output_buffer == NULL) { + gvw->ghostview.output_buffer = XtMalloc(GV_BUFSIZ); + } + GLInsert(gvw); + /* + ** Fake a completed I/O so Output will get called to queue the first I/O. + */ + gvw->ghostview.interpreter_output_iosb[0] = SS$_NORMAL; + gvw->ghostview.interpreter_output_iosb[1] = -1; + IOComplete(); +} + +/* Stop the interperter, if present, and remove any Input sources. */ +/* Also reset the busy state. */ +static void +StopInterpreter(w) + Widget w; +{ + int ret; + + GhostviewWidget gvw = (GhostviewWidget) w; + if (gvw->ghostview.interpreter_pid >= 0) { + ret = sys$delprc(&gvw->ghostview.interpreter_pid, 0); + if(ret != SS$_NORMAL && ret != SS$_NONEXPR)lib$signal(ret, 0, 0); + gvw->ghostview.interpreter_pid = -1; + } + if (gvw->ghostview.interpreter_input >= 0) { + (void) sys$dassgn(gvw->ghostview.interpreter_input); + gvw->ghostview.interpreter_input = -1; + while (gvw->ghostview.ps_input) { + struct record_list *ps_old = gvw->ghostview.ps_input; + gvw->ghostview.ps_input = ps_old->next; + if (ps_old->close) fclose(ps_old->fp); + XtFree((char *)ps_old); + } + } + if (gvw->ghostview.interpreter_output >= 0) { + (void) sys$dassgn(gvw->ghostview.interpreter_output); + gvw->ghostview.interpreter_output = -1; + } + gvw->ghostview.busy = False; + XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor); + GLRemove(gvw); +} + +/* GhostviewSendPS: + * Queue a portion of a PostScript file for output to ghostscript. + * fp: FILE * of the file in question. NOTE: if you have several + * Ghostview widgets reading from the same file. You must open + * a unique FILE * for each widget. + * SendPS does not actually send the PostScript, it merely queues it + * for output. + * begin: position in file (returned from ftell()) to start. + * len: number of bytes to write. + * + * If an interpreter is not running, nothing is queued and + * False is returned. + */ +Boolean +GhostviewSendPS(w, fp, begin, len, close) + Widget w; + FILE *fp; + long begin; + unsigned int len; + Bool close; +{ + GhostviewWidget gvw = (GhostviewWidget) w; + struct record_list *ps_new; + + if (gvw->ghostview.interpreter_input < 0) return False; + if(len != 0){ + ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list)); + ps_new->fp = fp; + ps_new->begin = begin; + ps_new->len = len; + ps_new->seek_needed = True; + ps_new->close = close; + ps_new->next = NULL; + + if (gvw->ghostview.input_buffer == NULL) { + gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ); + } + + if (gvw->ghostview.ps_input == NULL) { + gvw->ghostview.bytes_left = len; + gvw->ghostview.ps_input = ps_new; + /* + ** Fake a completed I/O so Input will get called to queue the + ** first I/O. + */ + gvw->ghostview.interpreter_input_iosb[0] = SS$_NORMAL; + gvw->ghostview.interpreter_input_iosb[1] = -1; + IOComplete(); + } else { + struct record_list *p = gvw->ghostview.ps_input; + while (p->next != NULL) { + p = p->next; + } + p->next = ps_new; + } + } + return True; +} +#endif /* VMS */ |