1 /* Copyright (C) 2001-2012 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied,
8    modified or distributed except as expressly authorized under the terms
9    of the license contained in the file LICENSE in this distribution.
10 
11    Refer to licensing information at http://www.artifex.com or contact
12    Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134, San Rafael,
13    CA  94903, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 
17 /* Ghostscript DLL loader for OS/2 */
18 /* For WINDOWCOMPAT (console mode) application */
19 
20 /* Russell Lang  1996-06-05 */
21 
22 /* Updated 2001-03-10 by rjl
23  *  New DLL interface, uses display device.
24  *  Uses same interface to gspmdrv.c as os2pm device.
25  */
26 
27 #define INCL_DOS
28 #define INCL_DOSERRORS
29 #define INCL_WIN	/* to get bits/pixel of display */
30 #define INCL_GPI	/* to get bits/pixel of display */
31 #include <os2.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 #include <io.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <sys/select.h>
39 #include "gscdefs.h"
40 #define GS_REVISION gs_revision
41 #include "ierrors.h"
42 #include "iapi.h"
43 #include "gdevdsp.h"
44 
45 #define MAXSTR 256
46 #define BITMAPINFO2_SIZE 40
47 const char *szDllName = "GSDLL2.DLL";
48 char start_string[] = "systemdict /start get exec\n";
49 int debug = TRUE /* FALSE */;
50 
51 #define MIN_COMMIT 4096		/* memory is committed in these size chunks */
52 #define ID_NAME "GSPMDRV_%u_%u"
53 #define SHARED_NAME "\\SHAREMEM\\%s"
54 #define SYNC_NAME   "\\SEM32\\SYNC_%s"
55 #define MUTEX_NAME  "\\SEM32\\MUTEX_%s"
56 
57 LONG display_planes;
58 LONG display_bitcount;
59 LONG display_hasPalMan;
60 ULONG os_version;
61 
62 /* main structure with info about the GS DLL */
63 typedef struct tagGSDLL {
64         HMODULE hmodule;	/* DLL module handle */
65         PFN_gsapi_revision revision;
66         PFN_gsapi_new_instance new_instance;
67         PFN_gsapi_delete_instance delete_instance;
68         PFN_gsapi_set_stdio set_stdio;
69         PFN_gsapi_set_poll set_poll;
70         PFN_gsapi_set_display_callback set_display_callback;
71         PFN_gsapi_init_with_args init_with_args;
72         PFN_gsapi_run_string run_string;
73         PFN_gsapi_exit exit;
74 } GSDLL;
75 
76 GSDLL gsdll;
77 void *instance;
78 TID tid;
79 
80 void
gs_addmess(char * str)81 gs_addmess(char *str)
82 {
83     fputs(str, stdout);
84     fflush(stdout);
85 }
86 
87 /*********************************************************************/
88 /* load and unload the Ghostscript DLL */
89 
90 /* free GS DLL */
91 /* This should only be called when gsdll_execute has returned */
92 /* TRUE means no error */
93 BOOL
gs_free_dll(void)94 gs_free_dll(void)
95 {
96     char buf[MAXSTR];
97     APIRET rc;
98 
99     if (gsdll.hmodule == (HMODULE) NULL)
100         return TRUE;
101     rc = DosFreeModule(gsdll.hmodule);
102     if (rc) {
103         sprintf(buf, "DosFreeModule returns %d\n", rc);
104         gs_addmess(buf);
105         sprintf(buf, "Unloaded GSDLL\n\n");
106         gs_addmess(buf);
107     }
108     return !rc;
109 }
110 
111 void
gs_load_dll_cleanup(void)112 gs_load_dll_cleanup(void)
113 {
114     char buf[MAXSTR];
115 
116     gs_free_dll();
117 }
118 
119 /* load GS DLL if not already loaded */
120 /* return TRUE if OK */
121 BOOL
gs_load_dll(void)122 gs_load_dll(void)
123 {
124     char buf[MAXSTR + 40];
125     APIRET rc;
126     char *p;
127     int i;
128     const char *dllname;
129     PTIB pptib;
130     PPIB pppib;
131     char szExePath[MAXSTR];
132     char fullname[1024];
133     const char *shortname;
134     gsapi_revision_t rv;
135 
136     if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) {
137         fprintf(stdout, "Couldn't get pid, rc = \n", rc);
138         return FALSE;
139     }
140     /* get path to EXE */
141     if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(szExePath),
142         szExePath)) != 0) {
143         fprintf(stdout, "Couldn't get module name, rc = %d\n", rc);
144         return FALSE;
145     }
146     if ((p = strrchr(szExePath, '\\')) != (char *)NULL) {
147         p++;
148         *p = '\0';
149     }
150     dllname = szDllName;
151 #ifdef DEBUG
152     if (debug) {
153         sprintf(buf, "Trying to load %s\n", dllname);
154         gs_addmess(buf);
155     }
156 #endif
157     memset(buf, 0, sizeof(buf));
158     rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule);
159     if (rc) {
160         /* failed */
161         /* try again, with path of EXE */
162         if ((shortname = strrchr((char *)szDllName, '\\'))
163             == (const char *)NULL)
164             shortname = szDllName;
165         strcpy(fullname, szExePath);
166         if ((p = strrchr(fullname, '\\')) != (char *)NULL)
167             p++;
168         else
169             p = fullname;
170         *p = '\0';
171         strcat(fullname, shortname);
172         dllname = fullname;
173 #ifdef DEBUG
174         if (debug) {
175             sprintf(buf, "Trying to load %s\n", dllname);
176             gs_addmess(buf);
177         }
178 #endif
179         rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule);
180         if (rc) {
181             /* failed again */
182             /* try once more, this time on system search path */
183             dllname = shortname;
184 #ifdef DEBUG
185             if (debug) {
186                 sprintf(buf, "Trying to load %s\n", dllname);
187                 gs_addmess(buf);
188             }
189 #endif
190             rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule);
191         }
192     }
193     if (rc == 0) {
194 #ifdef DEBUG
195         if (debug)
196             gs_addmess("Loaded Ghostscript DLL\n");
197 #endif
198         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_REVISION",
199                 (PFN *) (&gsdll.revision))) != 0) {
200             sprintf(buf, "Can't find GSAPI_REVISION, rc = %d\n", rc);
201             gs_addmess(buf);
202             gs_load_dll_cleanup();
203             return FALSE;
204         }
205         /* check DLL version */
206         if (gsdll.revision(&rv, sizeof(rv)) != 0) {
207             sprintf(buf, "Unable to identify Ghostscript DLL revision - it must be newer than needed.\n");
208             gs_addmess(buf);
209             gs_load_dll_cleanup();
210             return FALSE;
211         }
212 
213         if (rv.revision != GS_REVISION) {
214             sprintf(buf, "Wrong version of DLL found.\n  Found version %ld\n  Need version  %ld\n", rv.revision, (long)GS_REVISION);
215             gs_addmess(buf);
216             gs_load_dll_cleanup();
217             return FALSE;
218         }
219 
220         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_NEW_INSTANCE",
221                 (PFN *) (&gsdll.new_instance))) != 0) {
222             sprintf(buf, "Can't find GSAPI_NEW_INSTANCE, rc = %d\n", rc);
223             gs_addmess(buf);
224             gs_load_dll_cleanup();
225             return FALSE;
226         }
227         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_DELETE_INSTANCE",
228                 (PFN *) (&gsdll.delete_instance))) != 0) {
229             sprintf(buf, "Can't find GSAPI_DELETE_INSTANCE, rc = %d\n", rc);
230             gs_addmess(buf);
231             gs_load_dll_cleanup();
232             return FALSE;
233         }
234         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_STDIO",
235                 (PFN *) (&gsdll.set_stdio))) != 0) {
236             sprintf(buf, "Can't find GSAPI_SET_STDIO, rc = %d\n", rc);
237             gs_addmess(buf);
238             gs_load_dll_cleanup();
239             return FALSE;
240         }
241         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_DISPLAY_CALLBACK",
242                 (PFN *) (&gsdll.set_display_callback))) != 0) {
243             sprintf(buf, "Can't find GSAPI_SET_DISPLAY_CALLBACK, rc = %d\n", rc);
244             gs_addmess(buf);
245             gs_load_dll_cleanup();
246             return FALSE;
247         }
248         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_POLL",
249                 (PFN *) (&gsdll.set_poll))) != 0) {
250             sprintf(buf, "Can't find GSAPI_SET_POLL, rc = %d\n", rc);
251             gs_addmess(buf);
252             gs_load_dll_cleanup();
253             return FALSE;
254         }
255         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0,
256                 "GSAPI_INIT_WITH_ARGS",
257                 (PFN *) (&gsdll.init_with_args))) != 0) {
258             sprintf(buf, "Can't find GSAPI_INIT_WITH_ARGS, rc = %d\n", rc);
259             gs_addmess(buf);
260             gs_load_dll_cleanup();
261             return FALSE;
262         }
263         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_RUN_STRING",
264                 (PFN *) (&gsdll.run_string))) != 0) {
265             sprintf(buf, "Can't find GSAPI_RUN_STRING, rc = %d\n", rc);
266             gs_addmess(buf);
267             gs_load_dll_cleanup();
268             return FALSE;
269         }
270         if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_EXIT",
271                 (PFN *) (&gsdll.exit))) != 0) {
272             sprintf(buf, "Can't find GSAPI_EXIT, rc = %d\n", rc);
273             gs_addmess(buf);
274             gs_load_dll_cleanup();
275             return FALSE;
276         }
277     } else {
278         sprintf(buf, "Can't load Ghostscript DLL %s \nDosLoadModule rc = %d\n",
279             szDllName, rc);
280         gs_addmess(buf);
281         gs_load_dll_cleanup();
282         return FALSE;
283     }
284     return TRUE;
285 }
286 
287 /*********************************************************************/
288 /* stdio functions */
289 
290 static int
gsdll_stdin(void * instance,char * buf,int len)291 gsdll_stdin(void *instance, char *buf, int len)
292 {
293     return read(fileno(stdin), buf, len);
294 }
295 
296 static int
gsdll_stdout(void * instance,const char * str,int len)297 gsdll_stdout(void *instance, const char *str, int len)
298 {
299     fwrite(str, 1, len, stdout);
300     fflush(stdout);
301     return len;
302 }
303 
304 static int
gsdll_stderr(void * instance,const char * str,int len)305 gsdll_stderr(void *instance, const char *str, int len)
306 {
307     fwrite(str, 1, len, stderr);
308     fflush(stderr);
309     return len;
310 }
311 
312 /*********************************************************************/
313 /* display device */
314 
315 /*
316 #define DISPLAY_DEBUG
317 */
318 
319 typedef struct IMAGE_S IMAGE;
320 struct IMAGE_S {
321     void *handle;
322     void *device;
323     PID pid;		/* PID of our window (CMD.EXE) */
324     HEV sync_event;	/* tell gspmdrv to redraw window */
325     HMTX bmp_mutex;	/* protects access to bitmap */
326     HQUEUE term_queue;	/* notification that gspmdrv has finished */
327     ULONG session_id;	/* id of gspmdrv */
328     PID process_id;	/* of gspmdrv */
329 
330     int width;
331     int height;
332     int raster;
333     int format;
334 
335     BOOL format_known;
336 
337     unsigned char *bitmap;
338     ULONG committed;
339     IMAGE *next;
340 };
341 
342 IMAGE *first_image = NULL;
343 static IMAGE *image_find(void *handle, void *device);
344 
345 static IMAGE *
image_find(void * handle,void * device)346 image_find(void *handle, void *device)
347 {
348     IMAGE *img;
349     for (img = first_image; img!=0; img=img->next) {
350         if ((img->handle == handle) && (img->device == device))
351             return img;
352     }
353     return NULL;
354 }
355 
356 /* start gspmdrv.exe */
run_gspmdrv(IMAGE * img)357 static int run_gspmdrv(IMAGE *img)
358 {
359     int ccode;
360     PCHAR pdrvname = "gspmdrv.exe";
361     CHAR error_message[256];
362     CHAR term_queue_name[128];
363     CHAR id[128];
364     CHAR arg[1024];
365     STARTDATA sdata;
366     APIRET rc;
367     PTIB pptib;
368     PPIB pppib;
369     CHAR progname[256];
370     PCHAR tail;
371 
372 #ifdef DEBUG
373     if (debug)
374         fprintf(stdout, "run_gspmdrv: starting\n");
375 #endif
376     sprintf(id, ID_NAME, img->pid, (ULONG)img->device);
377 
378     /* Create termination queue - used to find out when gspmdrv terminates */
379     sprintf(term_queue_name, "\\QUEUES\\TERMQ_%s", id);
380     if (DosCreateQueue(&(img->term_queue), QUE_FIFO, term_queue_name)) {
381         fprintf(stdout, "run_gspmdrv: failed to create termination queue\n");
382         return e_limitcheck;
383     }
384     /* get full path to gsos2.exe and hence path to gspmdrv.exe */
385     if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) {
386         fprintf(stdout, "run_gspmdrv: Couldn't get module handle, rc = %d\n",
387             rc);
388         return e_limitcheck;
389     }
390     if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(progname) - 1,
391         progname)) != 0) {
392         fprintf(stdout, "run_gspmdrv: Couldn't get module name, rc = %d\n",
393             rc);
394         return e_limitcheck;
395     }
396     if ((tail = strrchr(progname, '\\')) != (PCHAR) NULL) {
397         tail++;
398         *tail = '\0';
399     } else
400         tail = progname;
401     strcat(progname, pdrvname);
402 
403     /* Open the PM driver session gspmdrv.exe */
404     /* arguments are: */
405     /*  (1) -d (display) option */
406     /*  (2) id string */
407     sprintf(arg, "-d %s", id);
408 
409     /* because gspmdrv.exe is a different EXE type to gs.exe,
410      * we must use start session not DosExecPgm() */
411     sdata.Length = sizeof(sdata);
412     sdata.Related = SSF_RELATED_CHILD;	/* to be a child  */
413     sdata.FgBg = SSF_FGBG_BACK;	/* start in background */
414     sdata.TraceOpt = 0;
415     sdata.PgmTitle = "Ghostscript PM driver session";
416     sdata.PgmName = progname;
417     sdata.PgmInputs = arg;
418     sdata.TermQ = term_queue_name;
419     sdata.Environment = pppib->pib_pchenv;	/* use Parent's environment */
420     sdata.InheritOpt = 0;	/* Can't inherit from parent because */
421                                 /* different sesison type */
422     sdata.SessionType = SSF_TYPE_DEFAULT;	/* default is PM */
423     sdata.IconFile = NULL;
424     sdata.PgmHandle = 0;
425     sdata.PgmControl = 0;
426     sdata.InitXPos = 0;
427     sdata.InitYPos = 0;
428     sdata.InitXSize = 0;
429     sdata.InitYSize = 0;
430     sdata.ObjectBuffer = error_message;
431     sdata.ObjectBuffLen = sizeof(error_message);
432 
433     rc = DosStartSession(&sdata, &img->session_id, &img->process_id);
434     if (rc == ERROR_FILE_NOT_FOUND) {
435         sdata.PgmName = pdrvname;
436         rc = DosStartSession(&sdata, &img->session_id, &img->process_id);
437     }
438     if (rc) {
439         fprintf(stdout, "run_gspmdrv: failed to run %s, rc = %d\n",
440             sdata.PgmName, rc);
441         fprintf(stdout, "run_gspmdrv: error_message: %s\n", error_message);
442         return e_limitcheck;
443     }
444 #ifdef DEBUG
445     if (debug)
446         fprintf(stdout, "run_gspmdrv: returning\n");
447 #endif
448     return 0;
449 }
450 
451 void
image_color(unsigned int format,int index,unsigned char * r,unsigned char * g,unsigned char * b)452 image_color(unsigned int format, int index,
453     unsigned char *r, unsigned char *g, unsigned char *b)
454 {
455     switch (format & DISPLAY_COLORS_MASK) {
456         case DISPLAY_COLORS_NATIVE:
457             switch (format & DISPLAY_DEPTH_MASK) {
458                 case DISPLAY_DEPTH_1:
459                     *r = *g = *b = (index ? 0 : 255);
460                     break;
461                 case DISPLAY_DEPTH_4:
462                     if (index == 7)
463                         *r = *g = *b = 170;
464                     else if (index == 8)
465                         *r = *g = *b = 85;
466                     else {
467                         int one = index & 8 ? 255 : 128;
468                         *r = (index & 4 ? one : 0);
469                         *g = (index & 2 ? one : 0);
470                         *b = (index & 1 ? one : 0);
471                     }
472                     break;
473                 case DISPLAY_DEPTH_8:
474                     /* palette of 96 colors */
475                     /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
476                     if (index < 64) {
477                         int one = 255 / 3;
478                         *r = ((index & 0x30) >> 4) * one;
479                         *g = ((index & 0x0c) >> 2) * one;
480                         *b =  (index & 0x03) * one;
481                     }
482                     else {
483                         int val = index & 0x1f;
484                         *r = *g = *b = (val << 3) + (val >> 2);
485                     }
486                     break;
487             }
488             break;
489         case DISPLAY_COLORS_GRAY:
490             switch (format & DISPLAY_DEPTH_MASK) {
491                 case DISPLAY_DEPTH_1:
492                     *r = *g = *b = (index ? 255 : 0);
493                     break;
494                 case DISPLAY_DEPTH_4:
495                     *r = *g = *b = (unsigned char)((index<<4) + index);
496                     break;
497                 case DISPLAY_DEPTH_8:
498                     *r = *g = *b = (unsigned char)index;
499                     break;
500             }
501             break;
502     }
503 }
504 
image_palette_size(int format)505 static int image_palette_size(int format)
506 {
507     int palsize = 0;
508     switch (format & DISPLAY_COLORS_MASK) {
509         case DISPLAY_COLORS_NATIVE:
510             switch (format & DISPLAY_DEPTH_MASK) {
511                 case DISPLAY_DEPTH_1:
512                     palsize = 2;
513                     break;
514                 case DISPLAY_DEPTH_4:
515                     palsize = 16;
516                     break;
517                 case DISPLAY_DEPTH_8:
518                     palsize = 96;
519                     break;
520             }
521             break;
522         case DISPLAY_COLORS_GRAY:
523             switch (format & DISPLAY_DEPTH_MASK) {
524                 case DISPLAY_DEPTH_1:
525                     palsize = 2;
526                     break;
527                 case DISPLAY_DEPTH_4:
528                     palsize = 16;
529                     break;
530                 case DISPLAY_DEPTH_8:
531                     palsize = 256;
532                     break;
533             }
534             break;
535     }
536     return palsize;
537 }
538 
539 /* New device has been opened */
540 /* Tell user to use another device */
display_open(void * handle,void * device)541 int display_open(void *handle, void *device)
542 {
543     APIRET rc;
544     IMAGE *img;
545     PTIB pptib;
546     PPIB pppib;
547     CHAR id[128];
548     CHAR name[128];
549     PBITMAPINFO2 bmi;
550 
551 #ifdef DISPLAY_DEBUG
552     if (debug)
553         fputc('o', stdout);
554     fprintf(stdout, "display_open(0x%x, 0x%x)\n", handle, device);
555 #endif
556 
557     if (first_image) {
558         /* gsos2.exe is a console application, and displays using
559          * gspmdrv.exe which is a PM application.  To start
560          * gspmdrv.exe, DosStartSession is used with SSF_RELATED_CHILD.
561          * A process can have only one child session marked SSF_RELATED_CHILD.
562          * When we call DosStopSession for the second session, it will
563          * close, but it will not write to the termination queue.
564          * When we wait for the session to end by reading the
565          * termination queue, we wait forever.
566          * For this reason, multiple image windows are disabled
567          * for OS/2.
568          * To get around this, we would need to replace the current
569          * method of one gspmdrv.exe session per window, to having
570          * a new PM application which can display multiple windows
571          * within a single session.
572          */
573         return e_limitcheck;
574     }
575 
576     img = (IMAGE *)malloc(sizeof(IMAGE));
577     if (img == NULL)
578         return e_limitcheck;
579     memset(img, 0, sizeof(IMAGE));
580 
581     /* add to list */
582     img->next = first_image;
583     first_image = img;
584 
585     /* remember device and handle */
586     img->handle = handle;
587     img->device = device;
588 
589     /* Derive ID from process ID */
590     if (DosGetInfoBlocks(&pptib, &pppib)) {
591         fprintf(stdout, "\ndisplay_open: Couldn't get pid\n");
592         return e_limitcheck;
593     }
594     img->pid = pppib->pib_ulppid;	/* use parent (CMD.EXE) pid */
595     sprintf(id, ID_NAME, img->pid, (ULONG) img->device);
596 
597     /* Create update event semaphore */
598     sprintf(name, SYNC_NAME, id);
599     if (DosCreateEventSem(name, &(img->sync_event), 0, FALSE)) {
600         fprintf(stdout, "display_open: failed to create event semaphore %s\n", name);
601         return e_limitcheck;
602     }
603     /* Create mutex - used for preventing gspmdrv from accessing */
604     /* bitmap while we are changing the bitmap size. Initially unowned. */
605     sprintf(name, MUTEX_NAME, id);
606     if (DosCreateMutexSem(name, &(img->bmp_mutex), 0, FALSE)) {
607         DosCloseEventSem(img->sync_event);
608         fprintf(stdout, "display_open: failed to create mutex semaphore %s\n", name);
609         return e_limitcheck;
610     }
611 
612     /* Shared memory is common to all processes so we don't want to
613      * allocate too much.
614      */
615     sprintf(name, SHARED_NAME, id);
616     if (DosAllocSharedMem((PPVOID) & img->bitmap, name,
617                       13 * 1024 * 1024, PAG_READ | PAG_WRITE)) {
618         fprintf(stdout, "display_open: failed allocating shared BMP memory %s\n", name);
619         return e_limitcheck;
620     }
621 
622     /* commit one page so there is enough storage for a */
623     /* bitmap header and palette */
624     if (DosSetMem(img->bitmap, MIN_COMMIT, PAG_COMMIT | PAG_DEFAULT)) {
625         DosFreeMem(img->bitmap);
626         fprintf(stdout, "display: failed committing BMP memory\n");
627         return e_limitcheck;
628     }
629     img->committed = MIN_COMMIT;
630 
631     /* write a zero pixel BMP */
632     bmi = (PBITMAPINFO2) img->bitmap;
633     bmi->cbFix = BITMAPINFO2_SIZE; /* OS/2 2.0 and Windows 3.0 compatible */
634     bmi->cx = 0;
635     bmi->cy = 0;
636     bmi->cPlanes = 1;
637     bmi->cBitCount = 24;
638     bmi->ulCompression = BCA_UNCOMP;
639     bmi->cbImage = 0;
640     bmi->cxResolution = 0;
641     bmi->cyResolution = 0;
642     bmi->cclrUsed = 0;
643     bmi->cclrImportant = 0;
644 
645     /* delay start of gspmdrv until size is known */
646 
647 #ifdef DISPLAY_DEBUG
648     if (debug)
649         fputc('O', stdout);
650 #endif
651     return 0;
652 }
653 
display_preclose(void * handle,void * device)654 int display_preclose(void *handle, void *device)
655 {
656     IMAGE *img;
657     REQUESTDATA Request;
658     ULONG DataLength;
659     PVOID DataAddress;
660     PULONG QueueEntry;
661     BYTE ElemPriority;
662 #ifdef DISPLAY_DEBUG
663     if (debug)
664         fputc('l', stdout);
665     fprintf(stdout, "display_preclose(0x%x, 0x%x)\n", handle, device);
666 #endif
667     img = image_find(handle, device);
668     if (img) {
669         if (img->session_id) {
670             /* Close gspmdrv driver */
671             DosStopSession(STOP_SESSION_SPECIFIED, img->session_id);
672             Request.pid = img->pid;
673             Request.ulData = 0;
674             /* wait for termination queue, queue is then closed */
675             /* by session manager */
676             DosReadQueue(img->term_queue, &Request, &DataLength,
677                      &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL);
678             /* queue needs to be closed by us */
679             DosCloseQueue(img->term_queue);
680         }
681         img->session_id = 0;
682         img->term_queue = 0;
683 
684         DosCloseEventSem(img->sync_event);
685         DosCloseMutexSem(img->bmp_mutex);
686     }
687 #ifdef DISPLAY_DEBUG
688     if (debug)
689         fputc('L', stdout);
690 #endif
691     return 0;
692 }
693 
display_close(void * handle,void * device)694 int display_close(void *handle, void *device)
695 {
696     IMAGE *img;
697 #ifdef DISPLAY_DEBUG
698     if (debug)
699         fputc('c', stdout);
700     fprintf(stdout, "display_close(0x%x, 0x%x)\n", handle, device);
701 #endif
702     img = image_find(handle, device);
703     if (img) {
704         /* gspmdrv was closed by display_preclose */
705         /* release memory */
706         DosFreeMem(img->bitmap);
707         img->bitmap = (unsigned char *)NULL;
708         img->committed = 0;
709     }
710 #ifdef DISPLAY_DEBUG
711     if (debug)
712         fputc('C', stdout);
713 #endif
714     return 0;
715 }
716 
display_presize(void * handle,void * device,int width,int height,int raster,unsigned int format)717 int display_presize(void *handle, void *device, int width, int height,
718         int raster, unsigned int format)
719 {
720     IMAGE *img;
721 #ifdef DISPLAY_DEBUG
722     if (debug)
723         fputc('r', stdout);
724     fprintf(stdout, "display_presize(0x%x 0x%x, %d, %d, %d, %d)\n",
725         handle, device, width, height, raster, format);
726 #endif
727     img = image_find(handle, device);
728     if (img) {
729         int color = format & DISPLAY_COLORS_MASK;
730         int depth = format & DISPLAY_DEPTH_MASK;
731         int alpha = format & DISPLAY_ALPHA_MASK;
732         img->format_known = FALSE;
733         if ( ((color == DISPLAY_COLORS_NATIVE) ||
734               (color == DISPLAY_COLORS_GRAY))
735                  &&
736              ((depth == DISPLAY_DEPTH_1) ||
737               (depth == DISPLAY_DEPTH_4) ||
738               (depth == DISPLAY_DEPTH_8)) )
739             img->format_known = TRUE;
740         if ((color == DISPLAY_COLORS_RGB) && (depth == DISPLAY_DEPTH_8) &&
741             (alpha == DISPLAY_ALPHA_NONE))
742             img->format_known = TRUE;
743         if (!img->format_known) {
744             fprintf(stdout, "display_presize: format %d = 0x%x is unsupported\n", format, format);
745             return e_limitcheck;
746         }
747         /* grab mutex to stop other thread using bitmap */
748         DosRequestMutexSem(img->bmp_mutex, 120000);
749         /* remember parameters so we can figure out where to allocate bitmap */
750         img->width = width;
751         img->height = height;
752         img->raster = raster;
753         img->format = format;
754     }
755 #ifdef DISPLAY_DEBUG
756     if (debug)
757         fputc('R', stdout);
758 #endif
759     return 0;
760 }
761 
display_size(void * handle,void * device,int width,int height,int raster,unsigned int format,unsigned char * pimage)762 int display_size(void *handle, void *device, int width, int height,
763         int raster, unsigned int format, unsigned char *pimage)
764 {
765     IMAGE *img;
766     PBITMAPINFO2 bmi;
767     int nColors;
768     int i;
769 #ifdef DISPLAY_DEBUG
770     if (debug)
771         fputc('z', stdout);
772     fprintf(stdout, "display_size(0x%x 0x%x, %d, %d, %d, %d, %d, 0x%x)\n",
773         handle, device, width, height, raster, format, pimage);
774 #endif
775     img = image_find(handle, device);
776     if (img) {
777         if (!img->format_known)
778             return e_limitcheck;
779 
780         img->width = width;
781         img->height = height;
782         img->raster = raster;
783         img->format = format;
784         /* write BMP header including palette */
785         bmi = (PBITMAPINFO2) img->bitmap;
786         bmi->cbFix = BITMAPINFO2_SIZE;
787         bmi->cx = img->width;
788         bmi->cy = img->height;
789         bmi->cPlanes = 1;
790         bmi->cBitCount = 24;
791         bmi->ulCompression = BCA_UNCOMP;
792         bmi->cbImage = 0;
793         bmi->cxResolution = 0;
794         bmi->cyResolution = 0;
795         bmi->cclrUsed = bmi->cclrImportant = image_palette_size(format);
796 
797         switch (img->format & DISPLAY_DEPTH_MASK) {
798             default:
799             case DISPLAY_DEPTH_1:
800                 bmi->cBitCount = 1;
801                 break;
802             case DISPLAY_DEPTH_4:
803                 bmi->cBitCount = 4;
804                 break;
805             case DISPLAY_DEPTH_8:
806                 if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_NATIVE)
807                     bmi->cBitCount = 8;
808                 else if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_GRAY)
809                     bmi->cBitCount = 8;
810                 else
811                     bmi->cBitCount = 24;
812                 break;
813         }
814 
815         /* add palette if needed */
816         nColors = bmi->cclrUsed;
817         if (nColors) {
818             unsigned char *p;
819             p = img->bitmap + BITMAPINFO2_SIZE;
820             for (i = 0; i < nColors; i++) {
821                 image_color(img->format, i, p+2, p+1, p);
822                 *(p+3) = 0;
823                 p += 4;
824             }
825         }
826 
827         /* release mutex to allow other thread to use bitmap */
828         DosReleaseMutexSem(img->bmp_mutex);
829     }
830 #ifdef DISPLAY_DEBUG
831     if (debug) {
832         fprintf(stdout, "\nBMP dump\n");
833         fprintf(stdout, " bitmap=%lx\n", img->bitmap);
834         fprintf(stdout, " committed=%lx\n", img->committed);
835         fprintf(stdout, " cx=%d\n", bmi->cx);
836         fprintf(stdout, " cy=%d\n", bmi->cy);
837         fprintf(stdout, " cPlanes=%d\n", bmi->cPlanes);
838         fprintf(stdout, " cBitCount=%d\n", bmi->cBitCount);
839         fprintf(stdout, " ulCompression=%d\n", bmi->ulCompression);
840         fprintf(stdout, " cbImage=%d\n", bmi->cbImage);
841         fprintf(stdout, " cxResolution=%d\n", bmi->cxResolution);
842         fprintf(stdout, " cyResolution=%d\n", bmi->cyResolution);
843         fprintf(stdout, " cclrUsed=%d\n", bmi->cclrUsed);
844         fprintf(stdout, " cclrImportant=%d\n", bmi->cclrImportant);
845     }
846     if (debug)
847         fputc('Z', stdout);
848 #endif
849     return 0;
850 }
851 
display_sync(void * handle,void * device)852 int display_sync(void *handle, void *device)
853 {
854     IMAGE *img;
855 #ifdef DISPLAY_DEBUG
856     if (debug)
857         fputc('s', stdout);
858     fprintf(stdout, "display_sync(0x%x, 0x%x)\n", handle, device);
859 #endif
860     img = image_find(handle, device);
861     if (img) {
862         if (!img->format_known)
863             return e_limitcheck;
864         /* delay starting gspmdrv until display_size has been called */
865         if (!img->session_id && (img->width != 0) && (img->height != 0))
866            run_gspmdrv(img);
867         DosPostEventSem(img->sync_event);
868     }
869 #ifdef DISPLAY_DEBUG
870     if (debug)
871         fputc('S', stdout);
872 #endif
873     return 0;
874 }
875 
display_page(void * handle,void * device,int copies,int flush)876 int display_page(void *handle, void *device, int copies, int flush)
877 {
878 #ifdef DISPLAY_DEBUG
879     if (debug)
880         fputc('p', stdout);
881     fprintf(stdout, "display_page(0x%x, 0x%x, copies=%d, flush=%d)\n",
882         handle, device, copies, flush);
883 #endif
884     display_sync(handle, device);
885 #ifdef DISPLAY_DEBUG
886     if (debug)
887         fputc('P', stdout);
888 #endif
889     return 0;
890 }
891 
display_memalloc(void * handle,void * device,unsigned long size)892 void *display_memalloc(void *handle, void *device, unsigned long size)
893 {
894     IMAGE *img;
895     unsigned long needed;
896     unsigned long header;
897     APIRET rc;
898     void *mem = NULL;
899 
900 #ifdef DISPLAY_DEBUG
901     if (debug)
902         fputc('m', stdout);
903     fprintf(stdout, "display_memalloc(0x%x 0x%x %d)\n",
904         handle, device, size);
905 #endif
906     img = image_find(handle, device);
907     if (img) {
908         /* we don't actually allocate memory here, we only commit
909          * preallocated shared memory.
910          * First work out size of header + palette.
911          * We allocate space for the header and tell Ghostscript
912          * that the memory starts just after the header.
913          * We rely on the Ghostscript memory device placing the
914          * raster at the start of this memory and having a
915          * raster length the same as the length of a BMP row.
916          */
917         header = BITMAPINFO2_SIZE + image_palette_size(img->format) * 4;
918 
919         /* Work out if we need to commit more */
920         needed = (size + header + MIN_COMMIT - 1) & (~(MIN_COMMIT - 1));
921         if (needed > img->committed) {
922             /* commit more memory */
923             if (rc = DosSetMem(img->bitmap + img->committed,
924                            needed - img->committed,
925                            PAG_COMMIT | PAG_DEFAULT)) {
926                 fprintf(stdout, "No memory in display_memalloc rc = %d\n", rc);
927                 return NULL;
928             }
929             img->committed = needed;
930         }
931         mem = img->bitmap + header;
932     }
933 #ifdef DISPLAY_DEBUG
934     fprintf(stdout, "  returning 0x%x\n", (int)mem);
935     if (debug)
936         fputc('M', stdout);
937 #endif
938     return mem;
939 }
940 
display_memfree(void * handle,void * device,void * mem)941 int display_memfree(void *handle, void *device, void *mem)
942 {
943     /* we can't uncommit shared memory, so do nothing */
944     /* memory will be released when device is closed */
945 #ifdef DISPLAY_DEBUG
946     fprintf(stdout, "display_memfree(0x%x, 0x%x, 0x%x)\n",
947         handle, device, mem);
948 #endif
949 }
950 
display_update(void * handle,void * device,int x,int y,int w,int h)951 int display_update(void *handle, void *device,
952     int x, int y, int w, int h)
953 {
954     /* unneeded - we are running image window in a separate process */
955     return 0;
956 }
957 
958 display_callback display = {
959     sizeof(display_callback),
960     DISPLAY_VERSION_MAJOR,
961     DISPLAY_VERSION_MINOR,
962     display_open,
963     display_preclose,
964     display_close,
965     display_presize,
966     display_size,
967     display_sync,
968     display_page,
969     display_update,
970     display_memalloc,
971     display_memfree
972 };
973 
974 /*********************************************************************/
975 
976 int
main(int argc,char * argv[])977 main(int argc, char *argv[])
978 {
979     int code, code1;
980     int exit_code;
981     int exit_status;
982     int nargc;
983     char **nargv;
984     char dformat[64];
985     ULONG version[3];
986     void *instance;
987 
988     if (DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION,
989             &version, sizeof(version)))
990         os_version = 201000;	/* a guess */
991     else
992         os_version = version[0] * 10000 + version[1] * 100 + version[2];
993 
994     if (!gs_load_dll()) {
995         fprintf(stdout, "Can't load %s\n", szDllName);
996         return -1;
997     }
998 
999     /* insert -dDisplayFormat=XXXXX as first argument */
1000     {   int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
1001                 DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
1002         int depth;
1003         HPS ps = WinGetPS(HWND_DESKTOP);
1004         HDC hdc = GpiQueryDevice(ps);
1005         DevQueryCaps(hdc, CAPS_COLOR_PLANES, 1, &display_planes);
1006         DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1, &display_bitcount);
1007         DevQueryCaps(hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &display_hasPalMan);
1008         display_hasPalMan &= CAPS_PALETTE_MANAGER;
1009         depth = display_planes * display_bitcount;
1010         if ((depth <= 8) && !display_hasPalMan)
1011             depth = 24;		/* disaster: limited colours and no palette */
1012         WinReleasePS(ps);
1013 
1014         if (depth > 8)
1015             format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE |
1016                 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
1017         else if (depth >= 8)
1018             format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
1019                 DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
1020         else if (depth >= 4)
1021             format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE |
1022                 DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST;
1023         sprintf(dformat, "-dDisplayFormat=%d", format);
1024     }
1025     nargc = argc + 1;
1026 
1027 #ifdef DEBUG
1028     if (debug)
1029         fprintf(stdout, "%s\n", dformat);
1030 #endif
1031     nargc = argc + 1;
1032     nargv = (char **)malloc((nargc + 1) * sizeof(char *));
1033     nargv[0] = argv[0];
1034     nargv[1] = dformat;
1035     memcpy(&nargv[2], &argv[1], argc * sizeof(char *));
1036 
1037     if ( (code = gsdll.new_instance(&instance, NULL)) == 0) {
1038         gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr);
1039         gsdll.set_display_callback(instance, &display);
1040 
1041         code = gsdll.init_with_args(instance, nargc, nargv);
1042         if (code == 0)
1043             code = gsdll.run_string(instance, start_string, 0, &exit_code);
1044         code1 = gsdll.exit(instance);
1045         if (code == 0 || (code == e_Quit && code1 != 0))
1046             code = code1;
1047 
1048         gsdll.delete_instance(instance);
1049     }
1050 
1051     gs_free_dll();
1052 
1053     free(nargv);
1054 
1055     exit_status = 0;
1056     switch (code) {
1057         case 0:
1058         case e_Info:
1059         case e_Quit:
1060             break;
1061         case e_Fatal:
1062             exit_status = 1;
1063             break;
1064         default:
1065             exit_status = 255;
1066     }
1067 
1068     return exit_status;
1069 }
1070