From 63028863e4742b4f1f303ebf3402ddb3d26694b0 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Sun, 23 Jul 2023 11:31:24 -1000 Subject: [PATCH] SCP: Default to dynamically loading LIBEDIT, LIBPCRE and LIBPNG --- README.md | 1 + makefile | 38 ++++++++----- scp.c | 97 ++++++++++++++++++------------- sim_disk.c | 4 +- sim_ether.c | 11 ++-- sim_scp_private.h | 141 ++++++++++++++++++++++++++++++++++++++++++++-- sim_video.c | 137 ++++++++++++++++++++++++++++++-------------- 7 files changed, 317 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index d1fcd806..4417476c 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ Simulator binaries for x86 Linus, x86 macOS, and Windows for all recent changes - Various failing bugs in tape detach logic are fixed. - Clean building on Android Termux. - Proper line editing behavior on all Unix platforms. +- Simulators with video displays have a working SCREENSHOT command. #### Changes to the PDP-11 and VAX simulators also not in the Open SIMH repo diff --git a/makefile b/makefile index bf8b7f2e..cf09f8ba 100644 --- a/makefile +++ b/makefile @@ -718,11 +718,13 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) # Find PCRE RegEx library. ifneq (,$(call find_include,pcre)) ifneq (,$(call find_lib,pcre)) - OS_CCDEFS += -DHAVE_PCRE_H - OS_LDFLAGS += -lpcre $(info using libpcre: $(call find_lib,pcre) $(call find_include,pcre)) - ifeq ($(LD_SEARCH_NEEDED),$(call need_search,pcre)) - OS_LDFLAGS += -L$(dir $(call find_lib,pcre)) + ifneq (,$(ALL_DEPENDENCIES)) + OS_CCDEFS += -DHAVE_PCRE_H + OS_LDFLAGS += -lpcre + ifeq ($(LD_SEARCH_NEEDED),$(call need_search,pcre)) + OS_LDFLAGS += -L$(dir $(call find_lib,pcre)) + endif endif else NEEDED_PKGS += DPKG_PCRE @@ -733,14 +735,16 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) # Find libedit BSD licensed library for readline support. ifneq (,$(call find_lib,edit)) ifneq (,$(call find_include,editline/readline)) - OS_CCDEFS += -DHAVE_LIBEDIT - OS_LDFLAGS += -ledit $(info using libedit: $(call find_lib,edit) $(call find_include,editline/readline)) - ifneq (,$(call find_lib,termcap)) - OS_LDFLAGS += -ltermcap - endif - ifeq ($(LD_SEARCH_NEEDED),$(call need_search,edit)) - OS_LDFLAGS += -L$(dir $(call find_lib,edit)) + ifneq (,$(ALL_DEPENDENCIES)) + OS_CCDEFS += -DHAVE_LIBEDIT + OS_LDFLAGS += -ledit + ifneq (,$(call find_lib,termcap)) + OS_LDFLAGS += -ltermcap + endif + ifeq ($(LD_SEARCH_NEEDED),$(call need_search,edit)) + OS_LDFLAGS += -L$(dir $(call find_lib,edit)) + endif endif else NEEDED_PKGS += DPKG_EDITLINE @@ -802,14 +806,18 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin) endif ifneq (,$(call find_include,png)) ifneq (,$(call find_lib,png)) - OS_CCDEFS += -DHAVE_LIBPNG - OS_LDFLAGS += -lpng $(info using libpng: $(call find_lib,png) $(call find_include,png)) + OS_CCDEFS += -DHAVE_LIBPNG + ifneq (,$(ALL_DEPENDENCIES)) + OS_LDFLAGS += -lpng + endif ifneq (,$(call find_include,zlib)) ifneq (,$(call find_lib,z)) - OS_CCDEFS += -DHAVE_ZLIB - OS_LDFLAGS += -lz $(info using zlib: $(call find_lib,z) $(call find_include,zlib)) + OS_CCDEFS += -DHAVE_ZLIB + ifneq (,$(ALL_DEPENDENCIES)) + OS_LDFLAGS += -lz + endif else NEEDED_PKGS += DPKG_ZLIB endif diff --git a/scp.c b/scp.c index 62108e5f..a6f6998d 100644 --- a/scp.c +++ b/scp.c @@ -239,10 +239,6 @@ #include #endif -#if defined(SIM_HAVE_DLOPEN) /* Dynamic Readline support */ -#include -#endif - #ifndef MAX #define MAX(a,b) (((a) >= (b)) ? (a) : (b)) #endif @@ -693,6 +689,16 @@ static int sim_external_env_count = 0; static char *sim_tmpnam; static FILE *sim_tmpfile = NULL; static int sim_editline_version = 0; +static t_bool sim_pcre_regex_available = FALSE; +/* Dynamically loaded pcre support */ +#if !defined(HAVE_PCRE_H) +pcre *(*pcre_compile) (const char *, int, const char **, int *, const unsigned char *); +const char *(*pcre_version) (void); +void (*pcre_free) (void *); +int (*pcre_fullinfo) (const pcre *, const pcre_extra *, int, void *); +int (*pcre_exec) (const pcre *, const pcre_extra *, const char *, int, int, int, int *, int); +#endif +static void sim_exp_initialize (void); t_stat sim_last_cmd_stat; /* Command Status */ struct timespec cmd_time; /* */ @@ -3036,8 +3042,10 @@ AIO_INIT; /* init Asynch I/O */ sim_finit (); /* init fio package */ sim_disk_init (); /* init disk package */ sim_tape_init (); /* init tape package */ +sim_exp_initialize (); /* init expect package regex support */ if ((argc > 2) && (sim_strcasecmp (argv[1], "CheckSourceCode") == 0)) { + if (sim_pcre_regex_available) return sim_check_source (argc - 1, argv + 1); sim_messagef (SCPE_NOFNC, "Missing PCRE support.\n"); sim_messagef (SCPE_NOFNC, "Install the Perl Compatible Regular Expression (PCRE) package for\n"); @@ -3187,9 +3195,6 @@ if (!sim_quiet) { sim_timer_precalibrate_execution_rate (); sim_reset_time (); show_version (stdnul, NULL, NULL, 1, NULL); /* Quietly set SIM_OSTYPE */ -#if defined (HAVE_PCRE_H) -setenv ("SIM_REGEX_TYPE", "PCRE", 1); /* Publish regex type */ -#endif sim_argv = argv; if (sim_switches & SWMASK ('T')) /* Command Line -T switch */ @@ -7047,18 +7052,18 @@ if (flag) { fprintf (st, "\n Memory Pointer Size: %d bits", (int)sizeof(dptr)*8); fprintf (st, "\n %s", sim_toffset_64 ? "Large File (>2GB) support" : "No Large File support"); fprintf (st, "\n SDL Video support: %s", vid_version()); -#if defined (HAVE_PCRE_H) - fprintf (st, "\n PCRE RegEx (Version %s) support for EXPECT commands", pcre_version()); -#else - fprintf (st, "\n No RegEx support for EXPECT commands"); -#endif + if (sim_pcre_regex_available) + fprintf (st, "\n PCRE RegEx (Version %s) support for EXPECT commands", pcre_version()); + else + fprintf (st, "\n No RegEx support for EXPECT commands"); fprintf (st, "\n OS clock resolution: %dms", os_tick_size); fprintf (st, "\n Time taken by msleep(1): %dms", os_ms_sleep_1); - if (sim_editline_version != 0) + if (sim_editline_version != 0) { if (sim_editline_version > 0xFFFF) fprintf (st, "\n WinEditLine Version: %d.%d%02X", sim_editline_version >> 16, (sim_editline_version >> 8) & 0xFF, sim_editline_version & 0xFF); else fprintf (st, "\n EditLine Version: %d.%d", (sim_editline_version >> 8) & 0xFF, sim_editline_version & 0xFF); + } if (eth_version ()) fprintf (st, "\n Ethernet packet info: %s", eth_version()); #if defined(__VMS) @@ -10769,22 +10774,9 @@ return read_line_p (NULL, cptr, size, stream); #include #endif #if defined(_WIN32) -#define dlopen(X,Y) LoadLibraryA((X)) -#define dlsym(X,Y) GetProcAddress((HINSTANCE)(X),(Y)) -#define dlclose(X) FreeLibrary((X)) -#ifndef RTLD_NOW -#define RTLD_NOW 0 -#endif -#ifndef RTLD_LOCAL -#define RTLD_LOCAL 0 -#endif #define EDIT_DEFAULT_LIB "edit." -#define SIM_DLOPEN_EXTENSION DLL #else /* !defined(_WIN32) */ #define EDIT_DEFAULT_LIB "libedit." -#if defined(SIM_HAVE_DLOPEN) -#define SIM_DLOPEN_EXTENSION SIM_HAVE_DLOPEN -#endif #endif /* defined(_WIN32) */ char *read_line_p (const char *prompt, char *cptr, int32 size, FILE *stream) @@ -13458,6 +13450,7 @@ return msg; The package contains the following public routines: + sim_exp_initialize initialize the expect facility regex sypport sim_set_expect expect command parser and intializer sim_set_noexpect noexpect command parser sim_exp_init initialize an expect context @@ -13469,6 +13462,40 @@ return msg; sim_exp_check test for rule match */ +/* Optionally dynamically locate and load pcre support */ + +void sim_exp_initialize (void) +{ +#if defined (SIM_HAVE_DLOPEN) && !defined (HAVE_PCRE_H) +static void *hPCRELib = 0; /* handle to Library */ +/* generic function pointer used when loading shared object */ +typedef int (*_func)(); + +hPCRELib = dlopen("libpcre." __STR(SIM_DLOPEN_EXTENSION), RTLD_NOW|RTLD_GLOBAL); +if (!hPCRELib) + hPCRELib = dlopen("libpcre." __STR(SIM_DLOPEN_EXTENSION) ".2", RTLD_NOW|RTLD_GLOBAL); +if (!hPCRELib) + hPCRELib = dlopen("libpcre." __STR(SIM_DLOPEN_EXTENSION) ".3", RTLD_NOW|RTLD_GLOBAL); + +#define _load_function(function) *((_func **)&function) = (_func *)((size_t)dlsym(hPCRELib, __STR(function))) + +_load_function(pcre_compile); +_load_function(pcre_version); +_load_function(pcre_free); +_load_function(pcre_fullinfo); +_load_function(pcre_exec); +sim_pcre_regex_available = (pcre_compile != NULL); +if (sim_pcre_regex_available) + *((_func *)&pcre_free) = *((_func *)pcre_free); /* Fixup initially indirect pointer */ +#else +#if defined (HAVE_PCRE_H) +sim_pcre_regex_available = TRUE; +#endif +#endif /* defined (SIM_HAVE_DLOPEN) && !defined (HAVE_PCRE_H) */ +if (sim_pcre_regex_available) + setenv ("SIM_REGEX_TYPE", "PCRE", 1); /* Publish regex type */ +} + /* Initialize an expect context. */ t_stat sim_exp_init (EXPECT *exp) @@ -13565,10 +13592,8 @@ if (!ep) /* not there? ok */ free (ep->match); /* deallocate match string */ free (ep->match_pattern); /* deallocate the display format match string */ free (ep->act); /* deallocate action */ -#if defined(USE_REGEX) if (ep->switches & EXP_TYP_REGEX) pcre_free (ep->regex); /* release compiled regex */ -#endif exp->size -= 1; /* decrement count */ for (i=ep-exp->rules; isize; i++) /* shuffle up remaining rules */ exp->rules[i] = exp->rules[i+1]; @@ -13600,10 +13625,8 @@ for (i=0; isize; i++) { free (exp->rules[i].match); /* deallocate match string */ free (exp->rules[i].match_pattern); /* deallocate display format match string */ free (exp->rules[i].act); /* deallocate action */ -#if defined(USE_REGEX) if (exp->rules[i].switches & EXP_TYP_REGEX) pcre_free (exp->rules[i].regex); /* release compiled regex */ -#endif } free (exp->rules); exp->rules = NULL; @@ -13628,8 +13651,7 @@ int i; match_buf = (uint8 *)calloc (strlen (match) + 1, 1); if (!match_buf) return SCPE_MEM; -if (switches & EXP_TYP_REGEX) { -#if !defined (USE_REGEX) +if ((switches & EXP_TYP_REGEX) && !sim_pcre_regex_available) { free (match_buf); sim_messagef (SCPE_ARG, "RegEx support is not available\n"); sim_messagef (SCPE_ARG, "The necessary components for expect command regular expression\n"); @@ -13637,7 +13659,7 @@ if (switches & EXP_TYP_REGEX) { sim_messagef (SCPE_ARG, "Install the Perl Compatible Regular Expression (PCRE) package for\n"); return sim_messagef (SCPE_ARG, "your system and try again.\n"); } -#else /* USE_REGEX */ +if (switches & EXP_TYP_REGEX) { pcre *re; const char *errmsg; int erroffset, re_nsub; @@ -13650,11 +13672,10 @@ if (switches & EXP_TYP_REGEX) { free (match_buf); return SCPE_ARG|SCPE_NOMESSAGE; } - (void)pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &re_nsub); + (void)pcre_fullinfo (re, NULL, PCRE_INFO_CAPTURECOUNT, &re_nsub); sim_debug (exp->dbit, exp->dptr, "Expect Regular Expression: \"%s\" has %d sub expressions\n", match_buf, re_nsub); pcre_free (re); } -#endif else { if (switches & EXP_TYP_REGEX_I) { free (match_buf); @@ -13691,15 +13712,13 @@ if ((match_buf == NULL) || (ep->match_pattern == NULL)) { return SCPE_MEM; } if (switches & EXP_TYP_REGEX) { -#if defined(USE_REGEX) const char *errmsg; int erroffset; memcpy (match_buf, match+1, strlen(match)-2); /* extract string without surrounding quotes */ match_buf[strlen(match)-2] = '\0'; ep->regex = pcre_compile ((char *)match_buf, (switches & EXP_TYP_REGEX_I) ? PCRE_CASELESS : 0, &errmsg, &erroffset, NULL); - (void)pcre_fullinfo(ep->regex, NULL, PCRE_INFO_CAPTURECOUNT, &ep->re_nsub); -#endif + (void)pcre_fullinfo (ep->regex, NULL, PCRE_INFO_CAPTURECOUNT, &ep->re_nsub); free (match_buf); match_buf = NULL; } @@ -13830,7 +13849,6 @@ if (exp->buf_data < exp->buf_size) for (i=0; i < exp->size; i++) { ep = &exp->rules[i]; if (ep->switches & EXP_TYP_REGEX) { -#if defined (USE_REGEX) int *ovector = NULL; int rc; char *cbuf = (char *)exp->buf; @@ -13885,7 +13903,6 @@ for (i=0; i < exp->size; i++) { break; } free (ovector); -#endif } else { if (exp->buf_data < ep->size) /* Too little data to match yet? */ diff --git a/sim_disk.c b/sim_disk.c index 996c0544..a3e2b881 100644 --- a/sim_disk.c +++ b/sim_disk.c @@ -6341,15 +6341,13 @@ else _rand_uuid_gen (uuidaddr); } #elif defined (SIM_HAVE_DLOPEN) -#include - static void uuid_gen (void *uuidaddr) { void (*uuid_generate_c) (void *) = NULL; void *handle; - handle = dlopen("libuuid." __STR(SIM_HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL); + handle = dlopen("libuuid." __STR(SIM_DLOPEN_EXTENSION), RTLD_NOW|RTLD_GLOBAL); if (handle) uuid_generate_c = (void (*)(void *))((size_t)dlsym(handle, "uuid_generate")); if (uuid_generate_c) diff --git a/sim_ether.c b/sim_ether.c index ac4f2db7..8fe13475 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -373,6 +373,9 @@ #include "sim_ether.h" #include "sim_sock.h" #include "sim_timer.h" + +#include "sim_scp_private.h" + #if defined(_WIN32) #include #else @@ -1245,10 +1248,6 @@ extern "C" { #include #endif -#ifdef SIM_HAVE_DLOPEN -#include -#endif - #if defined(USE_SHARED) && (defined(_WIN32) || defined(SIM_HAVE_DLOPEN)) /* Dynamic DLL loading technique and modified source comes from Etherial/WireShark capture_pcap.c */ @@ -1267,7 +1266,7 @@ static const char* lib_name = #elif defined(__APPLE__) "/usr/lib/libpcap.A.dylib"; #else - "libpcap." __STR(SIM_HAVE_DLOPEN); + "libpcap." __STR(SIM_DLOPEN_EXTENSION); #endif static char no_pcap[PCAP_ERRBUF_SIZE] = @@ -1276,7 +1275,7 @@ static char no_pcap[PCAP_ERRBUF_SIZE] = #elif defined(__APPLE__) "/usr/lib/libpcap.A.dylib failed to load, install libpcap to use pcap networking"; #else - "libpcap." __STR(SIM_HAVE_DLOPEN) " failed to load, install libpcap to use pcap networking"; + "libpcap." __STR(SIM_DLOPEN_EXTENSION) " failed to load, install libpcap to use pcap networking"; #endif /* define pointers to pcap functions needed */ diff --git a/sim_scp_private.h b/sim_scp_private.h index a3f87ae4..c67813a7 100644 --- a/sim_scp_private.h +++ b/sim_scp_private.h @@ -37,14 +37,144 @@ extern "C" { #include "sim_sock.h" -#ifdef USE_REGEX -#undef USE_REGEX +#if defined(_WIN32) +#define dlopen(X,Y) LoadLibraryA((X)) +#define dlsym(X,Y) GetProcAddress((HINSTANCE)(X),(Y)) +#define dlclose(X) FreeLibrary((X)) +#define SIM_DLOPEN_EXTENSION DLL +#else /* !defined(_WIN32) */ +#if defined(SIM_HAVE_DLOPEN) +#include +#define SIM_DLOPEN_EXTENSION SIM_HAVE_DLOPEN #endif +#endif /* defined(_WIN32) */ + #if defined(HAVE_PCRE_H) #include -#define USE_REGEX 1 +#else /* !defined(HAVE_PCRE_H) */ +/* Dynamically loaded PCRE support */ +#if !defined(PCRE_DYNAMIC_SETUP) +#define PCRE_DYNAMIC_SETUP +typedef void pcre; +typedef void pcre_extra; +#ifndef PCRE_INFO_CAPTURECOUNT +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_ERROR_NOMATCH (-1) #endif +#ifndef PCRE_NOTBOL +#define PCRE_NOTBOL 0x00000080 /* E D J */ +#endif +#ifndef PCRE_CASELESS +#define PCRE_CASELESS 0x00000001 /* C1 */ +#endif +/* Pointers to useful PCRE functions */ +extern pcre *(*pcre_compile) (const char *, int, const char **, int *, const unsigned char *); +extern const char *(*pcre_version) (void); +extern void (*pcre_free) (void *); +extern int (*pcre_fullinfo) (const pcre *, const pcre_extra *, int, void *); +extern int (*pcre_exec) (const pcre *, const pcre_extra *, const char *, int, int, int, int *, int); +#endif /* PCRE_DYNAMIC_SETUP */ +#endif /* HAVE_PCRE_H */ +/* Dynamically loaded PNG support */ +#if defined(PNG_H) /* This symbol has been defined by png.h since png 1.0.7 in 2000 */ +#if !defined(PNG_ROUTINE) +#if PNG_LIBPNG_VER < 10600 +#define png_const_structrp png_structp +#define png_structrp png_structp +#define png_const_inforp png_infop +#define png_inforp png_infop +#define png_const_colorp png_colorp +#define png_const_bytep png_bytep +#undef PNG_SETJMP_SUPPORTED +#endif +/* Pointers to useful PNG functions */ +#if defined(_WIN32) || defined(ALL_DEPENDENCIES) /* This would be appropriate anytime libpng is specifically listed at link time */ +#define PNG_ROUTINE(_type, _name, _args) _type (*p_##_name) _args = &_name; +#else +#define PNG_ROUTINE(_type, _name, _args) _type (*p_##_name) _args; +#endif +#else /* !defined(PNG_ROUTINE) */ +#undef PNG_ROUTINE +static struct PNG_Entry { + const char *entry_name; + void **entry_pointer; + } libpng_entries[] = { +#define DEFINING_PNG_ARRAY 1 +#define PNG_ROUTINE(_type, _name, _args) {#_name, (void **)&p_##_name}, +#endif +/* Return the user pointer associated with the I/O functions */ +PNG_ROUTINE(png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)) +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +PNG_ROUTINE(png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +PNG_ROUTINE(png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) +/* Allocate and initialize the info structure */ +PNG_ROUTINE(png_infop, png_create_info_struct, (png_const_structrp png_ptr)) +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_ROUTINE(void, png_destroy_read_struct, (png_structpp png_ptr_ptr, + png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)) +/* Free any memory associated with the png_struct and the png_info_structs */ +PNG_ROUTINE(void, png_destroy_write_struct, (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)) +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +PNG_ROUTINE(void, png_set_write_fn, (png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)) +PNG_ROUTINE(void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)) +PNG_ROUTINE(void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)) +/* Use blue, green, red order for pixels. */ +PNG_ROUTINE(void, png_set_bgr, (png_structrp png_ptr)) +PNG_ROUTINE(void, png_write_info, + (png_structrp png_ptr, png_const_inforp info_ptr)) +/* Write a row of image data */ +PNG_ROUTINE(void, png_write_row, (png_structrp png_ptr, + png_const_bytep row)) +/* Write the image data */ +PNG_ROUTINE(void, png_write_image, (png_structrp png_ptr, png_bytepp image)) +/* Write the end of the PNG file. */ +PNG_ROUTINE(void, png_write_end, (png_structrp png_ptr, + png_inforp info_ptr)) +#if defined(PNG_SETJMP_SUPPORTED) +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_ROUTINE(jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)) +#endif /* PNG_SETJMP_SUPPORTED */ +PNG_ROUTINE(png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)) +#if defined(ZLIB_VERSION) +PNG_ROUTINE(png_const_charp, zlibVersion, + (void)) +#endif +#if defined(DEFINING_PNG_ARRAY) + {0}, + }; +#undef DEFINING_PNG_ARRAY +#else /* !defined(DEFINING_PNG_ARRAY) */ +#undef SIM_SCP_PRIVATE_H_ +#include "sim_scp_private.h" /* recurse to generate libpng_entries array */ +#endif /* !defined(DEFINING_PNG_ARRAY) */ +#endif /* PNG_H */ /* Asynch/Threaded I/O support */ @@ -234,6 +364,8 @@ extern int32 sim_asynch_inst_latency; /* Private SCP only structures */ +#if !defined(SIM_SCP_PRIVATE_DONT_REPEAT) +#define SIM_SCP_PRIVATE_DONT_REPEAT /* Expect rule */ struct EXPTAB { @@ -248,10 +380,8 @@ struct EXPTAB { #define EXP_TYP_REGEX (SWMASK ('R')) /* rule pattern is a regular expression */ #define EXP_TYP_REGEX_I (SWMASK ('I')) /* regular expression pattern matching should be case independent */ #define EXP_TYP_TIME (SWMASK ('T')) /* halt delay is in microseconds instead of instructions */ -#if defined(USE_REGEX) pcre *regex; /* compiled regular expression */ int re_nsub; /* regular expression sub expression count */ -#endif char *act; /* action string */ }; @@ -282,6 +412,7 @@ struct SEND { int32 insoff; /* insert offset */ int32 extoff; /* extra offset */ }; +#endif /* defined(SIM_SCP_PRIVATE_DONT_REPEAT) */ #ifdef __cplusplus } diff --git a/sim_video.c b/sim_video.c index a306ad8d..49a64f47 100644 --- a/sim_video.c +++ b/sim_video.c @@ -28,8 +28,17 @@ */ #if defined(HAVE_LIBPNG) && defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) +/* + png.h is included here (before sim_video.h) since some older + versions of png.h report errors when included after setjmp.h + which is included in sim_defs.h. +*/ #include +#if defined(HAVE_ZLIB) +#include #endif +#endif + #include "sim_video.h" #include "scp.h" @@ -45,6 +54,7 @@ static VID_QUIT_CALLBACK vid_quit_callback = NULL; static VID_GAMEPAD_CALLBACK motion_callback[10]; static VID_GAMEPAD_CALLBACK button_callback[10]; static int vid_gamepad_inited = 0; +static t_bool sim_libpng_available = FALSE; t_stat vid_register_quit_callback (VID_QUIT_CALLBACK callback) { @@ -155,9 +165,10 @@ static char tmp_key_name[40]; * * This code is free software, available under zlib/libpng license. * http://www.libpng.org/pub/png/src/libpng-LICENSE.txt + * This code has been slightly modified to leverage indirect pointers + * to the various png routines. */ #include -#include #define SUCCESS 0 #define ERROR -1 @@ -183,7 +194,7 @@ static void png_error_SDL(png_structp ctx, png_const_charp str) } static void png_write_SDL(png_structp png_ptr, png_bytep data, png_size_t length) { - SDL_RWops *rw = (SDL_RWops*)png_get_io_ptr(png_ptr); + SDL_RWops *rw = (SDL_RWops*)p_png_get_io_ptr(png_ptr); SDL_RWwrite(rw, data, sizeof(png_byte), length); } @@ -230,30 +241,31 @@ static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) if (freedst) SDL_RWclose(dst); return (ERROR); } - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */ + png_ptr = p_png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error_SDL, NULL); /* err_ptr, err_fn, warn_fn */ if (!png_ptr) { SDL_SetError("Unable to png_create_write_struct on %s\n", PNG_LIBPNG_VER_STRING); if (freedst) SDL_RWclose(dst); return (ERROR); } - info_ptr = png_create_info_struct(png_ptr); + info_ptr = p_png_create_info_struct(png_ptr); if (!info_ptr) { SDL_SetError("Unable to png_create_info_struct\n"); - png_destroy_write_struct(&png_ptr, NULL); + p_png_destroy_write_struct(&png_ptr, NULL); if (freedst) SDL_RWclose(dst); return (ERROR); } - if (setjmp(png_jmpbuf(png_ptr))) /* All other errors, see also "png_error_SDL" */ +#if defined(PNG_SETJMP_SUPPORTED) + if (setjmp(*p_png_set_longjmp_fn(png_ptr, longjmp, sizeof (jmp_buf)))) /* All other errors, see also "png_error_SDL" */ { - png_destroy_write_struct(&png_ptr, &info_ptr); + p_png_destroy_write_struct(&png_ptr, &info_ptr); if (freedst) SDL_RWclose(dst); return (ERROR); } - +#endif /* Setup our RWops writer */ - png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */ + p_png_set_write_fn(png_ptr, dst, png_write_SDL, NULL); /* w_ptr, write_fn, flush_fn */ /* Prepare chunks */ colortype = PNG_COLOR_MASK_COLOR; @@ -268,13 +280,13 @@ static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) pal_ptr[i].green = pal->colors[i].g; pal_ptr[i].blue = pal->colors[i].b; } - png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors); + p_png_set_PLTE(png_ptr, info_ptr, pal_ptr, pal->ncolors); free(pal_ptr); } else if (surface->format->BytesPerPixel > 3 || surface->format->Amask) colortype |= PNG_COLOR_MASK_ALPHA; - png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype, + p_png_set_IHDR(png_ptr, info_ptr, surface->w, surface->h, 8, colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // png_set_packing(png_ptr); @@ -283,24 +295,24 @@ static int SDL_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) if (surface->format->Rmask == bmask && surface->format->Gmask == gmask && surface->format->Bmask == rmask) - png_set_bgr(png_ptr); + p_png_set_bgr(png_ptr); /* Write everything */ - png_write_info(png_ptr, info_ptr); + p_png_write_info(png_ptr, info_ptr); #ifdef USE_ROW_POINTERS row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surface->h); for (i = 0; i < surface->h; i++) row_pointers[i] = (png_bytep)(Uint8*)surface->pixels + i * surface->pitch; - png_write_image(png_ptr, row_pointers); + p_png_write_image(png_ptr, row_pointers); free(row_pointers); #else for (i = 0; i < surface->h; i++) - png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch); + p_png_write_row(png_ptr, (png_bytep)(Uint8*)surface->pixels + i * surface->pitch); #endif - png_write_end(png_ptr, info_ptr); + p_png_write_end(png_ptr, info_ptr); /* Done */ - png_destroy_write_struct(&png_ptr, &info_ptr); + p_png_destroy_write_struct(&png_ptr, &info_ptr); if (freedst) SDL_RWclose(dst); return (SUCCESS); } @@ -467,6 +479,8 @@ switch (ev->type) { break; case SDL_USEREVENT: uev = (SDL_UserEvent *)ev; + if (uev->code == EVENT_SCREENSHOT) + return &vid_first; /* Currently only support screenshot of the first windown */ sim_messagef (SCPE_OK, "Unrecognized user event.\n"); sim_messagef (SCPE_OK, " type = %u\n", uev->type); sim_messagef (SCPE_OK, " timestamp = %u\n", uev->timestamp); @@ -480,10 +494,12 @@ switch (ev->type) { break; } -sim_messagef (SCPE_OK, -"\nSIMH has encountered a bug in SDL2. An upgrade to SDL2\n" -"version 2.0.14 should fix this problem.\n"); - +sim_messagef (SCPE_IERR, + "\nSIMH has encountered a bug in SDL2 (or in SIMH's use of SDL2).\n"); +sim_messagef (SCPE_IERR, + "Create an issue at https://github.com/simh/simh/issues and report\n"); +sim_messagef (SCPE_IERR, + "your simulator setup and the above details\n"); return NULL; } @@ -590,7 +606,7 @@ user_event.user.data1 = vptr; user_event.user.data2 = NULL; SDL_PushEvent (&user_event); -while ((!vptr->vid_ready) && (++wait_count < 20)) +while ((!vptr->vid_ready) && (++wait_count < 100)) /* Wait up to 10 seconds for video startup */ sim_os_ms_sleep (100); if (!vptr->vid_ready) { vid_close (); @@ -619,7 +635,7 @@ if (vid_thread_handle == NULL) { vid_close (); return SCPE_OPENERR; } -while ((!vptr->vid_ready) && (++wait_count < 20)) +while ((!vptr->vid_ready) && (++wait_count < 100)) /* Wait up to 10 seconds for video startup */ sim_os_ms_sleep (100); if (!vptr->vid_ready) { vid_close (); @@ -2249,11 +2265,40 @@ SDL_Quit (); return 0; } +/* Optionally dynamically locate and load png support */ +#if defined(SIM_HAVE_DLOPEN) +#include +#endif + const char *vid_version(void) { -static char SDLVersion[160]; +static char SDLVersion[200]; SDL_version compiled = { 0}, running = { 0}; +if (SDLVersion[0] != '\0') /* If we already did this, */ + return (const char *)SDLVersion; /* the result will be the same */ + +#if defined(SIM_DLOPEN_EXTENSION) && defined(HAVE_LIBPNG) +if (1) { + struct PNG_Entry *p; + void *hPNGLib = 0; /* handle to Library */ + +#if defined(SIM_DLOPEN_EXTENSION) + hPNGLib = dlopen("libpng." __STR(SIM_DLOPEN_EXTENSION), RTLD_NOW|RTLD_GLOBAL); + if (!hPNGLib) + hPNGLib = dlopen("libpng." __STR(SIM_DLOPEN_EXTENSION) ".2", RTLD_NOW|RTLD_GLOBAL); + if (!hPNGLib) + hPNGLib = dlopen("libpng." __STR(SIM_DLOPEN_EXTENSION) ".3", RTLD_NOW|RTLD_GLOBAL); +#endif + + for (p = libpng_entries; p->entry_name != NULL; p++) { + if (*p->entry_pointer == NULL) + *p->entry_pointer = dlsym(hPNGLib, p->entry_name); + } + } +#endif /* defined(SIM_DLOPEN_EXTENSION) && defined(HAVE_LIBPNG) */ + +sim_libpng_available = (*libpng_entries->entry_pointer != NULL); SDL_GetVersion(&running); SDL_VERSION(&compiled); @@ -2269,26 +2314,31 @@ else compiled.major, compiled.minor, compiled.patch, running.major, running.minor, running.patch); #if defined (HAVE_LIBPNG) -if (1) { - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); +if (sim_libpng_available) { + png_structp png = p_png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (strcmp (PNG_LIBPNG_VER_STRING, png_get_libpng_ver (png))) + if (strcmp (PNG_LIBPNG_VER_STRING, p_png_get_libpng_ver (png))) { snprintf(&SDLVersion[strlen (SDLVersion)], sizeof (SDLVersion) - (strlen (SDLVersion) + 1), - ", PNG Version (Compiled: %s, Runtime: %s)", - PNG_LIBPNG_VER_STRING, png_get_libpng_ver (png)); + ", PNG Version (Compiled: %s, Runtime: %s - png screenshots disabled)", + PNG_LIBPNG_VER_STRING, p_png_get_libpng_ver (png)); + sim_libpng_available = FALSE; + } else snprintf(&SDLVersion[strlen (SDLVersion)], sizeof (SDLVersion) - (strlen (SDLVersion) + 1), ", PNG Version %s", PNG_LIBPNG_VER_STRING); - png_destroy_read_struct(&png, NULL, NULL); + p_png_destroy_read_struct(&png, NULL, NULL); #if defined (ZLIB_VERSION) - if (strcmp (ZLIB_VERSION, zlibVersion ())) + if (strcmp (ZLIB_VERSION, p_zlibVersion ())) snprintf(&SDLVersion[strlen (SDLVersion)], sizeof (SDLVersion) - (strlen (SDLVersion) + 1), - ", zlib: (Compiled: %s, Runtime: %s)", ZLIB_VERSION, zlibVersion ()); + ", zlib: (Compiled: %s, Runtime: %s)", ZLIB_VERSION, p_zlibVersion ()); else snprintf(&SDLVersion[strlen (SDLVersion)], sizeof (SDLVersion) - (strlen (SDLVersion) + 1), ", zlib: %s", ZLIB_VERSION); #endif } +else + snprintf(&SDLVersion[strlen (SDLVersion)], sizeof (SDLVersion) - (strlen (SDLVersion) + 1), + ", PNG Not currently available"); #endif return (const char *)SDLVersion; } @@ -2594,19 +2644,20 @@ if (1) { SDL_Surface *sshot = sim_end ? SDL_CreateRGBSurface(0, vptr->vid_width, vptr->vid_height, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000) : SDL_CreateRGBSurface(0, vptr->vid_width, vptr->vid_height, 32, 0x0000ff00, 0x000ff000, 0xff000000, 0x000000ff) ; SDL_RenderReadPixels(vptr->vid_renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch); -#if defined(HAVE_LIBPNG) - if (!match_ext (filename, "bmp")) { - sprintf (fullname, "%s%s", filename, match_ext (filename, "png") ? "" : ".png"); - stat = SDL_SavePNG(sshot, fullname); + if (sim_libpng_available) { + if (!match_ext (filename, "bmp")) { + sprintf (fullname, "%s%s", filename, match_ext (filename, "png") ? "" : ".png"); + stat = SDL_SavePNG(sshot, fullname); + } + else { + sprintf (fullname, "%s", filename); + stat = SDL_SaveBMP(sshot, fullname); + } } else { - sprintf (fullname, "%s", filename); + sprintf (fullname, "%s%s", filename, match_ext (filename, "bmp") ? "" : ".bmp"); stat = SDL_SaveBMP(sshot, fullname); } -#else - sprintf (fullname, "%s%s", filename, match_ext (filename, "bmp") ? "" : ".bmp"); - stat = SDL_SaveBMP(sshot, fullname); -#endif /* defined(HAVE_LIBPNG) */ SDL_FreeSurface(sshot); } if (stat) { @@ -2828,8 +2879,8 @@ return; const char *vid_version (void) { #if defined(HAVE_LIBSDL) -static char SDLVersion[160]; -SDL_version compiled, running; +static char SDLVersion[200] = ""; +SDL_version compiled = { 0}, running = { 0}; SDL_GetVersion(&running);