mirror of
https://github.com/simh/simh.git
synced 2026-01-11 23:52:58 +00:00
SCP: Default to dynamically loading LIBEDIT, LIBPCRE and LIBPNG
This commit is contained in:
parent
9932fd1610
commit
63028863e4
@ -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
|
||||
|
||||
|
||||
38
makefile
38
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
|
||||
|
||||
97
scp.c
97
scp.c
@ -239,10 +239,6 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#if defined(SIM_HAVE_DLOPEN) /* Dynamic Readline support */
|
||||
#include <dlfcn.h>
|
||||
#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 <editline/readline.h>
|
||||
#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; i<exp->size; i++) /* shuffle up remaining rules */
|
||||
exp->rules[i] = exp->rules[i+1];
|
||||
@ -13600,10 +13625,8 @@ for (i=0; i<exp->size; 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? */
|
||||
|
||||
@ -6341,15 +6341,13 @@ else
|
||||
_rand_uuid_gen (uuidaddr);
|
||||
}
|
||||
#elif defined (SIM_HAVE_DLOPEN)
|
||||
#include <dlfcn.h>
|
||||
|
||||
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)
|
||||
|
||||
11
sim_ether.c
11
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 <direct.h>
|
||||
#else
|
||||
@ -1245,10 +1248,6 @@ extern "C" {
|
||||
#include <winreg.h>
|
||||
#endif
|
||||
|
||||
#ifdef SIM_HAVE_DLOPEN
|
||||
#include <dlfcn.h>
|
||||
#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 */
|
||||
|
||||
@ -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 <dlfcn.h>
|
||||
#define SIM_DLOPEN_EXTENSION SIM_HAVE_DLOPEN
|
||||
#endif
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#if defined(HAVE_PCRE_H)
|
||||
#include <pcre.h>
|
||||
#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
|
||||
}
|
||||
|
||||
137
sim_video.c
137
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 <png.h>
|
||||
#if defined(HAVE_ZLIB)
|
||||
#include <zlib.h>
|
||||
#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 <SDL.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#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 <dlfcn.h>
|
||||
#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);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user