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