/* Copyright (C) 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevpm.c,v 1.4.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* * OS/2 Presentation manager driver * * By Russell Lang (based on gdevmswn.c and gdevwdib.c) * * If Ghostscript is a PM application, stdin/stdout are not * provided and so no text window is available. * If Ghostscript is a windowed text application, a message queue * can't be created so a PM window for graphics can't be created. * The solution used here is to have two programs - gsos2.exe is a * text application and the outboard PM driver is gspmdrv.exe. * Another solution may be to make Ghostscript a PM application * and use VIO calls to provide a text window. * * If PM GSview starts Ghostscript, PM GSview displays the * bitmap instead of the PM driver (gspmdrv.exe). * * Since Ghostscript is not a PM application, this driver creates a * BMP bitmap in a named shared memory block and a second process * gspmdrv.exe reads this memory block and provides the PM window. * Communication to gspmdrv.exe is via the shared memory block * and semaphores. */ #define INCL_DOS #define INCL_DOSERRORS #define INCL_WINWINDOWMGR #define INCL_DEV #define INCL_GPIBITMAPS #include #include "string_.h" #include #include "gx.h" #include "gserrors.h" #include "gsexit.h" /* for gs_exit_status */ #include "gxdevice.h" #include "gp.h" #include "gpcheck.h" #include "gsparam.h" #include "gdevpccm.h" #include "gxdevmem.h" #include "gdevpm.h" #ifdef __DLL__ #include "gsdll.h" #include "gsdllos2.h" #endif #define MIN_COMMIT 4096 /* memory is committed in these size chunks */ #define ID_NAME "GSPMDRV_%u_%u" /* Initial values for width and height */ #define INITIAL_RESOLUTION 96 #define INITIAL_WIDTH (INITIAL_RESOLUTION * 85 / 10 + 1) #define INITIAL_HEIGHT (INITIAL_RESOLUTION * 11 + 1) /* A macro for casting the device argument */ #define pmdev ((gx_device_pm *)dev) #define pm_gsview_sizeof 80 typedef struct gx_device_pm_s gx_device_pm; #define gx_device_pm_common\ int BitsPerPixel;\ int UpdateInterval;\ char GSVIEW[pm_gsview_sizeof];\ BOOL dll;\ int nColors;\ BOOL updating;\ HTIMER update_timer;\ HEV sync_event;\ HEV next_event;\ HMTX bmp_mutex;\ HQUEUE drv_queue;\ HQUEUE term_queue;\ ULONG session_id;\ PID process_id;\ PID gspid;\ unsigned char *bitmap;\ ULONG committed;\ PBITMAPINFO2 bmi /* The device descriptor */ struct gx_device_pm_s { gx_device_common; gx_device_pm_common; gx_device_memory mdev; }; /* Device procedures */ /* See gxdevice.h for the definitions of the procedures. */ private dev_proc_open_device(pm_open); private dev_proc_get_initial_matrix(pm_get_initial_matrix); private dev_proc_sync_output(pm_sync_output); private dev_proc_output_page(pm_output_page); private dev_proc_close_device(pm_close); private dev_proc_map_rgb_color(pm_map_rgb_color); private dev_proc_map_color_rgb(pm_map_color_rgb); private dev_proc_fill_rectangle(pm_fill_rectangle); private dev_proc_copy_mono(pm_copy_mono); private dev_proc_copy_color(pm_copy_color); private dev_proc_get_bits(pm_get_bits); private dev_proc_get_params(pm_get_params); private dev_proc_put_params(pm_put_params); private gx_device_procs pm_procs = { pm_open, pm_get_initial_matrix, pm_sync_output, pm_output_page, pm_close, pm_map_rgb_color, pm_map_color_rgb, pm_fill_rectangle, NULL, /* tile rectangle */ pm_copy_mono, pm_copy_color, NULL, /* draw line */ pm_get_bits, pm_get_params, pm_put_params, NULL, /* map_cmyk_color */ gx_default_get_xfont_procs, NULL, /* get_xfont_device */ NULL, /* map_rgb_alpha_color */ gx_page_device_get_page_device }; #ifdef __DLL__ gx_device_pm far_data gs_os2dll_device = { std_device_std_body(gx_device_pm, &pm_procs, "os2dll", INITIAL_WIDTH, INITIAL_HEIGHT, INITIAL_RESOLUTION, INITIAL_RESOLUTION), {0}, /* std_procs */ 8, /* BitsPerPixel */ 5000, /* UpdateInterval */ "\0", /* GSVIEW */ 1 /* is DLL device */ }; #endif gx_device_pm far_data gs_os2pm_device = { std_device_std_body(gx_device_pm, &pm_procs, "os2pm", INITIAL_WIDTH, INITIAL_HEIGHT, INITIAL_RESOLUTION, INITIAL_RESOLUTION), {0}, /* std_procs */ 8, /* BitsPerPixel */ 5000, /* UpdateInterval */ "\0", /* GSVIEW */ 0 /* is not DLL device */ }; /* Compress a gx_color_value into an 8-bit PM color value, */ /* using only the high order 5 bits. */ #define pm_color_value(z)\ ((((z) >> (gx_color_value_bits - 5)) << 3) +\ ((z) >> (gx_color_value_bits - 3))) /* prototypes for internal procedures */ private void pm_makepalette(gx_device_pm *); private void pm_update(gx_device_pm *); private uint pm_set_bits_per_pixel(gx_device_pm *, int); private uint pm_palette_size(gx_device_pm *); private int pm_alloc_bitmap(gx_device_pm *, gx_device *); private int pm_run_gspmdrv(gx_device_pm *); private void pm_write_bmp(gx_device_pm *); /* Open the pm driver */ int pm_open(gx_device * dev) { int ccode; CHAR id[128]; CHAR name[128]; PTIB pptib; PPIB pppib; if (!pmdev->dll && (_osmode == DOS_MODE)) { eprintf("os2pm driver can't be used under DOS\n"); return gs_error_limitcheck; } if (DosGetInfoBlocks(&pptib, &pppib)) { eprintf("\npm_open: Couldn't get pid\n"); return gs_error_limitcheck; } #ifdef __DLL__ if (pppib->pib_ultype == 3) /* if caller is PM app */ pmdev->gspid = pppib->pib_ulpid; /* use caller pid */ else #endif pmdev->gspid = pppib->pib_ulppid; /* use parent (CMD.EXE) pid */ sprintf(id, ID_NAME, pmdev->gspid, (ULONG) dev); /* Allocate, but don't commit, enough memory for the largest */ /* possible bitmap (13Mbytes = A3 x 150dpi x 24bits) */ #ifdef __DLL__ if (pmdev->dll) { /* We don't need to use shared memory for the DLL */ if (DosAllocMem((PPVOID) & pmdev->bitmap, 13 * 1024 * 1024, PAG_READ | PAG_WRITE)) { eprintf("pm_open: failed allocating BMP memory\n"); return gs_error_limitcheck; } } else #endif { /* Shared memory is common to all processes so we don't want to allocate too much */ sprintf(name, SHARED_NAME, *pmdev->GSVIEW ? pmdev->GSVIEW : id); if (DosAllocSharedMem((PPVOID) & pmdev->bitmap, name, 13 * 1024 * 1024, PAG_READ | PAG_WRITE)) { eprintf1("pm_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(pmdev->bitmap, MIN_COMMIT, PAG_COMMIT | PAG_DEFAULT)) { DosFreeMem(pmdev->bitmap); eprintf("pm_open: failed committing BMP memory\n"); return gs_error_limitcheck; } pmdev->committed = MIN_COMMIT; if (pmdev->dll) { /* Create mutex - used for preventing another thread from accessing */ /* bitmap while we are changing the bitmap size. Initially unowned. */ sprintf(name, MUTEX_NAME, id); if (DosCreateMutexSem(name, &(pmdev->bmp_mutex), 0, FALSE)) { DosFreeMem(pmdev->bitmap); DosCloseEventSem(pmdev->sync_event); DosCloseQueue(pmdev->drv_queue); eprintf1("pm_open: failed to create mutex semaphore %s\n", name); return gs_error_limitcheck; } } else { if (*pmdev->GSVIEW) { APIRET rc; /* GSview has already created the necessary objects */ /* so we use Open instead of Create */ rc = 0; if (!rc) { sprintf(name, NEXT_NAME, pmdev->GSVIEW); rc = DosOpenEventSem(name, &pmdev->next_event); } if (!rc) { sprintf(name, MUTEX_NAME, pmdev->GSVIEW); rc = DosOpenMutexSem(name, &pmdev->bmp_mutex); } if (!rc) { PID owner_pid; sprintf(name, QUEUE_NAME, pmdev->GSVIEW); rc = DosOpenQueue(&owner_pid, &pmdev->drv_queue, name); } if (rc) { DosFreeMem(pmdev->bitmap); DosCloseEventSem(pmdev->next_event); eprintf2("pm_open: failed to open %s, rc = %u\n", name, rc); return gs_error_limitcheck; } } else { /* not GSVIEW */ /* Create update event semaphore */ sprintf(name, SYNC_NAME, id); if (DosCreateEventSem(name, &(pmdev->sync_event), 0, FALSE)) { DosFreeMem(pmdev->bitmap); eprintf1("pm_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. */ sprintf(name, MUTEX_NAME, id); if (DosCreateMutexSem(name, &(pmdev->bmp_mutex), 0, FALSE)) { DosFreeMem(pmdev->bitmap); DosCloseEventSem(pmdev->sync_event); DosCloseQueue(pmdev->drv_queue); eprintf1("pm_open: failed to create mutex semaphore %s\n", name); return gs_error_limitcheck; } } } if ((pm_set_bits_per_pixel(pmdev, pmdev->BitsPerPixel) < 0) || (gdev_mem_device_for_bits(dev->color_info.depth) == 0)) { if (!pmdev->dll) { if (*pmdev->GSVIEW) { DosCloseQueue(pmdev->drv_queue); DosCloseEventSem(pmdev->next_event); } else DosCloseEventSem(pmdev->sync_event); } DosCloseMutexSem(pmdev->bmp_mutex); DosFreeMem(pmdev->bitmap); return gs_error_limitcheck; } /* initialise bitmap header */ pmdev->bmi = (PBITMAPINFO2) pmdev->bitmap; pmdev->bmi->cbFix = 40; /* OS/2 2.0 and Windows 3.0 compatible */ pmdev->bmi->cx = dev->width; pmdev->bmi->cy = dev->height; pmdev->bmi->cPlanes = 1; pmdev->bmi->cBitCount = dev->color_info.depth; pmdev->bmi->ulCompression = BCA_UNCOMP; pmdev->bmi->cbImage = 0; pmdev->bmi->cxResolution = (ULONG) (dev->x_pixels_per_inch / 25.4 * 1000); pmdev->bmi->cyResolution = (ULONG) (dev->y_pixels_per_inch / 25.4 * 1000); if (pmdev->BitsPerPixel <= 8) { pmdev->bmi->cclrUsed = 1 << (pmdev->BitsPerPixel); pmdev->bmi->cclrImportant = pmdev->nColors; } else { pmdev->bmi->cclrUsed = 0; pmdev->bmi->cclrImportant = 0; } pm_makepalette(pmdev); /* commit pages */ ccode = pm_alloc_bitmap((gx_device_pm *) dev, dev); if (ccode < 0) { if (!pmdev->dll) { if (*pmdev->GSVIEW) { DosCloseQueue(pmdev->drv_queue); DosCloseEventSem(pmdev->next_event); } else DosCloseEventSem(pmdev->sync_event); } DosCloseMutexSem(pmdev->bmp_mutex); DosFreeMem(pmdev->bitmap); return ccode; } if (*pmdev->GSVIEW) return 0; /* GSview will handle displaying */ #ifdef __DLL__ if (pmdev->dll && pgsdll_callback) { /* notify caller about new device */ (*pgsdll_callback) (GSDLL_DEVICE, (unsigned char *)pmdev, 1); return 0; /* caller will handle displaying */ } #endif ccode = pm_run_gspmdrv(pmdev); if (ccode < 0) { DosFreeMem(pmdev->bitmap); DosCloseEventSem(pmdev->sync_event); DosCloseMutexSem(pmdev->bmp_mutex); } return ccode; } /* Get the initial matrix. BMPs, unlike most displays, */ /* put (0,0) in the lower left corner. */ private void pm_get_initial_matrix(gx_device * dev, gs_matrix * pmat) { pmat->xx = dev->x_pixels_per_inch / 72.0; pmat->xy = 0; pmat->yx = 0; pmat->yy = dev->y_pixels_per_inch / 72.0; pmat->tx = 0; pmat->ty = 0; if (*pmdev->GSVIEW) pm_update((gx_device_pm *) dev); /* let GSVIEW know we are drawing */ } /* Make the output appear on the screen. */ int pm_sync_output(gx_device * dev) { #ifdef __DLL__ if (pmdev->dll && pgsdll_callback) { (*pgsdll_callback) (GSDLL_SYNC, (unsigned char *)dev, 0); return 0; } #endif /* tell gspmdrv or GSview process to update display */ if (*pmdev->GSVIEW) { APIRET rc; rc = DosWriteQueue(pmdev->drv_queue, GS_SYNC, 0, NULL, 0); if (rc) eprintf1("pm_sync_output: DosWriteQueue error %d\n", rc); } else { if (pmdev->updating) DosStopTimer(pmdev->update_timer); DosPostEventSem(pmdev->sync_event); } pmdev->updating = FALSE; return (0); } /* Make the output appear on the screen */ /* and bring image window to foreground. */ private int pm_do_output_page(gx_device * dev, int copies, int flush) { int code; APIRET rc; #ifdef DEBUG pm_write_bmp(pmdev); #endif #ifdef __DLL__ if (pmdev->dll && pgsdll_callback) { (*pgsdll_callback) (GSDLL_PAGE, (unsigned char *)dev, 0); return 0; } #endif if (*pmdev->GSVIEW) { if (copies == -2) { rc = DosWriteQueue(pmdev->drv_queue, GS_END, 0, NULL, 0); if (rc) eprintf1("pm_output_page: DosWriteQueue error %d\n", rc); } else if (copies == -1) { rc = DosWriteQueue(pmdev->drv_queue, GS_BEGIN, 0, NULL, 0); if (rc) eprintf1("pm_output_page: DosWriteQueue error %d\n", rc); } else { ULONG count; pmdev->updating = FALSE; /* signal GSview that another page is ready */ rc = DosWriteQueue(pmdev->drv_queue, GS_PAGE, 0, NULL, 0); if (rc) eprintf1("pm_output_page: DosWriteQueue error %d\n", rc); /* wait for GSview to signal we can move on to next page */ DosWaitEventSem(pmdev->next_event, SEM_INDEFINITE_WAIT); DosResetEventSem(pmdev->next_event, &count); } code = 0; } else { code = pm_sync_output(dev); rc = DosSelectSession(pmdev->session_id); if (rc) { DosSleep(2000); /* give gspmdrv.exe a chance to run */ rc = DosSelectSession(pmdev->session_id); if (rc == ERROR_SMG_NO_TARGET_WINDOW) { DosSleep(5000); /* give gspmdrv.exe a chance to run */ rc = DosSelectSession(pmdev->session_id); /* try yet again */ } if ((rc == ERROR_SMG_SESSION_NOT_FOUND) || (rc == ERROR_SMG_INVALID_SESSION_ID)) { /* someone has killed the session */ REQUESTDATA Request; ULONG DataLength; PVOID DataAddress; PULONG QueueEntry; BYTE ElemPriority; /* Close gspmdrv driver */ DosStopSession(STOP_SESSION_SPECIFIED, pmdev->session_id); Request.pid = pmdev->gspid; Request.ulData = 0; /* wait for termination queue, queue is then closed by session manager */ DosReadQueue(pmdev->term_queue, &Request, &DataLength, &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL); DosCloseQueue(pmdev->term_queue); pmdev->term_queue = (HQUEUE) 0; /* restart it */ pm_run_gspmdrv(pmdev); DosSleep(2000); /* give gspmdrv.exe a chance to run */ rc = DosSelectSession(pmdev->session_id); } if (rc == ERROR_SMG_SESSION_NOT_FOREGRND) DosBeep(400, 50); else if (rc) eprintf1("pm_output_page: Select Session error code %u\n", rc); } } return code; } int pm_output_page(gx_device * dev, int copies, int flush) { int code = pm_do_output_page(dev, copies, flush); if (code >= 0) code = gx_finish_output_page(dev, copies, flush); return code; } /* Close the pm driver */ int pm_close(gx_device * dev) { APIRET rc; #ifdef __DLL__ if (pmdev->dll) { DosRequestMutexSem(pmdev->bmp_mutex, 60000); if (pgsdll_callback) (*pgsdll_callback) (GSDLL_DEVICE, (unsigned char *)dev, 0); DosReleaseMutexSem(pmdev->bmp_mutex); } else #endif { if (*pmdev->GSVIEW) { rc = DosWriteQueue(pmdev->drv_queue, GS_CLOSE, 0, NULL, 0); if (rc) eprintf1("pm_close: DosWriteQueue error %d\n", rc); } else { REQUESTDATA Request; ULONG DataLength; PVOID DataAddress; PULONG QueueEntry; BYTE ElemPriority; /* Close gspmdrv driver */ DosStopSession(STOP_SESSION_SPECIFIED, pmdev->session_id); Request.pid = pmdev->gspid; Request.ulData = 0; /* wait for termination queue, queue is then closed by session manager */ DosReadQueue(pmdev->term_queue, &Request, &DataLength, &DataAddress, 0, DCWW_WAIT, &ElemPriority, (HEV) NULL); /* queue needs to be closed by us */ DosCloseQueue(pmdev->term_queue); } } /* release memory */ DosFreeMem(pmdev->bitmap); pmdev->bitmap = (unsigned char *)NULL; pmdev->committed = 0; if (!pmdev->dll) { /* close objects */ if (*pmdev->GSVIEW) { DosCloseQueue(pmdev->drv_queue); DosCloseEventSem(pmdev->next_event); } else { DosCloseEventSem(pmdev->sync_event); /* stop update timer */ if (pmdev->updating) DosStopTimer(pmdev->update_timer); pmdev->updating = FALSE; } } DosCloseMutexSem(pmdev->bmp_mutex); return (0); } /* Map a r-g-b color to the colors available under PM */ gx_color_index pm_map_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g, gx_color_value b) { switch (dev->color_info.depth) { case 24: return ((b >> (gx_color_value_bits - 8)) << 16) + ((g >> (gx_color_value_bits - 8)) << 8) + ((r >> (gx_color_value_bits - 8))); case 8:{ int i; RGB2 *prgb; byte cr, cg, cb; /* map colors to 0->255 in 32 steps */ cr = pm_color_value(r); cg = pm_color_value(g); cb = pm_color_value(b); prgb = (RGB2 *) ((PBYTE) pmdev->bmi + pmdev->bmi->cbFix); /* search in palette */ for (i = 0; i < pmdev->nColors; i++, prgb++) { if (!((cr ^ prgb->bRed) & 0xf8) && !((cg ^ prgb->bGreen) & 0xf8) && !((cb ^ prgb->bBlue) & 0xf8) ) return ((gx_color_index) i); /* found it */ } /* next try adding it to palette */ if (i < 230) { /* allow 26 for PM and other apps */ prgb->bRed = cr; prgb->bGreen = cg; prgb->bBlue = cb; prgb->fcOptions = 0; pmdev->nColors = i + 1; pmdev->bmi->cclrImportant = pmdev->nColors; if (*pmdev->GSVIEW) { APIRET rc; rc = DosWriteQueue(pmdev->drv_queue, GS_PALCHANGE, 0, NULL, 0); if (rc) eprintf1("pm_sync_output: DosWriteQueue error %d\n", rc); } return ((gx_color_index) i); /* return new palette index */ } return (gx_no_color_index); /* not found - dither instead */ } case 4: if ((r == g) && (g == b) && (r >= gx_max_color_value / 3 * 2 - 1) && (r < gx_max_color_value / 4 * 3)) return ((gx_color_index) 8); /* light gray */ return pc_4bit_map_rgb_color(dev, r, g, b); } return (gx_default_map_rgb_color(dev, r, g, b)); } /* Map a color code to r-g-b. */ int pm_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value prgb[3]) { gx_color_value one; switch (dev->color_info.depth) { case 24: one = (gx_color_value) (gx_max_color_value / 255); prgb[0] = ((color) & 255) * one; prgb[1] = ((color >> 8) & 255) * one; prgb[2] = ((color >> 16) & 255) * one; break; case 8: if (!dev->is_open) return -1; { RGB2 *argb = (RGB2 *) ((PBYTE) pmdev->bmi + pmdev->bmi->cbFix); one = (gx_color_value) (gx_max_color_value / 255); prgb[0] = argb[(int)color].bRed * one; prgb[1] = argb[(int)color].bGreen * one; prgb[2] = argb[(int)color].bBlue * one; } break; case 4: if (color == 8) /* VGA light gray */ prgb[0] = prgb[1] = prgb[2] = (gx_max_color_value / 4 * 3); else pc_4bit_map_color_rgb(dev, color, prgb); break; default: prgb[0] = prgb[1] = prgb[2] = (int)color ? gx_max_color_value : 0; } return 0; } #define pmmdev ((gx_device *)&pmdev->mdev) #define pmmproc(proc) (*dev_proc(&pmdev->mdev, proc)) /* Fill a rectangle. */ private int pm_fill_rectangle(gx_device * dev, int x, int y, int w, int h, gx_color_index color) { pmmproc(fill_rectangle) (pmmdev, x, y, w, h, color); pm_update((gx_device_pm *) dev); return 0; } /* Copy a monochrome bitmap. The colors are given explicitly. */ /* Color = gx_no_color_index means transparent (no effect on the image). */ private int pm_copy_mono(gx_device * dev, const byte * base, int sourcex, int raster, gx_bitmap_id id, int x, int y, int w, int h, gx_color_index zero, gx_color_index one) { pmmproc(copy_mono) (pmmdev, base, sourcex, raster, id, x, y, w, h, zero, one); pm_update((gx_device_pm *) dev); return 0; } /* Copy a color pixel map. This is just like a bitmap, except that */ /* each pixel takes 8 or 4 bits instead of 1 when device driver has color. */ private int pm_copy_color(gx_device * dev, const byte * base, int sourcex, int raster, gx_bitmap_id id, int x, int y, int w, int h) { pmmproc(copy_color) (pmmdev, base, sourcex, raster, id, x, y, w, h); pm_update((gx_device_pm *) dev); return 0; } int pm_get_bits(gx_device * dev, int y, byte * str, byte ** actual_data) { return pmmproc(get_bits) (pmmdev, y, str, actual_data); } /* Get PM parameters */ int pm_get_params(gx_device * dev, gs_param_list * plist) { int code = gx_default_get_params(dev, plist); gs_param_string gvs; gvs.data = pmdev->GSVIEW, gvs.size = strlen(gvs.data), gvs.persistent = false; code < 0 || (code = param_write_int(plist, "UpdateInterval", &pmdev->UpdateInterval)) < 0 || (code = param_write_string(plist, "GSVIEW", &gvs)) < 0; return code; } /* Put parameters. */ /* Set PM parameters -- size and resolution. */ /* We implement this ourselves so that we can do it without */ /* closing and opening the device. */ /* Also set BitsPerPixel and GSVIEW if device not open */ int pm_put_params(gx_device * dev, gs_param_list * plist) { int ecode = 0, code; bool reopen = false; bool is_open = dev->is_open; int width = dev->width; int height = dev->height; int old_bpp = dev->color_info.depth; int bpp = old_bpp; int uii = pmdev->UpdateInterval; gs_param_string gsvs; /* Handle extra parameters */ switch (code = param_read_string(plist, "GSVIEW", &gsvs)) { case 0: if (gsvs.size == strlen(pmdev->GSVIEW) && !memcmp(pmdev->GSVIEW, gsvs.data, gsvs.size) ) { gsvs.data = 0; break; } if (dev->is_open) ecode = gs_error_rangecheck; else if (gsvs.size >= pm_gsview_sizeof) ecode = gs_error_limitcheck; else break; goto gsve; default: ecode = code; gsve:param_signal_error(plist, "GSVIEW", ecode); case 1: gsvs.data = 0; break; } switch (code = param_read_int(plist, "UpdateInterval", &uii)) { case 0: if (uii < 0) ecode = gs_error_rangecheck; else break; goto uie; default: ecode = code; uie:param_signal_error(plist, "UpdateInterval", ecode); case 1: break; } switch (code = param_read_int(plist, "BitsPerPixel", &bpp)) { case 0: if (dev->is_open && bpp != old_bpp) ecode = gs_error_rangecheck; else { code = pm_set_bits_per_pixel(pmdev, bpp); if (code < 0) ecode = code; else break; } goto bppe; default: ecode = code; bppe:param_signal_error(plist, "BitsPerPixel", ecode); case 1: break; } if (ecode >= 0) { /* Prevent gx_default_put_params from closing the device. */ dev->is_open = false; ecode = gx_default_put_params(dev, plist); dev->is_open = is_open; } if (ecode < 0) { if (bpp != old_bpp) pm_set_bits_per_pixel(pmdev, old_bpp); return ecode; } /* Hand off the change to the implementation. */ /* obtain mutex - to prevent gspmdrv from using bitmap */ /* while we change its size */ if (DosRequestMutexSem(pmdev->bmp_mutex, 20000) == ERROR_TIMEOUT) eprintf("pm_put_params: mutex timeout\n"); if (is_open && (old_bpp != bpp || dev->width != width || dev->height != height) ) { int ccode; ccode = pm_alloc_bitmap(pmdev, dev); if (ccode < 0) { /* Bad news! Some of the other device parameters */ /* may have changed. We don't handle this. */ /* This is ****** WRONG ******. */ dev->width = width; dev->height = height; pm_set_bits_per_pixel(pmdev, old_bpp); pm_alloc_bitmap(pmdev, dev); DosReleaseMutexSem(pmdev->bmp_mutex); return ccode; } reopen = true; } pmdev->UpdateInterval = uii; if (gsvs.data != 0) { memcpy(pmdev->GSVIEW, gsvs.data, gsvs.size); pmdev->GSVIEW[gsvs.size] = 0; } if (dev->is_open && reopen) { /* need to update bitmap info header also */ pmdev->bmi->cx = dev->width; pmdev->bmi->cy = dev->height; /* update bit count and palette */ pmdev->bmi->cBitCount = dev->color_info.depth; pmdev->bmi->cclrUsed = 1 << (pmdev->BitsPerPixel); pmdev->bmi->cclrImportant = pmdev->nColors; pm_makepalette(pmdev); /* erase bitmap - before window gets redrawn */ (*dev_proc(dev, fill_rectangle)) (dev, 0, 0, dev->width, dev->height, pm_map_rgb_color(dev, gx_max_color_value, gx_max_color_value, gx_max_color_value)); /* cause scroll bars to be redrawn */ /* need to signal gspmdrv that bitmap size has changed */ /* or perhaps gspmdrv can check if the bitmap size has */ /* before each use */ #ifdef __DLL__ if (pmdev->dll && pgsdll_callback) (*pgsdll_callback) (GSDLL_SIZE, (unsigned char *)dev, (dev->width & 0xffff) + ((dev->height & 0xffff) << 16)); #endif } /* release bmp mutex */ DosReleaseMutexSem(pmdev->bmp_mutex); return 0; } #ifdef __DLL__ /* ------ DLL routines ------ */ /* store at pbitmap the address of the bitmap */ /* device is a pointer to Ghostscript device from GSDLL_DEVICE message */ unsigned long GSDLLAPI gsdll_get_bitmap(unsigned char *device, unsigned char **pbitmap) { gx_device *dev = (gx_device *) device; *pbitmap = (unsigned char *)(pmdev->bmi); return 0; } /* Lock the device (so it's size cannot be changed) if flag = TRUE */ /* or unlock the device if flag = FALSE */ /* device is a pointer to Ghostscript device from GSDLL_DEVICE message */ int GSDLLAPI gsdll_lock_device(unsigned char *device, int flag) { gx_device *dev = (gx_device *) device; APIRET rc; if (flag) rc = DosRequestMutexSem(pmdev->bmp_mutex, 60000); else rc = DosReleaseMutexSem(pmdev->bmp_mutex); return rc; } #endif /* __DLL__ */ /* ------ Internal routines ------ */ #undef pmdev /* start gspmdrv.exe */ private int pm_run_gspmdrv(gx_device_pm * pmdev) { 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; sprintf(id, ID_NAME, pmdev->gspid, (ULONG) pmdev); /* Create termination queue - used to find out when gspmdrv terminates */ sprintf(term_queue_name, "\\QUEUES\\TERMQ_%s", id); if (DosCreateQueue(&(pmdev->term_queue), QUE_FIFO, term_queue_name)) { eprintf("pm_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) { eprintf1("pm_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) { eprintf1("pm_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 */ 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, &pmdev->session_id, &pmdev->process_id); if (rc == ERROR_FILE_NOT_FOUND) { sdata.PgmName = pdrvname; rc = DosStartSession(&sdata, &pmdev->session_id, &pmdev->process_id); } if (rc) { eprintf2("pm_run_gspmdrv: failed to run %s, rc = %d\n", sdata.PgmName, rc); eprintf1("pm_run_gspmdrv: error_message: %s\n", error_message); return gs_error_limitcheck; } return 0; } /* Allocate the backing bitmap. */ private int pm_alloc_bitmap(gx_device_pm * pmdev, gx_device * param_dev) { gx_device_memory mdev; byte *base; ulong data_size; uint ptr_size; uint pal_size; uint raster; ULONG rc; ULONG needed; /* Finish initializing the bitmap. */ gs_make_mem_device(&mdev, gdev_mem_device_for_bits(pmdev->color_info.depth), 0, 0, (gx_device *) pmdev); mdev.width = param_dev->width; mdev.height = param_dev->height; /* BMP files need width rounded up so that a scan line is */ /* a multiple of 4 bytes. */ /* This is currently done by gdev_mem_raster(). */ /* It may be better to do it here explicitly in case */ /* gdev_mem_raster changes. */ raster = gdev_mem_raster(&mdev); data_size = (ulong) raster *mdev.height; ptr_size = sizeof(byte **) * mdev.height; pal_size = pm_palette_size(pmdev); needed = pmdev->bmi->cbFix + pal_size + data_size + ptr_size; /* round up to page boundary */ needed = (needed + MIN_COMMIT - 1) & (~(MIN_COMMIT - 1)); if (needed > pmdev->committed) { /* commit more memory */ if (rc = DosSetMem(pmdev->bitmap + pmdev->committed, needed - pmdev->committed, PAG_COMMIT | PAG_DEFAULT)) { eprintf1("No memory in pm_alloc_bitmap, rc = %d\n", rc); return gs_error_limitcheck; } pmdev->committed = needed; } /* Shared memory can't be decommitted */ #ifdef __DLL__ if (pmdev->dll && (needed < pmdev->committed)) { /* decommit memory */ if (rc = DosSetMem(pmdev->bitmap + needed, pmdev->committed - needed, PAG_DECOMMIT)) { eprintf1("Failed to decommit memory in pm_alloc_bitmap, rc = %d\n", rc); return gs_error_limitcheck; } pmdev->committed = needed; } #endif /* Nothing can go wrong now.... */ base = pmdev->bitmap + pmdev->bmi->cbFix + pm_palette_size(pmdev); pmdev->mdev = mdev; pmdev->mdev.base = (byte *) base; pmmproc(open_device) ((gx_device *) & pmdev->mdev); pmdev->bmi->cbImage = data_size; return 0; } private void pm_makepalette(gx_device_pm * pmdev) { int i, val; RGB2 *argb = (RGB2 *) ((PBYTE) pmdev->bmi + pmdev->bmi->cbFix); if (pmdev->BitsPerPixel > 8) return; /* these don't use a palette */ for (i = 0; i < pmdev->nColors; i++) { switch (pmdev->nColors) { case 64: /* colors are rrggbb */ argb[i].bRed = ((i & 0x30) >> 4) * 85; argb[i].bGreen = ((i & 0xC) >> 2) * 85; argb[i].bBlue = (i & 3) * 85; argb[i].fcOptions = 0; /* zero unused entries */ argb[i + 64].bRed = argb[i + 64].bGreen = argb[i + 64].bBlue = 0; argb[i + 64].fcOptions = 0; argb[i + 128].bRed = argb[i + 128].bGreen = argb[i + 128].bBlue = 0; argb[i + 128].fcOptions = 0; argb[i + 192].bRed = argb[i + 192].bGreen = argb[i + 192].bBlue = 0; argb[i + 192].fcOptions = 0; break; case 16: /* colors are irgb */ val = (i & 8 ? 255 : 128); argb[i].bRed = i & 4 ? val : 0; argb[i].bGreen = i & 2 ? val : 0; argb[i].bBlue = i & 1 ? val : 0; if (i == 8) { /* light gray */ argb[i].bRed = argb[i].bGreen = argb[i].bBlue = 192; argb[i].fcOptions = 0; } break; case 2: argb[i].bRed = argb[i].bGreen = argb[i].bBlue = (i ? 255 : 0); argb[i].fcOptions = 0; break; } } } /* Cause display to be updated periodically */ private void pm_update(gx_device_pm * pmdev) { if (pmdev->updating) return; if (!pmdev->UpdateInterval) return; if (*pmdev->GSVIEW) { APIRET rc; rc = DosWriteQueue(pmdev->drv_queue, GS_UPDATING, 0, NULL, 0); if (rc) eprintf1("pm_update: DosWriteQueue error %d\n", rc); } else { DosStartTimer(pmdev->UpdateInterval, (HSEM) pmdev->sync_event, &pmdev->update_timer); } pmdev->updating = TRUE; } private uint pm_set_bits_per_pixel(gx_device_pm * pmdev, int bpp) { static const gx_device_color_info pm_24bit_color = dci_color(24, 255, 255); static const gx_device_color_info pm_8bit_color = dci_color(8, 31, 4); static const gx_device_color_info pm_4bit_color = dci_pc_4bit; static const gx_device_color_info pm_2color = dci_black_and_white; /* remember old anti_alias info */ gx_device_anti_alias_info anti_alias = pmdev->color_info.anti_alias; switch (bpp) { case 24: pmdev->color_info = pm_24bit_color; pmdev->nColors = (1 << 24); break; case 8: /* use 64 static colors and 166 dynamic colors from 8 planes */ pmdev->color_info = pm_8bit_color; pmdev->nColors = 64; break; case 4: pmdev->color_info = pm_4bit_color; pmdev->nColors = 16; break; case 1: pmdev->color_info = pm_2color; pmdev->nColors = 2; break; default: return (gs_error_rangecheck); } pmdev->BitsPerPixel = bpp; /* restore old anti_alias info */ pmdev->color_info.anti_alias = anti_alias; return 0; } /* return length of BMP palette in bytes */ private uint pm_palette_size(gx_device_pm * pmdev) { switch (pmdev->color_info.depth) { case 24: return 0; case 8: return 256 * sizeof(RGB2); case 4: return 16 * sizeof(RGB2); } /* must be two color */ return 2 * sizeof(RGB2); } /* This is used for testing */ /* Write out a BMP file to "out.bmp" */ private void pm_write_bmp(gx_device_pm * pmdev) { BITMAPFILEHEADER2 bmfh; uint bmfh_length = sizeof(BITMAPFILEHEADER2) - sizeof(BITMAPINFOHEADER2); uint length; /* bitmap length */ ULONG fh; /* file handle */ ULONG action; ULONG count; bmfh.usType = 0x4d42; /* "BM" */ length = pmdev->bmi->cbFix + pm_palette_size(pmdev) + ((gdev_mem_raster(&pmdev->mdev) * pmdev->mdev.height)); bmfh.cbSize = bmfh_length + length; bmfh.xHotspot = bmfh.yHotspot = 0; bmfh.offBits = bmfh_length + pmdev->bmi->cbFix + pm_palette_size(pmdev); if (DosOpen("out.bmp", /* filename */ &fh, /* pointer to handle */ &action, /* pointer to result */ 0, /* initial length */ FILE_NORMAL, /* normal file */ OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_REPLACE_IF_EXISTS, OPEN_ACCESS_WRITEONLY | OPEN_SHARE_DENYREADWRITE, 0)) { eprintf("error opening out.bmp\n"); return; } if (DosWrite(fh, (PBYTE) & bmfh, bmfh_length, &count)) eprintf("error writing header for out.bmp\n"); if (DosWrite(fh, pmdev->bitmap, length, &count)) eprintf("error writing out.bmp\n"); if (DosClose(fh)) eprintf("error closing out.bmp\n"); }