"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /*
2 * Ghostview.c -- Ghostview widget.
3 * Copyright (C) 1992 Timothy O. Theisen
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * Author: Tim Theisen Systems Programmer
20 * Internet: tim@cs.wisc.edu Department of Computer Sciences
21 * UUCP: uwvax!tim University of Wisconsin-Madison
22 * Phone: (608)262-0438 1210 West Dayton Street
23 * FAX: (608)262-9777 Madison, WI 53706
24 */
25
26 #include <X11/IntrinsicP.h>
27 #include <X11/StringDefs.h>
28 #include <X11/Xatom.h>
29 #include <X11/Xproto.h>
30 #include <X11/Xos.h>
31 #include "GhostviewP.h"
32 #include <ctype.h>
33
34 #ifndef XlibSpecificationRelease
35 typedef char *XPointer;
36 #endif
37
38 #include <signal.h>
39 #ifdef SIGNALRETURNSINT
40 #define SIGVAL int
41 #else
42 #define SIGVAL void
43 #endif
44
45 #ifdef NON_BLOCKING_IO
46 #include <fcntl.h>
47 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
48 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
49 #define O_NONBLOCK O_NDELAY
50 #endif
51 #endif
52
53 #include <errno.h>
54 /* BSD 4.3 errno.h does not declare errno */
55 extern int errno;
56 /* Both error returns are checked for non-blocking I/O. */
57 /* Manufacture the other error code if only one exists. */
58 #if !defined(EWOULDBLOCK) && defined(EAGAIN)
59 #define EWOULDBLOCK EAGAIN
60 #endif
61 #if !defined(EAGAIN) && defined(EWOULDBLOCK)
62 #define EAGAIN EWOULDBLOCK
63 #endif
64
65 #ifndef VMS
66 /* GV_BUFSIZ is set to the minimum POSIX PIPE_BUF to ensure that
67 * nonblocking writes to ghostscript will work properly.
68 */
69 #define GV_BUFSIZ 512
70 #else /* VMS */
71 /*
72 ** GV_BUFSIZ is the maximum length line we can handle, so we up it to 1024
73 */
74 #define GV_BUFSIZ 1024
75 #endif /* VMS */
76
77 /* length calculates string length at compile time */
78 /* can only be used with character constants */
79 #define length(a) (sizeof(a)-1)
80 #define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
81
82 static void ComputeXdpi();
83 static void ComputeYdpi();
84
85 static XtResource resources[] = {
86 #define offset(field) XtOffsetOf(GhostviewRec, ghostview.field)
87 { XtNbottomMargin, XtCMargin, XtRInt, sizeof(int),
88 offset(bottom_margin), XtRImmediate, (XtPointer)0 },
89 { XtNbusyCursor, XtCCursor, XtRCursor, sizeof(XtPointer),
90 offset(busy_cursor), XtRString, "watch" },
91 { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
92 offset(callback), XtRCallback, (XtPointer)NULL },
93 { XtNcursor, XtCCursor, XtRCursor, sizeof(XtPointer),
94 offset(cursor), XtRString, "crosshair" },
95 { XtNfilename, XtCFilename, XtRString, sizeof(String),
96 offset(filename), XtRString, (XtPointer)NULL },
97 { XtNinterpreter, XtCInterpreter, XtRString, sizeof(String),
98 offset(interpreter), XtRString, "gs" },
99 { XtNleftMargin, XtCMargin, XtRInt, sizeof(int),
100 offset(left_margin), XtRImmediate, (XtPointer)0 },
101 { XtNllx, XtCBoundingBox, XtRInt, sizeof(int),
102 offset(llx), XtRImmediate, (XtPointer)0 },
103 { XtNlly, XtCBoundingBox, XtRInt, sizeof(int),
104 offset(lly), XtRImmediate, (XtPointer)0 },
105 { XtNmessageCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
106 offset(message_callback), XtRCallback, (XtPointer)NULL },
107 { XtNorientation, XtCOrientation, XtRPageOrientation,
108 sizeof(XtPageOrientation), offset(orientation), XtRImmediate,
109 (XtPointer)XtPageOrientationPortrait },
110 { XtNoutputCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
111 offset(output_callback), XtRCallback, (XtPointer)NULL },
112 { XtNpreload, XtCPreload, XtRString, sizeof(String),
113 offset(preload), XtRString, (XtPointer)NULL },
114 { XtNquiet, XtCQuiet, XtRBoolean, sizeof(Boolean),
115 offset(quiet), XtRImmediate, (XtPointer)True },
116 { XtNrightMargin, XtCMargin, XtRInt, sizeof(int),
117 offset(right_margin), XtRImmediate, (XtPointer)0 },
118 { XtNtopMargin, XtCMargin, XtRInt, sizeof(int),
119 offset(top_margin), XtRImmediate, (XtPointer)0 },
120 { XtNuseBackingPixmap, XtCUseBackingPixmap, XtRBoolean, sizeof(Boolean),
121 offset(use_bpixmap), XtRImmediate, (XtPointer)True },
122 { XtNurx, XtCBoundingBox, XtRInt, sizeof(int),
123 offset(urx), XtRImmediate, (XtPointer)612 },
124 { XtNury, XtCBoundingBox, XtRInt, sizeof(int),
125 offset(ury), XtRImmediate, (XtPointer)792 },
126 { XtNxdpi, XtCResolution, XtRFloat, sizeof(float),
127 offset(xdpi), XtRCallProc, (XtPointer)ComputeXdpi },
128 { XtNydpi, XtCResolution, XtRFloat, sizeof(float),
129 offset(ydpi), XtRCallProc, (XtPointer)ComputeYdpi },
130 #undef offset
131 };
132
133 static void Message();
134 static void Notify();
135 static void Input();
136 static void Output();
137
138 static void ClassInitialize();
139 static void ClassPartInitialize();
140 static void Initialize();
141 static void Realize();
142 static void Destroy();
143 static void Resize();
144 static Boolean SetValues();
145 static XtGeometryResult QueryGeometry();
146
147 static void Layout();
148 static Boolean ComputeSize();
149 static void ChangeSize();
150 static Boolean Setup();
151 static void StartInterpreter();
152 static void StopInterpreter();
153 static void InterpreterFailed();
154
155 static XtActionsRec actions[] =
156 {
157 {"message", Message},
158 {"notify", Notify},
159 };
160
161 /* notify takes zero to four parameters. The first two give the width and
162 * height of the zoom requested in the default user coordinate system.
163 * If they are omitted, a default value of 72 is provided. If the second
164 * parameter is omitted, the zoom area is assumed to be a square.
165 * The next two parameters give the desired resolution of the zoom window.
166 * If they are omitted, a default value of 300 is provided. If the four
167 * parameter is omitted, the y resolution is assumed to be equal to the
168 * x resolution.
169 */
170 static char translations[] =
171 "<Message>: message() \n\
172 <Btn1Down>: notify(72) \n\
173 <Btn2Down>: notify(108) \n\
174 <Btn3Down>: notify(144) \n\
175 ";
176
177 GhostviewClassRec ghostviewClassRec = {
178 { /* core fields */
179 /* superclass */ (WidgetClass) &coreClassRec,
180 /* class_name */ "Ghostview",
181 /* widget_size */ sizeof(GhostviewRec),
182 /* class_initialize */ ClassInitialize,
183 /* class_part_initialize */ ClassPartInitialize,
184 /* class_inited */ FALSE,
185 /* initialize */ Initialize,
186 /* initialize_hook */ NULL,
187 /* realize */ Realize,
188 /* actions */ actions,
189 /* num_actions */ XtNumber(actions),
190 /* resources */ resources,
191 /* num_resources */ XtNumber(resources),
192 /* xrm_class */ NULLQUARK,
193 /* compress_motion */ TRUE,
194 /* compress_exposure */ TRUE,
195 /* compress_enterleave */ TRUE,
196 /* visible_interest */ FALSE,
197 /* destroy */ Destroy,
198 /* resize */ Resize,
199 /* expose */ NULL,
200 /* set_values */ SetValues,
201 /* set_values_hook */ NULL,
202 /* set_values_almost */ XtInheritSetValuesAlmost,
203 /* get_values_hook */ NULL,
204 /* accept_focus */ NULL,
205 /* version */ XtVersion,
206 /* callback_private */ NULL,
207 /* tm_table */ translations,
208 /* query_geometry */ QueryGeometry,
209 /* display_accelerator */ XtInheritDisplayAccelerator,
210 /* extension */ NULL
211 },
212 { /* ghostview fields */
213 /* ghostview */ NULL,
214 /* next */ NULL,
215 /* page */ NULL,
216 /* done */ NULL
217 }
218 };
219
220 WidgetClass ghostviewWidgetClass = (WidgetClass)&ghostviewClassRec;
221
222 /* Procedures that compute the default xdpi and ydpi from display parameters */
223
224 static void
225 ComputeXdpi(w, offset, value)
226 Widget w;
227 int offset;
228 XrmValue *value;
229 {
230 static float xdpi;
231 xdpi = 25.4 * WidthOfScreen(XtScreen(w)) / WidthMMOfScreen(XtScreen(w));
232 value->addr = (XtPointer) &xdpi;
233 }
234
235 static void
236 ComputeYdpi(w, offset, value)
237 Widget w;
238 int offset;
239 XrmValue *value;
240 {
241 static float ydpi;
242 ydpi = 25.4 * HeightOfScreen(XtScreen(w)) / HeightMMOfScreen(XtScreen(w));
243 value->addr = (XtPointer) &ydpi;
244 }
245
246 /* Message action routine.
247 * Passes ghostscript message events back to application via
248 * the message callback. It also marks the interpreter as
249 * being not busy at the end of page, and stops the interpreter
250 * when it send a "done" message.
251 */
252 static void
253 Message(w, event, params, num_params)
254 Widget w;
255 XEvent *event;
256 String *params; /* unused */
257 Cardinal *num_params; /* unused */
258 {
259 GhostviewWidget gvw = (GhostviewWidget) w;
260 GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
261
262 gvw->ghostview.mwin = event->xclient.data.l[0];
263 if (event->xclient.message_type ==
264 XmuInternAtom(XtDisplay(w), gvc->ghostview_class.page)) {
265 gvw->ghostview.busy = False;
266 XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
267 XtCallCallbackList(w, gvw->ghostview.message_callback, "Page");
268 } else if (event->xclient.message_type ==
269 XmuInternAtom(XtDisplay(w), gvc->ghostview_class.done)) {
270 StopInterpreter(w);
271 XtCallCallbackList(w, gvw->ghostview.message_callback, "Done");
272 }
273 }
274
275 /* Notify action routine.
276 * Calculates where the user clicked in the default user coordinate system.
277 * Call the callbacks with the point of click and size of zoom window
278 * requested.
279 */
280 static void
281 Notify(w, event, params, num_params)
282 Widget w;
283 XEvent *event;
284 String *params;
285 Cardinal *num_params;
286 {
287 GhostviewWidget gvw = (GhostviewWidget) w;
288 GhostviewReturnStruct ret_val;
289
290 /* notify takes zero to four parameters. The first two give the width and
291 * height of the zoom requested in the default user coordinate system.
292 * If they are omitted, a default value of 72 is provided. If the second
293 * parameter is omitted, the zoom area is assumed to be a square.
294 * The next two parameters give the desired resolution of the zoom window.
295 * If they are omitted, a default value of 300 is provided. If the four
296 * parameter is omitted, the y resolution is assumed to be equal to the
297 * x resolution.
298 */
299 switch (*num_params) {
300 case 0:
301 ret_val.width = ret_val.height = 72;
302 ret_val.xdpi = ret_val.ydpi = 300;
303 break;
304 case 1:
305 ret_val.width = ret_val.height = atoi(params[0]);
306 ret_val.xdpi = ret_val.ydpi = 300;
307 break;
308 case 2:
309 ret_val.width = atoi(params[0]);
310 ret_val.height = atoi(params[1]);
311 ret_val.xdpi = ret_val.ydpi = 300;
312 break;
313 case 3:
314 ret_val.width = atoi(params[0]);
315 ret_val.height = atoi(params[1]);
316 ret_val.xdpi = ret_val.ydpi = atoi(params[2]);
317 break;
318 default:
319 ret_val.width = atoi(params[0]);
320 ret_val.height = atoi(params[1]);
321 ret_val.xdpi = atoi(params[2]);
322 ret_val.ydpi = atoi(params[3]);
323 break;
324 }
325
326 switch (gvw->ghostview.orientation) {
327 case XtPageOrientationPortrait:
328 ret_val.psx = gvw->ghostview.llx +
329 event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
330 ret_val.psy = gvw->ghostview.ury -
331 event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
332 break;
333 case XtPageOrientationLandscape:
334 ret_val.psx = gvw->ghostview.llx +
335 event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
336 ret_val.psy = gvw->ghostview.lly +
337 event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
338 break;
339 case XtPageOrientationUpsideDown:
340 ret_val.psx = gvw->ghostview.urx -
341 event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
342 ret_val.psy = gvw->ghostview.lly +
343 event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
344 break;
345 case XtPageOrientationSeascape:
346 ret_val.psx = gvw->ghostview.urx -
347 event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
348 ret_val.psy = gvw->ghostview.ury -
349 event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
350 break;
351 }
352 XtCallCallbackList(w, gvw->ghostview.callback, (XtPointer) &ret_val);
353 }
354
355 #ifndef SEEK_SET
356 #define SEEK_SET 0
357 #endif
358
359 static Boolean broken_pipe = False;
360
361 static SIGVAL
362 CatchPipe(i)
363 int i;
364 {
365 broken_pipe = True;
366 #ifdef SIGNALRETURNSINT
367 return 0;
368 #endif
369 }
370
371 #ifndef VMS
372
373 /* Input - Feed data to ghostscript's stdin.
374 * Write bytes to ghostscript using non-blocking I/O.
375 * Also, pipe signals are caught during writing. The return
376 * values are checked and the appropriate action is taken. I do
377 * this at this low level, because it may not be appropriate for
378 * SIGPIPE to be caught for the overall application.
379 */
380
381 static void
382 Input(client_data, source, id)
383 XtPointer client_data;
384 int *source;
385 XtInputId *id;
386 {
387 Widget w = (Widget) client_data;
388 GhostviewWidget gvw = (GhostviewWidget) w;
389 int bytes_written;
390 SIGVAL (*oldsig)();
391
392 oldsig = signal(SIGPIPE, CatchPipe);
393
394 #ifdef NON_BLOCKING_IO
395 do {
396 #endif
397
398 if (gvw->ghostview.buffer_bytes_left == 0) {
399
400 /* Get a new section if required */
401 if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
402 struct record_list *ps_old = gvw->ghostview.ps_input;
403 gvw->ghostview.ps_input = ps_old->next;
404 if (ps_old->close) fclose(ps_old->fp);
405 XtFree((char *)ps_old);
406 }
407
408 /* Have to seek at the beginning of each section */
409 if (gvw->ghostview.ps_input &&
410 gvw->ghostview.ps_input->seek_needed) {
411 if (gvw->ghostview.ps_input->len > 0)
412 fseek(gvw->ghostview.ps_input->fp,
413 gvw->ghostview.ps_input->begin, SEEK_SET);
414 gvw->ghostview.ps_input->seek_needed = False;
415 gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
416 }
417
418 if (gvw->ghostview.bytes_left > GV_BUFSIZ) {
419 gvw->ghostview.buffer_bytes_left =
420 fread(gvw->ghostview.input_buffer,
421 sizeof (char), GV_BUFSIZ,
422 gvw->ghostview.ps_input->fp);
423 } else if (gvw->ghostview.bytes_left > 0) {
424 gvw->ghostview.buffer_bytes_left =
425 fread(gvw->ghostview.input_buffer,
426 sizeof (char), gvw->ghostview.bytes_left,
427 gvw->ghostview.ps_input->fp);
428 } else {
429 gvw->ghostview.buffer_bytes_left = 0;
430 }
431 if (gvw->ghostview.bytes_left > 0 &&
432 gvw->ghostview.buffer_bytes_left == 0) {
433 InterpreterFailed(w); /* Error occurred */
434 }
435 gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
436 gvw->ghostview.bytes_left -= gvw->ghostview.buffer_bytes_left;
437 }
438
439 if (gvw->ghostview.buffer_bytes_left > 0) {
440 bytes_written = write(gvw->ghostview.interpreter_input,
441 gvw->ghostview.input_buffer_ptr,
442 gvw->ghostview.buffer_bytes_left);
443
444 if (broken_pipe) {
445 broken_pipe = False;
446 InterpreterFailed(w); /* Something bad happened */
447 } else if (bytes_written == -1) {
448 if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
449 InterpreterFailed(w); /* Something bad happened */
450 }
451 } else {
452 gvw->ghostview.buffer_bytes_left -= bytes_written;
453 gvw->ghostview.input_buffer_ptr += bytes_written;
454 }
455 }
456 #ifdef NON_BLOCKING_IO
457 } while(gvw->ghostview.ps_input &&
458 gvw->ghostview.buffer_bytes_left == 0);
459 #endif
460 signal(SIGPIPE, oldsig);
461 if (gvw->ghostview.ps_input == NULL &&
462 gvw->ghostview.buffer_bytes_left == 0) {
463 if (gvw->ghostview.interpreter_input_id != None) {
464 XtRemoveInput(gvw->ghostview.interpreter_input_id);
465 gvw->ghostview.interpreter_input_id = None;
466 }
467 }
468 }
469
470 /* Output - receive I/O from ghostscript's stdout and stderr.
471 * Pass this to the application via the output_callback. */
472 static void
473 Output(client_data, source, id)
474 XtPointer client_data;
475 int *source;
476 XtInputId *id;
477 {
478 Widget w = (Widget) client_data;
479 GhostviewWidget gvw = (GhostviewWidget) w;
480 char buf[GV_BUFSIZ+1];
481 int bytes = 0;
482
483 if (*source == gvw->ghostview.interpreter_output) {
484 bytes = read(gvw->ghostview.interpreter_output, buf, GV_BUFSIZ);
485 if (bytes == 0) { /* EOF occurred */
486 close(gvw->ghostview.interpreter_output);
487 gvw->ghostview.interpreter_output = -1;
488 XtRemoveInput(gvw->ghostview.interpreter_output_id);
489 return;
490 } else if (bytes == -1) {
491 InterpreterFailed(w); /* Something bad happened */
492 return;
493 }
494 } else if (*source == gvw->ghostview.interpreter_error) {
495 bytes = read(gvw->ghostview.interpreter_error, buf, GV_BUFSIZ);
496 if (bytes == 0) { /* EOF occurred */
497 close(gvw->ghostview.interpreter_error);
498 gvw->ghostview.interpreter_error = -1;
499 XtRemoveInput(gvw->ghostview.interpreter_error_id);
500 return;
501 } else if (bytes == -1) {
502 InterpreterFailed(w); /* Something bad happened */
503 return;
504 }
505 }
506 if (bytes > 0) {
507 buf[bytes] = '\0';
508 XtCallCallbackList(w, gvw->ghostview.output_callback, (XtPointer) buf);
509 }
510 }
511
512 #endif /* VMS */
513
514 /* Register the type converter required for the PageOrientation. */
515 /* This routine is called exactly once. */
516 static void
517 ClassInitialize()
518 {
519 XtSetTypeConverter(XtRString, XtRPageOrientation,
520 XmuCvtStringToPageOrientation, NULL, 0,
521 XtCacheAll, NULL);
522 }
523
524 /* Get atoms needed to communicate with ghostscript. */
525 /* This routine is called once per display. */
526 static void
527 ClassPartInitialize(class)
528 WidgetClass class;
529 {
530 GhostviewWidgetClass gvc = (GhostviewWidgetClass)class;
531 gvc->ghostview_class.ghostview = XmuMakeAtom("GHOSTVIEW");
532 gvc->ghostview_class.next = XmuMakeAtom("NEXT");
533 gvc->ghostview_class.page = XmuMakeAtom("PAGE");
534 gvc->ghostview_class.done = XmuMakeAtom("DONE");
535 }
536
537 /* Initialize private state. */
538
539 static void
540 Initialize(request, new, args, num_args)
541 Widget request, new;
542 ArgList args; /* unused */
543 Cardinal *num_args; /* unused */
544 {
545 XGCValues values;
546 XtGCMask mask;
547 GhostviewWidget ngvw = (GhostviewWidget) new;
548 GhostviewWidget rgvw = (GhostviewWidget) request;
549
550 values.foreground = new->core.background_pixel;
551 mask = GCForeground;
552 ngvw->ghostview.gc = XtGetGC(new, mask, &values);
553 ngvw->ghostview.mwin = None;
554 ngvw->ghostview.disable_start = False;
555 ngvw->ghostview.interpreter_pid = -1;
556 ngvw->ghostview.input_buffer = NULL;
557 ngvw->ghostview.bytes_left = 0;
558 #ifndef VMS
559 ngvw->ghostview.input_buffer_ptr = NULL;
560 ngvw->ghostview.buffer_bytes_left = 0;
561 #endif
562 ngvw->ghostview.ps_input = NULL;
563 ngvw->ghostview.interpreter_input = -1;
564 ngvw->ghostview.interpreter_output = -1;
565 #ifndef VMS
566 ngvw->ghostview.interpreter_error = -1;
567 ngvw->ghostview.interpreter_input_id = None;
568 ngvw->ghostview.interpreter_output_id = None;
569 ngvw->ghostview.interpreter_error_id = None;
570 #else /* VMS */
571 memset(ngvw->ghostview.interpreter_input_iosb, 0, 8);
572 memset(ngvw->ghostview.interpreter_output_iosb, 0, 8);
573 ngvw->ghostview.output_buffer = NULL;
574 #endif /* VMS */
575 ngvw->ghostview.gs_width = 0;
576 ngvw->ghostview.gs_height = 0;
577 ngvw->ghostview.changed = False;
578 ngvw->ghostview.busy = False;
579
580 /* Compute window size */
581 Layout(new, (rgvw->core.width == 0), (rgvw->core.height == 0));
582 }
583
584 /* Create Window and start interpreter if needed */
585 static void
586 Realize(w, valueMask, attributes)
587 Widget w;
588 Mask *valueMask;
589 XSetWindowAttributes *attributes;
590 {
591 GhostviewWidget gvw = (GhostviewWidget) w;
592
593 if (gvw->ghostview.cursor != None) {
594 attributes->cursor = gvw->ghostview.cursor;
595 *valueMask |= CWCursor;
596 }
597
598 XtCreateWindow(w, (unsigned int) InputOutput, (Visual *) CopyFromParent,
599 *valueMask, attributes);
600
601 Setup(w);
602 }
603
604 /* Destroy routine: kill the interpreter and release the GC */
605 static void
606 Destroy(w)
607 Widget w;
608 {
609 GhostviewWidget gvw = (GhostviewWidget) w;
610
611 StopInterpreter(w);
612 XtReleaseGC(w, gvw->ghostview.gc);
613 if (gvw->ghostview.input_buffer) XtFree(gvw->ghostview.input_buffer);
614 #ifdef VMS
615 if (gvw->ghostview.output_buffer) XtFree(gvw->ghostview.output_buffer);
616 #endif VMS
617 if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
618 XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
619 }
620
621 /* Process resize request. Requested size cannot be changed.
622 * NOTE: This routine may be called before the widget is realized.
623 * (It was a surprise to me.)
624 * If the widget is realized, start a new interpreter by calling Setup().
625 * If Setup() actually started a new interpreter and it is taking input
626 * from stdin, send a refresh message to the application. This is the
627 * only way that the application can be notified that it needs to resend
628 * the input because someone forced a new window size on the widget.
629 */
630 static void
631 Resize(w)
632 Widget w;
633 {
634 Layout(w, False, False);
635 if (!XtIsRealized(w)) return;
636 if (Setup(w)) {
637 GhostviewWidget gvw = (GhostviewWidget) w;
638 if (gvw->ghostview.filename == NULL) {
639 XtCallCallbackList(w, gvw->ghostview.message_callback, "Refresh");
640 }
641 }
642 }
643
644 /* SetValues routine. Set new private state, based on changed values
645 * in the widget. Always returns False, because redisplay is never needed.
646 */
647 static Boolean
648 SetValues(current, request, new)
649 Widget current, request, new;
650 {
651 GhostviewWidget cgvw = (GhostviewWidget) current;
652 GhostviewWidget rgvw = (GhostviewWidget) request;
653 GhostviewWidget ngvw = (GhostviewWidget) new;
654 String cfilename;
655 String rfilename;
656 String cpreload;
657 String rpreload;
658
659 cfilename = cgvw->ghostview.filename;
660 if (cfilename == NULL) cfilename = "(null)";
661 rfilename = rgvw->ghostview.filename;
662 if (rfilename == NULL) rfilename = "(null)";
663 cpreload = cgvw->ghostview.preload;
664 if (cpreload == NULL) cpreload = "(null)";
665 rpreload = rgvw->ghostview.preload;
666 if (rpreload == NULL) rpreload = "(null)";
667
668 if (XtIsRealized(new) && !ngvw->ghostview.busy &&
669 (cgvw->ghostview.cursor != ngvw->ghostview.cursor)) {
670 XDefineCursor(XtDisplay(new), XtWindow(new), ngvw->ghostview.cursor);
671 }
672 if (XtIsRealized(new) && ngvw->ghostview.busy &&
673 (cgvw->ghostview.busy_cursor != ngvw->ghostview.busy_cursor)) {
674 XDefineCursor(XtDisplay(new), XtWindow(new),
675 ngvw->ghostview.busy_cursor);
676 }
677 if ((cgvw->core.width != rgvw->core.width) ||
678 (cgvw->core.height != rgvw->core.height) ||
679 strcmp(cgvw->ghostview.interpreter, rgvw->ghostview.interpreter) ||
680 strcmp(cpreload, rpreload) ||
681 (cgvw->ghostview.quiet != rgvw->ghostview.quiet) ||
682 strcmp(cfilename, rfilename) ||
683 (cgvw->ghostview.orientation != rgvw->ghostview.orientation) ||
684 (cgvw->ghostview.use_bpixmap != rgvw->ghostview.use_bpixmap) ||
685 (cgvw->ghostview.xdpi != rgvw->ghostview.xdpi) ||
686 (cgvw->ghostview.ydpi != rgvw->ghostview.ydpi) ||
687 (cgvw->ghostview.bottom_margin != rgvw->ghostview.bottom_margin) ||
688 (cgvw->ghostview.left_margin != rgvw->ghostview.left_margin) ||
689 (cgvw->ghostview.right_margin != rgvw->ghostview.right_margin) ||
690 (cgvw->ghostview.top_margin != rgvw->ghostview.top_margin) ||
691 (cgvw->ghostview.llx != rgvw->ghostview.llx) ||
692 (cgvw->ghostview.lly != rgvw->ghostview.lly) ||
693 (cgvw->ghostview.urx != rgvw->ghostview.urx) ||
694 (cgvw->ghostview.ury != rgvw->ghostview.ury)) {
695
696 ngvw->ghostview.changed = True;
697 Layout(new, True, True);
698 }
699
700 if (ngvw->ghostview.changed && XtIsRealized(current)) Setup(new);
701 return(False);
702 }
703
704 /* Function Name: QueryGeometry
705 * Description: This tells the parent what size we would like to be
706 * given certain constraints.
707 * Arguments: w - the widget.
708 * intended - what the parent intends to do with us.
709 * requested - what we want to happen.
710 */
711
712 static XtGeometryResult
713 QueryGeometry(w, intended, requested)
714 Widget w;
715 XtWidgetGeometry *intended, *requested;
716 {
717 Dimension new_width, new_height;
718 Boolean change, width_req, height_req;
719
720 width_req = intended->request_mode & CWWidth;
721 height_req = intended->request_mode & CWHeight;
722
723 if (width_req)
724 new_width = intended->width;
725 else
726 new_width = w->core.width;
727
728 if (height_req)
729 new_height = intended->height;
730 else
731 new_height = w->core.height;
732
733 requested->request_mode = 0;
734
735 /*
736 * We only care about our height and width.
737 */
738
739 if (!width_req && !height_req)
740 return(XtGeometryYes);
741
742 change = ComputeSize(w, !width_req, !height_req, &new_width, &new_height);
743
744 requested->request_mode |= CWWidth;
745 requested->width = new_width;
746 requested->request_mode |= CWHeight;
747 requested->height = new_height;
748
749 if (change)
750 return(XtGeometryAlmost);
751 return(XtGeometryYes);
752 }
753
754 /* Layout the widget. */
755
756 static void
757 Layout(w, xfree, yfree)
758 Widget w;
759 Boolean xfree, yfree;
760 {
761 Dimension width = w->core.width;
762 Dimension height = w->core.height;
763 Boolean different_size = ComputeSize(w, xfree, yfree, &width, &height);
764 if (different_size) ChangeSize(w, width, height);
765 }
766
767 /* Compute new size of window, sets xdpi and ydpi if necessary.
768 * returns True if new window size is different */
769 static Boolean
770 ComputeSize(w, xfree, yfree, width, height)
771 Widget w;
772 Boolean xfree, yfree; /* Am I allowed to change width or height */
773 Dimension *width, *height;
774 {
775 GhostviewWidget gvw = (GhostviewWidget) w;
776 Dimension new_width = *width;
777 Dimension new_height = *height;
778 float newxdpi, newydpi;
779 Boolean change;
780
781 if (xfree && yfree) {
782 /* width and height can be changed, calculate window size according */
783 /* to xpdi and ydpi */
784 switch (gvw->ghostview.orientation) {
785 case XtPageOrientationPortrait:
786 case XtPageOrientationUpsideDown:
787 new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
788 gvw->ghostview.xdpi + 0.5;
789 new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
790 gvw->ghostview.ydpi + 0.5;
791 break;
792 case XtPageOrientationLandscape:
793 case XtPageOrientationSeascape:
794 new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
795 gvw->ghostview.xdpi + 0.5;
796 new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
797 gvw->ghostview.ydpi + 0.5;
798 break;
799 }
800 } else if (xfree) {
801 /* height is fixed. Preserve aspect ratio by recomputing */
802 /* ydpi and xdpi */
803 switch (gvw->ghostview.orientation) {
804 case XtPageOrientationPortrait:
805 case XtPageOrientationUpsideDown:
806 newydpi = gvw->core.height * 72.0 /
807 (gvw->ghostview.ury - gvw->ghostview.lly);
808 newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
809 gvw->ghostview.xdpi = newxdpi;
810 gvw->ghostview.ydpi = newydpi;
811 new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
812 gvw->ghostview.xdpi + 0.5;
813 break;
814 case XtPageOrientationLandscape:
815 case XtPageOrientationSeascape:
816 newydpi = gvw->core.height * 72.0 /
817 (gvw->ghostview.urx - gvw->ghostview.llx);
818 newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
819 gvw->ghostview.xdpi = newxdpi;
820 gvw->ghostview.ydpi = newydpi;
821 new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
822 gvw->ghostview.xdpi + 0.5;
823 break;
824 }
825 } else if (yfree) {
826 /* width is fixed. Preserve aspect ratio by recomputing */
827 /* xdpi and ydpi */
828 switch (gvw->ghostview.orientation) {
829 case XtPageOrientationPortrait:
830 case XtPageOrientationUpsideDown:
831 newxdpi = gvw->core.width * 72.0 /
832 (gvw->ghostview.urx - gvw->ghostview.llx);
833 newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
834 gvw->ghostview.xdpi = newxdpi;
835 gvw->ghostview.ydpi = newydpi;
836 new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
837 gvw->ghostview.ydpi + 0.5;
838 break;
839 case XtPageOrientationLandscape:
840 case XtPageOrientationSeascape:
841 newxdpi = gvw->core.width * 72.0 /
842 (gvw->ghostview.ury - gvw->ghostview.lly);
843 newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
844 gvw->ghostview.xdpi = newxdpi;
845 gvw->ghostview.ydpi = newydpi;
846 new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
847 gvw->ghostview.ydpi + 0.5;
848 break;
849 }
850 } else {
851 /* height and width are fixed. Just have to live with it. */
852 switch (gvw->ghostview.orientation) {
853 case XtPageOrientationPortrait:
854 case XtPageOrientationUpsideDown:
855 gvw->ghostview.xdpi = gvw->core.width * 72.0 /
856 (gvw->ghostview.urx - gvw->ghostview.llx);
857 gvw->ghostview.ydpi = gvw->core.height * 72.0 /
858 (gvw->ghostview.ury - gvw->ghostview.lly);
859 break;
860 case XtPageOrientationLandscape:
861 case XtPageOrientationSeascape:
862 gvw->ghostview.xdpi = gvw->core.width * 72.0 /
863 (gvw->ghostview.ury - gvw->ghostview.lly);
864 gvw->ghostview.ydpi = gvw->core.height * 72.0 /
865 (gvw->ghostview.urx - gvw->ghostview.llx);
866 break;
867 }
868 }
869
870 change = (new_width != *width) || (new_height != *height);
871 *width = new_width;
872 *height = new_height;
873 return (change);
874 }
875
876 /* Function Name: ChangeSize.
877 * Description: Request a size change.
878 * Arguments: w - the widget to try change the size of.
879 */
880
881 static void
882 ChangeSize(w, width, height)
883 Widget w;
884 Dimension width, height;
885 {
886 XtWidgetGeometry request, reply;
887 Boolean changed = False;
888
889 request.request_mode = CWWidth | CWHeight;
890 request.width = width;
891 request.height = height;
892
893 switch ( XtMakeGeometryRequest(w, &request, &reply) ) {
894 case XtGeometryYes:
895 changed = True;
896 break;
897 case XtGeometryNo:
898 break;
899 case XtGeometryAlmost:
900 ComputeSize(w, (request.height != reply.height),
901 (request.width != reply.width),
902 &(reply.width), &(reply.height));
903 request = reply;
904 switch (XtMakeGeometryRequest(w, &request, &reply) ) {
905 case XtGeometryYes:
906 changed = True;
907 break;
908 case XtGeometryNo:
909 break;
910 case XtGeometryAlmost:
911 request = reply;
912 ComputeSize(w, FALSE, FALSE, &(request.width), &(request.height));
913 request.request_mode = CWWidth | CWHeight;
914 XtMakeGeometryRequest(w, &request, &reply);
915 changed = True;
916 break;
917 }
918 break;
919 }
920
921 /* If success, setup the widet for the new size. */
922 if (changed && XtIsRealized(w)) Setup(w);
923 }
924
925 /* Catch the alloc error when there is not enough resources for the
926 * backing pixmap. Automatically shut off backing pixmap and let the
927 * user know when this happens.
928 */
929 static Boolean alloc_error;
930 static XErrorHandler oldhandler;
931
932 static int
933 catch_alloc (dpy, err)
934 Display *dpy;
935 XErrorEvent *err;
936 {
937 if (err->error_code == BadAlloc) {
938 alloc_error = True;
939 }
940 if (alloc_error) return 0;
941 oldhandler(dpy, err);
942 }
943
944 /* Setup - sets up the backing pixmap, and GHOSTVIEW property and
945 * starts interpreter if needed.
946 * NOTE: the widget must be realized before calling Setup().
947 * Returns True if a new interpreter was started, False otherwise.
948 */
949
950 static Boolean
951 Setup(w)
952 Widget w;
953 {
954 GhostviewWidget gvw = (GhostviewWidget) w;
955 GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
956 char buf[GV_BUFSIZ];
957 Pixmap bpixmap;
958 XSetWindowAttributes xswa;
959
960 if (!gvw->ghostview.changed &&
961 (gvw->core.width == gvw->ghostview.gs_width) &&
962 (gvw->core.height == gvw->ghostview.gs_height)) return False;
963
964 StopInterpreter(w);
965
966 if ((gvw->core.width != gvw->ghostview.gs_width) ||
967 (gvw->core.height != gvw->ghostview.gs_height) ||
968 (!gvw->ghostview.use_bpixmap)) {
969 if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
970 XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
971 gvw->core.background_pixmap = XtUnspecifiedPixmap;
972 XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
973 }
974 }
975
976 if (gvw->ghostview.use_bpixmap) {
977 if (gvw->core.background_pixmap == XtUnspecifiedPixmap) {
978 /* Get a Backing Pixmap, but be ready for the BadAlloc. */
979 XSync(XtDisplay(w), False); /* Get to known state */
980 oldhandler = XSetErrorHandler(catch_alloc);
981 alloc_error = False;
982 bpixmap = XCreatePixmap(XtDisplay(w), XtWindow(w),
983 gvw->core.width, gvw->core.height,
984 gvw->core.depth);
985 XSync(XtDisplay(w), False); /* Force the error */
986 if (alloc_error) {
987 XtCallCallbackList(w, gvw->ghostview.message_callback,
988 "BadAlloc");
989 if (bpixmap != None) {
990 XFreePixmap(XtDisplay(w), bpixmap);
991 XSync(XtDisplay(w), False); /* Force the error */
992 bpixmap = None;
993 }
994 }
995 oldhandler = XSetErrorHandler(oldhandler);
996 if (bpixmap != None) {
997 gvw->core.background_pixmap = bpixmap;
998 XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w),
999 gvw->core.background_pixmap);
1000 }
1001 } else {
1002 bpixmap = gvw->core.background_pixmap;
1003 }
1004 } else {
1005 if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
1006 XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
1007 gvw->core.background_pixmap = XtUnspecifiedPixmap;
1008 XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
1009 }
1010 bpixmap = None;
1011 }
1012
1013 if (bpixmap != None) {
1014 xswa.backing_store = NotUseful;
1015 XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
1016 CWBackingStore, &xswa);
1017 } else {
1018 xswa.backing_store = Always;
1019 XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
1020 CWBackingStore, &xswa);
1021 }
1022
1023 gvw->ghostview.gs_width = gvw->core.width;
1024 gvw->ghostview.gs_height = gvw->core.height;
1025
1026 sprintf(buf, "%d %d %d %d %d %d %g %g %d %d %d %d",
1027 bpixmap, gvw->ghostview.orientation,
1028 gvw->ghostview.llx, gvw->ghostview.lly,
1029 gvw->ghostview.urx, gvw->ghostview.ury,
1030 gvw->ghostview.xdpi, gvw->ghostview.ydpi,
1031 gvw->ghostview.left_margin, gvw->ghostview.bottom_margin,
1032 gvw->ghostview.right_margin, gvw->ghostview.top_margin);
1033 XChangeProperty(XtDisplay(w), XtWindow(w),
1034 XmuInternAtom(XtDisplay(w), gvc->ghostview_class.ghostview),
1035 XA_STRING, 8, PropModeReplace,
1036 (unsigned char *)buf, strlen(buf));
1037
1038 StartInterpreter(w);
1039 return True;
1040 }
1041
1042 #ifndef VMS
1043
1044 /* This routine starts the interpreter. It sets the DISPLAY and
1045 * GHOSTVIEW environment variables. The GHOSTVIEW environment variable
1046 * contains the Window that ghostscript should write on.
1047 *
1048 * This routine also opens pipes for stdout and stderr and initializes
1049 * application input events for them. If input to ghostscript is not
1050 * from a file, a pipe for stdin is created. This pipe is setup for
1051 * non-blocking I/O so that the user interface never "hangs" because of
1052 * a write to ghostscript.
1053 */
1054 static void
1055 StartInterpreter(w)
1056 Widget w;
1057 {
1058 GhostviewWidget gvw = (GhostviewWidget) w;
1059 int std_in[2];
1060 int std_out[2];
1061 int std_err[2];
1062 char buf[GV_BUFSIZ];
1063 #define NUM_ARGS 100
1064 char *argv[NUM_ARGS];
1065 char *preload = NULL;
1066 char *cptr;
1067 int argc = 0;
1068 int ret;
1069
1070 StopInterpreter(w);
1071
1072 /* Clear the window before starting a new interpreter. */
1073 if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
1074 XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
1075 gvw->ghostview.gc,
1076 0, 0, gvw->core.width, gvw->core.height);
1077 }
1078 XClearArea(XtDisplay(w), XtWindow(w),
1079 0, 0, gvw->core.width, gvw->core.height, False);
1080
1081 if (gvw->ghostview.disable_start) return;
1082
1083 argv[argc++] = gvw->ghostview.interpreter;
1084 if (gvw->ghostview.quiet) argv[argc++] = "-dQUIET";
1085 argv[argc++] = "-dNOPAUSE";
1086 if (gvw->ghostview.preload) {
1087 cptr = preload = XtNewString(gvw->ghostview.preload);
1088 while (isspace(*cptr)) cptr++;
1089 while (*cptr) {
1090 argv[argc++] = cptr;
1091 while (*cptr && !isspace(*cptr)) cptr++;
1092 *cptr++ = '\0';
1093 if (argc + 2 >= NUM_ARGS) {
1094 fprintf(stderr, "Too many files to preload.\n");
1095 exit(1);
1096 }
1097 while (isspace(*cptr)) cptr++;
1098 }
1099 }
1100 argv[argc++] = "-";
1101 argv[argc++] = NULL;
1102
1103 if (gvw->ghostview.filename == NULL) {
1104 ret = pipe(std_in);
1105 if (ret == -1) {
1106 perror("Could not create pipe");
1107 exit(1);
1108 }
1109 } else if (strcmp(gvw->ghostview.filename, "-")) {
1110 std_in[0] = open(gvw->ghostview.filename, O_RDONLY, 0);
1111 }
1112 ret = pipe(std_out);
1113 if (ret == -1) {
1114 perror("Could not create pipe");
1115 exit(1);
1116 }
1117 ret = pipe(std_err);
1118 if (ret == -1) {
1119 perror("Could not create pipe");
1120 exit(1);
1121 }
1122
1123 gvw->ghostview.changed = False;
1124 gvw->ghostview.busy = True;
1125 XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
1126 #if defined(SYSV) || defined(USG)
1127 #define vfork fork
1128 #endif
1129 gvw->ghostview.interpreter_pid = vfork();
1130
1131 if (gvw->ghostview.interpreter_pid == 0) { /* child */
1132 close(std_out[0]);
1133 close(std_err[0]);
1134 dup2(std_out[1], 1);
1135 close(std_out[1]);
1136 dup2(std_err[1], 2);
1137 close(std_err[1]);
1138 sprintf(buf, "%d", XtWindow(w));
1139 setenv("GHOSTVIEW", buf, True);
1140 setenv("DISPLAY", XDisplayString(XtDisplay(w)), True);
1141 if (gvw->ghostview.filename == NULL) {
1142 close(std_in[1]);
1143 dup2(std_in[0], 0);
1144 close(std_in[0]);
1145 } else if (strcmp(gvw->ghostview.filename, "-")) {
1146 dup2(std_in[0], 0);
1147 close(std_in[0]);
1148 }
1149 execvp(argv[0], argv);
1150 sprintf(buf, "Exec of %s failed", argv[0]);
1151 perror(buf);
1152 _exit(1);
1153 } else {
1154 if (gvw->ghostview.filename == NULL) {
1155 #ifdef NON_BLOCKING_IO
1156 int result;
1157 #endif
1158 close(std_in[0]);
1159
1160 #ifdef NON_BLOCKING_IO
1161 result = fcntl(std_in[1], F_GETFL, 0);
1162 result = result | O_NONBLOCK;
1163 result = fcntl(std_in[1], F_SETFL, result);
1164 #endif
1165 gvw->ghostview.interpreter_input = std_in[1];
1166 gvw->ghostview.interpreter_input_id = None;
1167 } else if (strcmp(gvw->ghostview.filename, "-")) {
1168 close(std_in[0]);
1169 }
1170 close(std_out[1]);
1171 gvw->ghostview.interpreter_output = std_out[0];
1172 gvw->ghostview.interpreter_output_id =
1173 XtAppAddInput(XtWidgetToApplicationContext(w), std_out[0],
1174 (XtPointer)XtInputReadMask, Output, (XtPointer)w);
1175 close(std_err[1]);
1176 gvw->ghostview.interpreter_error = std_err[0];
1177 gvw->ghostview.interpreter_error_id =
1178 XtAppAddInput(XtWidgetToApplicationContext(w), std_err[0],
1179 (XtPointer)XtInputReadMask, Output, (XtPointer)w);
1180 }
1181 if (preload) XtFree(preload);
1182 }
1183
1184 /* Stop the interperter, if present, and remove any Input sources. */
1185 /* Also reset the busy state. */
1186 static void
1187 StopInterpreter(w)
1188 Widget w;
1189 {
1190 GhostviewWidget gvw = (GhostviewWidget) w;
1191 if (gvw->ghostview.interpreter_pid >= 0) {
1192 kill(gvw->ghostview.interpreter_pid, SIGTERM);
1193 wait(0);
1194 gvw->ghostview.interpreter_pid = -1;
1195 }
1196 if (gvw->ghostview.interpreter_input >= 0) {
1197 close(gvw->ghostview.interpreter_input);
1198 gvw->ghostview.interpreter_input = -1;
1199 if (gvw->ghostview.interpreter_input_id != None) {
1200 XtRemoveInput(gvw->ghostview.interpreter_input_id);
1201 gvw->ghostview.interpreter_input_id = None;
1202 }
1203 while (gvw->ghostview.ps_input) {
1204 struct record_list *ps_old = gvw->ghostview.ps_input;
1205 gvw->ghostview.ps_input = ps_old->next;
1206 if (ps_old->close) fclose(ps_old->fp);
1207 XtFree((char *)ps_old);
1208 }
1209 }
1210 if (gvw->ghostview.interpreter_output >= 0) {
1211 close(gvw->ghostview.interpreter_output);
1212 gvw->ghostview.interpreter_output = -1;
1213 XtRemoveInput(gvw->ghostview.interpreter_output_id);
1214 }
1215 if (gvw->ghostview.interpreter_error >= 0) {
1216 close(gvw->ghostview.interpreter_error);
1217 gvw->ghostview.interpreter_error = -1;
1218 XtRemoveInput(gvw->ghostview.interpreter_error_id);
1219 }
1220 gvw->ghostview.busy = False;
1221 XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
1222 }
1223
1224 #endif /* VMS */
1225
1226 /* The interpeter failed, Stop what's left and notify application */
1227 static void
1228 InterpreterFailed(w)
1229 Widget w;
1230 {
1231 GhostviewWidget gvw = (GhostviewWidget) w;
1232 StopInterpreter(w);
1233 XtCallCallbackList(w, gvw->ghostview.message_callback, "Failed");
1234 }
1235
1236 /*
1237 * Public Routines
1238 */
1239
1240 /* GhostviewDisableInterpreter:
1241 * Stop any interpreter and disable new ones from starting.
1242 */
1243 void
1244 GhostviewDisableInterpreter(w)
1245 Widget w;
1246 {
1247 GhostviewWidget gvw = (GhostviewWidget) w;
1248 gvw->ghostview.disable_start = True;
1249 if (XtIsRealized(w)) StopInterpreter(w);
1250 }
1251
1252 /* GhostviewDisableInterpreter:
1253 * Allow an interpreter to start and start one if the widget is
1254 * currently realized.
1255 */
1256 void
1257 GhostviewEnableInterpreter(w)
1258 Widget w;
1259 {
1260 GhostviewWidget gvw = (GhostviewWidget) w;
1261 gvw->ghostview.disable_start = False;
1262 if (XtIsRealized(w)) StartInterpreter(w);
1263 }
1264
1265 /* GhostviewIsInterpreterReady:
1266 * Returns True if the interpreter is ready for new input.
1267 */
1268 Boolean
1269 GhostviewIsInterpreterReady(w)
1270 Widget w;
1271 {
1272 GhostviewWidget gvw = (GhostviewWidget) w;
1273 return gvw->ghostview.interpreter_pid != -1 &&
1274 !gvw->ghostview.busy &&
1275 gvw->ghostview.ps_input == NULL;
1276 }
1277
1278 /* GhostviewIsInterpreterRunning:
1279 * Returns True if the interpreter is running.
1280 */
1281 Boolean
1282 GhostviewIsInterpreterRunning(w)
1283 Widget w;
1284 {
1285 GhostviewWidget gvw = (GhostviewWidget) w;
1286 return gvw->ghostview.interpreter_pid != -1;
1287 }
1288
1289 /* GhostviewGetBackingPixmap:
1290 * Returns the current backing pixmap.
1291 */
1292 Pixmap
1293 GhostviewGetBackingPixmap(w)
1294 Widget w;
1295 {
1296 GhostviewWidget gvw = (GhostviewWidget) w;
1297 if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
1298 return(gvw->core.background_pixmap);
1299 else
1300 return(None);
1301 }
1302
1303 #ifndef VMS
1304
1305 /* GhostviewSendPS:
1306 * Queue a portion of a PostScript file for output to ghostscript.
1307 * fp: FILE * of the file in question. NOTE: if you have several
1308 * Ghostview widgets reading from the same file. You must open
1309 * a unique FILE * for each widget.
1310 * SendPS does not actually send the PostScript, it merely queues it
1311 * for output.
1312 * begin: position in file (returned from ftell()) to start.
1313 * len: number of bytes to write.
1314 *
1315 * If an interpreter is not running, nothing is queued and
1316 * False is returned.
1317 */
1318 Boolean
1319 GhostviewSendPS(w, fp, begin, len, close)
1320 Widget w;
1321 FILE *fp;
1322 long begin;
1323 unsigned int len;
1324 Bool close;
1325 {
1326 GhostviewWidget gvw = (GhostviewWidget) w;
1327 struct record_list *ps_new;
1328
1329 if (gvw->ghostview.interpreter_input < 0) return False;
1330 ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
1331 ps_new->fp = fp;
1332 ps_new->begin = begin;
1333 ps_new->len = len;
1334 ps_new->seek_needed = True;
1335 ps_new->close = close;
1336 ps_new->next = NULL;
1337
1338 if (gvw->ghostview.input_buffer == NULL) {
1339 gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
1340 }
1341
1342 if (gvw->ghostview.ps_input == NULL) {
1343 gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
1344 gvw->ghostview.bytes_left = len;
1345 gvw->ghostview.buffer_bytes_left = 0;
1346 gvw->ghostview.ps_input = ps_new;
1347 gvw->ghostview.interpreter_input_id =
1348 XtAppAddInput(XtWidgetToApplicationContext(w),
1349 gvw->ghostview.interpreter_input,
1350 (XtPointer)XtInputWriteMask, Input, (XtPointer)w);
1351 } else {
1352 struct record_list *p = gvw->ghostview.ps_input;
1353 while (p->next != NULL) {
1354 p = p->next;
1355 }
1356 p->next = ps_new;
1357 }
1358 return True;
1359 }
1360
1361 #endif /* VMS */
1362
1363 /* GhostviewNextPage:
1364 * Tell ghostscript to start the next page.
1365 * Returns False if ghostscript is not running, or not ready to start
1366 * another page.
1367 * If another page is started. Sets the busy flag and cursor.
1368 */
1369 Boolean
1370 GhostviewNextPage(w)
1371 Widget w;
1372 {
1373 GhostviewWidget gvw = (GhostviewWidget) w;
1374 GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
1375 XEvent event;
1376
1377 if (gvw->ghostview.interpreter_pid < 0) return False;
1378 if (gvw->ghostview.mwin == None) return False;
1379
1380 if (!gvw->ghostview.busy) {
1381 gvw->ghostview.busy = True;
1382 XDefineCursor(XtDisplay(gvw), XtWindow(gvw),
1383 gvw->ghostview.busy_cursor);
1384
1385 event.xclient.type = ClientMessage;
1386 event.xclient.display = XtDisplay(w);
1387 event.xclient.window = gvw->ghostview.mwin;
1388 event.xclient.message_type =
1389 XmuInternAtom(XtDisplay(w), gvc->ghostview_class.next);
1390 event.xclient.format = 32;
1391 XSendEvent(XtDisplay(w), gvw->ghostview.mwin, False, 0, &event);
1392 return True;
1393 } else {
1394 return False;
1395 }
1396 }
1397
1398 #define done(type, value) \
1399 { \
1400 if (toVal->addr != NULL) { \
1401 if (toVal->size < sizeof(type)) { \
1402 toVal->size = sizeof(type); \
1403 return False; \
1404 } \
1405 *(type*)(toVal->addr) = (value); \
1406 } \
1407 else { \
1408 static type static_val; \
1409 static_val = (value); \
1410 toVal->addr = (XPointer)&static_val; \
1411 } \
1412 toVal->size = sizeof(type); \
1413 return True; \
1414 }
1415
1416 /* PageOrienation Conversion Routine.
1417 * Returns True if Conversion is successful.
1418 */
1419 Boolean
1420 XmuCvtStringToPageOrientation(dpy, args, num_args, fromVal, toVal, data)
1421 Display *dpy;
1422 XrmValue *args; /* unused */
1423 Cardinal *num_args; /* unused */
1424 XrmValue *fromVal;
1425 XrmValue *toVal;
1426 XtPointer *data; /* unused */
1427 {
1428 static XrmQuark XrmQEportrait;
1429 static XrmQuark XrmQElandscape;
1430 static XrmQuark XrmQEupsideDown;
1431 static XrmQuark XrmQEseascape;
1432 static int haveQuarks;
1433 XrmQuark q;
1434 char *str = (XPointer) fromVal->addr;
1435 char lowerName[1000];
1436
1437 if (str == NULL) return False;
1438
1439 if (!haveQuarks) {
1440 XrmQEportrait = XrmStringToQuark(XtEportrait);
1441 XrmQElandscape = XrmStringToQuark(XtElandscape);
1442 XrmQEupsideDown = XrmStringToQuark(XtEupsideDown);
1443 XrmQEseascape = XrmStringToQuark(XtEseascape);
1444 haveQuarks = 1;
1445 }
1446
1447 XmuCopyISOLatin1Lowered(lowerName, str);
1448
1449 q = XrmStringToQuark(lowerName);
1450
1451 if (q == XrmQEportrait)
1452 done(XtPageOrientation, XtPageOrientationPortrait);
1453 if (q == XrmQElandscape)
1454 done(XtPageOrientation, XtPageOrientationLandscape);
1455 if (q == XrmQEupsideDown)
1456 done(XtPageOrientation, XtPageOrientationUpsideDown);
1457 if (q == XrmQEseascape)
1458 done(XtPageOrientation, XtPageOrientationSeascape);
1459
1460 XtDisplayStringConversionWarning(dpy, str, XtRPageOrientation);
1461 return False;
1462 }
1463
1464 #ifdef VMS
1465
1466 /*
1467 ** VMS specific include files
1468 */
1469 #include <descrip.h>
1470 #include <ssdef.h>
1471 #include <clidef.h>
1472 #include <lnmdef.h>
1473 #include <iodef.h>
1474 #include <dvidef.h>
1475 #include "vms_types.h"
1476
1477 #define ERR_SIGNAL(s) if(!((s) & 1))lib$signal((s), 0, 0)
1478 #define XtEFN 23
1479
1480 struct g_l_i
1481 {
1482 GhostviewWidget w;
1483 struct g_l_i *next;
1484 };
1485
1486 typedef struct g_l_i GhostListItem, *GLI_p;
1487
1488 static GhostListItem glhead = {(GhostviewWidget) -1, NULL};
1489 static GLI_p GL = &glhead;
1490 static size_t GLI_Size = sizeof(GhostListItem);
1491 static XtInputId EventId;
1492
1493 /*
1494 ** This routine is passed to XtAppAddInput(). It is called whenever the event
1495 ** flag number XtEFN is set and the Xt main loop becomes idle. It clears the
1496 ** event flag and then scans all the ghostview widgets for completed I/O
1497 ** requests, processing each as they are found. We have to do them all because
1498 ** there is no way to have Xt dispatch them individually without a window of
1499 ** vulnerability that can cause missed events, or by using a separate event
1500 ** flag for each I/O stream. Event flags are, unfortunately, a limited
1501 ** resource.
1502 */
1503 static Boolean
1504 IOProcess()
1505 {
1506 GhostviewWidget gvw;
1507 GLI_p cur;
1508
1509 /*
1510 ** Before we process any I/O's, clear the event flag.
1511 */
1512 sys$clref(XtEFN);
1513 /*
1514 ** Scan all the ghostview widgets and check for completed I/O's
1515 */
1516 for(cur = GL->next; cur; cur = cur->next){
1517 /*
1518 ** Get the widget and check for I/O complete on either mailbox.
1519 */
1520 gvw = cur->w;
1521 if(gvw->ghostview.interpreter_input_iosb[0])Input(gvw);
1522 if(gvw->ghostview.interpreter_output_iosb[0])Output(gvw);
1523 }
1524 }
1525
1526 /*
1527 ** This is an AST routine. It is called asynchronously whenever one of our
1528 ** mailbox I/O's completes.
1529 */
1530 static void
1531 IOComplete(client_data)
1532 XtPointer client_data;
1533 {
1534 /*
1535 ** Set the event flag to tell Xt to call IOProcess.
1536 */
1537 sys$setef(XtEFN);
1538 }
1539
1540 static void
1541 GLInsert(w)
1542 GhostviewWidget w;
1543 {
1544 GLI_p new;
1545 int first;
1546
1547 /*
1548 ** Insert this widget after the list head
1549 */
1550 first = (GL->next == NULL);
1551 new = XtMalloc(GLI_Size);
1552 new->w = w;
1553 new->next = GL->next;
1554 GL->next = new;
1555 /*
1556 ** If this is the first item on the list, call XtAppAddInput()
1557 */
1558 if(first)EventId = XtAppAddInput(XtWidgetToApplicationContext(w), XtEFN, 0,
1559 IOProcess, 0);
1560 }
1561
1562 static void
1563 GLRemove(w)
1564 GhostviewWidget w;
1565 {
1566 GLI_p prev, cur;
1567 int last = 0;
1568
1569 /*
1570 ** Find and remove this widget from the list.
1571 */
1572 prev = GL;
1573 cur = prev->next;
1574 while(cur && cur->w != w){
1575 prev = cur;
1576 cur = cur->next;
1577 }
1578 if(cur){
1579 prev->next = cur->next;
1580 XtFree(cur);
1581 last = (GL->next == NULL);
1582 }
1583 /*
1584 ** If this was the last item on the list, call XtRemoveInput()
1585 */
1586 if(last)XtRemoveInput(EventId);
1587 }
1588
1589 /* Input - Feed data to ghostscript's stdin.
1590 * Write bytes to ghostscript using non-blocking I/O.
1591 * Also, pipe signals are caught during writing. The return
1592 * values are checked and the appropriate action is taken. I do
1593 * this at this low level, because it may not be appropriate for
1594 * SIGPIPE to be caught for the overall application.
1595 */
1596
1597 static void
1598 Input(gvw)
1599 GhostviewWidget gvw;
1600 {
1601 int stat, bbytes;
1602 char *ch;
1603
1604 /*
1605 ** Check for error on previous I/O.
1606 */
1607 stat = gvw->ghostview.interpreter_input_iosb[0];
1608 if(stat != SS$_NORMAL){
1609 InterpreterFailed(gvw);
1610 } else {
1611
1612 /* Get a new section if required */
1613
1614 if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
1615 struct record_list *ps_old = gvw->ghostview.ps_input;
1616 gvw->ghostview.ps_input = ps_old->next;
1617 if (ps_old->close) fclose(ps_old->fp);
1618 XtFree((char *)ps_old);
1619 }
1620 if(gvw->ghostview.ps_input){
1621 /* Have to seek at the beginning of each section */
1622 if (gvw->ghostview.ps_input->seek_needed) {
1623 if (gvw->ghostview.ps_input->len > 0)
1624 fseek(gvw->ghostview.ps_input->fp,
1625 gvw->ghostview.ps_input->begin, SEEK_SET);
1626 gvw->ghostview.ps_input->seek_needed = False;
1627 gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
1628 }
1629 /*
1630 ** Read a line from the file.
1631 */
1632 ch = fgets(gvw->ghostview.input_buffer, GV_BUFSIZ,
1633 gvw->ghostview.ps_input->fp);
1634 if(!ch){
1635 /*
1636 ** Error, EOF when there's supposed to be data left.
1637 */
1638 InterpreterFailed(gvw);
1639 } else {
1640 /*
1641 ** Write it to the mailbox.
1642 */
1643 bbytes = strlen(gvw->ghostview.input_buffer);
1644 gvw->ghostview.bytes_left -= bbytes;
1645 stat = sys$qio(0, (short)gvw->ghostview.interpreter_input,
1646 IO$_WRITEVBLK, &gvw->ghostview.interpreter_input_iosb,
1647 IOComplete, 0, gvw->ghostview.input_buffer, bbytes,
1648 0, 0, 0, 0);
1649 ERR_SIGNAL(stat);
1650 }
1651 }
1652 }
1653 }
1654
1655 /* Output - receive I/O from ghostscript's stdout and stderr.
1656 * Pass this to the application via the output_callback. */
1657 static void
1658 Output(gvw)
1659 GhostviewWidget gvw;
1660 {
1661 char buf[GV_BUFSIZ+1];
1662 int bytes, stat;
1663
1664 stat = gvw->ghostview.interpreter_output_iosb[0];
1665 bytes = gvw->ghostview.interpreter_output_iosb[1];
1666 if (stat == SS$_NORMAL) {
1667 /*
1668 ** Got a message. If line complete, pass to the output_callback.
1669 **
1670 ** HACK ALERT, if bytes is -1 nothing happens, but an I/O is queued.
1671 ** This is our first time code, since Xt doesn't queue the I/O for us
1672 ** under VMS, just watches for completion. In StartInterpreter We setup
1673 ** an IOSB with a success status and -1 bytes so Xt will call us the
1674 ** first time to get the I/O queued.
1675 */
1676 if (bytes == 0) {
1677 strcpy(buf, "\n");
1678 } else if (bytes == 1) {
1679 buf[0] = gvw->ghostview.output_buffer[0];
1680 buf[1] = '\0';
1681 } else if (bytes > 1) {
1682 /*
1683 ** Copy the message to a local buffer and pass it to the callback.
1684 */
1685 memcpy(buf, gvw->ghostview.output_buffer, bytes);
1686 buf[bytes] = '\0';
1687 }
1688 if(bytes >= 0)XtCallCallbackList(gvw, gvw->ghostview.output_callback,
1689 (XtPointer) buf);
1690 /*
1691 ** Queue a new read to the mailbox
1692 */
1693 stat = sys$qio(0, (short)gvw->ghostview.interpreter_output,
1694 IO$_READVBLK, &gvw->ghostview.interpreter_output_iosb, IOComplete,
1695 0, gvw->ghostview.output_buffer, GV_BUFSIZ, 0, 0, 0, 0);
1696 ERR_SIGNAL(stat);
1697 } else {
1698 InterpreterFailed(gvw); /* Something bad happened */
1699 }
1700 }
1701
1702 /* This routine starts the interpreter. It sets the DISPLAY and
1703 * GHOSTVIEW environment variables. The GHOSTVIEW environment variable
1704 * contains the Window that ghostscript should write on.
1705 *
1706 * This routine also opens pipes for stdout and stderr and initializes
1707 * application input events for them. If input to ghostscript is not
1708 * from a file, a pipe for stdin is created. This pipe is setup for
1709 * non-blocking I/O so that the user interface never "hangs" because of
1710 * a write to ghostscript.
1711 */
1712 static void
1713 StartInterpreter(w)
1714 Widget w;
1715 {
1716 GhostviewWidget gvw = (GhostviewWidget) w;
1717 char buf[GV_BUFSIZ];
1718 char cmd[512];
1719 int ret;
1720 short ch1, ch2;
1721 char in_mbx_name[65], out_mbx_name[65];
1722 long pid, nowait = CLI$M_NOWAIT;
1723 const $DESCRIPTOR(ghostview_desc, "GHOSTVIEW");
1724 const $DESCRIPTOR(display_desc, "DECW$DISPLAY");
1725 const $DESCRIPTOR(lnt_desc, "LNM$PROCESS");
1726 $DESCRIPTOR(in_desc, "");
1727 $DESCRIPTOR(out_desc, "");
1728 $DESCRIPTOR(lnm_desc, "");
1729 $DESCRIPTOR(cmd_desc, cmd);
1730 ITEM_LIST_3_T(gv_list, 1) = {{{0, LNM$_STRING, buf, NULL}}, 0};
1731 ITEM_LIST_3_T(dis_list, 1) = {{{0, LNM$_STRING, NULL, NULL}}, 0};
1732 ITEM_LIST_3_T(dvi_list, 1) = {{{64, DVI$_DEVNAM, NULL, NULL}}, 0};
1733 IOSB_GET_T dvi_iosb;
1734
1735 /*
1736 ** Stop interpreter if running
1737 */
1738 StopInterpreter(w);
1739 /*
1740 ** Clear the window before starting a new interpreter.
1741 */
1742 if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
1743 XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
1744 gvw->ghostview.gc,
1745 0, 0, gvw->core.width, gvw->core.height);
1746 }
1747 XClearArea(XtDisplay(w), XtWindow(w),
1748 0, 0, gvw->core.width, gvw->core.height, False);
1749 /*
1750 ** Check for disabled.
1751 */
1752 if (gvw->ghostview.disable_start) return;
1753 /*
1754 ** Build Ghostscript startup command
1755 */
1756 strcpy(cmd, gvw->ghostview.interpreter);
1757 strcat(cmd, " ");
1758 if (gvw->ghostview.quiet) strcat(cmd, "\"-dQUIET\" ");
1759 strcat(cmd, "\"-dNOPAUSE\" ");
1760 if (gvw->ghostview.preload) {
1761 strcat(cmd, gvw->ghostview.preload);
1762 strcat(cmd, " ");
1763 }
1764 strcat(cmd, "\"-\" ");
1765
1766 /*
1767 ** Determine input source.
1768 */
1769 if (gvw->ghostview.filename == NULL) {
1770 /*
1771 ** Create a mailbox to feed input to Ghostscript and get its name.
1772 */
1773 ret = sys$crembx(0, &ch1, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
1774 ERR_SIGNAL(ret);
1775 dvi_list.item[0].buffer_p = in_mbx_name;
1776 ret = sys$getdvi(0, ch1, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
1777 ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
1778 in_mbx_name[64] = '\0';
1779 in_desc.dsc$a_pointer = in_mbx_name;
1780 in_desc.dsc$w_length = strlen(in_mbx_name);
1781 } else {
1782 /*
1783 ** Set up file name to give Ghostscript as standard input.
1784 */
1785 in_desc.dsc$a_pointer = gvw->ghostview.filename;
1786 in_desc.dsc$w_length = strlen(gvw->ghostview.filename);
1787 }
1788 /*
1789 ** Create mailbox to receive Ghostscript's output
1790 */
1791 ret = sys$crembx(0, &ch2, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
1792 ERR_SIGNAL(ret);
1793 dvi_list.item[0].buffer_p = out_mbx_name;
1794 ret = sys$getdvi(0, ch2, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
1795 ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
1796 out_mbx_name[64] = '\0';
1797 out_desc.dsc$a_pointer = out_mbx_name;
1798 out_desc.dsc$w_length = strlen(out_mbx_name);
1799 /*
1800 ** Create GHOSTVIEW and DECW$DISPLAY logical names.
1801 **
1802 ** We use CRELNM rather than LIB$SET_LOGICAL because we want these to be
1803 ** user mode and go away when the program exits. It doesn't matter that we
1804 ** may set them multiple times, as with the mailbox logicals, since once
1805 ** Ghostscript starts we don't need them any more.
1806 */
1807 sprintf(buf, "%d", XtWindow(w));
1808 gv_list.item[0].buffer_size = strlen(buf);
1809 ret = sys$crelnm(0, &lnt_desc, &ghostview_desc, 0, &gv_list);
1810 ERR_SIGNAL(ret);
1811 dis_list.item[0].buffer_p = XDisplayString(XtDisplay(w));
1812 dis_list.item[0].buffer_size = strlen(dis_list.item[0].buffer_p);
1813 ret = sys$crelnm(0, &lnt_desc, &display_desc, 0, &dis_list);
1814 ERR_SIGNAL(ret);
1815 /*
1816 ** Spawn Ghostscript process
1817 */
1818 gvw->ghostview.changed = False;
1819 gvw->ghostview.busy = True;
1820 cmd_desc.dsc$w_length = strlen(cmd);
1821 ret = lib$spawn(&cmd_desc, &in_desc, &out_desc, &nowait, 0, &pid, 0, 0,
1822 0, 0, 0, 0, 0);
1823 ERR_SIGNAL(ret);
1824 XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
1825 /*
1826 ** Everything worked, initialize IOSBs and save info about interpretter.
1827 */
1828 gvw->ghostview.interpreter_pid = pid;
1829 if (gvw->ghostview.filename == NULL) {
1830 gvw->ghostview.interpreter_input = ch1;
1831 gvw->ghostview.interpreter_input_iosb[0] = 0;
1832 }
1833 gvw->ghostview.interpreter_output = ch2;
1834 if (gvw->ghostview.output_buffer == NULL) {
1835 gvw->ghostview.output_buffer = XtMalloc(GV_BUFSIZ);
1836 }
1837 GLInsert(gvw);
1838 /*
1839 ** Fake a completed I/O so Output will get called to queue the first I/O.
1840 */
1841 gvw->ghostview.interpreter_output_iosb[0] = SS$_NORMAL;
1842 gvw->ghostview.interpreter_output_iosb[1] = -1;
1843 IOComplete();
1844 }
1845
1846 /* Stop the interperter, if present, and remove any Input sources. */
1847 /* Also reset the busy state. */
1848 static void
1849 StopInterpreter(w)
1850 Widget w;
1851 {
1852 int ret;
1853
1854 GhostviewWidget gvw = (GhostviewWidget) w;
1855 if (gvw->ghostview.interpreter_pid >= 0) {
1856 ret = sys$delprc(&gvw->ghostview.interpreter_pid, 0);
1857 if(ret != SS$_NORMAL && ret != SS$_NONEXPR)lib$signal(ret, 0, 0);
1858 gvw->ghostview.interpreter_pid = -1;
1859 }
1860 if (gvw->ghostview.interpreter_input >= 0) {
1861 (void) sys$dassgn(gvw->ghostview.interpreter_input);
1862 gvw->ghostview.interpreter_input = -1;
1863 while (gvw->ghostview.ps_input) {
1864 struct record_list *ps_old = gvw->ghostview.ps_input;
1865 gvw->ghostview.ps_input = ps_old->next;
1866 if (ps_old->close) fclose(ps_old->fp);
1867 XtFree((char *)ps_old);
1868 }
1869 }
1870 if (gvw->ghostview.interpreter_output >= 0) {
1871 (void) sys$dassgn(gvw->ghostview.interpreter_output);
1872 gvw->ghostview.interpreter_output = -1;
1873 }
1874 gvw->ghostview.busy = False;
1875 XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
1876 GLRemove(gvw);
1877 }
1878
1879 /* GhostviewSendPS:
1880 * Queue a portion of a PostScript file for output to ghostscript.
1881 * fp: FILE * of the file in question. NOTE: if you have several
1882 * Ghostview widgets reading from the same file. You must open
1883 * a unique FILE * for each widget.
1884 * SendPS does not actually send the PostScript, it merely queues it
1885 * for output.
1886 * begin: position in file (returned from ftell()) to start.
1887 * len: number of bytes to write.
1888 *
1889 * If an interpreter is not running, nothing is queued and
1890 * False is returned.
1891 */
1892 Boolean
1893 GhostviewSendPS(w, fp, begin, len, close)
1894 Widget w;
1895 FILE *fp;
1896 long begin;
1897 unsigned int len;
1898 Bool close;
1899 {
1900 GhostviewWidget gvw = (GhostviewWidget) w;
1901 struct record_list *ps_new;
1902
1903 if (gvw->ghostview.interpreter_input < 0) return False;
1904 if(len != 0){
1905 ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
1906 ps_new->fp = fp;
1907 ps_new->begin = begin;
1908 ps_new->len = len;
1909 ps_new->seek_needed = True;
1910 ps_new->close = close;
1911 ps_new->next = NULL;
1912
1913 if (gvw->ghostview.input_buffer == NULL) {
1914 gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
1915 }
1916
1917 if (gvw->ghostview.ps_input == NULL) {
1918 gvw->ghostview.bytes_left = len;
1919 gvw->ghostview.ps_input = ps_new;
1920 /*
1921 ** Fake a completed I/O so Input will get called to queue the
1922 ** first I/O.
1923 */
1924 gvw->ghostview.interpreter_input_iosb[0] = SS$_NORMAL;
1925 gvw->ghostview.interpreter_input_iosb[1] = -1;
1926 IOComplete();
1927 } else {
1928 struct record_list *p = gvw->ghostview.ps_input;
1929 while (p->next != NULL) {
1930 p = p->next;
1931 }
1932 p->next = ps_new;
1933 }
1934 }
1935 return True;
1936 }
1937 #endif /* VMS */