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..39d192d08 --- /dev/null +++ b/src/printer/prt_ps.c @@ -0,0 +1,371 @@ +/* + * 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 "../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; + + 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; + + dev->buffer[0] = 0; + 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 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) +{ + if (ghostscript_handle != NULL) + convert_to_pdf(dev); + + 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 +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) +{ + 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); + + 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"); + 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; + + if (dev->buffer[0] != 0) + { + 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 \