diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2019-10-15 12:24:12 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2020-08-13 11:26:55 +0200 |
commit | e088156d5b620e5e639580dacf85c6dc13823c74 (patch) | |
tree | 57f5c025e203279944da512166c20bc0521d8ccd /psi/dpmain.c | |
download | ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2 ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip |
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'psi/dpmain.c')
-rw-r--r-- | psi/dpmain.c | 1085 |
1 files changed, 1085 insertions, 0 deletions
diff --git a/psi/dpmain.c b/psi/dpmain.c new file mode 100644 index 00000000..7cb72078 --- /dev/null +++ b/psi/dpmain.c @@ -0,0 +1,1085 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + + +/* Ghostscript DLL loader for OS/2 */ +/* For WINDOWCOMPAT (console mode) application */ + +/* Russell Lang 1996-06-05 */ + +/* Updated 2001-03-10 by rjl + * New DLL interface, uses display device. + * Uses same interface to gspmdrv.c as os2pm device. + */ + +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_WIN /* to get bits/pixel of display */ +#define INCL_GPI /* to get bits/pixel of display */ +#include <os2.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <io.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/select.h> +#include "gscdefs.h" +#define GS_REVISION gs_revision +#include "ierrors.h" +#include "iapi.h" +#include "gdevdsp.h" + +#define MAXSTR 256 +#define BITMAPINFO2_SIZE 40 +const char *szDllName = "GSDLL2.DLL"; +char start_string[] = "systemdict /start get exec\n"; +int debug = TRUE /* FALSE */; + +#define MIN_COMMIT 4096 /* memory is committed in these size chunks */ +#define ID_NAME "GSPMDRV_%u_%u" +#define SHARED_NAME "\\SHAREMEM\\%s" +#define SYNC_NAME "\\SEM32\\SYNC_%s" +#define MUTEX_NAME "\\SEM32\\MUTEX_%s" + +LONG display_planes; +LONG display_bitcount; +LONG display_hasPalMan; +ULONG os_version; + +/* main structure with info about the GS DLL */ +typedef struct tagGSDLL { + HMODULE hmodule; /* DLL module handle */ + PFN_gsapi_revision revision; + PFN_gsapi_new_instance new_instance; + PFN_gsapi_delete_instance delete_instance; + PFN_gsapi_set_stdio set_stdio; + PFN_gsapi_set_poll set_poll; + PFN_gsapi_set_display_callback set_display_callback; + PFN_gsapi_init_with_args init_with_args; + PFN_gsapi_run_string run_string; + PFN_gsapi_exit exit; + PFN_gsapi_set_arg_encoding set_arg_encoding; + PFN_gsapi_set_default_device_list set_default_device_list; + PFN_gsapi_get_default_device_list get_default_device_list; + PFN_gsapi_run_file run_file; + PFN_gsapi_run_string_begin run_string_begin; + PFN_gsapi_run_string_continue run_string_continue; + PFN_gsapi_run_string_end run_string_end; + PFN_gsapi_run_string_with_length run_string_with_length; + PFN_gsapi_set_param set_param; + PFN_gsapi_add_control_path add_control_path; + PFN_gsapi_remove_control_path remove_control_path; + PFN_gsapi_purge_control_paths purge_control_paths; + PFN_gsapi_activate_path_control activate_path_control; + PFN_gsapi_is_path_control_active is_path_control_active; + PFN_gsapi_add_fs add_fs; + PFN_gsapi_remove_fs remove_fs; +} GSDLL; + +GSDLL gsdll; +void *instance; +TID tid; + +void +gs_addmess(char *str) +{ + fputs(str, stdout); + fflush(stdout); +} + +/*********************************************************************/ +/* load and unload the Ghostscript DLL */ + +/* free GS DLL */ +/* This should only be called when gsdll_execute has returned */ +/* TRUE means no error */ +BOOL +gs_free_dll(void) +{ + char buf[MAXSTR]; + APIRET rc; + + if (gsdll.hmodule == (HMODULE) NULL) + return TRUE; + rc = DosFreeModule(gsdll.hmodule); + if (rc) { + gs_sprintf(buf, "DosFreeModule returns %d\n", rc); + gs_addmess(buf); + gs_sprintf(buf, "Unloaded GSDLL\n\n"); + gs_addmess(buf); + } + return !rc; +} + +void +gs_load_dll_cleanup(void) +{ + char buf[MAXSTR]; + + gs_free_dll(); +} + +/* load GS DLL if not already loaded */ +/* return TRUE if OK */ +BOOL +gs_load_dll(void) +{ + char buf[MAXSTR + 40]; + APIRET rc; + char *p; + int i; + const char *dllname; + PTIB pptib; + PPIB pppib; + char szExePath[MAXSTR]; + char fullname[1024]; + const char *shortname; + gsapi_revision_t rv; + + if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) { + fprintf(stdout, "Couldn't get pid, rc = \n", rc); + return FALSE; + } + /* get path to EXE */ + if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(szExePath), + szExePath)) != 0) { + fprintf(stdout, "Couldn't get module name, rc = %d\n", rc); + return FALSE; + } + if ((p = strrchr(szExePath, '\\')) != (char *)NULL) { + p++; + *p = '\0'; + } + dllname = szDllName; +#ifdef DEBUG + if (debug) { + gs_sprintf(buf, "Trying to load %s\n", dllname); + gs_addmess(buf); + } +#endif + memset(buf, 0, sizeof(buf)); + rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule); + if (rc) { + /* failed */ + /* try again, with path of EXE */ + if ((shortname = strrchr((char *)szDllName, '\\')) + == (const char *)NULL) + shortname = szDllName; + strcpy(fullname, szExePath); + if ((p = strrchr(fullname, '\\')) != (char *)NULL) + p++; + else + p = fullname; + *p = '\0'; + strcat(fullname, shortname); + dllname = fullname; +#ifdef DEBUG + if (debug) { + gs_sprintf(buf, "Trying to load %s\n", dllname); + gs_addmess(buf); + } +#endif + rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule); + if (rc) { + /* failed again */ + /* try once more, this time on system search path */ + dllname = shortname; +#ifdef DEBUG + if (debug) { + gs_sprintf(buf, "Trying to load %s\n", dllname); + gs_addmess(buf); + } +#endif + rc = DosLoadModule(buf, sizeof(buf), dllname, &gsdll.hmodule); + } + } + if (rc == 0) { +#ifdef DEBUG + if (debug) + gs_addmess("Loaded Ghostscript DLL\n"); +#endif + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_REVISION", + (PFN *) (&gsdll.revision))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_REVISION, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + /* check DLL version */ + if (gsdll.revision(&rv, sizeof(rv)) != 0) { + gs_sprintf(buf, "Unable to identify Ghostscript DLL revision - it must be newer than needed.\n"); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + + if (rv.revision != GS_REVISION) { + gs_sprintf(buf, "Wrong version of DLL found.\n Found version %ld\n Need version %ld\n", rv.revision, (long)GS_REVISION); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_NEW_INSTANCE", + (PFN *) (&gsdll.new_instance))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_NEW_INSTANCE, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_DELETE_INSTANCE", + (PFN *) (&gsdll.delete_instance))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_DELETE_INSTANCE, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_STDIO", + (PFN *) (&gsdll.set_stdio))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_SET_STDIO, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_DISPLAY_CALLBACK", + (PFN *) (&gsdll.set_display_callback))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_SET_DISPLAY_CALLBACK, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_SET_POLL", + (PFN *) (&gsdll.set_poll))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_SET_POLL, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, + "GSAPI_INIT_WITH_ARGS", + (PFN *) (&gsdll.init_with_args))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_INIT_WITH_ARGS, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_RUN_STRING", + (PFN *) (&gsdll.run_string))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_RUN_STRING, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + if ((rc = DosQueryProcAddr(gsdll.hmodule, 0, "GSAPI_EXIT", + (PFN *) (&gsdll.exit))) != 0) { + gs_sprintf(buf, "Can't find GSAPI_EXIT, rc = %d\n", rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + } else { + gs_sprintf(buf, "Can't load Ghostscript DLL %s \nDosLoadModule rc = %d\n", + szDllName, rc); + gs_addmess(buf); + gs_load_dll_cleanup(); + return FALSE; + } + return TRUE; +} + +/*********************************************************************/ +/* stdio functions */ + +static int +gsdll_stdin(void *instance, char *buf, int len) +{ + return read(fileno(stdin), buf, len); +} + +static int +gsdll_stdout(void *instance, const char *str, int len) +{ + fwrite(str, 1, len, stdout); + fflush(stdout); + return len; +} + +static int +gsdll_stderr(void *instance, const char *str, int len) +{ + fwrite(str, 1, len, stderr); + fflush(stderr); + return len; +} + +/*********************************************************************/ +/* display device */ + +/* +#define DISPLAY_DEBUG +*/ + +typedef struct IMAGE_S IMAGE; +struct IMAGE_S { + void *handle; + void *device; + PID pid; /* PID of our window (CMD.EXE) */ + HEV sync_event; /* tell gspmdrv to redraw window */ + HMTX bmp_mutex; /* protects access to bitmap */ + HQUEUE term_queue; /* notification that gspmdrv has finished */ + ULONG session_id; /* id of gspmdrv */ + PID process_id; /* of gspmdrv */ + + int width; + int height; + int raster; + int format; + + BOOL format_known; + + unsigned char *bitmap; + ULONG committed; + IMAGE *next; +}; + +IMAGE *first_image = NULL; +static IMAGE *image_find(void *handle, void *device); + +static IMAGE * +image_find(void *handle, void *device) +{ + IMAGE *img; + for (img = first_image; img!=0; img=img->next) { + if ((img->handle == handle) && (img->device == device)) + return img; + } + return NULL; +} + +/* start gspmdrv.exe */ +static int run_gspmdrv(IMAGE *img) +{ + int ccode; + PCHAR pdrvname = "gspmdrv.exe"; + CHAR error_message[256]; + CHAR term_queue_name[128]; + CHAR id[128]; + CHAR arg[1024]; + STARTDATA sdata; + APIRET rc; + PTIB pptib; + PPIB pppib; + CHAR progname[256]; + PCHAR tail; + +#ifdef DEBUG + if (debug) + fprintf(stdout, "run_gspmdrv: starting\n"); +#endif + gs_sprintf(id, ID_NAME, img->pid, (ULONG)img->device); + + /* Create termination queue - used to find out when gspmdrv terminates */ + gs_sprintf(term_queue_name, "\\QUEUES\\TERMQ_%s", id); + if (DosCreateQueue(&(img->term_queue), QUE_FIFO, term_queue_name)) { + fprintf(stdout, "run_gspmdrv: failed to create termination queue\n"); + return gs_error_limitcheck; + } + /* get full path to gsos2.exe and hence path to gspmdrv.exe */ + if ((rc = DosGetInfoBlocks(&pptib, &pppib)) != 0) { + fprintf(stdout, "run_gspmdrv: Couldn't get module handle, rc = %d\n", + rc); + return gs_error_limitcheck; + } + if ((rc = DosQueryModuleName(pppib->pib_hmte, sizeof(progname) - 1, + progname)) != 0) { + fprintf(stdout, "run_gspmdrv: Couldn't get module name, rc = %d\n", + rc); + return gs_error_limitcheck; + } + if ((tail = strrchr(progname, '\\')) != (PCHAR) NULL) { + tail++; + *tail = '\0'; + } else + tail = progname; + strcat(progname, pdrvname); + + /* Open the PM driver session gspmdrv.exe */ + /* arguments are: */ + /* (1) -d (display) option */ + /* (2) id string */ + gs_sprintf(arg, "-d %s", id); + + /* because gspmdrv.exe is a different EXE type to gs.exe, + * we must use start session not DosExecPgm() */ + sdata.Length = sizeof(sdata); + sdata.Related = SSF_RELATED_CHILD; /* to be a child */ + sdata.FgBg = SSF_FGBG_BACK; /* start in background */ + sdata.TraceOpt = 0; + sdata.PgmTitle = "Ghostscript PM driver session"; + sdata.PgmName = progname; + sdata.PgmInputs = arg; + sdata.TermQ = term_queue_name; + sdata.Environment = pppib->pib_pchenv; /* use Parent's environment */ + sdata.InheritOpt = 0; /* Can't inherit from parent because */ + /* different sesison type */ + sdata.SessionType = SSF_TYPE_DEFAULT; /* default is PM */ + sdata.IconFile = NULL; + sdata.PgmHandle = 0; + sdata.PgmControl = 0; + sdata.InitXPos = 0; + sdata.InitYPos = 0; + sdata.InitXSize = 0; + sdata.InitYSize = 0; + sdata.ObjectBuffer = error_message; + sdata.ObjectBuffLen = sizeof(error_message); + + rc = DosStartSession(&sdata, &img->session_id, &img->process_id); + if (rc == ERROR_FILE_NOT_FOUND) { + sdata.PgmName = pdrvname; + rc = DosStartSession(&sdata, &img->session_id, &img->process_id); + } + if (rc) { + fprintf(stdout, "run_gspmdrv: failed to run %s, rc = %d\n", + sdata.PgmName, rc); + fprintf(stdout, "run_gspmdrv: error_message: %s\n", error_message); + return gs_error_limitcheck; + } +#ifdef DEBUG + if (debug) + fprintf(stdout, "run_gspmdrv: returning\n"); +#endif + return 0; +} + +void +image_color(unsigned int format, int index, + unsigned char *r, unsigned char *g, unsigned char *b) +{ + switch (format & DISPLAY_COLORS_MASK) { + case DISPLAY_COLORS_NATIVE: + switch (format & DISPLAY_DEPTH_MASK) { + case DISPLAY_DEPTH_1: + *r = *g = *b = (index ? 0 : 255); + break; + case DISPLAY_DEPTH_4: + if (index == 7) + *r = *g = *b = 170; + else if (index == 8) + *r = *g = *b = 85; + else { + int one = index & 8 ? 255 : 128; + *r = (index & 4 ? one : 0); + *g = (index & 2 ? one : 0); + *b = (index & 1 ? one : 0); + } + break; + case DISPLAY_DEPTH_8: + /* palette of 96 colors */ + /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */ + if (index < 64) { + int one = 255 / 3; + *r = ((index & 0x30) >> 4) * one; + *g = ((index & 0x0c) >> 2) * one; + *b = (index & 0x03) * one; + } + else { + int val = index & 0x1f; + *r = *g = *b = (val << 3) + (val >> 2); + } + break; + } + break; + case DISPLAY_COLORS_GRAY: + switch (format & DISPLAY_DEPTH_MASK) { + case DISPLAY_DEPTH_1: + *r = *g = *b = (index ? 255 : 0); + break; + case DISPLAY_DEPTH_4: + *r = *g = *b = (unsigned char)((index<<4) + index); + break; + case DISPLAY_DEPTH_8: + *r = *g = *b = (unsigned char)index; + break; + } + break; + } +} + +static int image_palette_size(int format) +{ + int palsize = 0; + switch (format & DISPLAY_COLORS_MASK) { + case DISPLAY_COLORS_NATIVE: + switch (format & DISPLAY_DEPTH_MASK) { + case DISPLAY_DEPTH_1: + palsize = 2; + break; + case DISPLAY_DEPTH_4: + palsize = 16; + break; + case DISPLAY_DEPTH_8: + palsize = 96; + break; + } + break; + case DISPLAY_COLORS_GRAY: + switch (format & DISPLAY_DEPTH_MASK) { + case DISPLAY_DEPTH_1: + palsize = 2; + break; + case DISPLAY_DEPTH_4: + palsize = 16; + break; + case DISPLAY_DEPTH_8: + palsize = 256; + break; + } + break; + } + return palsize; +} + +/* New device has been opened */ +/* Tell user to use another device */ +int display_open(void *handle, void *device) +{ + APIRET rc; + IMAGE *img; + PTIB pptib; + PPIB pppib; + CHAR id[128]; + CHAR name[128]; + PBITMAPINFO2 bmi; + +#ifdef DISPLAY_DEBUG + if (debug) + fputc('o', stdout); + fprintf(stdout, "display_open(0x%x, 0x%x)\n", handle, device); +#endif + + if (first_image) { + /* gsos2.exe is a console application, and displays using + * gspmdrv.exe which is a PM application. To start + * gspmdrv.exe, DosStartSession is used with SSF_RELATED_CHILD. + * A process can have only one child session marked SSF_RELATED_CHILD. + * When we call DosStopSession for the second session, it will + * close, but it will not write to the termination queue. + * When we wait for the session to end by reading the + * termination queue, we wait forever. + * For this reason, multiple image windows are disabled + * for OS/2. + * To get around this, we would need to replace the current + * method of one gspmdrv.exe session per window, to having + * a new PM application which can display multiple windows + * within a single session. + */ + return gs_error_limitcheck; + } + + img = (IMAGE *)malloc(sizeof(IMAGE)); + if (img == NULL) + return gs_error_limitcheck; + memset(img, 0, sizeof(IMAGE)); + + /* add to list */ + img->next = first_image; + first_image = img; + + /* remember device and handle */ + img->handle = handle; + img->device = device; + + /* Derive ID from process ID */ + if (DosGetInfoBlocks(&pptib, &pppib)) { + fprintf(stdout, "\ndisplay_open: Couldn't get pid\n"); + return gs_error_limitcheck; + } + img->pid = pppib->pib_ulppid; /* use parent (CMD.EXE) pid */ + gs_sprintf(id, ID_NAME, img->pid, (ULONG) img->device); + + /* Create update event semaphore */ + gs_sprintf(name, SYNC_NAME, id); + if (DosCreateEventSem(name, &(img->sync_event), 0, FALSE)) { + fprintf(stdout, "display_open: failed to create event semaphore %s\n", name); + return gs_error_limitcheck; + } + /* Create mutex - used for preventing gspmdrv from accessing */ + /* bitmap while we are changing the bitmap size. Initially unowned. */ + gs_sprintf(name, MUTEX_NAME, id); + if (DosCreateMutexSem(name, &(img->bmp_mutex), 0, FALSE)) { + DosCloseEventSem(img->sync_event); + fprintf(stdout, "display_open: failed to create mutex semaphore %s\n", name); + return gs_error_limitcheck; + } + + /* Shared memory is common to all processes so we don't want to + * allocate too much. + */ + gs_sprintf(name, SHARED_NAME, id); + if (DosAllocSharedMem((PPVOID) & img->bitmap, name, + 13 * 1024 * 1024, PAG_READ | PAG_WRITE)) { + fprintf(stdout, "display_open: failed allocating shared BMP memory %s\n", name); + return gs_error_limitcheck; + } + + /* commit one page so there is enough storage for a */ + /* bitmap header and palette */ + if (DosSetMem(img->bitmap, MIN_COMMIT, PAG_COMMIT | PAG_DEFAULT)) { + DosFreeMem(img->bitmap); + fprintf(stdout, "display: failed committing BMP memory\n"); + return gs_error_limitcheck; + } + img->committed = MIN_COMMIT; + + /* write a zero pixel BMP */ + bmi = (PBITMAPINFO2) img->bitmap; + bmi->cbFix = BITMAPINFO2_SIZE; /* OS/2 2.0 and Windows 3.0 compatible */ + bmi->cx = 0; + bmi->cy = 0; + bmi->cPlanes = 1; + bmi->cBitCount = 24; + bmi->ulCompression = BCA_UNCOMP; + bmi->cbImage = 0; + bmi->cxResolution = 0; + bmi->cyResolution = 0; + bmi->cclrUsed = 0; + bmi->cclrImportant = 0; + + /* delay start of gspmdrv until size is known */ + +#ifdef DISPLAY_DEBUG + if (debug) + fputc('O', stdout); +#endif + return 0; +} + +int display_preclose(void *handle, void *device) +{ + IMAGE *img; + REQUESTDATA Request; + ULONG DataLength; + PVOID DataAddress; + PULONG QueueEntry; + BYTE ElemPriority; +#ifdef DISPLAY_DEBUG + if (debug) + fputc('l', stdout); + fprintf(stdout, "display_preclose(0x%x, 0x%x)\n", handle, device); +#endif + img = image_find(handle, device); + if (img) { + if (img->session_id) { + /* Close gspmdrv driver */ + DosStopSession(STOP_SESSION_SPECIFIED, img->session_id); + Request.pid = img->pid; + Request.ulData = 0; + /* wait for termination queue, queue is then closed */ + /* by session manager */ + DosReadQueue(img->term_queue, &Request, &DataLength, + &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL); + /* queue needs to be closed by us */ + DosCloseQueue(img->term_queue); + } + img->session_id = 0; + img->term_queue = 0; + + DosCloseEventSem(img->sync_event); + DosCloseMutexSem(img->bmp_mutex); + } +#ifdef DISPLAY_DEBUG + if (debug) + fputc('L', stdout); +#endif + return 0; +} + +int display_close(void *handle, void *device) +{ + IMAGE *img; +#ifdef DISPLAY_DEBUG + if (debug) + fputc('c', stdout); + fprintf(stdout, "display_close(0x%x, 0x%x)\n", handle, device); +#endif + img = image_find(handle, device); + if (img) { + /* gspmdrv was closed by display_preclose */ + /* release memory */ + DosFreeMem(img->bitmap); + img->bitmap = (unsigned char *)NULL; + img->committed = 0; + } +#ifdef DISPLAY_DEBUG + if (debug) + fputc('C', stdout); +#endif + return 0; +} + +int display_presize(void *handle, void *device, int width, int height, + int raster, unsigned int format) +{ + IMAGE *img; +#ifdef DISPLAY_DEBUG + if (debug) + fputc('r', stdout); + fprintf(stdout, "display_presize(0x%x 0x%x, %d, %d, %d, %d)\n", + handle, device, width, height, raster, format); +#endif + img = image_find(handle, device); + if (img) { + int color = format & DISPLAY_COLORS_MASK; + int depth = format & DISPLAY_DEPTH_MASK; + int alpha = format & DISPLAY_ALPHA_MASK; + img->format_known = FALSE; + if ( ((color == DISPLAY_COLORS_NATIVE) || + (color == DISPLAY_COLORS_GRAY)) + && + ((depth == DISPLAY_DEPTH_1) || + (depth == DISPLAY_DEPTH_4) || + (depth == DISPLAY_DEPTH_8)) ) + img->format_known = TRUE; + if ((color == DISPLAY_COLORS_RGB) && (depth == DISPLAY_DEPTH_8) && + (alpha == DISPLAY_ALPHA_NONE)) + img->format_known = TRUE; + if (!img->format_known) { + fprintf(stdout, "display_presize: format %d = 0x%x is unsupported\n", format, format); + return gs_error_limitcheck; + } + /* grab mutex to stop other thread using bitmap */ + DosRequestMutexSem(img->bmp_mutex, 120000); + /* remember parameters so we can figure out where to allocate bitmap */ + img->width = width; + img->height = height; + img->raster = raster; + img->format = format; + } +#ifdef DISPLAY_DEBUG + if (debug) + fputc('R', stdout); +#endif + return 0; +} + +int display_size(void *handle, void *device, int width, int height, + int raster, unsigned int format, unsigned char *pimage) +{ + IMAGE *img; + PBITMAPINFO2 bmi; + int nColors; + int i; +#ifdef DISPLAY_DEBUG + if (debug) + fputc('z', stdout); + fprintf(stdout, "display_size(0x%x 0x%x, %d, %d, %d, %ld, 0x%x)\n", + handle, device, width, height, raster, format, pimage); +#endif + img = image_find(handle, device); + if (img) { + if (!img->format_known) + return gs_error_limitcheck; + + img->width = width; + img->height = height; + img->raster = raster; + img->format = format; + /* write BMP header including palette */ + bmi = (PBITMAPINFO2) img->bitmap; + bmi->cbFix = BITMAPINFO2_SIZE; + bmi->cx = img->width; + bmi->cy = img->height; + bmi->cPlanes = 1; + bmi->cBitCount = 24; + bmi->ulCompression = BCA_UNCOMP; + bmi->cbImage = 0; + bmi->cxResolution = 0; + bmi->cyResolution = 0; + bmi->cclrUsed = bmi->cclrImportant = image_palette_size(format); + + switch (img->format & DISPLAY_DEPTH_MASK) { + default: + case DISPLAY_DEPTH_1: + bmi->cBitCount = 1; + break; + case DISPLAY_DEPTH_4: + bmi->cBitCount = 4; + break; + case DISPLAY_DEPTH_8: + if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_NATIVE) + bmi->cBitCount = 8; + else if ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_GRAY) + bmi->cBitCount = 8; + else + bmi->cBitCount = 24; + break; + } + + /* add palette if needed */ + nColors = bmi->cclrUsed; + if (nColors) { + unsigned char *p; + p = img->bitmap + BITMAPINFO2_SIZE; + for (i = 0; i < nColors; i++) { + image_color(img->format, i, p+2, p+1, p); + *(p+3) = 0; + p += 4; + } + } + + /* release mutex to allow other thread to use bitmap */ + DosReleaseMutexSem(img->bmp_mutex); + } +#ifdef DISPLAY_DEBUG + if (debug) { + fprintf(stdout, "\nBMP dump\n"); + fprintf(stdout, " bitmap=%lx\n", img->bitmap); + fprintf(stdout, " committed=%lx\n", img->committed); + fprintf(stdout, " cx=%d\n", bmi->cx); + fprintf(stdout, " cy=%d\n", bmi->cy); + fprintf(stdout, " cPlanes=%d\n", bmi->cPlanes); + fprintf(stdout, " cBitCount=%d\n", bmi->cBitCount); + fprintf(stdout, " ulCompression=%d\n", bmi->ulCompression); + fprintf(stdout, " cbImage=%d\n", bmi->cbImage); + fprintf(stdout, " cxResolution=%d\n", bmi->cxResolution); + fprintf(stdout, " cyResolution=%d\n", bmi->cyResolution); + fprintf(stdout, " cclrUsed=%d\n", bmi->cclrUsed); + fprintf(stdout, " cclrImportant=%d\n", bmi->cclrImportant); + } + if (debug) + fputc('Z', stdout); +#endif + return 0; +} + +int display_sync(void *handle, void *device) +{ + IMAGE *img; +#ifdef DISPLAY_DEBUG + if (debug) + fputc('s', stdout); + fprintf(stdout, "display_sync(0x%x, 0x%x)\n", handle, device); +#endif + img = image_find(handle, device); + if (img) { + if (!img->format_known) + return gs_error_limitcheck; + /* delay starting gspmdrv until display_size has been called */ + if (!img->session_id && (img->width != 0) && (img->height != 0)) + run_gspmdrv(img); + DosPostEventSem(img->sync_event); + } +#ifdef DISPLAY_DEBUG + if (debug) + fputc('S', stdout); +#endif + return 0; +} + +int display_page(void *handle, void *device, int copies, int flush) +{ +#ifdef DISPLAY_DEBUG + if (debug) + fputc('p', stdout); + fprintf(stdout, "display_page(0x%x, 0x%x, copies=%d, flush=%d)\n", + handle, device, copies, flush); +#endif + display_sync(handle, device); +#ifdef DISPLAY_DEBUG + if (debug) + fputc('P', stdout); +#endif + return 0; +} + +void *display_memalloc(void *handle, void *device, unsigned long size) +{ + IMAGE *img; + unsigned long needed; + unsigned long header; + APIRET rc; + void *mem = NULL; + +#ifdef DISPLAY_DEBUG + if (debug) + fputc('m', stdout); + fprintf(stdout, "display_memalloc(0x%x 0x%x %d)\n", + handle, device, size); +#endif + img = image_find(handle, device); + if (img) { + /* we don't actually allocate memory here, we only commit + * preallocated shared memory. + * First work out size of header + palette. + * We allocate space for the header and tell Ghostscript + * that the memory starts just after the header. + * We rely on the Ghostscript memory device placing the + * raster at the start of this memory and having a + * raster length the same as the length of a BMP row. + */ + header = BITMAPINFO2_SIZE + image_palette_size(img->format) * 4; + + /* Work out if we need to commit more */ + needed = (size + header + MIN_COMMIT - 1) & (~(MIN_COMMIT - 1)); + if (needed > img->committed) { + /* commit more memory */ + if (rc = DosSetMem(img->bitmap + img->committed, + needed - img->committed, + PAG_COMMIT | PAG_DEFAULT)) { + fprintf(stdout, "No memory in display_memalloc rc = %d\n", rc); + return NULL; + } + img->committed = needed; + } + mem = img->bitmap + header; + } +#ifdef DISPLAY_DEBUG + fprintf(stdout, " returning 0x%x\n", (int)mem); + if (debug) + fputc('M', stdout); +#endif + return mem; +} + +int display_memfree(void *handle, void *device, void *mem) +{ + /* we can't uncommit shared memory, so do nothing */ + /* memory will be released when device is closed */ +#ifdef DISPLAY_DEBUG + fprintf(stdout, "display_memfree(0x%x, 0x%x, 0x%x)\n", + handle, device, mem); +#endif +} + +int display_update(void *handle, void *device, + int x, int y, int w, int h) +{ + /* unneeded - we are running image window in a separate process */ + return 0; +} + +display_callback display = { + sizeof(display_callback), + DISPLAY_VERSION_MAJOR, + DISPLAY_VERSION_MINOR, + display_open, + display_preclose, + display_close, + display_presize, + display_size, + display_sync, + display_page, + display_update, + display_memalloc, + display_memfree +}; + +/*********************************************************************/ + +int +main(int argc, char *argv[]) +{ + int code, code1; + int exit_code; + int exit_status; + int nargc; + char **nargv; + char dformat[64]; + ULONG version[3]; + void *instance = NULL; + + if (DosQuerySysInfo(QSV_VERSION_MAJOR, QSV_VERSION_REVISION, + &version, sizeof(version))) + os_version = 201000; /* a guess */ + else + os_version = version[0] * 10000 + version[1] * 100 + version[2]; + + if (!gs_load_dll()) { + fprintf(stdout, "Can't load %s\n", szDllName); + return -1; + } + + /* insert -dDisplayFormat=XXXXX as first argument */ + { int format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | + DISPLAY_DEPTH_1 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST; + int depth; + HPS ps = WinGetPS(HWND_DESKTOP); + HDC hdc = GpiQueryDevice(ps); + DevQueryCaps(hdc, CAPS_COLOR_PLANES, 1, &display_planes); + DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1, &display_bitcount); + DevQueryCaps(hdc, CAPS_ADDITIONAL_GRAPHICS, 1, &display_hasPalMan); + display_hasPalMan &= CAPS_PALETTE_MANAGER; + depth = display_planes * display_bitcount; + if ((depth <= 8) && !display_hasPalMan) + depth = 24; /* disaster: limited colours and no palette */ + WinReleasePS(ps); + + if (depth > 8) + format = DISPLAY_COLORS_RGB | DISPLAY_ALPHA_NONE | + DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST; + else if (depth >= 8) + format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | + DISPLAY_DEPTH_8 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST; + else if (depth >= 4) + format = DISPLAY_COLORS_NATIVE | DISPLAY_ALPHA_NONE | + DISPLAY_DEPTH_4 | DISPLAY_LITTLEENDIAN | DISPLAY_BOTTOMFIRST; + gs_sprintf(dformat, "-dDisplayFormat=%d", format); + } + nargc = argc + 1; + +#ifdef DEBUG + if (debug) + fprintf(stdout, "%s\n", dformat); +#endif + nargc = argc + 1; + nargv = (char **)malloc(nargc * sizeof(char *)); + nargv[0] = argv[0]; + nargv[1] = dformat; + memcpy(&nargv[2], &argv[1], (argc-1) * sizeof(char *)); + + if ( (code = gsdll.new_instance(&instance, NULL)) == 0) { + gsdll.set_stdio(instance, gsdll_stdin, gsdll_stdout, gsdll_stderr); + gsdll.set_display_callback(instance, &display); + + code = gsdll.init_with_args(instance, nargc, nargv); + if (code == 0) + code = gsdll.run_string(instance, start_string, 0, &exit_code); + code1 = gsdll.exit(instance); + if (code == 0 || (code == gs_error_Quit && code1 != 0)) + code = code1; + + gsdll.delete_instance(instance); + } + + gs_free_dll(); + + free(nargv); + + exit_status = 0; + switch (code) { + case 0: + case gs_error_Info: + case gs_error_Quit: + break; + case gs_error_Fatal: + exit_status = 1; + break; + default: + exit_status = 255; + } + + return exit_status; +} |