/* * 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, */ /* on Nov 17, 1994. Neither the modification, */ /* nor the original program provides any warranty. */ #include #include #include #include #include #include "GhostviewP.h" #include "pdf.h" #include #ifndef XlibSpecificationRelease typedef char *XPointer; #endif #include #ifdef SIGNALRETURNSINT #define SIGVAL int #else #define SIGVAL void #endif #ifdef NON_BLOCKING_IO #include /* 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 /* 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() \n\ : notify(72) \n\ : notify(108) \n\ : 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 #include #include #include #include #include #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 */