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