From 938f83b298e58c6eb003b73ece4a75171e85c3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= Date: Sun, 1 Dec 2019 01:26:52 +0100 Subject: [PATCH 1/2] prt_ps: initial commit --- src/lpt.c | 1 + src/printer/prt_devs.h | 3 +- src/printer/prt_ps.c | 260 +++++++++++++++++++++++++++++++++++++++++ src/win/Makefile.mingw | 2 +- 4 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 src/printer/prt_ps.c diff --git a/src/lpt.c b/src/lpt.c index 761554b82..f67eba3cc 100644 --- a/src/lpt.c +++ b/src/lpt.c @@ -30,6 +30,7 @@ static const struct {"Stereo LPT DAC", "lpt_dac_stereo", &lpt_dac_stereo_device}, {"Generic Text Printer", "text_prt", &lpt_prt_text_device}, {"Generic ESC/P Dot-Matrix", "dot_matrix", &lpt_prt_escp_device}, + {"Generic PostScript Printer", "postscript", &lpt_prt_ps_device}, {"", "", NULL} }; diff --git a/src/printer/prt_devs.h b/src/printer/prt_devs.h index e43ec67cc..7ef25e5cd 100644 --- a/src/printer/prt_devs.h +++ b/src/printer/prt_devs.h @@ -1,2 +1,3 @@ extern const lpt_device_t lpt_prt_text_device; -extern const lpt_device_t lpt_prt_escp_device; \ No newline at end of file +extern const lpt_device_t lpt_prt_escp_device; +extern const lpt_device_t lpt_prt_ps_device; \ No newline at end of file diff --git a/src/printer/prt_ps.c b/src/printer/prt_ps.c new file mode 100644 index 000000000..2f87722b4 --- /dev/null +++ b/src/printer/prt_ps.c @@ -0,0 +1,260 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Implementation of a generic PostScript printer. + * + * Version: @(#)prt_ps.c 1.0.0 2019/xx/xx + * + * Authors: David Hrdlička, + * + * Copyright 2019 David Hrdlička. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../86box.h" +#include "../lpt.h" +#include "../timer.h" +#include "../pit.h" +#include "../plat.h" +#include "prt_devs.h" + +typedef struct +{ + const char *name; + + void *lpt; + + pc_timer_t pulse_timer; + pc_timer_t timeout_timer; + + char data; + bool ack; + bool select; + bool busy; + bool int_pending; + bool error; + bool autofeed; + uint8_t ctrl; + + wchar_t printer_path[260]; + + wchar_t filename[260]; + + char buffer[65536]; + uint16_t buffer_pos; +} ps_t; + +static void +reset_ps(ps_t *dev) +{ + if (dev == NULL) return; + + dev->ack = false; + + memset(&dev->buffer, 0x00, sizeof(dev->buffer)); + dev->buffer_pos = 0; + + timer_disable(&dev->pulse_timer); + timer_disable(&dev->timeout_timer); +} + +static void +pulse_timer(void *priv) +{ + ps_t *dev = (ps_t *) priv; + + if (dev->ack) { + dev->ack = 0; + lpt_irq(dev->lpt, 1); + } + + timer_disable(&dev->pulse_timer); +} + +static void +finish_document(ps_t *dev) +{ + // todo: convert to PDF + + dev->filename[0] = 0; +} + +static void +write_buffer(ps_t *dev) +{ + wchar_t path[1024]; + FILE *fp; + + if (dev->filename[0] == 0) + { + plat_tempfile(dev->filename, NULL, L".ps"); + } + + path[0] = 0; + wcscat(path, dev->printer_path); + wcscat(path, dev->filename); + + fp = plat_fopen(path, L"a"); + if (fp == NULL) + return; + + fseek(fp, 0, SEEK_END); + + fprintf(fp, "%s\n", dev->buffer); + + fclose(fp); +} + +static void +timeout_timer(void *priv) +{ + ps_t *dev = (ps_t *) priv; + + if (dev == NULL) return; + + write_buffer(dev); + finish_document(dev); + + timer_disable(&dev->timeout_timer); +} + +static void +ps_write_data(uint8_t val, void *p) +{ + ps_t *dev = (ps_t *) p; + + if (dev == NULL) return; + + dev->data = (char) val; +} + +static void +ps_write_ctrl(uint8_t val, void *p) +{ + ps_t *dev = (ps_t *) p; + + if (dev == NULL) return; + + dev->autofeed = val & 0x02 ? true : false; + + if (val & 0x08) + { + dev->select = true; + } + + if((val & 0x04) && !(dev->ctrl & 0x04)) + { + // reset printer + dev->select = false; + + reset_ps(dev); + } + + if(!(val & 0x01) && (dev->ctrl & 0x01)) + { + if (dev->data < 0x20 && dev->data != '\t') + { + switch (dev->data) + { + case '\b': + dev->buffer[dev->buffer_pos--] = 0; + break; + case '\r': + dev->buffer_pos = 0; + if(!dev->autofeed) + break; + case '\n': + write_buffer(dev); + dev->buffer[0] = 0; + dev->buffer_pos = 0; + break; + } + } + else + { + dev->buffer[dev->buffer_pos++] = dev->data; + dev->buffer[dev->buffer_pos] = 0; + } + + dev->ack = true; + + timer_set_delay_u64(&dev->pulse_timer, ISACONST); + timer_set_delay_u64(&dev->timeout_timer, 500000 * TIMER_USEC); + } + + dev->ctrl = val; +} + +static uint8_t +ps_read_status(void *p) +{ + ps_t *dev = (ps_t *) p; + uint8_t ret = 0x1f; + + ret |= 0x80; + + if(!dev->ack) + ret |= 0x40; + + return(ret); +} + +static void * +ps_init(void *lpt) +{ + ps_t *dev; + + dev = (ps_t *) malloc(sizeof(ps_t)); + memset(dev, 0x00, sizeof(ps_t)); + dev->ctrl = 0x04; + dev->lpt = lpt; + + reset_ps(dev); + + // Cache print folder path + memset(dev->printer_path, 0x00, sizeof(dev->printer_path)); + plat_append_filename(dev->printer_path, usr_path, L"printer"); + if (! plat_dir_check(dev->printer_path)) + plat_dir_create(dev->printer_path); + plat_path_slash(dev->printer_path); + + timer_add(&dev->pulse_timer, pulse_timer, dev, 0); + timer_add(&dev->timeout_timer, timeout_timer, dev, 0); + + return(dev); +} + +static void +ps_close(void *p) +{ + ps_t *dev = (ps_t *) p; + + if (dev == NULL) return; + + write_buffer(dev); + finish_document(dev); + + free(dev); +} + +const lpt_device_t lpt_prt_ps_device = { + name: "Generic PostScript printer", + init: ps_init, + close: ps_close, + write_data: ps_write_data, + write_ctrl: ps_write_ctrl, + read_data: NULL, + read_status: ps_read_status, + read_ctrl: NULL +}; \ No newline at end of file diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 8e6f39e38..d160e2a38 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -610,7 +610,7 @@ NETOBJ := network.o \ net_wd8003.o PRINTOBJ := png.o prt_cpmap.o \ - prt_escp.o prt_text.o + prt_escp.o prt_text.o prt_ps.o SNDOBJ := sound.o \ openal.o \ From 4a427288f0ea7578580432a7fb4b48cc47b8215d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= Date: Sun, 1 Dec 2019 23:05:43 +0100 Subject: [PATCH 2/2] prt_ps: add PDF print --- src/printer/prt_ps.c | 119 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 4 deletions(-) diff --git a/src/printer/prt_ps.c b/src/printer/prt_ps.c index 2f87722b4..39d192d08 100644 --- a/src/printer/prt_ps.c +++ b/src/printer/prt_ps.c @@ -27,8 +27,39 @@ #include "../timer.h" #include "../pit.h" #include "../plat.h" +#include "../plat_dynld.h" +#include "../ui.h" #include "prt_devs.h" +#if defined(_WIN32) && !defined(__WINDOWS__) +#define __WINDOWS__ +#endif +#include +#include + + +#define PATH_GHOSTSCRIPT_DLL "gsdll32.dll" + +static GSDLLAPI int (*ghostscript_revision)(gsapi_revision_t *pr, int len); +static GSDLLAPI int (*ghostscript_new_instance)(void **pinstance, void *caller_handle); +static GSDLLAPI void (*ghostscript_delete_instance)(void *instance); +static GSDLLAPI int (*ghostscript_set_arg_encoding)(void *instance, int encoding); +static GSDLLAPI int (*ghostscript_init_with_args)(void *instance, int argc, char **argv); +static GSDLLAPI int (*ghostscript_exit)(void *instance); + +static dllimp_t ghostscript_imports[] = { + { "gsapi_revision", &ghostscript_revision }, + { "gsapi_new_instance", &ghostscript_new_instance }, + { "gsapi_delete_instance", &ghostscript_delete_instance }, + { "gsapi_set_arg_encoding", &ghostscript_set_arg_encoding }, + { "gsapi_init_with_args", &ghostscript_init_with_args }, + { "gsapi_exit", &ghostscript_exit }, + { NULL, NULL } +}; + +static void *ghostscript_handle = NULL; +static bool ghostscript_initialized = false; + typedef struct { const char *name; @@ -62,7 +93,7 @@ reset_ps(ps_t *dev) dev->ack = false; - memset(&dev->buffer, 0x00, sizeof(dev->buffer)); + dev->buffer[0] = 0; dev->buffer_pos = 0; timer_disable(&dev->pulse_timer); @@ -82,10 +113,60 @@ pulse_timer(void *priv) timer_disable(&dev->pulse_timer); } +static int +convert_to_pdf(ps_t *dev) +{ + volatile int code; + void *instance = NULL; + wchar_t input_fn[1024], output_fn[1024], *gsargv[9]; + + input_fn[0] = 0; + wcscat(input_fn, dev->printer_path); + wcscat(input_fn, dev->filename); + + output_fn[0] = 0; + wcscat(output_fn, input_fn); + wcscpy(output_fn + wcslen(output_fn) - 3, L".pdf"); + + gsargv[0] = L""; + gsargv[1] = L"-dNOPAUSE"; + gsargv[2] = L"-dBATCH"; + gsargv[3] = L"-dSAFER"; + gsargv[4] = L"-sDEVICE=pdfwrite"; + gsargv[5] = L"-q"; + gsargv[6] = L"-o"; + gsargv[7] = output_fn; + gsargv[8] = input_fn; + + code = ghostscript_new_instance(&instance, dev); + if(code < 0) + return code; + + code = ghostscript_set_arg_encoding(instance, GS_ARG_ENCODING_UTF16LE); + + if (code == 0) + code = ghostscript_init_with_args(instance, 9, gsargv); + + if (code == 0 || code == gs_error_Quit) + code = ghostscript_exit(instance); + else + ghostscript_exit(instance); + + ghostscript_delete_instance(instance); + + if (code == 0 || code == gs_error_Quit) + plat_remove(input_fn); + else + plat_remove(output_fn); + + return code; +} + static void finish_document(ps_t *dev) { - // todo: convert to PDF + if (ghostscript_handle != NULL) + convert_to_pdf(dev); dev->filename[0] = 0; } @@ -210,6 +291,30 @@ ps_read_status(void *p) return(ret); } +static void +ghostscript_init() +{ + gsapi_revision_t rev; + + ghostscript_initialized = true; + + /* Try loading the DLL. */ + ghostscript_handle = dynld_module(PATH_GHOSTSCRIPT_DLL, ghostscript_imports); + if (ghostscript_handle == NULL) { + ui_msgbox(MBX_ERROR, L"Couldn't initialize Ghostscript!"); + return; + } + + if (ghostscript_revision(&rev, sizeof(rev)) == 0) + { + pclog("Loaded %s, rev %ld (%ld)\n", rev.product, rev.revision, rev.revisiondate); + } + else + { + ghostscript_handle = NULL; + } +} + static void * ps_init(void *lpt) { @@ -222,6 +327,9 @@ ps_init(void *lpt) reset_ps(dev); + if(! ghostscript_initialized) + ghostscript_init(); + // Cache print folder path memset(dev->printer_path, 0x00, sizeof(dev->printer_path)); plat_append_filename(dev->printer_path, usr_path, L"printer"); @@ -242,8 +350,11 @@ ps_close(void *p) if (dev == NULL) return; - write_buffer(dev); - finish_document(dev); + if (dev->buffer[0] != 0) + { + write_buffer(dev); + finish_document(dev); + } free(dev); }