From 0c4c1d219f23cb1706a22963534c61fc3f622c5c Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 26 Sep 2021 08:45:16 +1000 Subject: [PATCH 01/47] COMMON: Update plugin system loading - Allows loading to be driven by the IMPORT statement - Avoids all or nothing loading at runtime --- configure.ac | 2 +- src/common/Makefile.am | 12 +- src/common/brun.c | 12 +- src/common/eval.c | 4 +- src/common/extlib.c | 622 --------------------- src/common/extlib.h | 149 ----- src/common/file.c | 15 +- src/common/plugins.c | 584 +++++++++++++++++++ src/common/plugins.h | 71 +++ src/common/sbapp.h | 2 +- src/common/scan.c | 16 +- src/common/smbas.h | 1 - src/common/units.c | 4 +- src/common/units.h | 2 +- src/platform/android/build.gradle | 2 +- src/platform/android/jni/common/Android.mk | 2 +- src/platform/android/jni/runtime.cpp | 1 - src/platform/console/device.cpp | 52 +- src/platform/console/main.cpp | 10 +- src/platform/fltk/MainWindow.cxx | 1 - src/platform/sdl/main.cpp | 1 - src/platform/web/main.cpp | 1 - src/ui/system.cpp | 1 - 23 files changed, 724 insertions(+), 843 deletions(-) delete mode 100644 src/common/extlib.c delete mode 100644 src/common/extlib.h create mode 100644 src/common/plugins.c create mode 100644 src/common/plugins.h diff --git a/configure.ac b/configure.ac index a98f6580..b912b4ca 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ dnl This program is distributed under the terms of the GPL v2.0 dnl Download the GNU Public License (GPL) from www.gnu.org dnl -AC_INIT([smallbasic], [12.23]) +AC_INIT([smallbasic], [12.24]) AC_CONFIG_SRCDIR([configure.ac]) AC_CANONICAL_TARGET diff --git a/src/common/Makefile.am b/src/common/Makefile.am index eb3b7670..fef1242f 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -19,7 +19,13 @@ EXTRA_DIST = \ ../include/module.h \ ../include/osd.h \ ../include/var.h \ - ../include/var_map.h + ../include/var_map.h \ + ../lib/jsmn/jsmn.h \ + ../lib/lodepng/lodepng.cpp \ + ../lib/lodepng/lodepng.h \ + ../lib/miniaudio/extras/dr_wav.h \ + ../lib/miniaudio/miniaudio.h \ + ../lib/stb/stb_textedit.h noinst_LIBRARIES = libsb_common.a @@ -42,7 +48,7 @@ libsb_common_a_SOURCES = \ system.c \ random.c \ eval.c \ - extlib.c extlib.h \ + plugins.c plugins.h \ file.c \ ffill.c \ fmt.c fmt.h \ @@ -70,5 +76,3 @@ libsb_common_a_SOURCES = \ sbapp.h \ smbas.h \ sys.h - - diff --git a/src/common/brun.c b/src/common/brun.c index 9e1022b6..3dd9f289 100644 --- a/src/common/brun.c +++ b/src/common/brun.c @@ -15,7 +15,7 @@ #include "common/blib.h" #include "common/str.h" #include "common/fmt.h" -#include "common/extlib.h" +#include "common/plugins.h" #include "common/units.h" #include "common/kw.h" #include "common/var.h" @@ -688,7 +688,7 @@ static inline void bc_loop_call_extp() { prog_error = gsb_last_error; } } else { - slib_procexec(lib, prog_symtable[idx].exp_idx); + plugin_procexec(lib, prog_symtable[idx].exp_idx); } } @@ -1132,12 +1132,12 @@ void brun_load_libraries(int tid) { // update lib-table's task-id field (in this code; not in lib's code) prog_libtable[i].tid = -1; // not a task int lib_id = prog_libtable[i].id; - slib_import(lib_id, 0); + plugin_open(prog_libtable[i].lib, lib_id); // update lib-symbols's task-id field (in this code; not in lib's code) for (int j = 0; j < prog_symcount; j++) { if (prog_symtable[j].lib_id == lib_id) { - prog_symtable[j].exp_idx = slib_get_kid(lib_id, prog_symtable[j].symbol); + prog_symtable[j].exp_idx = plugin_get_kid(lib_id, prog_symtable[j].symbol); prog_symtable[j].task_id = -1; } } @@ -1698,7 +1698,7 @@ int sbasic_main(const char *file) { // initialize managers init_tasks(); unit_mgr_init(); - slib_init(); + plugin_init(); if (prog_error) { success = 0; @@ -1707,7 +1707,7 @@ int sbasic_main(const char *file) { } // clean up managers - slib_close(); + plugin_close(); unit_mgr_close(); destroy_tasks(); diff --git a/src/common/eval.c b/src/common/eval.c index 3087c79c..f40366ca 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -13,7 +13,7 @@ #include "common/kw.h" #include "common/blib.h" #include "common/device.h" -#include "common/extlib.h" +#include "common/plugins.h" #include "common/var_eval.h" #define IP prog_ip @@ -826,7 +826,7 @@ static inline void eval_extf(var_t *r) { if (lib & UID_UNIT_BIT) { unit_exec(lib & (~UID_UNIT_BIT), idx, r); } else { - slib_funcexec(lib, prog_symtable[idx].exp_idx, r); + plugin_funcexec(lib, prog_symtable[idx].exp_idx, r); } } diff --git a/src/common/extlib.c b/src/common/extlib.c deleted file mode 100644 index 377027c4..00000000 --- a/src/common/extlib.c +++ /dev/null @@ -1,622 +0,0 @@ -// This file is part of SmallBASIC -// -// SmallBASIC - External library support (plugins) -// -// This program is distributed under the terms of the GPL v2.0 or later -// Download the GNU Public License (GPL) from www.gnu.org -// -// Copyright(C) 2001 Nicholas Christopoulos - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "common/smbas.h" - -#if defined(__MINGW32__) - #include - #include - #define WIN_EXTLIB - #define LIB_EXT ".dll" -#elif defined(_UnixOS) - #include - #define LNX_EXTLIB - #define LIB_EXT ".so" -#endif - -#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB) -#include "common/extlib.h" -#include "common/pproc.h" -#include - -#define MAX_SLIBS 64 -#define MAX_PARAM 16 -#define TABLE_GROW_SIZE 16 -#define NAME_SIZE 256 -#define PATH_SIZE 1024 - -typedef int (*sblib_exec_fn)(int, int, slib_par_t *, var_t *); -typedef int (*sblib_getname_fn) (int, char *); -typedef int (*sblib_count_fn) (void); -typedef int (*sblib_init_fn) (const char *); -typedef void (*sblib_close_fn) (void); -typedef const char *(*sblib_get_module_name_fn) (void); - -typedef struct { - char name[NAME_SIZE]; - char fullname[PATH_SIZE]; - void *handle; - sblib_exec_fn sblib_proc_exec; - sblib_exec_fn sblib_func_exec; - uint32_t id; - uint32_t flags; - uint8_t imported; - uint32_t proc_count; - uint32_t func_count; - uint32_t proc_list_size; - uint32_t func_list_size; - ext_func_node_t *func_list; - ext_proc_node_t *proc_list; -} slib_t; - -static slib_t slib_list[MAX_SLIBS]; -static uint32_t slib_count; - -#if defined(LNX_EXTLIB) -int slib_llopen(slib_t *lib) { - lib->handle = dlopen(lib->fullname, RTLD_NOW); - if (lib->handle == NULL) { - sc_raise("LIB: error on loading %s\n%s", lib->name, dlerror()); - } - return (lib->handle != NULL); -} - -void *slib_getoptptr(slib_t *lib, const char *name) { - return dlsym(lib->handle, name); -} - -int slib_llclose(slib_t *lib) { - if (!lib->handle) { - return 0; - } - dlclose(lib->handle); - lib->handle = NULL; - return 1; -} - -#elif defined(WIN_EXTLIB) -int slib_llopen(slib_t *lib) { - lib->handle = LoadLibraryA(lib->fullname); - if (lib->handle == NULL) { - int error = GetLastError(); - switch (error) { - case ERROR_MOD_NOT_FOUND: - sc_raise("LIB: DLL dependency error [%d] loading %s [%s]\n", error, lib->fullname, lib->name); - break; - case ERROR_DYNLINK_FROM_INVALID_RING: - sc_raise("LIB: DLL build error [%d] loading %s [%s]\n", error, lib->fullname, lib->name); - break; - default: - sc_raise("LIB: error [%d] loading %s [%s]\n", error, lib->fullname, lib->name); - break; - } - } - return (lib->handle != NULL); -} - -void *slib_getoptptr(slib_t *lib, const char *name) { - return GetProcAddress((HMODULE) lib->handle, name); -} - -int slib_llclose(slib_t *lib) { - if (!lib->handle) { - return 0; - } - FreeLibrary(lib->handle); - lib->handle = NULL; - return 1; -} -#endif - -/** - * returns slib_t* for the given id - */ -slib_t *get_lib(int lib_id) { - if (lib_id < 0 || lib_id >= slib_count) { - return NULL; - } - return &slib_list[lib_id]; -} - -/** - * add an external procedure to the list - */ -int slib_add_external_proc(const char *proc_name, int lib_id) { - slib_t *lib = get_lib(lib_id); - - if (lib->proc_list == NULL) { - lib->proc_list_size = TABLE_GROW_SIZE; - lib->proc_list = (ext_proc_node_t *)malloc(sizeof(ext_proc_node_t) * lib->proc_list_size); - } else if (lib->proc_list_size <= (lib->proc_count + 1)) { - lib->proc_list_size += TABLE_GROW_SIZE; - lib->proc_list = (ext_proc_node_t *)realloc(lib->proc_list, sizeof(ext_proc_node_t) * lib->proc_list_size); - } - - lib->proc_list[lib->proc_count].lib_id = lib_id; - lib->proc_list[lib->proc_count].symbol_index = 0; - strlcpy(lib->proc_list[lib->proc_count].name, proc_name, sizeof(lib->proc_list[lib->proc_count].name)); - strupper(lib->proc_list[lib->proc_count].name); - - if (opt_verbose) { - log_printf("LIB: %d, Idx: %d, PROC '%s'\n", lib_id, lib->proc_count, - lib->proc_list[lib->proc_count].name); - } - lib->proc_count++; - return lib->proc_count - 1; -} - -/** - * Add an external function to the list - */ -int slib_add_external_func(const char *func_name, uint32_t lib_id) { - slib_t *lib = get_lib(lib_id); - - if (lib->func_list == NULL) { - lib->func_list_size = TABLE_GROW_SIZE; - lib->func_list = (ext_func_node_t *)malloc(sizeof(ext_func_node_t) * lib->func_list_size); - } else if (lib->func_list_size <= (lib->func_count + 1)) { - lib->func_list_size += TABLE_GROW_SIZE; - lib->func_list = (ext_func_node_t *) - realloc(lib->func_list, sizeof(ext_func_node_t) * lib->func_list_size); - } - - lib->func_list[lib->func_count].lib_id = lib_id; - lib->func_list[lib->func_count].symbol_index = 0; - strlcpy(lib->func_list[lib->func_count].name, func_name, sizeof(lib->func_list[lib->func_count].name)); - strupper(lib->func_list[lib->func_count].name); - - if (opt_verbose) { - log_printf("LIB: %d, Idx: %d, FUNC '%s'\n", lib_id, lib->func_count, - lib->func_list[lib->func_count].name); - } - lib->func_count++; - return lib->func_count - 1; -} - -/** - * returns the ID of the keyword - */ -int slib_get_kid(int lib_id, const char *name) { - slib_t *lib = get_lib(lib_id); - if (lib != NULL) { - const char *dot = strchr(name, '.'); - const char *field = (dot != NULL ? dot + 1 : name); - for (int i = 0; i < lib->proc_count; i++) { - if (lib->proc_list[i].lib_id == lib_id && - strcmp(lib->proc_list[i].name, field) == 0) { - return i; - } - } - for (int i = 0; i < lib->func_count; i++) { - if (lib->func_list[i].lib_id == lib_id && - strcmp(lib->func_list[i].name, field) == 0) { - return i; - } - } - } - return -1; -} - -/** - * returns the library-id (index of library of the current process) - */ -int slib_get_module_id(const char *name, const char *alias) { - for (int i = 0; i < slib_count; i++) { - slib_t *lib = &slib_list[i]; - if (strcasecmp(lib->name, name) == 0) { - strcpy(lib->name, alias); - return i; - } - } - // not found - return -1; -} - -void slib_import_routines(slib_t *lib, int comp) { - int total = 0; - char buf[SB_KEYWORD_SIZE]; - - lib->sblib_func_exec = slib_getoptptr(lib, "sblib_func_exec"); - lib->sblib_proc_exec = slib_getoptptr(lib, "sblib_proc_exec"); - sblib_count_fn fcount = slib_getoptptr(lib, "sblib_proc_count"); - sblib_getname_fn fgetname = slib_getoptptr(lib, "sblib_proc_getname"); - - if (fcount && fgetname) { - int count = fcount(); - total += count; - for (int i = 0; i < count; i++) { - if (fgetname(i, buf)) { - strupper(buf); - if (!lib->imported && slib_add_external_proc(buf, lib->id) == -1) { - break; - } else if (comp) { - char name[NAME_SIZE]; - strlcpy(name, lib->name, sizeof(name)); - strlcat(name, ".", sizeof(name)); - strlcat(name, buf, sizeof(name)); - strupper(name); - comp_add_external_proc(name, lib->id); - } - } - } - } - - fcount = slib_getoptptr(lib, "sblib_func_count"); - fgetname = slib_getoptptr(lib, "sblib_func_getname"); - - if (fcount && fgetname) { - int count = fcount(); - total += count; - for (int i = 0; i < count; i++) { - if (fgetname(i, buf)) { - strupper(buf); - if (!lib->imported && slib_add_external_func(buf, lib->id) == -1) { - break; - } else if (comp) { - char name[NAME_SIZE]; - strlcpy(name, lib->name, sizeof(name)); - strlcat(name, ".", sizeof(name)); - strlcat(name, buf, sizeof(name)); - strupper(name); - comp_add_external_func(name, lib->id); - } - } - } - } - - if (!total) { - log_printf("LIB: module '%s' has no exports\n", lib->name); - } -} - -/** - * updates compiler with the module's keywords - */ -void slib_import(int lib_id, int comp) { - slib_t *lib = get_lib(lib_id); - if (lib && (comp || !lib->imported)) { - slib_import_routines(lib, comp); - lib->imported = 1; - } - if (lib && !comp) { - sblib_init_fn minit = slib_getoptptr(lib, "sblib_init"); - if (minit && !minit(gsb_last_file)) { - rt_raise("LIB: %s->sblib_init(), failed", lib->name); - } - } -} - -/** - * opens the library - */ -void slib_open(const char *fullname, const char *name) { - int name_index = 0; - - if (strncmp(name, "lib", 3) == 0) { - // libmysql -> store mysql - name_index = 3; - } - - slib_t *lib = &slib_list[slib_count]; - memset(lib, 0, sizeof(slib_t)); - strlcpy(lib->name, name + name_index, NAME_SIZE); - strlcpy(lib->fullname, fullname, PATH_SIZE); - lib->id = slib_count; - lib->imported = 0; - - if (!opt_quiet) { - log_printf("LIB: registering '%s'", fullname); - } - if (slib_llopen(lib)) { - slib_count++; - // override default name - sblib_get_module_name_fn get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); - if (get_module_name) { - strlcpy(lib->name, get_module_name(), NAME_SIZE); - } - } else { - sc_raise("LIB: can't open %s", fullname); - } -} - -/** - * whether name ends with LIB_EXT and does not contain '-', eg libstdc++-6.dll - */ -int slib_is_module(const char *name) { - int result = 0; - if (name && name[0] != '\0') { - int offs = strlen(name) - (sizeof(LIB_EXT) - 1); - result = offs > 0 && strchr(name, '-') == NULL && strcasecmp(name + offs, LIB_EXT) == 0; - } - return result; -} - -void slib_open_path(const char *path, const char *name) { - if (slib_is_module(name)) { - // ends with LIB_EXT - char full[PATH_SIZE]; - char libname[NAME_SIZE]; - - // copy name without extension - strlcpy(libname, name, sizeof(libname)); - char *p = strchr(libname, '.'); - *p = '\0'; - - // copy full path to name - strlcpy(full, path, sizeof(full)); - if (path[strlen(path) - 1] != '/') { - // add trailing separator - strlcat(full, "/", sizeof(full)); - } - strlcat(full, name, sizeof(full)); - slib_open(full, libname); - } -} - -void slib_scan_path(const char *path) { - struct stat stbuf; - if (stat(path, &stbuf) != -1) { - if (S_ISREG(stbuf.st_mode)) { - char *name = strrchr(path, '/'); - slib_open_path(path, (name ? name + 1 : path)); - } else { - DIR *dp = opendir(path); - if (dp != NULL) { - struct dirent *e; - while ((e = readdir(dp)) != NULL) { - char *name = e->d_name; - if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0) { - slib_open_path(path, name); - } - } - closedir(dp); - } - } - } else if (!opt_quiet) { - log_printf("LIB: module path '%s' not found.\n", path); - } -} - -void slib_init_path() { - char *path = opt_modpath; - while (path && path[0] != '\0') { - char *sep = strchr(path, ':'); - if (sep) { - // null terminate the current path - *sep = '\0'; - slib_scan_path(path); - *sep = ':'; - path = sep + 1; - } else { - slib_scan_path(path); - path = NULL; - } - } -} - -/** - * slib-manager: initialize manager - */ -void slib_init() { - slib_count = 0; - - if (!prog_error && opt_loadmod) { - if (opt_modpath[0] == '\0') { - const char *modpath = getenv("SBASICPATH"); - if (modpath != NULL) { - strlcpy(opt_modpath, modpath, OPT_MOD_SZ); - } - } - if (!opt_quiet) { - log_printf("LIB: scanning for modules in '%s'\n", opt_modpath); - } - slib_init_path(); - } -} - -/** - * slib-manager: close everything - */ -void slib_close() { - for (int i = 0; i < slib_count; i++) { - slib_t *lib = &slib_list[i]; - if (lib->handle) { - sblib_close_fn mclose = slib_getoptptr(lib, "sblib_close"); - if (mclose) { - mclose(); - } - slib_llclose(lib); - } - free(lib->proc_list); - free(lib->func_list); - lib->proc_count = 0; - lib->func_count = 0; - lib->proc_list_size = 0; - lib->func_list_size = 0; - lib->func_list = NULL; - lib->proc_list = NULL; - } -} - -/** - * build parameter table - */ -int slib_build_ptable(slib_par_t *ptable) { - int pcount = 0; - var_t *arg; - bcip_t ofs; - - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - code_skipnext(); - byte ready = 0; - do { - byte code = code_peek(); - switch (code) { - case kwTYPE_EOC: - code_skipnext(); - break; - case kwTYPE_SEP: - code_skipsep(); - break; - case kwTYPE_LEVEL_END: - ready = 1; - break; - case kwTYPE_VAR: - // variable - ofs = prog_ip; - if (code_isvar()) { - // push parameter - ptable[pcount].var_p = code_getvarptr(); - ptable[pcount].byref = 1; - pcount++; - break; - } - - // restore IP - prog_ip = ofs; - // no 'break' here - default: - // default --- expression (BYVAL ONLY) - arg = v_new(); - eval(arg); - if (!prog_error) { - // push parameter - ptable[pcount].var_p = arg; - ptable[pcount].byref = 0; - pcount++; - } else { - v_free(arg); - v_detach(arg); - return pcount; - } - } - if (pcount == MAX_PARAM) { - err_parm_limit(MAX_PARAM); - } - } while (!ready && !prog_error); - // kwTYPE_LEVEL_END - code_skipnext(); - } - return pcount; -} - -/** - * free parameter table - */ -void slib_free_ptable(slib_par_t *ptable, int pcount) { - for (int i = 0; i < pcount; i++) { - if (ptable[i].byref == 0) { - v_free(ptable[i].var_p); - v_detach(ptable[i].var_p); - } - } -} - -/** - * execute a function or procedure - */ -int slib_exec(slib_t *lib, var_t *ret, int index, int proc) { - slib_par_t *ptable; - int pcount; - if (code_peek() == kwTYPE_LEVEL_BEGIN) { - ptable = (slib_par_t *)malloc(sizeof(slib_par_t) * MAX_PARAM); - pcount = slib_build_ptable(ptable); - } else { - ptable = NULL; - pcount = 0; - } - if (prog_error) { - slib_free_ptable(ptable, pcount); - free(ptable); - return 0; - } - - int success; - v_init(ret); - if (proc) { - success = lib->sblib_proc_exec(index, pcount, ptable, ret); - } else { - success = lib->sblib_func_exec(index, pcount, ptable, ret); - } - - // error - if (!success) { - if (ret->type == V_STR) { - err_throw("LIB:%s: %s\n", lib->name, ret->v.p.ptr); - } else { - err_throw("LIB:%s: Unspecified error calling %s\n", lib->name, (proc ? "SUB" : "FUNC")); - } - } - - // clean-up - if (ptable) { - slib_free_ptable(ptable, pcount); - free(ptable); - } - - return success; -} - -/** - * execute a procedure - */ -int slib_procexec(int lib_id, int index) { - int result; - slib_t *lib = get_lib(lib_id); - if (lib && lib->sblib_proc_exec) { - var_t ret; - v_init(&ret); - result = slib_exec(lib, &ret, index, 1); - v_free(&ret); - } else { - result = 0; - } - return result; -} - -/** - * execute a function - */ -int slib_funcexec(int lib_id, int index, var_t *ret) { - int result; - slib_t *lib = get_lib(lib_id); - if (lib && lib->sblib_func_exec) { - result = slib_exec(lib, ret, index, 0); - } else { - result = 0; - } - return result; -} - -void *slib_get_func(const char *name) { - void *result = NULL; - for (int i = 0; i < slib_count && result == NULL; i++) { - slib_t *lib = &slib_list[i]; - if (lib->imported) { - result = slib_getoptptr(lib, name); - } - } - return result; -} - -#else -// dummy implementations -int slib_funcexec(int lib_id, int index, var_t *ret) { return 0; } -int slib_procexec(int lib_id, int index) { return 0; } -int slib_get_kid(int lib_id, const char *name) { return -1; } -int slib_get_module_id(const char *name, const char *alias) { return -1; } -void slib_close() {} -void slib_init(int mcount, const char *mlist) {} -void *slib_get_func(const char *name) { return 0; } -void slib_import(int lib_id, int comp) {} -#endif diff --git a/src/common/extlib.h b/src/common/extlib.h deleted file mode 100644 index bfdec042..00000000 --- a/src/common/extlib.h +++ /dev/null @@ -1,149 +0,0 @@ -// This file is part of SmallBASIC -// -// SmallBASIC plugin manager -// -// This program is distributed under the terms of the GPL v2.0 or later -// Download the GNU Public License (GPL) from www.gnu.org -// -// Copyright(C) 2000 Nicholas Christopoulos - -/** - * @ingroup mod - * @page moddoc Modules - * - * The modules are common shared-libraries which can call back the SB's code. - * - * The module-manager loads the modules at the startup and unloads them - * before SB's exit. Which module will be loaded is specified by the - * user in SB's command-line parameters (option -m). The path of the modules - * is predefined to /usr/lib/sbasic/modules and/or /usr/local/lib/sbasic/modules - * - * Standard interface - * - * All modules must provides at least the three following functions: - * sblib_init(), sblib_close, sblib_type(). - * - * the sblib_init() called on startup, the sblib_close() called on SB's exit, - * and the sblib_type() called after sblib_init() to inform the module-manager - * about the type of the module (slib_tp). - * - * Library-modules - * - * The SB before compiles the .bas program, the module-manager - * asks every module about the number of the functions and procedures - * which are supported. (sblib_proc_count, sblib_func_count) - * - * It continues by asking the name of each function and procedure and - * updates the compiler. (sblib_proc_getname, sblib_func_getname) - * - * On the execution time, if the program wants to execute a library's - * procedure or function, the module-manager builds the parameter table - * and calls the sblib_proc_exec or the sblib_func_exec. - * - * See modules/example1.c - * Notes: - * - * Procedure & functions names are limited to 32 characters (33 with \0) - */ - -/** - * @defgroup mod Module Manager - */ -/** - * @defgroup modstd Module interface - Standard - */ -/** - * @defgroup modlib Module interface - Library - */ - -#if !defined(_sb_extlib_h) -#define _sb_extlib_h - -#include "common/sys.h" -#include "common/var.h" -#include "common/device.h" -#include "include/module.h" - -#if defined(__cplusplus) -extern "C" { -#endif - -/** - * @ingroup mod - * - * Initialize module-manager - * - * default path /usr/lib/sbasic/modules/:/usr/local/lib/sbasic/modules/ - */ -void slib_init(); - -/** - * @ingroup mod - * - * close module manager - */ -void slib_close(void); - -/** - * @ingroup mod - * - * set the alias and returns a library's ID - * - * @param name is the name of the library (without the file-extention) - * @param alias updates the internal name with the given alias - * @return the id or -1 for error - */ -int slib_get_module_id(const char *name, const char *alias); - -/** - * @ingroup mod - * - * imports the modules routine and optionally updates the compiler - * with the module (mid) keywords. - */ -void slib_import(int lib_id, int comp); - -/** - * @ingroup mod - * - * returns the ID of the keyword. used at run-time to assign BCs ID with slib_mgr's one - */ -int slib_get_kid(int lib_id, const char *name); - -/** - * @ingroup mod - * - * execute a library's procedure - * - * @param lib is the lib-id - * @param index is the index of the procedure - * @return non-zero on success - */ -int slib_procexec(int lib, int index); - -/** - * @ingroup mod - * - * execute a library's function - * - * @param lib is the lib-id - * @param index is the index of the function - * @param ret is the variable to store the result - * @return non-zero on success - */ -int slib_funcexec(int lib, int index, var_t *ret); - -/** - * @ingroup mod - * - * returns the function from the first available module - * - * @param name the function name - * @return non-zero on success - */ -void *slib_get_func(const char *name); - -#if defined(__cplusplus) -} -#endif -#endif diff --git a/src/common/file.c b/src/common/file.c index d6dae52d..175d3a0b 100644 --- a/src/common/file.c +++ b/src/common/file.c @@ -10,7 +10,6 @@ #include "common/sys.h" #include "common/device.h" #include "common/pproc.h" -#include "common/extlib.h" #include "common/messages.h" #include @@ -158,7 +157,19 @@ int dev_fopen(int sb_handle, const char *name, int flags) { f->handle = -1; f->open_flags = flags; f->drv_data = NULL; - strlcpy(f->name, name, sizeof(f->name)); + + if (name[0] == '~') { + if (getenv("HOME")) { + strlcpy(f->name, getenv("HOME"), sizeof(f->name)); + } else { + strlcpy(f->name, getenv("HOMEDRIVE"), sizeof(f->name)); + strlcpy(f->name, getenv("HOMEPATH"), sizeof(f->name)); + } + strlcpy(f->name, "/", sizeof(f->name)); + strlcpy(f->name, name + 1, sizeof(f->name)); + } else { + strlcpy(f->name, name, sizeof(f->name)); + } f->type = ft_stream; diff --git a/src/common/plugins.c b/src/common/plugins.c new file mode 100644 index 00000000..210b2988 --- /dev/null +++ b/src/common/plugins.c @@ -0,0 +1,584 @@ +// This file is part of SmallBASIC +// +// SmallBASIC - External library support (plugins) +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2001 Nicholas Christopoulos + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "common/smbas.h" + +#if defined(__MINGW32__) + #include + #include + #define WIN_EXTLIB + #define LIB_EXT ".dll" +#elif defined(_UnixOS) + #include + #define LNX_EXTLIB + #define LIB_EXT ".so" +#endif + +#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB) +#include "common/plugins.h" +#include "common/pproc.h" +#include + +#define MAX_SLIBS 64 +#define MAX_PARAM 16 +#define TABLE_GROW_SIZE 16 +#define NAME_SIZE 256 +#define PATH_SIZE 1024 + +typedef int (*sblib_exec_fn)(int, int, slib_par_t *, var_t *); +typedef int (*sblib_getname_fn) (int, char *); +typedef int (*sblib_count_fn) (void); +typedef int (*sblib_init_fn) (const char *); +typedef void (*sblib_close_fn) (void); +typedef const char *(*sblib_get_module_name_fn) (void); + +typedef struct { + char _fullname[PATH_SIZE]; + char _name[NAME_SIZE]; + void *_handle; + sblib_exec_fn _sblib_proc_exec; + sblib_exec_fn _sblib_func_exec; + ext_func_node_t *_func_list; + ext_proc_node_t *_proc_list; + uint32_t _id; + uint32_t _flags; + uint32_t _proc_count; + uint32_t _func_count; + uint32_t _proc_list_size; + uint32_t _func_list_size; + uint8_t _imported; +} slib_t; + +static slib_t *plugins[MAX_SLIBS]; + +#if defined(LNX_EXTLIB) +int slib_llopen(slib_t *lib) { + lib->_handle = dlopen(lib->_fullname, RTLD_NOW); + if (lib->_handle == NULL) { + sc_raise("LIB: error on loading %s\n%s", lib->_name, dlerror()); + } + return (lib->_handle != NULL); +} + +void *slib_getoptptr(slib_t *lib, const char *name) { + return dlsym(lib->_handle, name); +} + +static int slib_llclose(slib_t *lib) { + if (!lib->_handle) { + return 0; + } + dlclose(lib->_handle); + lib->_handle = NULL; + return 1; +} + +#elif defined(WIN_EXTLIB) +static int slib_llopen(slib_t *lib) { + lib->_handle = LoadLibraryA(lib->_fullname); + if (lib->_handle == NULL) { + int error = GetLastError(); + switch (error) { + case ERROR_MOD_NOT_FOUND: + sc_raise("LIB: DLL dependency error [%d] loading %s [%s]\n", error, lib->_fullname, lib->_name); + break; + case ERROR_DYNLINK_FROM_INVALID_RING: + sc_raise("LIB: DLL build error [%d] loading %s [%s]\n", error, lib->_fullname, lib->_name); + break; + default: + sc_raise("LIB: error [%d] loading %s [%s]\n", error, lib->_fullname, lib->_name); + break; + } + } + return (lib->_handle != NULL); +} + +static void *slib_getoptptr(slib_t *lib, const char *name) { + return GetProcAddress((HMODULE) lib->_handle, name); +} + +static int slib_llclose(slib_t *lib) { + if (!lib->_handle) { + return 0; + } + FreeLibrary(lib->_handle); + lib->_handle = NULL; + return 1; +} +#endif + +// +// returns slib_t* for the given id +// +static slib_t *get_lib(int lib_id) { + if (lib_id < 0 || lib_id >= MAX_SLIBS) { + return NULL; + } + return plugins[lib_id]; +} + +// +// add an external procedure to the list +// +static int slib_add_external_proc(const char *proc_name, int lib_id) { + slib_t *lib = get_lib(lib_id); + + if (lib->_proc_list == NULL) { + lib->_proc_list_size = TABLE_GROW_SIZE; + lib->_proc_list = (ext_proc_node_t *)malloc(sizeof(ext_proc_node_t) * lib->_proc_list_size); + } else if (lib->_proc_list_size <= (lib->_proc_count + 1)) { + lib->_proc_list_size += TABLE_GROW_SIZE; + lib->_proc_list = (ext_proc_node_t *)realloc(lib->_proc_list, sizeof(ext_proc_node_t) * lib->_proc_list_size); + } + + lib->_proc_list[lib->_proc_count].lib_id = lib_id; + lib->_proc_list[lib->_proc_count].symbol_index = 0; + strlcpy(lib->_proc_list[lib->_proc_count].name, proc_name, sizeof(lib->_proc_list[lib->_proc_count].name)); + strupper(lib->_proc_list[lib->_proc_count].name); + + if (opt_verbose) { + log_printf("LIB: %d, Idx: %d, PROC '%s'\n", lib_id, lib->_proc_count, + lib->_proc_list[lib->_proc_count].name); + } + lib->_proc_count++; + return lib->_proc_count - 1; +} + +// +// Add an external function to the list +// +static int slib_add_external_func(const char *func_name, uint32_t lib_id) { + slib_t *lib = get_lib(lib_id); + + if (lib->_func_list == NULL) { + lib->_func_list_size = TABLE_GROW_SIZE; + lib->_func_list = (ext_func_node_t *)malloc(sizeof(ext_func_node_t) * lib->_func_list_size); + } else if (lib->_func_list_size <= (lib->_func_count + 1)) { + lib->_func_list_size += TABLE_GROW_SIZE; + lib->_func_list = (ext_func_node_t *) + realloc(lib->_func_list, sizeof(ext_func_node_t) * lib->_func_list_size); + } + + lib->_func_list[lib->_func_count].lib_id = lib_id; + lib->_func_list[lib->_func_count].symbol_index = 0; + strlcpy(lib->_func_list[lib->_func_count].name, func_name, sizeof(lib->_func_list[lib->_func_count].name)); + strupper(lib->_func_list[lib->_func_count].name); + + if (opt_verbose) { + log_printf("LIB: %d, Idx: %d, FUNC '%s'\n", lib_id, lib->_func_count, + lib->_func_list[lib->_func_count].name); + } + lib->_func_count++; + return lib->_func_count - 1; +} + +static void slib_import_routines(slib_t *lib, int comp) { + int total = 0; + char buf[SB_KEYWORD_SIZE]; + + lib->_sblib_func_exec = slib_getoptptr(lib, "sblib_func_exec"); + lib->_sblib_proc_exec = slib_getoptptr(lib, "sblib_proc_exec"); + sblib_count_fn fcount = slib_getoptptr(lib, "sblib_proc_count"); + sblib_getname_fn fgetname = slib_getoptptr(lib, "sblib_proc_getname"); + + if (fcount && fgetname) { + int count = fcount(); + total += count; + for (int i = 0; i < count; i++) { + if (fgetname(i, buf)) { + strupper(buf); + if (!lib->_imported && slib_add_external_proc(buf, lib->_id) == -1) { + break; + } else if (comp) { + char name[NAME_SIZE]; + strlcpy(name, lib->_name, sizeof(name)); + strlcat(name, ".", sizeof(name)); + strlcat(name, buf, sizeof(name)); + strupper(name); + comp_add_external_proc(name, lib->_id); + } + } + } + } + + fcount = slib_getoptptr(lib, "sblib_func_count"); + fgetname = slib_getoptptr(lib, "sblib_func_getname"); + + if (fcount && fgetname) { + int count = fcount(); + total += count; + for (int i = 0; i < count; i++) { + if (fgetname(i, buf)) { + strupper(buf); + if (!lib->_imported && slib_add_external_func(buf, lib->_id) == -1) { + break; + } else if (comp) { + char name[NAME_SIZE]; + strlcpy(name, lib->_name, sizeof(name)); + strlcat(name, ".", sizeof(name)); + strlcat(name, buf, sizeof(name)); + strupper(name); + comp_add_external_func(name, lib->_id); + } + } + } + } + + if (!total) { + log_printf("LIB: module '%s' has no exports\n", lib->_name); + } +} + +// +// whether name ends with LIB_EXT and does not contain '-', eg libstdc++-6.dll +// +static int slib_is_module(const char *name) { + int result = 0; + if (name && name[0] != '\0') { + int offs = strlen(name) - (sizeof(LIB_EXT) - 1); + result = offs > 0 && strchr(name, '-') == NULL && strcasecmp(name + offs, LIB_EXT) == 0; + } + return result; +} + +// +// opens the library +// +static void slib_open(const char *fullname, const char *name) { + int name_index = 0; + + if (strncmp(name, "lib", 3) == 0) { + // libmysql -> store mysql + name_index = 3; + } + + int id = 0; // fixme + slib_t *lib = plugins[id]; + memset(lib, 0, sizeof(slib_t)); + strlcpy(lib->_name, name + name_index, NAME_SIZE); + strlcpy(lib->_fullname, fullname, PATH_SIZE); + lib->_id = id; + lib->_imported = 0; + + if (!opt_quiet) { + log_printf("LIB: registering '%s'", fullname); + } + if (slib_llopen(lib)) { + // override default name + sblib_get_module_name_fn get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); + if (get_module_name) { + strlcpy(lib->_name, get_module_name(), NAME_SIZE); + } + } else { + sc_raise("LIB: can't open %s", fullname); + } +} + +// +// +// +static void slib_open_path(const char *path, const char *name) { + if (slib_is_module(name)) { + // ends with LIB_EXT + char full[PATH_SIZE]; + char libname[NAME_SIZE]; + + // copy name without extension + strlcpy(libname, name, sizeof(libname)); + char *p = strchr(libname, '.'); + *p = '\0'; + + // copy full path to name + strlcpy(full, path, sizeof(full)); + if (path[strlen(path) - 1] != '/') { + // add trailing separator + strlcat(full, "/", sizeof(full)); + } + strlcat(full, name, sizeof(full)); + slib_open(full, libname); + } +} + +// +// build parameter table +// +static int slib_build_ptable(slib_par_t *ptable) { + int pcount = 0; + var_t *arg; + bcip_t ofs; + + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + code_skipnext(); + byte ready = 0; + do { + byte code = code_peek(); + switch (code) { + case kwTYPE_EOC: + code_skipnext(); + break; + case kwTYPE_SEP: + code_skipsep(); + break; + case kwTYPE_LEVEL_END: + ready = 1; + break; + case kwTYPE_VAR: + // variable + ofs = prog_ip; + if (code_isvar()) { + // push parameter + ptable[pcount].var_p = code_getvarptr(); + ptable[pcount].byref = 1; + pcount++; + break; + } + + // restore IP + prog_ip = ofs; + // no 'break' here + default: + // default --- expression (BYVAL ONLY) + arg = v_new(); + eval(arg); + if (!prog_error) { + // push parameter + ptable[pcount].var_p = arg; + ptable[pcount].byref = 0; + pcount++; + } else { + v_free(arg); + v_detach(arg); + return pcount; + } + } + if (pcount == MAX_PARAM) { + err_parm_limit(MAX_PARAM); + } + } while (!ready && !prog_error); + // kwTYPE_LEVEL_END + code_skipnext(); + } + return pcount; +} + +// +// free parameter table +// +static void slib_free_ptable(slib_par_t *ptable, int pcount) { + for (int i = 0; i < pcount; i++) { + if (ptable[i].byref == 0) { + v_free(ptable[i].var_p); + v_detach(ptable[i].var_p); + } + } +} + +// +// execute a function or procedure +// +static int slib_exec(slib_t *lib, var_t *ret, int index, int proc) { + slib_par_t *ptable; + int pcount; + if (code_peek() == kwTYPE_LEVEL_BEGIN) { + ptable = (slib_par_t *)malloc(sizeof(slib_par_t) * MAX_PARAM); + pcount = slib_build_ptable(ptable); + } else { + ptable = NULL; + pcount = 0; + } + if (prog_error) { + slib_free_ptable(ptable, pcount); + free(ptable); + return 0; + } + + int success; + v_init(ret); + if (proc) { + success = lib->_sblib_proc_exec(index, pcount, ptable, ret); + } else { + success = lib->_sblib_func_exec(index, pcount, ptable, ret); + } + + // error + if (!success) { + if (ret->type == V_STR) { + err_throw("LIB:%s: %s\n", lib->_name, ret->v.p.ptr); + } else { + err_throw("LIB:%s: Unspecified error calling %s\n", lib->_name, (proc ? "SUB" : "FUNC")); + } + } + + // clean-up + if (ptable) { + slib_free_ptable(ptable, pcount); + free(ptable); + } + + return success; +} + +void plugin_init() { + for (int i = 0; i < MAX_SLIBS; i++) { + plugins[i] = NULL; + } +} + +int plugin_find(const char *file, const char *alias) { + // TODO fixme + //strcpy(file, name); + //strcat(file, ".bas"); + + // find in SBASICPATH + if (getenv("SBASICPATH")) { + if (sys_search_path(getenv("SBASICPATH"), file, NULL)) { + return 1; + } + } + + // find in program launch directory + if (gsb_bas_dir[0] && sys_search_path(gsb_bas_dir, file, NULL)) { + return 1; + } + + // TODO find in opt_modpath + + // find in current directory + if (sys_search_path(".", file, NULL)) { + return 1; + } + + // create corresponding sbu path version + //strcpy(unitname, bas_file); + + //if (strcmp(comp_file_name, bas_file) == 0) { + // unit and program are the same + // return -1; + //} + return -1; +} + +void plugin_import(int lib_id) { + // TODO fixme + slib_t *lib = get_lib(lib_id); + if (lib && !lib->_imported) { + slib_import_routines(lib, 1); + lib->_imported = 1; + } +} + +void plugin_open(const char *name, int lib_id) { + // TODO fixme + slib_t *lib = get_lib(lib_id); + if (lib && !lib->_imported) { + slib_import_routines(lib, 0); + lib->_imported = 1; + } + if (lib) { + sblib_init_fn minit = slib_getoptptr(lib, "sblib_init"); + if (minit && !minit(gsb_last_file)) { + rt_raise("LIB: %s->sblib_init(), failed", lib->_name); + } + } +} + +int plugin_get_kid(int lib_id, const char *name) { + slib_t *lib = get_lib(lib_id); + if (lib != NULL) { + const char *dot = strchr(name, '.'); + const char *field = (dot != NULL ? dot + 1 : name); + for (int i = 0; i < lib->_proc_count; i++) { + if (lib->_proc_list[i].lib_id == lib_id && + strcmp(lib->_proc_list[i].name, field) == 0) { + return i; + } + } + for (int i = 0; i < lib->_func_count; i++) { + if (lib->_func_list[i].lib_id == lib_id && + strcmp(lib->_func_list[i].name, field) == 0) { + return i; + } + } + } + return -1; +} + +void *plugin_get_func(const char *name) { + void *result = NULL; + for (int i = 0; i < MAX_SLIBS && result == NULL; i++) { + if (plugins[i]) { + slib_t *lib = plugins[i]; + if (lib->_imported) { + result = slib_getoptptr(lib, name); + } + } + } + return result; +} + +int plugin_procexec(int lib_id, int index) { + int result; + slib_t *lib = get_lib(lib_id); + if (lib && lib->_sblib_proc_exec) { + var_t ret; + v_init(&ret); + result = slib_exec(lib, &ret, index, 1); + v_free(&ret); + } else { + result = 0; + } + return result; +} + +int plugin_funcexec(int lib_id, int index, var_t *ret) { + int result; + slib_t *lib = get_lib(lib_id); + if (lib && lib->_sblib_func_exec) { + result = slib_exec(lib, ret, index, 0); + } else { + result = 0; + } + return result; +} + +void plugin_close() { + for (int i = 0; i < MAX_SLIBS; i++) { + if (plugins[i]) { + slib_t *lib = plugins[i]; + if (lib->_handle) { + sblib_close_fn mclose = slib_getoptptr(lib, "sblib_close"); + if (mclose) { + mclose(); + } + slib_llclose(lib); + } + free(lib->_proc_list); + free(lib->_func_list); + free(lib); + } + plugins[i] = NULL; + } +} + +#else +// dummy implementations +int plugin_find(const char *file, char *alias) { return -1; } +int plugin_funcexec(int lib_id, int index, var_t *ret) { return -1; } +int plugin_get_kid(int lib_id, const char *keyword) { return -1; } +int plugin_procexec(int lib_id, int index) { return -1; } +void *plugin_get_func(const char *name) { return 0; } +void plugin_close() {} +void plugin_import(int lib_id) {} +void plugin_init() {} +void plugin_open(const char *name, int lib_id) {} +#endif diff --git a/src/common/plugins.h b/src/common/plugins.h new file mode 100644 index 00000000..8ca19d1e --- /dev/null +++ b/src/common/plugins.h @@ -0,0 +1,71 @@ +// This file is part of SmallBASIC +// +// SmallBASIC - External library support (plugins) +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// +// Copyright(C) 2001 Nicholas Christopoulos + +#if !defined(SB_PLUGINS) +#define SB_PLUGINS + +#include "common/sys.h" +#include "common/var.h" +#include "common/device.h" +#include "include/module.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +// +// initialise the plugin system +// +void plugin_init(); + +// +// locates the plugin at compile time and returns the associated ID +// +int plugin_find(const char *file, const char *alias); + +// +// imports the plugin keywords into the compiler +// +void plugin_import(int lib_id); + +// +// opens the plugin ready for execution. reuses any existing compiler data +// when not invoked from an sbx. otherwise creates the name:id association. +// +void plugin_open(const char *name, int lib_id); + +// +// returns the keyword ID +// +int plugin_get_kid(int lib_id, const char *keyword); + +// +// returns the function pointer for the given function name +// +void *plugin_get_func(const char *name); + +// +// executes the plugin procedure at the given index +// +int plugin_procexec(int lib_id, int index); + +// +// executes the plugin function at the given index +// +int plugin_funcexec(int lib_id, int index, var_t *ret); + +// +// closes the plugin system +// +void plugin_close(); + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/src/common/sbapp.h b/src/common/sbapp.h index 7f50d33f..a3e5586a 100644 --- a/src/common/sbapp.h +++ b/src/common/sbapp.h @@ -15,7 +15,7 @@ #include "common/kw.h" #include "common/pproc.h" #include "common/var.h" -#include "common/extlib.h" +#include "common/plugins.h" #include "common/units.h" #if defined(__cplusplus) diff --git a/src/common/scan.c b/src/common/scan.c index 9aac196e..2cf359df 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -14,8 +14,8 @@ #include "common/bc.h" #include "common/scan.h" #include "common/smbas.h" +#include "common/plugins.h" #include "common/units.h" -#include "common/extlib.h" #include "common/messages.h" #include "languages/keywords.en.c" @@ -4277,11 +4277,11 @@ void comp_preproc_import(const char *slist) { // import name strlower(buf); - int uid = slib_get_module_id(buf, alias); + int uid = plugin_find(buf, alias); if (uid != -1) { // store C module lib-record - slib_import(uid, 1); - add_libtable_rec(alias, alias, uid, 0); + plugin_import(uid); + add_libtable_rec(buf, alias, uid, 0); } else { uid = open_unit(buf, alias); if (uid < 0) { @@ -4436,12 +4436,8 @@ char *comp_preproc_options(char *p) { opt_command[OPT_CMD_SZ - 1] = '\0'; } *pe = lc; - } else if (strncmp(LCN_LOAD_MODULES, p, LEN_LDMODULES) == 0 && - opt_modpath[0] != '\0') { - if (!opt_loadmod) { - opt_loadmod = 1; - slib_init(); - } + } else if (strncmp(LCN_LOAD_MODULES, p, LEN_LDMODULES) == 0) { + // does nothing - for backwards compatibility } else { SKIP_SPACES(p); char *pe = p; diff --git a/src/common/smbas.h b/src/common/smbas.h index 8fb9b5e0..6ddce361 100644 --- a/src/common/smbas.h +++ b/src/common/smbas.h @@ -104,7 +104,6 @@ EXTERN byte opt_graphics; /**< command-line option: start in graphics mode */ EXTERN byte opt_quiet; /**< command-line option: quiet */ EXTERN char opt_command[OPT_CMD_SZ]; /**< command-line parameters (COMMAND$) */ EXTERN int opt_base; /**< OPTION BASE x */ -EXTERN byte opt_loadmod; /**< load all modules */ EXTERN char opt_modpath[OPT_MOD_SZ]; /**< Modules path */ EXTERN int opt_verbose; /**< print some additional infos */ EXTERN int opt_ide; /**< 0=no IDE, 1=IDE is linked, 2=IDE is external exe) */ diff --git a/src/common/units.c b/src/common/units.c index 06b9c119..bd4e6bc6 100644 --- a/src/common/units.c +++ b/src/common/units.c @@ -31,9 +31,7 @@ void unit_mgr_init() { * close up */ void unit_mgr_close() { - int i; - - for (i = 0; i < unit_count; i++) { + for (int i = 0; i < unit_count; i++) { if (units[i].status == unit_loaded) { close_unit(i); } diff --git a/src/common/units.h b/src/common/units.h index 2af64a80..590968a2 100644 --- a/src/common/units.h +++ b/src/common/units.h @@ -81,7 +81,7 @@ typedef struct { unit_file_t hdr; /**< data from file */ unit_sym_t *symbols; /**< table of symbols */ -}unit_t; +} unit_t; /** * @ingroup exec diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index 4f2ce36a..24999131 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.1' + classpath 'com.android.tools.build:gradle:7.0.2' } } diff --git a/src/platform/android/jni/common/Android.mk b/src/platform/android/jni/common/Android.mk index 7bf63f18..445948e8 100644 --- a/src/platform/android/jni/common/Android.mk +++ b/src/platform/android/jni/common/Android.mk @@ -33,7 +33,7 @@ LOCAL_SRC_FILES := \ $(COMMON)/system.c \ $(COMMON)/random.c \ $(COMMON)/eval.c \ - $(COMMON)/extlib.c \ + $(COMMON)/plugins.c \ $(COMMON)/file.c \ $(COMMON)/ffill.c \ $(COMMON)/fmt.c \ diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index 5ef2bb75..e3a7b261 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -475,7 +475,6 @@ void Runtime::runShell() { os_graphics = 1; os_color_depth = 16; opt_mute_audio = 0; - opt_loadmod = 0; _app->activity->callbacks->onContentRectChanged = onContentRectChanged; loadConfig(); diff --git a/src/platform/console/device.cpp b/src/platform/console/device.cpp index 28b331aa..92060041 100644 --- a/src/platform/console/device.cpp +++ b/src/platform/console/device.cpp @@ -10,7 +10,7 @@ #include "config.h" #include "include/osd.h" #include "common/device.h" -#include "common/extlib.h" +#include "common/plugins.h" #include "common/smbas.h" #define WAIT_INTERVAL 5 @@ -92,31 +92,31 @@ void console_init() { // initialize driver int osd_devinit() { - p_arc = (arc_fn)slib_get_func("sblib_arc"); - p_audio = (audio_fn)slib_get_func("sblib_audio"); - p_beep = (beep_fn)slib_get_func("sblib_beep"); - p_clear_sound_queue = (clear_sound_queue_fn)slib_get_func("sblib_clear_sound_queue"); - p_cls = (cls_fn)slib_get_func("sblib_cls"); - p_ellipse = (ellipse_fn)slib_get_func("sblib_ellipse"); - p_events = (events_fn)slib_get_func("sblib_events"); - p_getpen = (getpen_fn)slib_get_func("sblib_getpen"); - p_getpixel = (getpixel_fn)slib_get_func("sblib_getpixel"); - p_getx = (getx_fn)slib_get_func("sblib_getx"); - p_gety = (gety_fn)slib_get_func("sblib_gety"); - p_line = (line_fn)slib_get_func("sblib_line"); - p_rect = (rect_fn)slib_get_func("sblib_rect"); - p_refresh = (refresh_fn)slib_get_func("sblib_refresh"); - p_setcolor = (setcolor_fn)slib_get_func("sblib_setcolor"); - p_setpenmode = (setpenmode_fn)slib_get_func("sblib_setpenmode"); - p_setpixel = (setpixel_fn)slib_get_func("sblib_setpixel"); - p_settextcolor = (settextcolor_fn)slib_get_func("sblib_settextcolor"); - p_setxy = (setxy_fn)slib_get_func("sblib_setxy"); - p_sound = (sound_fn)slib_get_func("sblib_sound"); - p_textheight = (textheight_fn)slib_get_func("sblib_textheight"); - p_textwidth = (textwidth_fn)slib_get_func("sblib_textwidth"); - p_write = (write_fn)slib_get_func("sblib_write"); - - init_fn devinit = (init_fn)slib_get_func("sblib_devinit"); + p_arc = (arc_fn)plugin_get_func("sblib_arc"); + p_audio = (audio_fn)plugin_get_func("sblib_audio"); + p_beep = (beep_fn)plugin_get_func("sblib_beep"); + p_clear_sound_queue = (clear_sound_queue_fn)plugin_get_func("sblib_clear_sound_queue"); + p_cls = (cls_fn)plugin_get_func("sblib_cls"); + p_ellipse = (ellipse_fn)plugin_get_func("sblib_ellipse"); + p_events = (events_fn)plugin_get_func("sblib_events"); + p_getpen = (getpen_fn)plugin_get_func("sblib_getpen"); + p_getpixel = (getpixel_fn)plugin_get_func("sblib_getpixel"); + p_getx = (getx_fn)plugin_get_func("sblib_getx"); + p_gety = (gety_fn)plugin_get_func("sblib_gety"); + p_line = (line_fn)plugin_get_func("sblib_line"); + p_rect = (rect_fn)plugin_get_func("sblib_rect"); + p_refresh = (refresh_fn)plugin_get_func("sblib_refresh"); + p_setcolor = (setcolor_fn)plugin_get_func("sblib_setcolor"); + p_setpenmode = (setpenmode_fn)plugin_get_func("sblib_setpenmode"); + p_setpixel = (setpixel_fn)plugin_get_func("sblib_setpixel"); + p_settextcolor = (settextcolor_fn)plugin_get_func("sblib_settextcolor"); + p_setxy = (setxy_fn)plugin_get_func("sblib_setxy"); + p_sound = (sound_fn)plugin_get_func("sblib_sound"); + p_textheight = (textheight_fn)plugin_get_func("sblib_textheight"); + p_textwidth = (textwidth_fn)plugin_get_func("sblib_textwidth"); + p_write = (write_fn)plugin_get_func("sblib_write"); + + init_fn devinit = (init_fn)plugin_get_func("sblib_devinit"); if (devinit) { devinit(prog_file, opt_pref_width, opt_pref_height); } diff --git a/src/platform/console/main.cpp b/src/platform/console/main.cpp index c6d23e9b..eb7e2d84 100644 --- a/src/platform/console/main.cpp +++ b/src/platform/console/main.cpp @@ -218,7 +218,7 @@ void decompile(const char *path) { opt_nosave = 1; init_tasks(); unit_mgr_init(); - slib_init(); + plugin_init(); if (sbasic_compile(path)) { int exec_tid = sbasic_exec_prepare(path); @@ -229,7 +229,7 @@ void decompile(const char *path) { // cleanup unit_mgr_close(); - slib_close(); + plugin_close(); destroy_tasks(); chdir(prev_cwd); } @@ -286,7 +286,6 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { opt_nosave = 0; break; case 'm': - opt_loadmod = 1; if (optarg) { strcpy(opt_modpath, optarg); } @@ -317,10 +316,6 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { } } - if (getenv("SBASICPATH") != nullptr) { - opt_loadmod = 1; - } - if (strcmp("--", argv[argc - 1]) == 0) { if (*runFile != nullptr) { // run file already set @@ -370,7 +365,6 @@ int main(int argc, char *argv[]) { opt_modpath[0] = '\0'; opt_file_permitted = 1; opt_ide = 0; - opt_loadmod = 0; opt_nosave = 1; opt_pref_height = 0; opt_pref_width = 0; diff --git a/src/platform/fltk/MainWindow.cxx b/src/platform/fltk/MainWindow.cxx index 7c84fb8a..e7abbf07 100644 --- a/src/platform/fltk/MainWindow.cxx +++ b/src/platform/fltk/MainWindow.cxx @@ -764,7 +764,6 @@ int arg_cb(int argc, char **argv, int &i) { return 1; case 'm': - opt_loadmod = 1; strcpy(opt_modpath, argv[i + 1]); i += 2; return 1; diff --git a/src/platform/sdl/main.cpp b/src/platform/sdl/main.cpp index e660f0bb..5182883d 100644 --- a/src/platform/sdl/main.cpp +++ b/src/platform/sdl/main.cpp @@ -322,7 +322,6 @@ int main(int argc, char* argv[]) { ide_option = IDE_INTERNAL; break; case 'm': - opt_loadmod = 1; if (optarg) { strcpy(opt_modpath, optarg); } diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index 46297898..5e557511 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -280,7 +280,6 @@ int main(int argc, char **argv) { g_maxTime = atoi(optarg); break; case 'm': - opt_loadmod = 1; if (optarg) { strcpy(opt_modpath, optarg); } diff --git a/src/ui/system.cpp b/src/ui/system.cpp index 94078fa0..db21323b 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -175,7 +175,6 @@ bool System::execute(const char *bas) { enableCursor(true); opt_file_permitted = 1; - opt_loadmod = 0; _output->selectScreen(USER_SCREEN1); _output->resetFont(); _output->flush(true); From 46fd63955cf0921861863370edc8ce37a084e9c8 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 3 Oct 2021 14:25:18 +1000 Subject: [PATCH 02/47] COMMON: Update plugin system loading - wip --- src/common/plugins.c | 141 +++++++++++++++++++++++-------------------- src/common/plugins.h | 2 +- src/common/scan.c | 13 +++- 3 files changed, 86 insertions(+), 70 deletions(-) diff --git a/src/common/plugins.c b/src/common/plugins.c index 210b2988..371f59bc 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -14,14 +14,14 @@ #include "common/smbas.h" #if defined(__MINGW32__) - #include - #include - #define WIN_EXTLIB - #define LIB_EXT ".dll" +#include +#include +#define WIN_EXTLIB +#define LIB_EXT ".dll" #elif defined(_UnixOS) - #include - #define LNX_EXTLIB - #define LIB_EXT ".so" +#include +#define LNX_EXTLIB +#define LIB_EXT ".so" #endif #if defined(LNX_EXTLIB) || defined(WIN_EXTLIB) @@ -166,7 +166,7 @@ static int slib_add_external_func(const char *func_name, uint32_t lib_id) { } else if (lib->_func_list_size <= (lib->_func_count + 1)) { lib->_func_list_size += TABLE_GROW_SIZE; lib->_func_list = (ext_func_node_t *) - realloc(lib->_func_list, sizeof(ext_func_node_t) * lib->_func_list_size); + realloc(lib->_func_list, sizeof(ext_func_node_t) * lib->_func_list_size); } lib->_func_list[lib->_func_count].lib_id = lib_id; @@ -240,30 +240,26 @@ static void slib_import_routines(slib_t *lib, int comp) { } // -// whether name ends with LIB_EXT and does not contain '-', eg libstdc++-6.dll +// opens the library // -static int slib_is_module(const char *name) { - int result = 0; - if (name && name[0] != '\0') { - int offs = strlen(name) - (sizeof(LIB_EXT) - 1); - result = offs > 0 && strchr(name, '-') == NULL && strcasecmp(name + offs, LIB_EXT) == 0; +static void slib_open(const char *path, const char *name, const char *alias, int id) { + char fullname[PATH_SIZE]; + + // copy full path to name + strlcpy(fullname, path, sizeof(fullname)); + if (path[strlen(path) - 1] != '/') { + // add trailing separator + strlcat(fullname, "/", sizeof(fullname)); } - return result; -} + strlcat(fullname, name, sizeof(fullname)); -// -// opens the library -// -static void slib_open(const char *fullname, const char *name) { int name_index = 0; - if (strncmp(name, "lib", 3) == 0) { // libmysql -> store mysql name_index = 3; } - int id = 0; // fixme - slib_t *lib = plugins[id]; + slib_t *lib = plugins[id]; memset(lib, 0, sizeof(slib_t)); strlcpy(lib->_name, name + name_index, NAME_SIZE); strlcpy(lib->_fullname, fullname, PATH_SIZE); @@ -285,28 +281,32 @@ static void slib_open(const char *fullname, const char *name) { } // +// locate the file int the given path or standard locations // -// -static void slib_open_path(const char *path, const char *name) { - if (slib_is_module(name)) { - // ends with LIB_EXT - char full[PATH_SIZE]; - char libname[NAME_SIZE]; - - // copy name without extension - strlcpy(libname, name, sizeof(libname)); - char *p = strchr(libname, '.'); - *p = '\0'; - - // copy full path to name - strlcpy(full, path, sizeof(full)); - if (path[strlen(path) - 1] != '/') { - // add trailing separator - strlcat(full, "/", sizeof(full)); +static int slib_find_path(char *path, int path_size, const char *file) { + int result = 0; + // find in path + if (path[0]) { + result = sys_search_path(path, file, NULL); + } else { + // find in SBASICPATH + if (getenv("SBASICPATH")) { + result = sys_search_path(getenv("SBASICPATH"), file, path); + } + // find in program launch directory + if (!result && gsb_bas_dir[0]) { + result = sys_search_path(gsb_bas_dir, file, path); + } + // find in modpath + if (!result && opt_modpath[0]) { + result = sys_search_path(opt_modpath, file, path); + } + if (!result) { + // find in current directory + result = sys_search_path(".", file, path); } - strlcat(full, name, sizeof(full)); - slib_open(full, libname); } + return result; } // @@ -434,38 +434,45 @@ void plugin_init() { } } -int plugin_find(const char *file, const char *alias) { - // TODO fixme - //strcpy(file, name); - //strcat(file, ".bas"); +int plugin_find(const char *name, const char *alias) { + char path[PATH_SIZE]; + char file[PATH_SIZE]; - // find in SBASICPATH - if (getenv("SBASICPATH")) { - if (sys_search_path(getenv("SBASICPATH"), file, NULL)) { - return 1; - } + if (path[0] == '"') { + strlcpy(path + 1, name, sizeof(path)); + path[strlen(path) - 1] = '\0'; + filep = slash + 1; + } else { + path[0] = '\0'; + filep = name; } - // find in program launch directory - if (gsb_bas_dir[0] && sys_search_path(gsb_bas_dir, file, NULL)) { - return 1; + if (strstr(filep, "lib") != filep) { + strlcpy(file, "lib", sizeof(file)); + strlcat(file, filep, sizeof(file)); + } else { + strlcpy(file, filep, sizeof(file)); } - // TODO find in opt_modpath - - // find in current directory - if (sys_search_path(".", file, NULL)) { - return 1; - } + strlcat(file, LIB_EXT, sizeof(file)); - // create corresponding sbu path version - //strcpy(unitname, bas_file); + int result = -1; + if (slib_find_path(path, sizeof(path), file)) { + for (int i = 0; i < MAX_SLIBS; i++) { + if (!plugins[i]) { + // found free slot + plugins[i] = (slib_t *)calloc(sizeof(slib_t), 1); + if (!plugins[i]) { + break; + } + slib_open(path, name, alias, i); + result = i; + break; + } + } + } - //if (strcmp(comp_file_name, bas_file) == 0) { - // unit and program are the same - // return -1; - //} - return -1; + return result; } void plugin_import(int lib_id) { diff --git a/src/common/plugins.h b/src/common/plugins.h index 8ca19d1e..ddd7fc6c 100644 --- a/src/common/plugins.h +++ b/src/common/plugins.h @@ -27,7 +27,7 @@ void plugin_init(); // // locates the plugin at compile time and returns the associated ID // -int plugin_find(const char *file, const char *alias); +int plugin_find(const char *name, const char *alias); // // imports the plugin keywords into the compiler diff --git a/src/common/scan.c b/src/common/scan.c index 2cf359df..3573cfb0 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -4231,6 +4231,16 @@ void comp_preproc_grmode(const char *source) { * copy the unit name from the source string to the given buffer */ const char *get_unit_name(const char *p, char *buf_p) { + if (*p == '"') { + // copy the quoted string + *buf_p++ = *p++; + while (*p && *p != '"') { + *buf_p++ = *p++; + } + if (*p == '"') { + *buf_p++ = *p++; + } + } while (is_alnum(*p) || *p == '_' || *p == '.') { if (*p == '.') { *buf_p++ = OS_DIRSEP; @@ -4265,12 +4275,11 @@ const char *get_alias(const char *p, char *alias, const char *def) { void comp_preproc_import(const char *slist) { char buf[OS_PATHNAME_SIZE + 1]; char alias[OS_PATHNAME_SIZE + 1]; - const char *p = slist; SKIP_SPACES(p); - while (is_alpha(*p)) { + while (is_alpha(*p) || *p == '"') { // get name - "Import other.Foo => "other/Foo" p = get_unit_name(p, buf); p = get_alias(p, alias, buf); From 437a999962743febc83dade97a19e90fe587de84 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Sun, 3 Oct 2021 21:53:49 +1000 Subject: [PATCH 03/47] COMMON: Update plugin system loading - wip --- src/common/plugins.c | 125 ++++++++++++++++++++++++------------------- src/common/proc.c | 4 +- 2 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/common/plugins.c b/src/common/plugins.c index 371f59bc..0f194d31 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -245,23 +245,18 @@ static void slib_import_routines(slib_t *lib, int comp) { static void slib_open(const char *path, const char *name, const char *alias, int id) { char fullname[PATH_SIZE]; - // copy full path to name strlcpy(fullname, path, sizeof(fullname)); - if (path[strlen(path) - 1] != '/') { - // add trailing separator - strlcat(fullname, "/", sizeof(fullname)); - } - strlcat(fullname, name, sizeof(fullname)); - - int name_index = 0; - if (strncmp(name, "lib", 3) == 0) { - // libmysql -> store mysql - name_index = 3; + if (access(fullname, R_OK) != 0) { + if (path[strlen(path) - 1] != '/') { + // add trailing separator + strlcat(fullname, "/", sizeof(fullname)); + } + strlcat(fullname, name, sizeof(fullname)); } slib_t *lib = plugins[id]; memset(lib, 0, sizeof(slib_t)); - strlcpy(lib->_name, name + name_index, NAME_SIZE); + strlcpy(lib->_name, alias, NAME_SIZE); strlcpy(lib->_fullname, fullname, PATH_SIZE); lib->_id = id; lib->_imported = 0; @@ -280,31 +275,56 @@ static void slib_open(const char *path, const char *name, const char *alias, int } } +// +// init path and file from the given name +// +static void slib_init_path(const char *name, char *path, char *file) { + if (name[0] == '"') { + // use quoted string to specify full system path + strlcpy(path, name + 1, PATH_SIZE); + int len = strlen(path); + if (len) { + path[len - 1] = '\0'; + } + // separate file-name from path + char *slash = strrchr(path, '/'); + if (slash) { + strlcpy(file, slash + 1, PATH_SIZE); + *slash = '\0'; + } + } else { + // non-quoted string to specify portable name + path[0] = '\0'; + strlcpy(file, "lib", PATH_SIZE); + strlcat(file, name, PATH_SIZE); + strlcat(file, LIB_EXT, PATH_SIZE); + } +} + // // locate the file int the given path or standard locations // -static int slib_find_path(char *path, int path_size, const char *file) { +static int slib_find_path(char *path, const char *file) { int result = 0; // find in path if (path[0]) { - result = sys_search_path(path, file, NULL); - } else { - // find in SBASICPATH - if (getenv("SBASICPATH")) { - result = sys_search_path(getenv("SBASICPATH"), file, path); - } - // find in program launch directory - if (!result && gsb_bas_dir[0]) { - result = sys_search_path(gsb_bas_dir, file, path); - } - // find in modpath - if (!result && opt_modpath[0]) { - result = sys_search_path(opt_modpath, file, path); - } - if (!result) { - // find in current directory - result = sys_search_path(".", file, path); - } + result = sys_search_path(path, file, path); + } + // find in SBASICPATH + if (!result && getenv("SBASICPATH")) { + result = sys_search_path(getenv("SBASICPATH"), file, path); + } + // find in program launch directory + if (!result && gsb_bas_dir[0]) { + result = sys_search_path(gsb_bas_dir, file, path); + } + // find in modpath + if (!result && opt_modpath[0]) { + result = sys_search_path(opt_modpath, file, path); + } + if (!result) { + // find in current directory + result = sys_search_path(".", file, path); } return result; } @@ -435,29 +455,13 @@ void plugin_init() { } int plugin_find(const char *name, const char *alias) { + int result = -1; char path[PATH_SIZE]; char file[PATH_SIZE]; - if (path[0] == '"') { - strlcpy(path + 1, name, sizeof(path)); - path[strlen(path) - 1] = '\0'; - filep = slash + 1; - } else { - path[0] = '\0'; - filep = name; - } + slib_init_path(name, path, file); - if (strstr(filep, "lib") != filep) { - strlcpy(file, "lib", sizeof(file)); - strlcat(file, filep, sizeof(file)); - } else { - strlcpy(file, filep, sizeof(file)); - } - - strlcat(file, LIB_EXT, sizeof(file)); - - int result = -1; - if (slib_find_path(path, sizeof(path), file)) { + if (slib_find_path(path, file)) { for (int i = 0; i < MAX_SLIBS; i++) { if (!plugins[i]) { // found free slot @@ -465,7 +469,7 @@ int plugin_find(const char *name, const char *alias) { if (!plugins[i]) { break; } - slib_open(path, name, alias, i); + slib_open(path, file, alias, i); result = i; break; } @@ -476,7 +480,6 @@ int plugin_find(const char *name, const char *alias) { } void plugin_import(int lib_id) { - // TODO fixme slib_t *lib = get_lib(lib_id); if (lib && !lib->_imported) { slib_import_routines(lib, 1); @@ -485,17 +488,27 @@ void plugin_import(int lib_id) { } void plugin_open(const char *name, int lib_id) { - // TODO fixme slib_t *lib = get_lib(lib_id); - if (lib && !lib->_imported) { - slib_import_routines(lib, 0); - lib->_imported = 1; + if (!lib && lib_id >= 0 && lib_id < MAX_SLIBS) { + lib = plugins[lib_id] = (slib_t *)calloc(sizeof(slib_t), 1); + if (lib) { + char path[PATH_SIZE]; + char file[PATH_SIZE]; + slib_init_path(name, path, file); + slib_open(path, file, name, lib_id); + } } if (lib) { + if (!lib->_imported) { + slib_import_routines(lib, 0); + lib->_imported = 1; + } sblib_init_fn minit = slib_getoptptr(lib, "sblib_init"); if (minit && !minit(gsb_last_file)) { rt_raise("LIB: %s->sblib_init(), failed", lib->_name); } + } else { + rt_raise("LIB: plugin_open failed"); } } diff --git a/src/common/proc.c b/src/common/proc.c index 96344873..ec65ef2c 100644 --- a/src/common/proc.c +++ b/src/common/proc.c @@ -70,7 +70,9 @@ int sys_search_path(const char *path, const char *file, char *retbuf) { strlcpy(cur_path, getenv("HOMEDRIVE"), sizeof(cur_path)); strlcat(cur_path, getenv("HOMEPATH"), sizeof(cur_path)); } - strlcat(cur_path, "/", sizeof(cur_path)); + if (old_path[0] != '/') { + strlcat(cur_path, "/", sizeof(cur_path)); + } strlcat(cur_path, old_path, sizeof(cur_path)); free(old_path); } From de6d8ac46b5fb95681b84706e97daccd033d8dd4 Mon Sep 17 00:00:00 2001 From: Chris Warren-Smith Date: Mon, 4 Oct 2021 13:47:50 +1000 Subject: [PATCH 04/47] COMMON: Update plugin system loading - wip --- src/common/plugins.c | 227 ++++++++++++++++++++++--------------------- src/common/plugins.h | 9 +- src/common/scan.c | 3 +- 3 files changed, 119 insertions(+), 120 deletions(-) diff --git a/src/common/plugins.c b/src/common/plugins.c index 0f194d31..a332d7cb 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -127,6 +127,98 @@ static slib_t *get_lib(int lib_id) { return plugins[lib_id]; } +// +// opens the library +// +static int slib_open(const char *path, const char *name, const char *alias, int id) { + char fullname[PATH_SIZE]; + + strlcpy(fullname, path, sizeof(fullname)); + if (access(fullname, R_OK) != 0) { + if (path[strlen(path) - 1] != '/') { + // add trailing separator + strlcat(fullname, "/", sizeof(fullname)); + } + strlcat(fullname, name, sizeof(fullname)); + } + + slib_t *lib = plugins[id]; + memset(lib, 0, sizeof(slib_t)); + strlcpy(lib->_name, alias, NAME_SIZE); + strlcpy(lib->_fullname, fullname, PATH_SIZE); + lib->_id = id; + lib->_imported = 0; + + if (!opt_quiet) { + log_printf("LIB: registering '%s'", fullname); + } + + int result = slib_llopen(lib); + if (result) { + // override default name + sblib_get_module_name_fn get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); + if (get_module_name) { + strlcpy(lib->_name, get_module_name(), NAME_SIZE); + } + } + return result; +} + +// +// init path and file from the given name +// +static void slib_init_path(const char *name, char *path, char *file) { + if (name[0] == '"') { + // use quoted string to specify full system path + strlcpy(path, name + 1, PATH_SIZE); + int len = strlen(path); + if (len) { + path[len - 1] = '\0'; + } + // separate file-name from path + char *slash = strrchr(path, '/'); + if (slash) { + strlcpy(file, slash + 1, PATH_SIZE); + *slash = '\0'; + } + } else { + // non-quoted string to specify portable name + path[0] = '\0'; + strlcpy(file, "lib", PATH_SIZE); + strlcat(file, name, PATH_SIZE); + strlcat(file, LIB_EXT, PATH_SIZE); + } +} + +// +// locate the file int the given path or standard locations +// +static int slib_find_path(char *path, const char *file) { + int result = 0; + // find in path + if (path[0]) { + result = sys_search_path(path, file, path); + } + // find in SBASICPATH + if (!result && getenv("SBASICPATH")) { + result = sys_search_path(getenv("SBASICPATH"), file, path); + } + // find in program launch directory + if (!result && gsb_bas_dir[0]) { + result = sys_search_path(gsb_bas_dir, file, path); + } + // find in modpath + if (!result && opt_modpath[0]) { + result = sys_search_path(opt_modpath, file, path); + } + if (!result) { + // find in current directory + result = sys_search_path(".", file, path); + } + + return result; +} + // // add an external procedure to the list // @@ -182,6 +274,9 @@ static int slib_add_external_func(const char *func_name, uint32_t lib_id) { return lib->_func_count - 1; } +// +// import functions from the external library +// static void slib_import_routines(slib_t *lib, int comp) { int total = 0; char buf[SB_KEYWORD_SIZE]; @@ -239,96 +334,6 @@ static void slib_import_routines(slib_t *lib, int comp) { } } -// -// opens the library -// -static void slib_open(const char *path, const char *name, const char *alias, int id) { - char fullname[PATH_SIZE]; - - strlcpy(fullname, path, sizeof(fullname)); - if (access(fullname, R_OK) != 0) { - if (path[strlen(path) - 1] != '/') { - // add trailing separator - strlcat(fullname, "/", sizeof(fullname)); - } - strlcat(fullname, name, sizeof(fullname)); - } - - slib_t *lib = plugins[id]; - memset(lib, 0, sizeof(slib_t)); - strlcpy(lib->_name, alias, NAME_SIZE); - strlcpy(lib->_fullname, fullname, PATH_SIZE); - lib->_id = id; - lib->_imported = 0; - - if (!opt_quiet) { - log_printf("LIB: registering '%s'", fullname); - } - if (slib_llopen(lib)) { - // override default name - sblib_get_module_name_fn get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); - if (get_module_name) { - strlcpy(lib->_name, get_module_name(), NAME_SIZE); - } - } else { - sc_raise("LIB: can't open %s", fullname); - } -} - -// -// init path and file from the given name -// -static void slib_init_path(const char *name, char *path, char *file) { - if (name[0] == '"') { - // use quoted string to specify full system path - strlcpy(path, name + 1, PATH_SIZE); - int len = strlen(path); - if (len) { - path[len - 1] = '\0'; - } - // separate file-name from path - char *slash = strrchr(path, '/'); - if (slash) { - strlcpy(file, slash + 1, PATH_SIZE); - *slash = '\0'; - } - } else { - // non-quoted string to specify portable name - path[0] = '\0'; - strlcpy(file, "lib", PATH_SIZE); - strlcat(file, name, PATH_SIZE); - strlcat(file, LIB_EXT, PATH_SIZE); - } -} - -// -// locate the file int the given path or standard locations -// -static int slib_find_path(char *path, const char *file) { - int result = 0; - // find in path - if (path[0]) { - result = sys_search_path(path, file, path); - } - // find in SBASICPATH - if (!result && getenv("SBASICPATH")) { - result = sys_search_path(getenv("SBASICPATH"), file, path); - } - // find in program launch directory - if (!result && gsb_bas_dir[0]) { - result = sys_search_path(gsb_bas_dir, file, path); - } - // find in modpath - if (!result && opt_modpath[0]) { - result = sys_search_path(opt_modpath, file, path); - } - if (!result) { - // find in current directory - result = sys_search_path(".", file, path); - } - return result; -} - // // build parameter table // @@ -454,7 +459,7 @@ void plugin_init() { } } -int plugin_find(const char *name, const char *alias) { +int plugin_import(const char *name, const char *alias) { int result = -1; char path[PATH_SIZE]; char file[PATH_SIZE]; @@ -465,28 +470,25 @@ int plugin_find(const char *name, const char *alias) { for (int i = 0; i < MAX_SLIBS; i++) { if (!plugins[i]) { // found free slot - plugins[i] = (slib_t *)calloc(sizeof(slib_t), 1); - if (!plugins[i]) { + slib_t *lib = plugins[i] = (slib_t *)calloc(sizeof(slib_t), 1); + if (!lib) { + sc_raise("LIB: plugin_import failed"); break; } - slib_open(path, file, alias, i); + if (slib_open(path, file, alias, i)) { + slib_import_routines(lib, 1); + lib->_imported = 1; + } else { + sc_raise("LIB: plugin_import failed"); + } result = i; break; } } } - return result; } -void plugin_import(int lib_id) { - slib_t *lib = get_lib(lib_id); - if (lib && !lib->_imported) { - slib_import_routines(lib, 1); - lib->_imported = 1; - } -} - void plugin_open(const char *name, int lib_id) { slib_t *lib = get_lib(lib_id); if (!lib && lib_id >= 0 && lib_id < MAX_SLIBS) { @@ -495,10 +497,14 @@ void plugin_open(const char *name, int lib_id) { char path[PATH_SIZE]; char file[PATH_SIZE]; slib_init_path(name, path, file); - slib_open(path, file, name, lib_id); + if (!slib_find_path(path, file)) { + rt_raise("LIB: can't open %s", name); + } else if (!slib_open(path, file, file, lib_id)) { + rt_raise("LIB: can't open %s", name); + } } } - if (lib) { + if (lib && !prog_error) { if (!lib->_imported) { slib_import_routines(lib, 0); lib->_imported = 1; @@ -592,13 +598,12 @@ void plugin_close() { #else // dummy implementations -int plugin_find(const char *file, char *alias) { return -1; } -int plugin_funcexec(int lib_id, int index, var_t *ret) { return -1; } +void plugin_init() {} +int plugin_import(const char *name, const char *alias) { return -1; } +void plugin_open(const char *name, int lib_id) { } int plugin_get_kid(int lib_id, const char *keyword) { return -1; } -int plugin_procexec(int lib_id, int index) { return -1; } void *plugin_get_func(const char *name) { return 0; } +int plugin_procexec(int lib_id, int index) { return -1; } +int plugin_funcexec(int lib_id, int index, var_t *ret) { return -1; } void plugin_close() {} -void plugin_import(int lib_id) {} -void plugin_init() {} -void plugin_open(const char *name, int lib_id) {} #endif diff --git a/src/common/plugins.h b/src/common/plugins.h index ddd7fc6c..0496f5cf 100644 --- a/src/common/plugins.h +++ b/src/common/plugins.h @@ -25,14 +25,9 @@ extern "C" { void plugin_init(); // -// locates the plugin at compile time and returns the associated ID +// locates the plugin, imports the keywords and returns the associated ID // -int plugin_find(const char *name, const char *alias); - -// -// imports the plugin keywords into the compiler -// -void plugin_import(int lib_id); +int plugin_import(const char *name, const char *alias); // // opens the plugin ready for execution. reuses any existing compiler data diff --git a/src/common/scan.c b/src/common/scan.c index 3573cfb0..d6d358ee 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -4286,10 +4286,9 @@ void comp_preproc_import(const char *slist) { // import name strlower(buf); - int uid = plugin_find(buf, alias); + int uid = plugin_import(buf, alias); if (uid != -1) { // store C module lib-record - plugin_import(uid); add_libtable_rec(buf, alias, uid, 0); } else { uid = open_unit(buf, alias); From 88b0c2518d8cfada7b888fa88dc53e3f1f9aba61 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 8 Oct 2021 21:07:13 +1000 Subject: [PATCH 05/47] COMMON: --- ChangeLog | 3 ++ src/common/plugins.c | 46 ++++++++++++++++++---------- src/include/module.h | 9 ------ src/platform/android/jni/runtime.cpp | 4 --- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 87d5fe23..b10135c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2021-08-22 (12.24) + COMMON: Update plugin system to allow loading to be driven by the IMPORT statement + 2021-08-22 (12.23) SDL: Validate window dimensions on loading to prevent hidden window diff --git a/src/common/plugins.c b/src/common/plugins.c index a332d7cb..b97452e0 100644 --- a/src/common/plugins.c +++ b/src/common/plugins.c @@ -40,7 +40,6 @@ typedef int (*sblib_getname_fn) (int, char *); typedef int (*sblib_count_fn) (void); typedef int (*sblib_init_fn) (const char *); typedef void (*sblib_close_fn) (void); -typedef const char *(*sblib_get_module_name_fn) (void); typedef struct { char _fullname[PATH_SIZE]; @@ -153,15 +152,7 @@ static int slib_open(const char *path, const char *name, const char *alias, int log_printf("LIB: registering '%s'", fullname); } - int result = slib_llopen(lib); - if (result) { - // override default name - sblib_get_module_name_fn get_module_name = slib_getoptptr(lib, "sblib_get_module_name"); - if (get_module_name) { - strlcpy(lib->_name, get_module_name(), NAME_SIZE); - } - } - return result; + return slib_llopen(lib); } // @@ -184,8 +175,17 @@ static void slib_init_path(const char *name, char *path, char *file) { } else { // non-quoted string to specify portable name path[0] = '\0'; + char *slash = strrchr(name, '/'); strlcpy(file, "lib", PATH_SIZE); - strlcat(file, name, PATH_SIZE); + if (strcmp(name, "android") == 0) { + strlcat(file, "smallbasic", PATH_SIZE); + } else if (slash) { + *slash = '\0'; + strlcat(file, slash + 1, PATH_SIZE); + strlcpy(path, name, PATH_SIZE); + } else { + strlcat(file, name, PATH_SIZE); + } strlcat(file, LIB_EXT, PATH_SIZE); } } @@ -202,14 +202,28 @@ static int slib_find_path(char *path, const char *file) { // find in SBASICPATH if (!result && getenv("SBASICPATH")) { result = sys_search_path(getenv("SBASICPATH"), file, path); - } - // find in program launch directory - if (!result && gsb_bas_dir[0]) { - result = sys_search_path(gsb_bas_dir, file, path); + if (!result && path[0]) { + char rel_path[PATH_SIZE]; + strlcpy(rel_path, getenv("SBASICPATH"), PATH_SIZE); + strlcat(rel_path, "/", PATH_SIZE); + strlcat(rel_path, path, PATH_SIZE); + result = sys_search_path(rel_path, file, path); + } } // find in modpath if (!result && opt_modpath[0]) { result = sys_search_path(opt_modpath, file, path); + if (!result && path[0]) { + char rel_path[PATH_SIZE]; + strlcpy(rel_path, opt_modpath, PATH_SIZE); + strlcat(rel_path, "/", PATH_SIZE); + strlcat(rel_path, path, PATH_SIZE); + result = sys_search_path(rel_path, file, path); + } + } + // find in program launch directory + if (!result && gsb_bas_dir[0]) { + result = sys_search_path(gsb_bas_dir, file, path); } if (!result) { // find in current directory @@ -258,7 +272,7 @@ static int slib_add_external_func(const char *func_name, uint32_t lib_id) { } else if (lib->_func_list_size <= (lib->_func_count + 1)) { lib->_func_list_size += TABLE_GROW_SIZE; lib->_func_list = (ext_func_node_t *) - realloc(lib->_func_list, sizeof(ext_func_node_t) * lib->_func_list_size); + realloc(lib->_func_list, sizeof(ext_func_node_t) * lib->_func_list_size); } lib->_func_list[lib->_func_count].lib_id = lib_id; diff --git a/src/include/module.h b/src/include/module.h index a8ea43b3..1618f3e6 100644 --- a/src/include/module.h +++ b/src/include/module.h @@ -33,15 +33,6 @@ typedef struct { */ int sblib_init(const char *sourceFile); -/** - * @ingroup modlib - * - * returns the module name - * - * @return module name - */ -const char *sblib_get_module_name(); - /** * @ingroup modstd * diff --git a/src/platform/android/jni/runtime.cpp b/src/platform/android/jni/runtime.cpp index e3a7b261..e3f4f20f 100644 --- a/src/platform/android/jni/runtime.cpp +++ b/src/platform/android/jni/runtime.cpp @@ -1342,10 +1342,6 @@ struct LibProcs { {"SPEAK", tts_speak} }; -const char *sblib_get_module_name() { - return "android"; -} - int sblib_proc_count(void) { return (sizeof(lib_procs) / sizeof(lib_procs[0])); } From a86ad4fed7c8a252a8d6e3b410d8c452bedf7a87 Mon Sep 17 00:00:00 2001 From: chrisws Date: Mon, 25 Oct 2021 19:51:25 +1000 Subject: [PATCH 06/47] SDL: trim any trailing newline from error messages --- src/platform/sdl/editor.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/sdl/editor.cpp b/src/platform/sdl/editor.cpp index 811a0e5a..ff8230b3 100644 --- a/src/platform/sdl/editor.cpp +++ b/src/platform/sdl/editor.cpp @@ -234,6 +234,13 @@ void System::editSource(String loadPath, bool restoreOnExit) { editWidget->setCursorRow(gsb_last_line); statusMessage.update(editWidget, _output, true); } else if (gsb_last_error && !isBack()) { + if (gsb_last_errmsg[0]) { + // trim any trailing new-line character + char *nl = strrchr(gsb_last_errmsg, '\n'); + if (nl) { + *nl = '\0'; + } + } String lastFile(gsb_last_file); if (lastFile.endsWith(".sbu")) { _output->setStatus(!gsb_last_errmsg[0] ? "Unit error" : gsb_last_errmsg); From 6c89cabf5bf87b63c9a538c33e26ca6116380e61 Mon Sep 17 00:00:00 2001 From: chrisws Date: Thu, 30 Dec 2021 16:50:10 +1000 Subject: [PATCH 07/47] COMMON: parse JSON with SB ";" dimension syntax --- ChangeLog | 3 +++ samples/distro-examples/tests/array.bas | 6 ++++++ src/common/var_map.c | 10 +++++----- src/lib/jsmn | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index b10135c3..52b2c940 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2021-12-30 (12.24) + COMMON: parse JSON with SB ";" dimension syntax + 2021-08-22 (12.24) COMMON: Update plugin system to allow loading to be driven by the IMPORT statement diff --git a/samples/distro-examples/tests/array.bas b/samples/distro-examples/tests/array.bas index 179fa13a..32c640b9 100644 --- a/samples/distro-examples/tests/array.bas +++ b/samples/distro-examples/tests/array.bas @@ -336,3 +336,9 @@ if (a[1] != [3,4]) then throw "err2" if (a[2] != [5,6]) then throw "err3" if (a[3] != [7,8,9]) then throw "err4" +# parse with jsmn and ";" dimension syntax +const font = {"a" : [1,2,3;4,5,6;7,8,999]} +if (ubound(font["a"],1) != 2) then throw "err1" +if (ubound(font["a"],2) != 2) then throw "err2" +a = font["a"] +if (a[2,2] != 999) then throw "err3" diff --git a/src/common/var_map.c b/src/common/var_map.c index a363a0bc..31d5936d 100644 --- a/src/common/var_map.c +++ b/src/common/var_map.c @@ -499,10 +499,10 @@ int map_create_array(var_p_t dest, JsonTokens *json, int end_position, int index break; } var_t *elem = map_array_list_add(&list, rows, curcol++); - if (token.type == JSMN_PRIMITIVE && json->tokens[0].type == JSMN_ARRAY) { - int len = token.end - token.start; - const char *str = json->js + token.start; - const char *delim = memchr(str, ';', len); + int len = token.end - token.start; + const char *str = json->js + token.start; + const char *delim = memchr(str, ';', len); + if (token.type == JSMN_PRIMITIVE && (delim != NULL || json->tokens[0].type == JSMN_ARRAY)) { if (delim != NULL) { if ((delim - str) > 0) { map_set_primative(elem, str, delim - str); @@ -538,7 +538,7 @@ int map_create_array(var_p_t dest, JsonTokens *json, int end_position, int index cols = curcol; } } - map_build_array(dest, list.head, rows+1, cols); + map_build_array(dest, list.head, rows + 1, cols); return i; } diff --git a/src/lib/jsmn b/src/lib/jsmn index 053d3cd2..25647e69 160000 --- a/src/lib/jsmn +++ b/src/lib/jsmn @@ -1 +1 @@ -Subproject commit 053d3cd29200edb1bfd181d917d140c16c1f8834 +Subproject commit 25647e692c7906b96ffd2b05ca54c097948e879c From 306072f38d3172963e88d86bcb8d6de32a4ab49f Mon Sep 17 00:00:00 2001 From: chrisws Date: Thu, 30 Dec 2021 18:08:36 +1000 Subject: [PATCH 08/47] COMMON: update dependencies --- src/lib/miniaudio | 2 +- src/lib/stb | 2 +- src/ui/audio.cpp | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/lib/miniaudio b/src/lib/miniaudio index 37fe1343..d3d4d425 160000 --- a/src/lib/miniaudio +++ b/src/lib/miniaudio @@ -1 +1 @@ -Subproject commit 37fe1343f04f6fd9bd82229ca50a48b77ecce564 +Subproject commit d3d4d425f12a7b367765d8b21ac5e540c5f41bfb diff --git a/src/lib/stb b/src/lib/stb index 3a117406..af1a5bc3 160000 --- a/src/lib/stb +++ b/src/lib/stb @@ -1 +1 @@ -Subproject commit 3a1174060a7dd4eb652d4e6854bc4cd98c159200 +Subproject commit af1a5bc352164740c1cc1354942b1c6b72eacb8a diff --git a/src/ui/audio.cpp b/src/ui/audio.cpp index 995859b6..9959ad97 100644 --- a/src/ui/audio.cpp +++ b/src/ui/audio.cpp @@ -7,7 +7,6 @@ // #define MINIAUDIO_IMPLEMENTATION -#define DR_WAV_IMPLEMENTATION #include "config.h" #include @@ -16,7 +15,6 @@ #include "include/osd.h" #include "ui/strlib.h" #include "ui/audio.h" -#include "lib/miniaudio/extras/dr_wav.h" #include "lib/miniaudio/miniaudio.h" extern "C" { @@ -87,11 +85,12 @@ static void data_callback(ma_device *device, void *output, const void *input, ma if (queue.empty()) { usleep(MILLIS_TO_MICROS(10)); } else { + ma_uint64 framesRead; queuePos = (queuePos + 1) % queue.size(); Sound *sound = queue[queuePos]; if (sound->_decoder != nullptr) { // play audio track - ma_uint64 framesRead = ma_decoder_read_pcm_frames(sound->_decoder, output, frameCount); + ma_decoder_read_pcm_frames(sound->_decoder, output, frameCount, &framesRead); if (framesRead == 0) { // finished playing queue.pop(false); @@ -99,13 +98,13 @@ static void data_callback(ma_device *device, void *output, const void *input, ma } else if (sound->_start == 0) { // start new sound sound->_start = dev_get_millisecond_count(); - ma_waveform_read_pcm_frames(sound->_tone, (float *)output, frameCount); + ma_waveform_read_pcm_frames(sound->_tone, (float *)output, frameCount, &framesRead); } else if (dev_get_millisecond_count() - sound->_start > sound->_duration) { // sound has timed out queue.pop(false); } else { // continue sound - ma_waveform_read_pcm_frames(sound->_tone, (float *)output, frameCount); + ma_waveform_read_pcm_frames(sound->_tone, (float *)output, frameCount, &framesRead); } } } @@ -133,7 +132,7 @@ static void setup_format(ma_format format, ma_uint32 channels, ma_uint32 sampleR } static void device_start() { - if (ma_device_get_state(&device) != MA_STATE_STARTED) { + if (ma_device_get_state(&device) != ma_device_state_started) { ma_result result = ma_device_start(&device); if (result != MA_SUCCESS) { err_throw("Failed to start audio [%d]", result); From 7cd459af5e873ab54e4de6c02a2254ba87cbebe2 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 31 Dec 2021 14:14:52 +1000 Subject: [PATCH 09/47] UI: align image.save() support between SDL and console versions --- ChangeLog | 3 +++ src/platform/console/image.cpp | 23 +++++++++++++++++++++-- src/ui/image.cpp | 32 +++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/ChangeLog b/ChangeLog index 52b2c940..85509b2e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2021-12-31 (12.24) + UI: add support for image.save("file.png") as per console + 2021-12-30 (12.24) COMMON: parse JSON with SB ";" dimension syntax diff --git a/src/platform/console/image.cpp b/src/platform/console/image.cpp index 04b17965..67c172a1 100644 --- a/src/platform/console/image.cpp +++ b/src/platform/console/image.cpp @@ -375,11 +375,12 @@ void cmd_image_save(var_s *self, var_s *) { dev_file_t *filep = nullptr; byte code = code_peek(); int error = -1; - int w = image->_width; - int h = image->_height; + var_t *array; var_t var; if (!prog_error && image != nullptr) { + unsigned w = image->_width; + unsigned h = image->_height; switch (code) { case kwTYPE_SEP: filep = get_file(); @@ -387,6 +388,24 @@ void cmd_image_save(var_s *self, var_s *) { error = lodepng_encode32_file(filep->name, image->_image, w, h); } break; + case kwTYPE_VAR: + array = par_getvar_ptr(); + v_tomatrix(array, h, w); + // x0 x1 x2 (w=3,h=2) + // y0 rgba rgba rgba ypos=0 + // y1 rgba rgba rgba ypos=12 + // + for (unsigned y = 0; y < h; y++) { + unsigned yoffs = (y * w * 4); + for (unsigned x = 0; x < w; x++) { + uint8_t a, r, g, b; + GET_IMAGE_ARGB(image->_image, yoffs + (x * 4), a, r, g, b); + pixel_t px = v_get_argb_px(a, r, g, b); + unsigned pos = y * w + x; + v_setint(v_elem(array, pos), px); + } + } + break; default: v_init(&var); eval(&var); diff --git a/src/ui/image.cpp b/src/ui/image.cpp index 53dd54c1..ddab2f71 100644 --- a/src/ui/image.cpp +++ b/src/ui/image.cpp @@ -464,23 +464,23 @@ void cmd_image_hide(var_s *self, var_s *) { void cmd_image_save(var_s *self, var_s *) { unsigned id = map_get_int(self, IMG_BID, -1); ImageBuffer *image = get_image(id); - var_t *array = nullptr; - dev_file_t *file = nullptr; - if (code_peek() == kwTYPE_SEP) { - file = eval_filep(); - } else { - array = par_getvar_ptr(); - } - + dev_file_t *file; + var_t *array; + var_t var; bool saved = false; if (!prog_error && image != nullptr) { unsigned w = image->_width; unsigned h = image->_height; - if (file != nullptr && file->open_flags == DEV_FILE_OUTPUT) { - if (!encode_png_file(file->name, image->_image, w, h)) { + switch (code_peek()) { + case kwTYPE_SEP: + file = eval_filep(); + if (file != nullptr && file->open_flags == DEV_FILE_OUTPUT && + !encode_png_file(file->name, image->_image, w, h)) { saved = true; } - } else if (array != nullptr) { + break; + case kwTYPE_VAR: + array = par_getvar_ptr(); v_tomatrix(array, h, w); // x0 x1 x2 (w=3,h=2) // y0 rgba rgba rgba ypos=0 @@ -497,6 +497,16 @@ void cmd_image_save(var_s *self, var_s *) { } } saved = true; + break; + default: + v_init(&var); + eval(&var); + if (var.type == V_STR && !prog_error && + !lodepng_encode32_file(var.v.p.ptr, image->_image, w, h)) { + saved = true; + } + v_free(&var); + break; } } From 4af61e8d7defa4a7cc4f92b87b96cb6c6bb9e4c1 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 14 Jan 2022 15:44:36 +1000 Subject: [PATCH 10/47] COMMON: Fix #131 - Scalar * Vector doesnt work --- ChangeLog | 3 + samples/distro-examples/tests/matrices.bas | 9 +++ src/common/eval.c | 76 ++++++++++++---------- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/ChangeLog b/ChangeLog index 85509b2e..94664432 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-12-31 (12.24) + COMMON: Fix #131 - Scalar * Vector doesnt work + 2021-12-31 (12.24) UI: add support for image.save("file.png") as per console diff --git a/samples/distro-examples/tests/matrices.bas b/samples/distro-examples/tests/matrices.bas index e0bcb904..897b3218 100644 --- a/samples/distro-examples/tests/matrices.bas +++ b/samples/distro-examples/tests/matrices.bas @@ -44,3 +44,12 @@ a1=[1,2,4] a2=[1,4,5] if (a1 * a2 != [1,8,20]) then throw "err" if (a1 % a2 != 29) then throw "err" + +rem - Scalar * Vector doesnt work #131 (https://github.com/smallbasic/SmallBASIC/issues/131) +dim r(2) +v = [5, 10, 10] +s = 2 +r = s * v +if (r[0] != 10) then throw "err1" +if (r[1] != 20) then throw "err2" +if (r[2] != 20) then throw "err3" diff --git a/src/common/eval.c b/src/common/eval.c index f40366ca..5f621e0f 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -32,9 +32,9 @@ v_free((v)); \ } -/** - * matrix: convert var_t to double[r][c] - */ +// +// matrix: convert var_t to double[r][c] +// var_num_t *mat_toc(var_t *v, int32_t *rows, int32_t *cols) { var_num_t *m = NULL; *rows = *cols = 0; @@ -70,9 +70,9 @@ var_num_t *mat_toc(var_t *v, int32_t *rows, int32_t *cols) { return m; } -/** - * matrix: conv. double[nr][nc] to var_t - */ +// +// matrix: conv. double[nr][nc] to var_t +// void mat_tov(var_t *v, var_num_t *m, int rows, int cols, int protect_col1) { if (cols > 1 || protect_col1) { v_tomatrix(v, rows, cols); @@ -89,9 +89,9 @@ void mat_tov(var_t *v, var_num_t *m, int rows, int cols, int protect_col1) { } } -/** - * matrix: 1op - */ +// +// matrix: 1op +// void mat_op1(var_t *l, int op, var_num_t n) { int lr, lc; @@ -114,29 +114,33 @@ void mat_op1(var_t *l, int op, var_num_t n) { } } } - mat_tov(l, m, lr, lc, 1); + if (v_maxdim(l) == 1) { + mat_tov(l, m, lc, 1, 0); + } else { + mat_tov(l, m, lr, lc, 1); + } free(m1); free(m); } } -/** - * M = -A - */ +// +// M = -A +// void mat_antithetos(var_t *v) { mat_op1(v, 'A', 0); } -/** - * M = A - */ +// +// M = A +// void mat_mulN(var_t *v, var_num_t N) { mat_op1(v, '*', N); } -/** - * matrix - add/sub - */ +// +// matrix - add/sub +// void mat_op2(var_t *l, var_t *r, int op) { int lr, lc, rr, rc; @@ -157,8 +161,7 @@ void mat_op2(var_t *l, var_t *r, int op) { } else { m[pos] = m2[pos] - m1[pos]; } - // array is comming reversed because of - // where to store + // array is reversed because of where to store } } } @@ -187,9 +190,9 @@ void mat_sub(var_t *l, var_t *r) { mat_op2(l, r, '-'); } -/** - * matrix: multiply two 1d arrays - */ +// +// matrix: multiply two 1d arrays +// void mat_mul_1d(var_t *l, var_t *r) { uint32_t size = v_asize(l); for (uint32_t i = 0; i < size; i++) { @@ -200,9 +203,9 @@ void mat_mul_1d(var_t *l, var_t *r) { } } -/** - * matrix: dot product of two 1d arrays - */ +// +// matrix: dot product of two 1d arrays +// void mat_dot(var_t *l, var_t *r) { var_num_t result = 0; uint32_t size = v_asize(l); @@ -214,11 +217,12 @@ void mat_dot(var_t *l, var_t *r) { v_setreal(r, result); } -/** - * matrix: multiply - */ +// +// matrix: multiply +// void mat_mul(var_t *l, var_t *r) { int lr, lc, rr, rc; + fprintf(stderr, "MM\n"); var_num_t *m1 = mat_toc(l, &lr, &lc); if (m1) { @@ -255,9 +259,9 @@ void mat_mul(var_t *l, var_t *r) { } } -/** - * The LIKE operator - */ +// +// The LIKE operator +// int v_wc_match(var_t *vwc, var_t *v) { int ri; @@ -1219,9 +1223,9 @@ static inline void eval_callf(var_t *r) { } } -/** - * executes the expression (Code[IP]) and returns the result (r) - */ +// +// executes the expression (Code[IP]) and returns the result (r) +// void eval(var_t *r) { var_t *left = NULL; bcip_t eval_pos = eval_sp; From 43697c08bcc48ee515969c705db8de81fb7e72d4 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 14 Jan 2022 15:57:56 +1000 Subject: [PATCH 11/47] COMMON: Fix #131 - Scalar * Vector doesnt work --- src/common/eval.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/eval.c b/src/common/eval.c index 5f621e0f..12093dfb 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -222,7 +222,6 @@ void mat_dot(var_t *l, var_t *r) { // void mat_mul(var_t *l, var_t *r) { int lr, lc, rr, rc; - fprintf(stderr, "MM\n"); var_num_t *m1 = mat_toc(l, &lr, &lc); if (m1) { From ededf997b79c976f7f4e64f9ac9f57290f9e8c22 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 15 Jan 2022 09:52:45 +1000 Subject: [PATCH 12/47] COMMON: Removed 'meaningless' CDBL, CINT and CREAL. These can be poly-filled with 'DEF' if required for compatibility. --- ChangeLog | 3 +++ documentation/sbasic_ref.csv | 3 --- samples/distro-examples/tests/all.bas | 3 --- samples/distro-examples/tests/output/all.out | 5 +---- src/common/blib_func.c | 9 --------- src/common/eval.c | 2 -- src/common/kw.h | 2 -- src/languages/keywords.en.c | 3 --- 8 files changed, 4 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index 94664432..a56de577 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-12-31 (12.24) + COMMON: Removed 'Meaningless' CDBL, CINT, CREAL. These can be poly-filled with 'DEF' if required. + 2022-12-31 (12.24) COMMON: Fix #131 - Scalar * Vector doesnt work diff --git a/documentation/sbasic_ref.csv b/documentation/sbasic_ref.csv index 1ada9c5d..1e22a7f1 100644 --- a/documentation/sbasic_ref.csv +++ b/documentation/sbasic_ref.csv @@ -26,9 +26,6 @@ Data,command,SEARCH,548,"SEARCH A, key, BYREF ridx [USE cmpfunc]","Scans an arra Data,command,SORT,549,"SORT array [USE cmpfunc]","Sorts an array. The cmpfunc if specified, takes 2 vars to compare and must return: -1 if x < y, +1 if x > y, 0 if x = y." Data,command,SWAP,550,"SWAP a, b","Exchanges the values of two variables. The parameters may be variables of any type." Data,function,ARRAY,1432,"ARRAY [var | expr]","Creates a ARRAY or MAP variable from the given string or expression" -Data,function,CDBL,552,"CDBL (x)","Convert x to 64b real number. Meaningless. Used for compatibility." -Data,function,CINT,553,"CINT (x)","Converts x to 32b integer. Meaningless. Used for compatibility." -Data,function,CREAL,554,"CREAL (x)","Convert x to 64b real number. Meaningless. Used for compatibility." Data,function,ISARRAY,555,"ISARRAY (x)","Returns true if x is an array." Data,function,ISDIR,556,"ISDIR (x)","Returns true if x is a directory." Data,function,ISFILE,557,"ISFILE (x)","Returns true if x is a regular file." diff --git a/samples/distro-examples/tests/all.bas b/samples/distro-examples/tests/all.bas index 1fa63c8f..6f85f909 100644 --- a/samples/distro-examples/tests/all.bas +++ b/samples/distro-examples/tests/all.bas @@ -123,16 +123,13 @@ print "BGETC:" '+ BGETC (fileN) print "BIN:" + BIN (x) print "CAT:" + CAT (x) print "CBS:" + CBS (s) -print "CDBL:" + CDBL (x) print "CEIL:" + CEIL (x) print "CHOP:" + CHOP ("123.45$") print "CHR:" + CHR (87) -print "CINT:" + CINT (x) print "COS:" + COS (x) print "COSH:" + COSH (x) print "COT:" + COT (x) print "COTH:" + COTH (x) -print "CREAL:" + CREAL (x) print "CSC:" + CSC (x) print "CSCH:" + CSCH (x) print "DATE:"' + DATE diff --git a/samples/distro-examples/tests/output/all.out b/samples/distro-examples/tests/output/all.out index f969ccc2..c60404df 100644 --- a/samples/distro-examples/tests/output/all.out +++ b/samples/distro-examples/tests/output/all.out @@ -106,16 +106,13 @@ BGETC: BIN:00000000000000000000000000001100 CAT: CBS:catsanddogs -CDBL:12.3 CEIL:13 CHOP:123.45 CHR:W -CINT:12 COS:0.96473261788661 COSH:109847.99433834482625 COT:-3.66495480230944 COTH:1.00000000004144 -CREAL:12.3 CSC:-3.79893323223389 CSCH:0.00000910348893 DATE: @@ -171,7 +168,7 @@ POINT:0 POLYAREA:0 POLYCENT: POW:12.3 -PROGLINE:191 +PROGLINE:188 PTDISTLN:0 PTDISTSEG:0 PTSIGN:0 diff --git a/src/common/blib_func.c b/src/common/blib_func.c index 31ff8fd9..47f1deca 100644 --- a/src/common/blib_func.c +++ b/src/common/blib_func.c @@ -620,9 +620,6 @@ var_num_t cmd_math1(long funcCode, var_t *arg) { case kwRAD: r = x * M_PI / 180.0; break; - case kwCDBL: - r = x; - break; default: rt_raise("Unsupported built-in function call %ld", funcCode); r = 0.0; @@ -775,12 +772,6 @@ var_int_t cmd_imath1(long funcCode, var_t *arg) { IF_ERR_RETURN_0; switch (funcCode) { - case kwCINT: - // - // int <- CINT(float) - // - r = x; - break; case kwEOF: // // int <- EOF(file-handle) diff --git a/src/common/eval.c b/src/common/eval.c index 12093dfb..a7b47e69 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -1147,7 +1147,6 @@ static inline void eval_callf(var_t *r) { case kwLOG10: case kwFIX: case kwINT: - case kwCDBL: case kwDEG: case kwRAD: case kwPENF: @@ -1158,7 +1157,6 @@ static inline void eval_callf(var_t *r) { break; case kwFRE: case kwSGN: - case kwCINT: case kwEOF: case kwSEEKF: case kwLOF: diff --git a/src/common/kw.h b/src/common/kw.h index e0b607a3..7af0dd8c 100644 --- a/src/common/kw.h +++ b/src/common/kw.h @@ -329,7 +329,6 @@ enum func_keywords { kwLOG10, kwFIX, kwINT, - kwCDBL, kwDEG, kwRAD, kwPENF, @@ -338,7 +337,6 @@ enum func_keywords { kwFRAC, kwFRE, kwSGN, - kwCINT, kwEOF, kwSEEKF, kwLOF, diff --git a/src/languages/keywords.en.c b/src/languages/keywords.en.c index 07507c20..9424f8c4 100644 --- a/src/languages/keywords.en.c +++ b/src/languages/keywords.en.c @@ -269,8 +269,6 @@ struct func_keyword_s func_table[] = { { "LOG10", kwLOG10 }, { "FIX", kwFIX }, { "INT", kwINT }, -{ "CDBL", kwCDBL }, -{ "CREAL", kwCDBL }, { "DEG", kwDEG }, { "RAD", kwRAD }, { "PEN", kwPENF }, @@ -279,7 +277,6 @@ struct func_keyword_s func_table[] = { { "FRAC", kwFRAC }, { "FRE", kwFRE }, { "SGN", kwSGN }, -{ "CINT", kwCINT }, { "EOF", kwEOF }, { "SEEK", kwSEEKF }, { "LOF", kwLOF }, From 89f67d820aebfe1317242bee5fa2f2d6f93411aa Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 16 Jan 2022 12:29:49 +1000 Subject: [PATCH 13/47] COMMON: Implemented window.setLocation(x,y) #102 --- ChangeLog | 7 ++++-- src/lib/lodepng | 2 +- src/lib/miniaudio | 2 +- src/platform/android/jni/runtime.h | 2 +- src/platform/fltk/MainWindow.cxx | 40 ++++++++++++++++-------------- src/platform/fltk/runtime.cxx | 14 ++++++++--- src/platform/fltk/runtime.h | 2 +- src/platform/sdl/runtime.cpp | 15 +++++++---- src/platform/sdl/runtime.h | 2 +- src/ui/system.h | 2 +- src/ui/window.cpp | 10 +++++++- 11 files changed, 63 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index a56de577..7d489049 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,10 @@ -2022-12-31 (12.24) +2022-01-16 (12.24) + COMMON: Implemented window.setLocation(x,y) #102 + +2022-01-15 (12.24) COMMON: Removed 'Meaningless' CDBL, CINT, CREAL. These can be poly-filled with 'DEF' if required. -2022-12-31 (12.24) +2022-01-14 (12.24) COMMON: Fix #131 - Scalar * Vector doesnt work 2021-12-31 (12.24) diff --git a/src/lib/lodepng b/src/lib/lodepng index 8c6a9e30..5601b827 160000 --- a/src/lib/lodepng +++ b/src/lib/lodepng @@ -1 +1 @@ -Subproject commit 8c6a9e30576f07bf470ad6f09458a2dcd7a6a84a +Subproject commit 5601b8272a6850b7c5d693dd0c0e16da50be8d8d diff --git a/src/lib/miniaudio b/src/lib/miniaudio index d3d4d425..e89650cc 160000 --- a/src/lib/miniaudio +++ b/src/lib/miniaudio @@ -1 +1 @@ -Subproject commit d3d4d425f12a7b367765d8b21ac5e540c5f41bfb +Subproject commit e89650cc4bfb71cc4c5535f370798e4282af859e diff --git a/src/platform/android/jni/runtime.h b/src/platform/android/jni/runtime.h index fab5a2d3..3e048917 100644 --- a/src/platform/android/jni/runtime.h +++ b/src/platform/android/jni/runtime.h @@ -62,7 +62,7 @@ struct Runtime : public System { char *loadResource(const char *fileName); void optionsBox(StringList *items); void saveWindowRect() {} - void setWindowSize(int width, int height) {}; + void setWindowRect(int x, int y, int width, int height) {}; void setWindowTitle(const char *title) {} void share(const char *path) { setString("share", path); } void showCursor(CursorType cursorType) {} diff --git a/src/platform/fltk/MainWindow.cxx b/src/platform/fltk/MainWindow.cxx index e7abbf07..d2fc0395 100644 --- a/src/platform/fltk/MainWindow.cxx +++ b/src/platform/fltk/MainWindow.cxx @@ -31,6 +31,7 @@ strlib::String recentPath[NUM_RECENT_ITEMS]; strlib::String recentLabel[NUM_RECENT_ITEMS]; int recentPosition[NUM_RECENT_ITEMS]; MainWindow *wnd; +Fl_Window *outputWindow; ExecState runMode = init_state; const char *fontCache = "fonts.txt"; @@ -110,13 +111,14 @@ bool MainWindow::basicMain(EditorWidget *editWidget, } } - Fl_Window *fullScreen = NULL; + Fl_Window *fullScreen = nullptr; Fl_Group *oldOutputGroup = _outputGroup; int old_w = _out->w(); int old_h = _out->h(); int old_x = _out->x(); int old_y = _out->y(); int interactive = opt_interactive; + outputWindow = this; if (!toolExec) { if (opt_ide == IDE_NONE) { @@ -130,6 +132,7 @@ bool MainWindow::basicMain(EditorWidget *editWidget, setTitle(fullScreen, filename); _profile->restoreAppPosition(fullScreen); _outputGroup = fullScreen; + outputWindow = fullScreen; resizeDisplay(0, 0, w(), h()); hide(); } else { @@ -148,10 +151,11 @@ bool MainWindow::basicMain(EditorWidget *editWidget, } while (restart); + outputWindow = this; opt_interactive = interactive; bool was_break = (runMode == break_state); - if (fullScreen != NULL) { + if (fullScreen != nullptr) { _profile->setAppPosition(*fullScreen); fullScreen->remove(_out); delete fullScreen; @@ -220,7 +224,7 @@ void MainWindow::close_other_tabs(Fl_Widget *w, void *eventData) { int n = _tabGroup->children(); Fl_Group *items[n]; for (int c = 0; c < n; c++) { - items[c] = NULL; + items[c] = nullptr; Fl_Group *child = (Fl_Group *)_tabGroup->child(c); if (child != selected && gw_editor == getGroupWidget(child)) { EditorWidget *editWidget = (EditorWidget *)child->child(0); @@ -230,7 +234,7 @@ void MainWindow::close_other_tabs(Fl_Widget *w, void *eventData) { } } for (int c = 0; c < n; c++) { - if (items[c] != NULL) { + if (items[c] != nullptr) { _tabGroup->remove(items[c]); delete items[c]; } @@ -327,7 +331,7 @@ void MainWindow::help_contents_brief(Fl_Widget *w, void *eventData) { int start, end; char *selection = editWidget->getSelection(&start, &end); const char *help = getBriefHelp(selection); - if (help != NULL) { + if (help != nullptr) { editWidget->getTty()->print(help); editWidget->getTty()->print("\n"); } @@ -364,19 +368,19 @@ void MainWindow::export_file(Fl_Widget *w, void *eventData) { if (dev_fopen(handle, _exportFile, DEV_FILE_OUTPUT)) { const char *data = editWidget->data(); if (token[0]) { - vsncat(buffer, sizeof(buffer), "# ", token, "\n", NULL); + vsncat(buffer, sizeof(buffer), "# ", token, "\n", nullptr); dev_fwrite(handle, (byte *)buffer, strlen(buffer)); } if (!dev_fwrite(handle, (byte *)data, editWidget->dataLength())) { - vsncat(buffer, sizeof(buffer), "Failed to write: ", _exportFile.c_str(), NULL); + vsncat(buffer, sizeof(buffer), "Failed to write: ", _exportFile.c_str(), nullptr); statusMsg(rs_err, buffer); } else { vsncat(buffer, sizeof(buffer), "Exported", editWidget->getFilename(), " to ", - _exportFile.c_str(), NULL); + _exportFile.c_str(), nullptr); statusMsg(rs_ready, buffer); } } else { - vsncat(buffer, sizeof(buffer), "Failed to open: ", _exportFile.c_str(), NULL); + vsncat(buffer, sizeof(buffer), "Failed to open: ", _exportFile.c_str(), nullptr); statusMsg(rs_err, buffer); } dev_fclose(handle); @@ -684,9 +688,9 @@ void MainWindow::scanPlugIns(Fl_Menu_Bar *menu) { snprintf(path, sizeof(path), "%s/%s", packageHome, pluginHome); DIR *dp = opendir(path); - while (dp != NULL) { + while (dp != nullptr) { struct dirent *e = readdir(dp); - if (e == NULL) { + if (e == nullptr) { break; } const char *filename = e->d_name; @@ -1073,7 +1077,7 @@ void MainWindow::new_file(Fl_Widget *w, void *eventData) { } void MainWindow::open_file(Fl_Widget *w, void *eventData) { - FileWidget *fileWidget = NULL; + FileWidget *fileWidget = nullptr; Fl_Group *openFileGroup = findTab(gw_file); if (!openFileGroup) { openFileGroup = createTab(gw_file, fileTabName); @@ -1185,7 +1189,7 @@ EditorWidget *MainWindow::getEditor(const char *fullpath) { } } } - return NULL; + return nullptr; } /** @@ -1427,7 +1431,7 @@ int MainWindow::handle(int e) { } void MainWindow::loadHelp(const char *path) { - if (!getHelp()->loadHelp(path) && getEditor() != NULL) { + if (!getHelp()->loadHelp(path) && getEditor() != nullptr) { getEditor()->statusMsg("Failed to open help file"); } } @@ -1440,16 +1444,16 @@ void MainWindow::loadIcon() { #if defined(WIN32) HICON ico = (HICON) icon(); if (!ico) { - ico = (HICON) LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(101), + ico = (HICON) LoadImage(GetModuleHandle(nullptr), MAKEINTRESOURCE(101), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR | LR_SHARED); if (!ico) { - ico = LoadIcon(NULL, IDI_APPLICATION); + ico = LoadIcon(nullptr, IDI_APPLICATION); } } icon((char *)ico); #else #include "icon.h" - Fl_RGB_Image *image = new Fl_PNG_Image(NULL, sb_desktop_128x128_png, sb_desktop_128x128_png_len); + Fl_RGB_Image *image = new Fl_PNG_Image(nullptr, sb_desktop_128x128_png, sb_desktop_128x128_png_len); icon(image); #endif } @@ -1496,7 +1500,7 @@ int BaseWindow::handle(int e) { } break; } - if (_mainSystem != NULL) { + if (_mainSystem != nullptr) { _mainSystem->handle(e); } break; diff --git a/src/platform/fltk/runtime.cxx b/src/platform/fltk/runtime.cxx index 222ee28a..6194621d 100644 --- a/src/platform/fltk/runtime.cxx +++ b/src/platform/fltk/runtime.cxx @@ -16,6 +16,7 @@ #include extern MainWindow *wnd; +extern Fl_Window *outputWindow; extern System *g_system; static auto *onlineUrl = "http://smallbasic.github.io/samples/index.bas"; @@ -234,12 +235,19 @@ void Runtime::setFontSize(int size) { _output->setFontSize(size); } -void Runtime::setWindowSize(int width, int height) { - wnd->size(width, height); +void Runtime::setWindowRect(int x, int y, int width, int height) { + if (wnd != outputWindow) { + if (width > 0 && height > 0) { + outputWindow->size(width, height); + } + if (x > 0 && y > 0) { + outputWindow->position(x, y); + } + } } void Runtime::setWindowTitle(const char *title) { - wnd->label(title); + outputWindow->label(title); } void Runtime::showCursor(CursorType cursorType) { diff --git a/src/platform/fltk/runtime.h b/src/platform/fltk/runtime.h index fd591d96..8f670411 100755 --- a/src/platform/fltk/runtime.h +++ b/src/platform/fltk/runtime.h @@ -35,7 +35,7 @@ struct Runtime : public System { void setFontSize(int size); void setLoadBreak(const char *url) {} void setLoadPath(const char *url) {} - void setWindowSize(int width, int height); + void setWindowRect(int x, int y, int width, int height); void setWindowTitle(const char *title); void share(const char *path) {} void showCursor(CursorType cursorType); diff --git a/src/platform/sdl/runtime.cpp b/src/platform/sdl/runtime.cpp index 61ec26e8..295c9726 100644 --- a/src/platform/sdl/runtime.cpp +++ b/src/platform/sdl/runtime.cpp @@ -621,11 +621,16 @@ void Runtime::processEvent(MAEvent &event) { } } -void Runtime::setWindowSize(int width, int height) { +void Runtime::setWindowRect(int x, int y, int width, int height) { logEntered(); - SDL_SetWindowSize(_window, width, height); - _graphics->resize(width, height); - resize(); + if (width > 0 && height > 0) { + SDL_SetWindowSize(_window, width, height); + _graphics->resize(width, height); + resize(); + } + if (x > 0 && y > 0) { + SDL_SetWindowPosition(_window, x, y); + } } void Runtime::setWindowTitle(const char *title) { @@ -811,7 +816,7 @@ char *Runtime::getClipboardText() { void Runtime::onRunCompleted() { SDL_SetWindowPosition(_window, _saveRect.x, _saveRect.y); SDL_SetWindowSize(_window, _saveRect.w, _saveRect.h); - setWindowSize(_saveRect.w, _saveRect.h); + setWindowRect(_saveRect.x, _saveRect.y, _saveRect.w, _saveRect.h); } void Runtime::saveWindowRect() { diff --git a/src/platform/sdl/runtime.h b/src/platform/sdl/runtime.h index 2eb34a67..6ff356a7 100644 --- a/src/platform/sdl/runtime.h +++ b/src/platform/sdl/runtime.h @@ -46,7 +46,7 @@ struct Runtime : public System { void processEvent(MAEvent &event); void pushEvent(MAEvent *event); void saveWindowRect(); - void setWindowSize(int width, int height); + void setWindowRect(int x, int y, int width, int height); void setWindowTitle(const char *title); void share(const char *path) {} void showCursor(CursorType cursorType); diff --git a/src/ui/system.h b/src/ui/system.h index e61cce93..f81f8c52 100755 --- a/src/ui/system.h +++ b/src/ui/system.h @@ -64,7 +64,7 @@ struct System { virtual void optionsBox(StringList *items) = 0; virtual void onRunCompleted() = 0; virtual void saveWindowRect() = 0; - virtual void setWindowSize(int width, int height) = 0; + virtual void setWindowRect(int x, int y, int width, int height) = 0; virtual void setWindowTitle(const char *title) = 0; virtual void share(const char *path) = 0; virtual void showCursor(CursorType cursorType) = 0; diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 6b5ae6bd..c6fe0f0b 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -28,6 +28,7 @@ extern System *g_system; #define WINDOW_INSET "insetTextScreen" #define WINDOW_SETFONT "setFont" #define WINDOW_SETSIZE "setSize" +#define WINDOW_SETLOCATION "setLocation" #define WINDOW_THEME "theme" // returns the next set of string variable arguments as a String list @@ -111,7 +112,13 @@ void cmd_window_set_font(var_s *self, var_s *) { void cmd_window_set_size(var_s *self, var_s *) { var_int_t width, height; par_massget("II", &width, &height); - g_system->setWindowSize(width, height); + g_system->setWindowRect(-1, -1, width, height); +} + +void cmd_window_set_location(var_s *self, var_s *) { + var_int_t x, y; + par_massget("II", &x, &y); + g_system->setWindowRect(x, y, -1, -1); } void cmd_window_get_theme(var_s *, var_s *retval) { @@ -186,6 +193,7 @@ extern "C" void v_create_window(var_p_t var) { v_create_func(var, WINDOW_INSET, cmd_window_inset); v_create_func(var, WINDOW_SETFONT, cmd_window_set_font); v_create_func(var, WINDOW_SETSIZE, cmd_window_set_size); + v_create_func(var, WINDOW_SETLOCATION, cmd_window_set_location); v_create_func(var, WINDOW_THEME, cmd_window_get_theme); } From e3eac78bf1eb07e173ca4eabf72d27c2646437cd Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 16 Jan 2022 15:22:01 +1000 Subject: [PATCH 14/47] COMMON: Implemented window.setLocation(x,y) --- src/platform/fltk/runtime.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/platform/fltk/runtime.cxx b/src/platform/fltk/runtime.cxx index 6194621d..2a6f844a 100644 --- a/src/platform/fltk/runtime.cxx +++ b/src/platform/fltk/runtime.cxx @@ -236,7 +236,7 @@ void Runtime::setFontSize(int size) { } void Runtime::setWindowRect(int x, int y, int width, int height) { - if (wnd != outputWindow) { + if (wnd != outputWindow && outputWindow != nullptr) { if (width > 0 && height > 0) { outputWindow->size(width, height); } @@ -247,7 +247,9 @@ void Runtime::setWindowRect(int x, int y, int width, int height) { } void Runtime::setWindowTitle(const char *title) { - outputWindow->label(title); + if (wnd != outputWindow && outputWindow != nullptr) { + outputWindow->label(title); + } } void Runtime::showCursor(CursorType cursorType) { From 97777ef31b5d73fa055838e69f3638f97b9c7e4f Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Feb 2022 12:21:36 +1000 Subject: [PATCH 15/47] COMMON: Fixed http_read to handle large HTTP headers Console INPUT no longer prints the entered string --- src/common/fs_socket_client.c | 128 +++++++++++++++++++-------------- src/common/var.c | 1 - src/lib/miniaudio | 2 +- src/platform/console/input.cpp | 1 - 4 files changed, 74 insertions(+), 58 deletions(-) diff --git a/src/common/fs_socket_client.c b/src/common/fs_socket_client.c index a2cbc5a4..7ec997d3 100644 --- a/src/common/fs_socket_client.c +++ b/src/common/fs_socket_client.c @@ -39,7 +39,9 @@ int sockcl_open(dev_file_t *f) { return 1; } +// // open a web server connection +// int http_open(dev_file_t *f) { char host[250]; char txbuf[1024]; @@ -101,10 +103,10 @@ int http_open(dev_file_t *f) { } sprintf(txbuf, "GET %s HTTP/1.0\r\n" - "Host: %s\r\n" - "Accept: */*\r\n" - "Accept-Language: en-au\r\n" - "User-Agent: SmallBASIC\r\n", slash ? slash : "/", host); + "Host: %s\r\n" + "Accept: */*\r\n" + "Accept-Language: en-au\r\n" + "User-Agent: SmallBASIC\r\n", slash ? slash : "/", host); if (f->drv_dw[2]) { // If-Modified-Since: Sun, 03 Apr 2005 04:45:47 GMT strcat(txbuf, "If-Modified-Since: "); @@ -116,70 +118,86 @@ int http_open(dev_file_t *f) { return 1; } +// // read from a web server connection +// int http_read(dev_file_t *f, var_t *var_p) { char rxbuff[1024]; - int inHeader = 1; + int inContent = 0; int httpOK = 0; - v_free(var_p); - var_p->type = V_STR; - var_p->v.p.ptr = 0; - var_p->v.p.length = 0; + v_setint(var_p, 0); while (1) { - int bytes = net_read(f->handle, (char *) rxbuff, sizeof(rxbuff)); + int bytes = net_read(f->handle, (char *)rxbuff, sizeof(rxbuff)); if (bytes == -1) { httpOK = 0; break; } else if (bytes == 0) { - break; // no more data + // no more data + break; } // assumes http header < 1024 bytes - if (inHeader) { + if (inContent) { + if (var_p->type == V_INT) { + v_setstrn(var_p, rxbuff, bytes); + } else { + var_p->v.p.ptr = realloc(var_p->v.p.ptr, var_p->v.p.length + bytes + 1); + memcpy(var_p->v.p.ptr + var_p->v.p.length, rxbuff, bytes); + var_p->v.p.length += bytes; + var_p->v.p.ptr[var_p->v.p.length] = '\0'; + } + } else { int i = 0; - while (1) { - int iattr = i; - while (rxbuff[i] != 0 && rxbuff[i] != '\n') { - i++; - } - if (rxbuff[i] == 0) { - inHeader = 0; - break; // no end delimiter + int countNL = 0; + while (i < bytes && rxbuff[i] != 0 && countNL != 4) { + // scan for CR + LF + CR + LF + if (rxbuff[i] == '\r' && (countNL % 2) == 0) { + countNL++; + } else if (rxbuff[i] == '\n' && (countNL % 2) == 1) { + countNL++; + } else { + countNL = 0; } - if (rxbuff[i + 2] == '\n') { - var_p->v.p.length = bytes - i - 3; + i++; + } + if (countNL == 4) { + // found start of content + if (i < bytes) { + // copy remaining characters from rxbuff + v_free(var_p); + var_p->type = V_STR; + var_p->v.p.length = bytes - i; var_p->v.p.ptr = malloc(var_p->v.p.length + 1); var_p->v.p.owner = 1; - memcpy(var_p->v.p.ptr, rxbuff + i + 3, var_p->v.p.length); - var_p->v.p.ptr[var_p->v.p.length] = 0; - inHeader = 0; - break; // found start of content - } - // null terminate attribute (in \r) - rxbuff[i - 1] = 0; - i++; - if (strstr(rxbuff + iattr, "200 OK") != 0) { - httpOK = 1; + memcpy(var_p->v.p.ptr, rxbuff + i, var_p->v.p.length); + var_p->v.p.ptr[var_p->v.p.length] = '\0'; } - if (strncmp(rxbuff + iattr, "Location: ", 10) == 0) { - // handle redirection + inContent = 1; + } + // null terminate headers fragment + rxbuff[i - 1] = '\0'; + if (strstr(rxbuff, "200 OK") != 0) { + httpOK = 1; + } + char *location = strstr(rxbuff, "Location: "); + if (location) { + // handle redirection + char *cr = strstr(location, "\r"); + if (cr) { + *cr = '\0'; sockcl_close(f); - strlcpy(f->name, rxbuff + iattr + 10, sizeof(f->name)); + strlcpy(f->name, location + 10, sizeof(f->name)); if (http_open(f) == 0) { - return 0; + httpOK = 0; + break; } - break; // scan next header + inContent = 0; + v_setint(var_p, 0); } } - } else { - var_p->v.p.ptr = realloc(var_p->v.p.ptr, var_p->v.p.length + bytes + 1); - memcpy(var_p->v.p.ptr + var_p->v.p.length, rxbuff, bytes); - var_p->v.p.length += bytes; - var_p->v.p.ptr[var_p->v.p.length] = 0; } } - return httpOK; } @@ -190,17 +208,17 @@ int sockcl_close(dev_file_t *f) { return 1; } -/* - * write to a socket - */ +// +// write to a socket +// int sockcl_write(dev_file_t *f, byte *data, uint32_t size) { net_send((socket_t) (long) f->handle, (char *)data, size); return size; } -/* - * read from a socket - */ +// +// read from a socket +// int sockcl_read(dev_file_t *f, byte *data, uint32_t size) { int result; if (f->handle != -1) { @@ -214,16 +232,16 @@ int sockcl_read(dev_file_t *f, byte *data, uint32_t size) { return result; } -/* - * Returns true (EOF) if the connection is broken - */ +// +// Returns true (EOF) if the connection is broken +// int sockcl_eof(dev_file_t *f) { return (((long) f->drv_dw[0]) <= 0) ? 1 : 0; } -/* - * returns the size of the data which are waiting in stream's queue - */ +// +// returns the size of the data which are waiting in stream's queue +// int sockcl_length(dev_file_t *f) { return net_peek((socket_t) (long) f->handle); } diff --git a/src/common/var.c b/src/common/var.c index f23bdffa..17929539 100644 --- a/src/common/var.c +++ b/src/common/var.c @@ -718,7 +718,6 @@ void v_strcat(var_t *var, const char *str) { strcpy(var->v.p.ptr, p); strcat(var->v.p.ptr, str); } - } else { err_typemismatch(); } diff --git a/src/lib/miniaudio b/src/lib/miniaudio index e89650cc..82e70f4c 160000 --- a/src/lib/miniaudio +++ b/src/lib/miniaudio @@ -1 +1 @@ -Subproject commit e89650cc4bfb71cc4c5535f370798e4282af859e +Subproject commit 82e70f4cbe6e613c8edc0ac7b97ff3dd00f2ca27 diff --git a/src/platform/console/input.cpp b/src/platform/console/input.cpp index 87a5efa2..3772af81 100644 --- a/src/platform/console/input.cpp +++ b/src/platform/console/input.cpp @@ -209,6 +209,5 @@ char *dev_gets(char *dest, int size) { } } while (ch != '\n' && ch != '\r'); dest[len] = '\0'; - dev_print(dest); return dest; } From 1e4f19cc728654b0e770ea68f80904b7457597fc Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Feb 2022 14:41:34 +1000 Subject: [PATCH 16/47] COMMON: Fixed http_read to handle large HTTP headers --- ChangeLog | 3 +++ src/common/fs_socket_client.c | 13 +++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7d489049..64108508 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-02-19 (12.24) + COMMON: Fixed http_read to handle large HTTP headers + 2022-01-16 (12.24) COMMON: Implemented window.setLocation(x,y) #102 diff --git a/src/common/fs_socket_client.c b/src/common/fs_socket_client.c index 7ec997d3..ed7188d9 100644 --- a/src/common/fs_socket_client.c +++ b/src/common/fs_socket_client.c @@ -122,6 +122,7 @@ int http_open(dev_file_t *f) { // read from a web server connection // int http_read(dev_file_t *f, var_t *var_p) { + static const char *delim = "\r\n\r\n"; char rxbuff[1024]; int inContent = 0; int httpOK = 0; @@ -140,7 +141,13 @@ int http_read(dev_file_t *f, var_t *var_p) { // assumes http header < 1024 bytes if (inContent) { if (var_p->type == V_INT) { - v_setstrn(var_p, rxbuff, bytes); + v_free(var_p); + var_p->type = V_STR; + var_p->v.p.length = bytes; + var_p->v.p.ptr = malloc(var_p->v.p.length + 1); + var_p->v.p.owner = 1; + memcpy(var_p->v.p.ptr, rxbuff, var_p->v.p.length); + var_p->v.p.ptr[var_p->v.p.length] = '\0'; } else { var_p->v.p.ptr = realloc(var_p->v.p.ptr, var_p->v.p.length + bytes + 1); memcpy(var_p->v.p.ptr + var_p->v.p.length, rxbuff, bytes); @@ -152,9 +159,7 @@ int http_read(dev_file_t *f, var_t *var_p) { int countNL = 0; while (i < bytes && rxbuff[i] != 0 && countNL != 4) { // scan for CR + LF + CR + LF - if (rxbuff[i] == '\r' && (countNL % 2) == 0) { - countNL++; - } else if (rxbuff[i] == '\n' && (countNL % 2) == 1) { + if (rxbuff[i] == delim[countNL]) { countNL++; } else { countNL = 0; From 02bfd6eef84fe63a7ad477d292bc1453b10786dc Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Feb 2022 18:32:32 +1000 Subject: [PATCH 17/47] COMMON: fix hashmap access issue --- samples/distro-examples/tests/hash.bas | 10 ++++++++++ src/common/scan.c | 3 +++ 2 files changed, 13 insertions(+) diff --git a/samples/distro-examples/tests/hash.bas b/samples/distro-examples/tests/hash.bas index 72f6c47f..e30de60e 100644 --- a/samples/distro-examples/tests/hash.bas +++ b/samples/distro-examples/tests/hash.bas @@ -61,3 +61,13 @@ f.inputs[6].value= "123" if (not isarray(f.inputs)) then throw "post: inputs not a map" endif + +' +' multi-layer access +' +dim lights(1) +shadow.vertices = [] +shadow.vertices << 1 +lights[0].shadows = [] +lights[0].shadows << shadow +lights[0].shadows[0].vertices[0] = [1,2,3,4] diff --git a/src/common/scan.c b/src/common/scan.c index d6d358ee..65aec20d 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -1872,6 +1872,9 @@ char *comp_array_params(char *src, char exitChar) { } if (*(p + 1) == '.') { p = comp_array_uds_field(p + 2, &comp_prog); + if (*p == '[' || *p == '(') { + continue; + } } } } From b16940339667683b047b69b2436236292cafec4c Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Feb 2022 18:33:29 +1000 Subject: [PATCH 18/47] COMMON: fix hashmap access issue --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 64108508..109f1378 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 2022-02-19 (12.24) COMMON: Fixed http_read to handle large HTTP headers + COMMON: Fixed hashmap access issue 2022-01-16 (12.24) COMMON: Implemented window.setLocation(x,y) #102 From fde05fb0ac020504d642860278ffe01612f4af8c Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Feb 2022 20:57:25 +1000 Subject: [PATCH 19/47] COMMON: fix hashmap access issue --- src/common/scan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/common/scan.c b/src/common/scan.c index 65aec20d..9724456e 100644 --- a/src/common/scan.c +++ b/src/common/scan.c @@ -1872,9 +1872,7 @@ char *comp_array_params(char *src, char exitChar) { } if (*(p + 1) == '.') { p = comp_array_uds_field(p + 2, &comp_prog); - if (*p == '[' || *p == '(') { - continue; - } + continue; } } } From 6d2cadc6c6eaa8266249d1b631662310e49a477a Mon Sep 17 00:00:00 2001 From: chrisws Date: Tue, 22 Feb 2022 20:27:15 +1000 Subject: [PATCH 20/47] WEB: Added simple REST server support --- ChangeLog | 3 + src/common/device.c | 2 +- src/platform/web/main.cpp | 149 ++++++++++++++++++++------------------ 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/ChangeLog b/ChangeLog index 109f1378..08d1f92b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-02-19 (12.24) + WEB: Added simple REST server support + 2022-02-19 (12.24) COMMON: Fixed http_read to handle large HTTP headers COMMON: Fixed hashmap access issue diff --git a/src/common/device.c b/src/common/device.c index 00c4c364..a8d30f29 100644 --- a/src/common/device.c +++ b/src/common/device.c @@ -166,7 +166,7 @@ void dev_trace_line(int lineNo) { #ifndef IMPL_LOG_WRITE void lwrite(const char *buf) { - fprintf(stderr, "%s\n", buf); + fprintf(stderr, "%s", buf); } #endif diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index 5e557511..0cdbebbd 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -25,22 +25,26 @@ uint32_t g_start = 0; uint32_t g_maxTime = 2000; bool g_graphicText = true; bool g_noExecute = false; +bool g_json = false; +char *execBas = nullptr; struct MHD_Connection *g_connection; StringList g_cookies; static struct option OPTIONS[] = { - {"help", no_argument, NULL, 'h'}, - {"verbose", no_argument, NULL, 'v'}, - {"file-permitted", no_argument, NULL, 'f'}, - {"no-execute", no_argument, NULL, 'x'}, - {"port", optional_argument, NULL, 'p'}, - {"run", optional_argument, NULL, 'r'}, - {"width", optional_argument, NULL, 'w'}, - {"height", optional_argument, NULL, 'e'}, - {"command", optional_argument, NULL, 'c'}, - {"graphic-text", optional_argument, NULL, 'g'}, - {"max-time", optional_argument, NULL, 't'}, - {"module", optional_argument, NULL, 'm'}, + {"help", no_argument, nullptr, 'h'}, + {"verbose", no_argument, nullptr, 'v'}, + {"file-permitted", no_argument, nullptr, 'f'}, + {"no-execute", no_argument, nullptr, 'x'}, + {"json", no_argument, nullptr, 'j'}, + {"port", optional_argument, nullptr, 'p'}, + {"run", optional_argument, nullptr, 'r'}, + {"width", optional_argument, nullptr, 'w'}, + {"height", optional_argument, nullptr, 'e'}, + {"command", optional_argument, nullptr, 'c'}, + {"graphic-text", optional_argument, nullptr, 'g'}, + {"max-time", optional_argument, nullptr, 't'}, + {"module", optional_argument, nullptr, 'm'}, + {"cgi-exec", optional_argument, nullptr, 'i'}, {0, 0, 0, 0} }; @@ -68,7 +72,7 @@ void show_help() { (int)sizeof(var_int_t), (int)sizeof(var_num_t)); fprintf(stdout, "usage: sbasicw [options]...\n"); int i = 0; - while (OPTIONS[i].name != NULL) { + while (OPTIONS[i].name != nullptr) { fprintf(stdout, OPTIONS[i].has_arg ? " -%c, --%s=''\n" : " -%c, --%s\n", OPTIONS[i].val, OPTIONS[i].name); @@ -80,7 +84,7 @@ void show_help() { void log(const char *format, ...) { va_list args; va_start(args, format); - unsigned size = format == NULL ? 0 : vsnprintf(NULL, 0, format, args); + unsigned size = format == nullptr ? 0 : vsnprintf(nullptr, 0, format, args); va_end(args); if (size) { @@ -92,7 +96,7 @@ void log(const char *format, ...) { buf[size] = '\0'; char date[18]; - time_t t = time(NULL); + time_t t = time(nullptr); struct tm *p = localtime(&t); strftime(date, sizeof(date), "%Y%m%d %H:%M:%S", p); fprintf(stdout, "%s %s\n", date, buf); @@ -106,27 +110,22 @@ MHD_Result accept_cb(void *cls, const struct sockaddr *addr, socklen_t addrlen) } MHD_Response *execute(struct MHD_Connection *connection, const char *bas) { - const char *width = - MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "width"); - const char *height = - MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "height"); - const char *command = - MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "command"); - const char *graphicText = - MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "graphic-text"); - const char *accept = - MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); - - if (width != NULL) { + const char *width = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "width"); + const char *height = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "height"); + const char *command = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "command"); + const char *graphicText = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "graphic-text"); + const char *accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); + + if (width != nullptr) { os_graf_mx = atoi(width); } - if (height != NULL) { + if (height != nullptr) { os_graf_my = atoi(height); } - if (graphicText != NULL) { + if (graphicText != nullptr) { g_graphicText = atoi(graphicText) > 0; } - if (command != NULL) { + if (command != nullptr) { strcpy(opt_command, command); } @@ -135,19 +134,15 @@ MHD_Response *execute(struct MHD_Connection *connection, const char *bas) { g_canvas.reset(); g_start = dev_get_millisecond_count(); g_canvas.setGraphicText(g_graphicText); - g_canvas.setJSON((strncmp(accept, "application/json", 16) == 0)); + g_canvas.setJSON(g_json || (strncmp(accept, "application/json", 16) == 0)); g_cookies.removeAll(); sbasic_main(bas); - g_connection = NULL; + g_connection = nullptr; String page = g_canvas.getPage(); - MHD_Response *response = - MHD_create_response_from_buffer(page.length(), (void *)page.c_str(), - MHD_RESPMEM_MUST_COPY); + MHD_Response *response = MHD_create_response_from_buffer(page.length(), (void *)page.c_str(), MHD_RESPMEM_MUST_COPY); List_each(String *, it, g_cookies) { String *next = (*it); - MHD_add_response_header(response, - MHD_HTTP_HEADER_SET_COOKIE, - next->c_str()); + MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, next->c_str()); } return response; } @@ -159,15 +154,21 @@ MHD_Response *serve_file(const char *path) { if (!fstat(fd, &stbuf)) { response = MHD_create_response_from_fd(stbuf.st_size, fd); } else { - response = NULL; + response = nullptr; } return response; } MHD_Response *get_response(struct MHD_Connection *connection, const char *path) { - MHD_Response *response = NULL; + MHD_Response *response = nullptr; struct stat stbuf; - if (path[0] == '\0') { + + if (execBas && stat(execBas, &stbuf) != -1 && S_ISREG(stbuf.st_mode)) { + if (path != nullptr && !opt_command[0]) { + strcpy(opt_command, path); + } + response = execute(connection, execBas); + } else if (path[0] == '\0') { if (stat("index.bas", &stbuf) != -1 && S_ISREG(stbuf.st_mode)) { response = execute(connection, "index.bas"); } else { @@ -175,7 +176,7 @@ MHD_Response *get_response(struct MHD_Connection *connection, const char *path) } } else if (strcmp(path, "favicon.ico") == 0) { response = serve_file(path); - } else if (strstr(path, "..") == NULL) { + } else if (strstr(path, "..") == nullptr) { const char *dot = strrchr(path, '.'); if (dot && !g_noExecute && strncasecmp(dot, ".bas", 4) == 0 && stat(path, &stbuf) != -1 && S_ISREG(stbuf.st_mode)) { @@ -205,26 +206,28 @@ MHD_Result access_cb(void *cls, return MHD_YES; } // clear context pointer - *ptr = NULL; + *ptr = nullptr; - if (upload_data != NULL) { + if (upload_data != nullptr) { size_t size = OPT_CMD_SZ - 1; if (*upload_data_size < size) { size = *upload_data_size; } strncpy(opt_command, upload_data, size); + *upload_data_size = 0; + return MHD_YES; } MHD_Result result; - struct MHD_Response *response = get_response(connection, url + 1); - if (response != NULL) { - result = MHD_queue_response(connection, MHD_HTTP_OK, response); + MHD_Response *response = get_response(connection, url + 1); + if (response != nullptr) { + int code = g_canvas.getPage().length() ? MHD_HTTP_OK : MHD_HTTP_NO_CONTENT; + result = MHD_queue_response(connection, code, response); } else { String error; error.append("File not found: ").append(url); log(error.c_str()); - response = MHD_create_response_from_buffer(error.length(), (void *)error.c_str(), - MHD_RESPMEM_MUST_COPY); + response = MHD_create_response_from_buffer(error.length(), (void *)error.c_str(), MHD_RESPMEM_MUST_COPY); result = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); } MHD_destroy_response(response); @@ -234,11 +237,11 @@ MHD_Result access_cb(void *cls, int main(int argc, char **argv) { init(); int port = 8080; - char *runBas = NULL; + char *runBas = nullptr; while (1) { int option_index = 0; - int c = getopt_long(argc, argv, "hvfxp:t:m::r:w:e:c:g:", OPTIONS, &option_index); + int c = getopt_long(argc, argv, "hvfxjp:t:m::r:w:e:c:g:i:", OPTIONS, &option_index); if (c == -1) { break; } @@ -284,9 +287,17 @@ int main(int argc, char **argv) { strcpy(opt_modpath, optarg); } break; + case 'i': + if (optarg) { + execBas = strdup(optarg); + } + break; case 'x': g_noExecute = true; break; + case 'j': + g_json = true; + break; default: show_help(); exit(1); @@ -294,24 +305,24 @@ int main(int argc, char **argv) { } } - if (runBas != NULL) { + if (runBas != nullptr) { g_canvas.reset(); g_start = dev_get_millisecond_count(); sbasic_main(runBas); puts(g_canvas.getPage().c_str()); } else { fprintf(stdout, "Starting SmallBASIC web server on port:%d\n", port); - MHD_Daemon *d = - MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, - &accept_cb, NULL, - &access_cb, NULL, MHD_OPTION_END); - if (d == NULL) { + MHD_Daemon *d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, + &accept_cb, nullptr, + &access_cb, nullptr, MHD_OPTION_END); + if (d == nullptr) { fprintf(stderr, "startup failed\n"); return 1; } getc(stdin); MHD_stop_daemon(d); } + free(execBas); return 0; } @@ -371,7 +382,7 @@ void osd_write(const char *str) { } char *dev_gets(char *dest, int maxSize) { - return NULL; + return nullptr; } void lwrite(const char *buf) { @@ -385,7 +396,7 @@ void lwrite(const char *buf) { // struct ValueIteratorClosure { ValueIteratorClosure(int el) : - _result(NULL), + _result(nullptr), _count(0), _element(el) { } @@ -419,33 +430,33 @@ int dev_setenv(const char *key, const char *value) { const char *dev_getenv(const char *key) { const char *result; - if (g_connection != NULL) { + if (g_connection != nullptr) { result = MHD_lookup_connection_value(g_connection, MHD_COOKIE_KIND, key); - if (result == NULL) { + if (result == nullptr) { result = MHD_lookup_connection_value(g_connection, MHD_HEADER_KIND, key); } - if (result == NULL) { + if (result == nullptr) { result = MHD_lookup_connection_value(g_connection, MHD_GET_ARGUMENT_KIND, key); } } else { - result = NULL; + result = nullptr; } return result; } int dev_env_count() { int count = 0; - if (g_connection != NULL) { - count += MHD_get_connection_values(g_connection, MHD_COOKIE_KIND, countIterator, NULL); - count += MHD_get_connection_values(g_connection, MHD_HEADER_KIND, countIterator, NULL); - count += MHD_get_connection_values(g_connection, MHD_GET_ARGUMENT_KIND, countIterator, NULL); + if (g_connection != nullptr) { + count += MHD_get_connection_values(g_connection, MHD_COOKIE_KIND, countIterator, nullptr); + count += MHD_get_connection_values(g_connection, MHD_HEADER_KIND, countIterator, nullptr); + count += MHD_get_connection_values(g_connection, MHD_GET_ARGUMENT_KIND, countIterator, nullptr); } return count; } const char *dev_getenv_n(int n) { - const char *result = NULL; - if (g_connection != NULL) { + const char *result = nullptr; + if (g_connection != nullptr) { ValueIteratorClosure closure(n); MHD_get_connection_values(g_connection, MHD_COOKIE_KIND, valueIterator, &closure); MHD_get_connection_values(g_connection, MHD_HEADER_KIND, valueIterator, &closure); From a018c58a71b40b75514c4636474469e868a94491 Mon Sep 17 00:00:00 2001 From: chrisws Date: Wed, 23 Feb 2022 07:25:24 +1000 Subject: [PATCH 21/47] WEB: Added simple REST server support --- src/platform/web/main.cpp | 43 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index 0cdbebbd..23c1e542 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -27,24 +27,26 @@ bool g_graphicText = true; bool g_noExecute = false; bool g_json = false; char *execBas = nullptr; -struct MHD_Connection *g_connection; +MHD_Connection *g_connection; StringList g_cookies; +String g_path; +String g_data; static struct option OPTIONS[] = { - {"help", no_argument, nullptr, 'h'}, - {"verbose", no_argument, nullptr, 'v'}, {"file-permitted", no_argument, nullptr, 'f'}, + {"help", no_argument, nullptr, 'h'}, + {"json-content", no_argument, nullptr, 'j'}, {"no-execute", no_argument, nullptr, 'x'}, - {"json", no_argument, nullptr, 'j'}, - {"port", optional_argument, nullptr, 'p'}, - {"run", optional_argument, nullptr, 'r'}, - {"width", optional_argument, nullptr, 'w'}, - {"height", optional_argument, nullptr, 'e'}, + {"verbose", no_argument, nullptr, 'v'}, {"command", optional_argument, nullptr, 'c'}, + {"exec-bas", optional_argument, nullptr, 'i'}, {"graphic-text", optional_argument, nullptr, 'g'}, + {"height", optional_argument, nullptr, 'e'}, {"max-time", optional_argument, nullptr, 't'}, {"module", optional_argument, nullptr, 'm'}, - {"cgi-exec", optional_argument, nullptr, 'i'}, + {"port", optional_argument, nullptr, 'p'}, + {"run", optional_argument, nullptr, 'r'}, + {"width", optional_argument, nullptr, 'w'}, {0, 0, 0, 0} }; @@ -109,7 +111,7 @@ MHD_Result accept_cb(void *cls, const struct sockaddr *addr, socklen_t addrlen) return MHD_YES; } -MHD_Response *execute(struct MHD_Connection *connection, const char *bas) { +MHD_Response *execute(MHD_Connection *connection, const char *bas) { const char *width = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "width"); const char *height = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "height"); const char *command = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "command"); @@ -159,14 +161,13 @@ MHD_Response *serve_file(const char *path) { return response; } -MHD_Response *get_response(struct MHD_Connection *connection, const char *path) { +MHD_Response *get_response(MHD_Connection *connection, const char *path) { MHD_Response *response = nullptr; struct stat stbuf; + g_path = path; + if (execBas && stat(execBas, &stbuf) != -1 && S_ISREG(stbuf.st_mode)) { - if (path != nullptr && !opt_command[0]) { - strcpy(opt_command, path); - } response = execute(connection, execBas); } else if (path[0] == '\0') { if (stat("index.bas", &stbuf) != -1 && S_ISREG(stbuf.st_mode)) { @@ -191,7 +192,7 @@ MHD_Response *get_response(struct MHD_Connection *connection, const char *path) // server callback // see: /usr/share/doc/libmicrohttpd-dev/examples MHD_Result access_cb(void *cls, - struct MHD_Connection *connection, + MHD_Connection *connection, const char *url, const char *method, const char *version, @@ -209,11 +210,13 @@ MHD_Result access_cb(void *cls, *ptr = nullptr; if (upload_data != nullptr) { + // curl -H "Accept: application/json" -d '{"productId": 123456, "quantity": 100}' http://localhost:8080/foo size_t size = OPT_CMD_SZ - 1; if (*upload_data_size < size) { size = *upload_data_size; } - strncpy(opt_command, upload_data, size); + g_data.clear(); + g_data.append(upload_data, size); *upload_data_size = 0; return MHD_YES; } @@ -430,7 +433,11 @@ int dev_setenv(const char *key, const char *value) { const char *dev_getenv(const char *key) { const char *result; - if (g_connection != nullptr) { + if (strcmp(key, "path") == 0) { + result = g_path.c_str(); + } else if (strcmp(key, "data") == 0) { + result = g_data.c_str(); + } else if (g_connection != nullptr) { result = MHD_lookup_connection_value(g_connection, MHD_COOKIE_KIND, key); if (result == nullptr) { result = MHD_lookup_connection_value(g_connection, MHD_HEADER_KIND, key); @@ -445,7 +452,7 @@ const char *dev_getenv(const char *key) { } int dev_env_count() { - int count = 0; + int count = 2; if (g_connection != nullptr) { count += MHD_get_connection_values(g_connection, MHD_COOKIE_KIND, countIterator, nullptr); count += MHD_get_connection_values(g_connection, MHD_HEADER_KIND, countIterator, nullptr); From e2bb88c85c86f4ac44fb632a142b58437ff8c38b Mon Sep 17 00:00:00 2001 From: chrisws Date: Wed, 23 Feb 2022 19:12:48 +1000 Subject: [PATCH 22/47] COMMON: ARRAY now parses json with true/false correctly --- ChangeLog | 3 +++ samples/distro-examples/tests/hash.bas | 9 +++++++++ src/common/var_map.c | 8 +++++++- src/platform/web/main.cpp | 8 ++++++-- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 08d1f92b..b01bf780 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2022-02-23 (12.24) + COMMON: ARRAY now parses json with true/false correctly + 2022-02-19 (12.24) WEB: Added simple REST server support diff --git a/samples/distro-examples/tests/hash.bas b/samples/distro-examples/tests/hash.bas index e30de60e..97f87e70 100644 --- a/samples/distro-examples/tests/hash.bas +++ b/samples/distro-examples/tests/hash.bas @@ -71,3 +71,12 @@ shadow.vertices << 1 lights[0].shadows = [] lights[0].shadows << shadow lights[0].shadows[0].vertices[0] = [1,2,3,4] + +' +' parse "true" and "false" as boolean fields +' +a= array("{\"stringT\", \"true\", \"stringF\", \"false\", \"booleanT\": true, \"booleanF\": false}") +if (a.stringT <> "true") then throw "not true" +if (a.stringF <> "false") then throw "not false" +if (a.booleanT <> 1) then throw "not true" +if (a.booleanF <> 0) then throw "not false" diff --git a/src/common/var_map.c b/src/common/var_map.c index 31d5936d..37bb263b 100644 --- a/src/common/var_map.c +++ b/src/common/var_map.c @@ -434,7 +434,13 @@ void map_set_primative(var_p_t dest, const char *s, int len) { } } if (text) { - v_setstrn(dest, s, len); + if (len == 4 && strncasecmp(s, "true", len) == 0) { + v_setint(dest, 1); + } else if (len == 5 && strncasecmp(s, "false", len) == 0) { + v_setint(dest, 0); + } else { + v_setstrn(dest, s, len); + } } else if (fract) { v_setreal(dest, atof(s)); } else { diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index 23c1e542..89cb3caf 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -117,6 +117,7 @@ MHD_Response *execute(MHD_Connection *connection, const char *bas) { const char *command = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "command"); const char *graphicText = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "graphic-text"); const char *accept = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT); + const char *contentType = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); if (width != nullptr) { os_graf_mx = atoi(width); @@ -131,7 +132,7 @@ MHD_Response *execute(MHD_Connection *connection, const char *bas) { strcpy(opt_command, command); } - log("%s dim:%dX%d", bas, os_graf_mx, os_graf_my); + log("%s dim:%dX%d [accept=%s, content-type=%s]", bas, os_graf_mx, os_graf_my, accept, contentType); g_connection = connection; g_canvas.reset(); g_start = dev_get_millisecond_count(); @@ -473,6 +474,10 @@ const char *dev_getenv_n(int n) { return result; } +void dev_delay(uint32_t ms) { + usleep(1000 * ms); +} + // // not implemented // @@ -495,5 +500,4 @@ void v_create_image(var_p_t var) {} void v_create_form(var_p_t var) {} void v_create_window(var_p_t var) {} void dev_show_page() {} -void dev_delay(uint32_t ms) {} void dev_log_stack(const char *keyword, int type, int line) {} From 7b180a4372f292c57eb95938dbbdff9b80d886c0 Mon Sep 17 00:00:00 2001 From: chrisws Date: Fri, 25 Feb 2022 22:43:48 +1000 Subject: [PATCH 23/47] WEB: Added simple REST server support --- src/platform/web/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index 89cb3caf..4cfd23ac 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -137,7 +137,7 @@ MHD_Response *execute(MHD_Connection *connection, const char *bas) { g_canvas.reset(); g_start = dev_get_millisecond_count(); g_canvas.setGraphicText(g_graphicText); - g_canvas.setJSON(g_json || (strncmp(accept, "application/json", 16) == 0)); + g_canvas.setJSON(g_json || (accept && strncmp(accept, "application/json", 16) == 0)); g_cookies.removeAll(); sbasic_main(bas); g_connection = nullptr; @@ -316,7 +316,7 @@ int main(int argc, char **argv) { puts(g_canvas.getPage().c_str()); } else { fprintf(stdout, "Starting SmallBASIC web server on port:%d\n", port); - MHD_Daemon *d = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port, + MHD_Daemon *d = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, port, &accept_cb, nullptr, &access_cb, nullptr, MHD_OPTION_END); if (d == nullptr) { From f26fe1713448de3a3ece631478335e87c98509d7 Mon Sep 17 00:00:00 2001 From: chrisws Date: Mon, 7 Mar 2022 22:03:07 +1000 Subject: [PATCH 24/47] CONSOLE: Added -i command switch for live mode --- ChangeLog | 12 +++++ src/platform/console/device.cpp | 54 +++++++++++++++++++++ src/platform/console/main.cpp | 83 ++++++++++++++++++++++++++------- 3 files changed, 133 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index b01bf780..3062cff6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2022-02-23 (12.24) + CONSOLE: Added -i command switch for live mode + + This will run the BASIC program in a loop. It can be used with the debug module + to rerun whenever the source code changes: + + import debug + run("xdotool windowactivate `xdotool search --onlyvisible --name \"Emacs\"`") + while (!debug.IsSourceModified()) + rem main loop + wend + 2022-02-23 (12.24) COMMON: ARRAY now parses json with true/false correctly diff --git a/src/platform/console/device.cpp b/src/platform/console/device.cpp index 92060041..1571567d 100644 --- a/src/platform/console/device.cpp +++ b/src/platform/console/device.cpp @@ -90,7 +90,9 @@ void console_init() { p_write = default_write; } +// // initialize driver +// int osd_devinit() { p_arc = (arc_fn)plugin_get_func("sblib_arc"); p_audio = (audio_fn)plugin_get_func("sblib_audio"); @@ -132,27 +134,35 @@ int osd_devinit() { return 1; } +// // close driver +// int osd_devrestore() { return 1; } +// // set foreground and background color // a value of -1 means not change that color +// void osd_settextcolor(long fg, long bg) { if (p_settextcolor) { p_settextcolor(fg, bg); } } +// // enable or disable PEN/MOUSE driver +// void osd_setpenmode(int enable) { if (p_setpenmode) { p_setpenmode(enable); } } +// // return pen/mouse info ('code' is the rq, see doc) +// int osd_getpen(int code) { int result; if (p_getpen) { @@ -163,14 +173,18 @@ int osd_getpen(int code) { return result; } +// // clear screen +// void osd_cls() { if (p_cls) { p_cls(); } } +// // returns the current x position (text-mode cursor) +// int osd_getx() { int result; if (p_getx) { @@ -181,7 +195,9 @@ int osd_getx() { return result; } +// // returns the current y position (text-mode cursor) +// int osd_gety() { int result; if (p_gety) { @@ -192,19 +208,25 @@ int osd_gety() { return result; } +// // set's text-mode (or graphics) cursor position +// void osd_setxy(int x, int y) { if (p_setxy) { p_setxy(x, y); } } +// // Basic output - print sans control codes +// void osd_write(const char *str) { p_write(str); } +// // events loop (called from main, every 50ms) +// int osd_events(int wait_flag) { int result = 0; if (p_events) { @@ -218,42 +240,54 @@ int osd_events(int wait_flag) { return result; } +// // sets foreground color +// void osd_setcolor(long color) { if (p_setcolor) { p_setcolor(color); } } +// // draw a line +// void osd_line(int x1, int y1, int x2, int y2) { if (p_line) { p_line(x1, y1, x2, y2); } } +// // draw an ellipse +// void osd_ellipse(int xc, int yc, int xr, int yr, int fill) { if (p_ellipse) { p_ellipse(xc, yc, xr, yr, fill); } } +// // draw an arc +// void osd_arc(int xc, int yc, double r, double as, double ae, double aspect) { if (p_arc) { p_arc(xc, yc, r, as, ae, aspect); } } +// // draw a pixel +// void osd_setpixel(int x, int y) { if (p_setpixel) { p_setpixel(x, y); } } +// // returns pixel's color +// long osd_getpixel(int x, int y) { long result; if (p_getpixel) { @@ -264,21 +298,27 @@ long osd_getpixel(int x, int y) { return result; } +// // draw rectangle (parallelogram) +// void osd_rect(int x1, int y1, int x2, int y2, int fill) { if (p_rect) { p_rect(x1, y1, x2, y2, fill); } } +// // refresh/flush the screen/stdout +// void osd_refresh() { if (p_refresh) { p_refresh(); } } +// // just a beep +// void osd_beep() { if (p_beep) { p_beep(); @@ -287,28 +327,36 @@ void osd_beep() { } } +// // play a sound +// void osd_sound(int frq, int ms, int vol, int bgplay) { if (p_sound) { p_sound(frq, ms, vol, bgplay); } } +// // clears sound-queue (stop background sound) +// void osd_clear_sound_queue() { if (p_clear_sound_queue) { p_clear_sound_queue(); } } +// // play the given audio file +// void osd_audio(const char *path) { if (p_audio) { p_audio(path); } } +// // text-width in pixels +// int osd_textwidth(const char *str) { int result; if (p_textwidth) { @@ -319,7 +367,9 @@ int osd_textwidth(const char *str) { return result; } +// // text-height in pixels +// int osd_textheight(const char *str) { int result; if (p_textheight) { @@ -330,7 +380,9 @@ int osd_textheight(const char *str) { return result; } +// // delay while pumping events +// void dev_delay(uint32_t timeout) { uint32_t slept = 0; uint32_t now = dev_get_millisecond_count(); @@ -349,7 +401,9 @@ void dev_delay(uint32_t timeout) { } } +// // unused +// void dev_log_stack(const char *keyword, int type, int line) {} void v_create_form(var_p_t var) {} void v_create_window(var_p_t var) {} diff --git a/src/platform/console/main.cpp b/src/platform/console/main.cpp index eb7e2d84..1f1e8f9a 100644 --- a/src/platform/console/main.cpp +++ b/src/platform/console/main.cpp @@ -28,6 +28,7 @@ static struct option OPTIONS[] = { {"keywords", no_argument, NULL, 'k'}, {"no-file-access", no_argument, NULL, 'f'}, {"gen-sbx", no_argument, NULL, 'x'}, + {"live-mode", no_argument, NULL, 'i'}, {"module-path", optional_argument, NULL, 'm'}, {"decompile", optional_argument, NULL, 's'}, {"option", optional_argument, NULL, 'o'}, @@ -92,9 +93,9 @@ void command_help(const char *selection) { } } -/* - * handles the command "sbasic -pkw" - */ +// +// handles the command "sbasic -pkw" +// void print_keywords() { printf("SmallBASIC keywords table\n"); printf("::':#:rem:\"\n"); // ted's format @@ -138,9 +139,9 @@ void print_keywords() { } } -/* - * setup to run a program passed from the command line - */ +// +// setup to run a program passed from the command line +// bool setup_command_program(const char *program, char **runFile) { char file[OS_PATHNAME_SIZE + 1]; @@ -234,14 +235,14 @@ void decompile(const char *path) { chdir(prev_cwd); } -/* - * process command-line parameters - */ -bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { +// +// process command-line parameters +// +bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile, bool *iterate) { bool result = true; while (result) { int option_index = 0; - int c = getopt_long(argc, argv, "vkfxm:s:o:c:h::", OPTIONS, &option_index); + int c = getopt_long(argc, argv, "vkfxim:s:o:c:h::", OPTIONS, &option_index); if (c == -1 && !option_index) { // no more options for (int i = 1; i < argc; i++) { @@ -309,6 +310,9 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { result = false; } break; + case 'i': + *iterate = true; + break; default: show_help(); result = false; @@ -356,9 +360,47 @@ bool process_options(int argc, char *argv[], char **runFile, bool *tmpFile) { return result; } -/* - * program entry point - */ +uint32_t get_modified_time(const char *file) { + uint32_t result = 0; + if (file != nullptr) { + struct stat st_file; + if (!stat(file, &st_file)) { + result = st_file.st_mtime; + } + } + return result; +} + +#if !defined(__MACH__) && !defined(_Win32) +bool wait_for_file(const char *file, uint32_t modifiedTime) { + bool result = false; + fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK); + fprintf(stdout, "Fix the error [in %s] to continue, or press enter to exit...\n", file); + while (!result && modifiedTime == get_modified_time(file)) { + char c = 0; + read(0, &c, 1); + if (c != 0) { + result = true; + break; + } + usleep(500 * 1000); + } + return result; +} +#else +bool wait_for_file(const char *file, uint32_t modifiedTime) { + bool result = false; + fprintf(stdout, "Fix the error [in %s] to continue...\n", file); + while (!result && modifiedTime == get_modified_time(file)) { + usleep(500 * 1000); + } + return result; +} +#endif + +// +// program entry point +// int main(int argc, char *argv[]) { opt_autolocal = 0; opt_command[0] = '\0'; @@ -378,11 +420,20 @@ int main(int argc, char *argv[]) { char *file = nullptr; bool tmpFile = false; - if (process_options(argc, argv, &file, &tmpFile)) { + bool iterate = false; + if (process_options(argc, argv, &file, &tmpFile, &iterate)) { char prev_cwd[OS_PATHNAME_SIZE + 1]; prev_cwd[0] = 0; getcwd(prev_cwd, sizeof(prev_cwd) - 1); - sbasic_main(file); + do { + uint32_t modifiedTime = get_modified_time(file); + bool result = sbasic_main(file); + if (!result && iterate) { + if (wait_for_file(file, modifiedTime)) { + break; + } + } + } while (iterate); chdir(prev_cwd); if (tmpFile) { unlink(file); From 48bc15d4cc3c3f5122d4b98a5c4f8e163d7dacc8 Mon Sep 17 00:00:00 2001 From: chrisws Date: Mon, 7 Mar 2022 22:04:01 +1000 Subject: [PATCH 25/47] EMCC: initial commit --- configure.ac | 22 +++++++++++++++++++ src/common/Makefile.am | 1 - src/platform/emcc/Makefile.am | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/platform/emcc/Makefile.am diff --git a/configure.ac b/configure.ac index b912b4ca..39be24ca 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,11 @@ PKG_PROG_PKG_CONFIG TARGET="" dnl define build arguments +AC_ARG_ENABLE(emcc, + AS_HELP_STRING([--enable-emcc],[build emscripten version(default=no)]), + [ac_build_emcc="yes"], + [ac_build_emcc="no"]) + AC_ARG_ENABLE(sdl, AS_HELP_STRING([--enable-sdl],[build SDL version(default=no)]), [ac_build_sdl="yes"], @@ -118,6 +123,10 @@ function checkPCRE() { have_pcre="no" fi + if test x$ac_build_emcc = xyes; then + have_pcre="no" + fi + if test "${have_pcre}" = "yes" ; then AC_DEFINE(USE_PCRE, 1, [match.c used with libpcre.]) PACKAGE_LIBS="${PACKAGE_LIBS} `pcre-config --libs`" @@ -321,6 +330,16 @@ function buildWeb() { AC_SUBST(BUILD_SUBDIRS) } +function buildEmscripten() { + TARGET="Building Emscripten version." + BUILD_SUBDIRS="src/common src/platform/emcc" + AC_CHECK_HEADERS([emscripten.h], [], [AC_MSG_ERROR([emscripten is not installed])]) + AM_CONDITIONAL(WITH_CYGWIN_CONSOLE, false) + AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) + AC_DEFINE(USE_TERM_IO, 0, [dont use the termios library.]) + AC_SUBST(BUILD_SUBDIRS) +} + function buildFLTK() { TARGET="Building FLTK version." @@ -389,6 +408,8 @@ elif test x$ac_build_web = xyes; then buildWeb elif test x$ac_build_fltk = xyes; then buildFLTK +elif test x$ac_build_emcc = xyes; then + buildEmscripten else buildConsole fi @@ -425,6 +446,7 @@ Makefile src/common/Makefile src/platform/android/Makefile src/platform/console/Makefile +src/platform/emcc/Makefile src/platform/sdl/Makefile src/platform/web/Makefile src/platform/fltk/Makefile diff --git a/src/common/Makefile.am b/src/common/Makefile.am index fef1242f..41864c78 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -23,7 +23,6 @@ EXTRA_DIST = \ ../lib/jsmn/jsmn.h \ ../lib/lodepng/lodepng.cpp \ ../lib/lodepng/lodepng.h \ - ../lib/miniaudio/extras/dr_wav.h \ ../lib/miniaudio/miniaudio.h \ ../lib/stb/stb_textedit.h diff --git a/src/platform/emcc/Makefile.am b/src/platform/emcc/Makefile.am new file mode 100644 index 00000000..dcde345f --- /dev/null +++ b/src/platform/emcc/Makefile.am @@ -0,0 +1,41 @@ +# SmallBASIC command line version +# Copyright(C) 2001-2022 Chris Warren-Smith. +# +# This program is distributed under the terms of the GPL v2.0 or later +# Download the GNU Public License (GPL) from www.gnu.org +# + +# +# Setup: +# +# $ git clone https://github.com/emscripten-core/emsdk.git +# $ emsdk update +# $ emsdk install latest +# $ emsdk activate latest +# $ . ./emsdk_env.sh +# +# Build: +# +# $ emconfigure ./configure --enable-emcc +# $ emmake make +# + +AM_CPPFLAGS = -I$(top_builddir)/src -I. @PACKAGE_CFLAGS@ + +CLEANFILES = sbasic.js sbasic.wasm + +bin_PROGRAMS = sbasic.html + +sbasic_html_SOURCES = \ + ../../lib/lodepng/lodepng.cpp ../../lib/lodepng/lodepng.h \ + ../console/main.cpp \ + ../console/input.cpp \ + ../console/device.cpp \ + ../console/image.cpp \ + ../console/decomp.c + +sbasic_html_LDFLAGS = --shell-file shell.html + +sbasic_html_LDADD = -L$(top_srcdir)/src/common -lsb_common @PACKAGE_LIBS@ + +sbasic_html_DEPENDENCIES = $(top_srcdir)/src/common/libsb_common.a From 814823f0856a2598c93b07661de6508550eac2bb Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 12 Mar 2022 11:37:12 +1000 Subject: [PATCH 26/47] EMCC: Emscripten version wip --- configure.ac | 4 + src/platform/emcc/Makefile.am | 26 ++- src/platform/emcc/display.cpp | 304 ++++++++++++++++++++++++++++++++++ src/platform/emcc/main.cpp | 66 ++++++++ src/platform/emcc/runtime.cpp | 144 ++++++++++++++++ src/platform/emcc/runtime.h | 42 +++++ src/ui/utils.h | 4 +- 7 files changed, 579 insertions(+), 11 deletions(-) create mode 100644 src/platform/emcc/display.cpp create mode 100644 src/platform/emcc/main.cpp create mode 100644 src/platform/emcc/runtime.cpp create mode 100644 src/platform/emcc/runtime.h diff --git a/configure.ac b/configure.ac index 39be24ca..c6f51ab8 100644 --- a/configure.ac +++ b/configure.ac @@ -336,8 +336,12 @@ function buildEmscripten() { AC_CHECK_HEADERS([emscripten.h], [], [AC_MSG_ERROR([emscripten is not installed])]) AM_CONDITIONAL(WITH_CYGWIN_CONSOLE, false) AC_DEFINE(_UnixOS, 1, [Building under Unix like systems.]) + AC_DEFINE(_EMCC, 1, [Defined when building emscripten version]) AC_DEFINE(USE_TERM_IO, 0, [dont use the termios library.]) + AC_DEFINE(IMPL_LOG_WRITE, 1, [Driver implements lwrite()]) + AC_DEFINE(IMPL_DEV_READ, 1, [Implement dev_read()]) AC_SUBST(BUILD_SUBDIRS) + (cd src/platform/android/app/src/main/assets && xxd -i main.bas > ../../../../../../../src/platform/emcc/main_bas.h) } function buildFLTK() { diff --git a/src/platform/emcc/Makefile.am b/src/platform/emcc/Makefile.am index dcde345f..4a8f262a 100644 --- a/src/platform/emcc/Makefile.am +++ b/src/platform/emcc/Makefile.am @@ -26,15 +26,23 @@ CLEANFILES = sbasic.js sbasic.wasm bin_PROGRAMS = sbasic.html -sbasic_html_SOURCES = \ - ../../lib/lodepng/lodepng.cpp ../../lib/lodepng/lodepng.h \ - ../console/main.cpp \ - ../console/input.cpp \ - ../console/device.cpp \ - ../console/image.cpp \ - ../console/decomp.c - -sbasic_html_LDFLAGS = --shell-file shell.html +sbasic_html_SOURCES = \ + ../../lib/lodepng/lodepng.cpp ../../lib/lodepng/lodepng.h \ + ../../ui/ansiwidget.cpp \ + ../../ui/window.cpp \ + ../../ui/screen.cpp \ + ../../ui/system.cpp \ + ../../ui/form.cpp \ + ../../ui/inputs.cpp \ + ../../ui/textedit.cpp \ + ../../ui/image.cpp \ + ../../ui/strlib.cpp \ + ../../ui/audio.cpp \ + runtime.cpp \ + display.cpp \ + main.cpp + +sbasic_html_LDFLAGS = --bind --shell-file shell.html sbasic_html_LDADD = -L$(top_srcdir)/src/common -lsb_common @PACKAGE_LIBS@ diff --git a/src/platform/emcc/display.cpp b/src/platform/emcc/display.cpp new file mode 100644 index 00000000..042f182f --- /dev/null +++ b/src/platform/emcc/display.cpp @@ -0,0 +1,304 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2022 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#include "config.h" +#include +#include +#include +#include "ui/utils.h" +#include "ui/rgb.h" +#include "lib/maapi.h" + +struct Canvas; +struct Font; +static int nextId = 1000; +pixel_t drawColor; +Canvas *screen; +Canvas *drawTarget; +Font *font; + +EM_JS(int, get_canvas_width, (), { + return document.getElementById("canvas").width; + }); + +EM_JS(int, get_canvas_height, (), { + return document.getElementById("canvas").height; + }); + +struct Font { + Font(int size, bool bold, bool italic) : + _size(size), + _bold(bold), + _italic(italic) { + } + + int _size; + bool _bold; + bool _italic; +}; + +struct Canvas { + Canvas() : _clip(nullptr), _id(-1), _w(0), _h(0) {} + ~Canvas(); + + bool create(int width, int height); + void drawArc(int xc, int yc, double r, double start, double end, double aspect); + void drawEllipse(int xc, int yc, int rx, int ry, bool fill); + void drawLine(int startX, int startY, int endX, int endY); + void drawPixel(int posX, int posY); + void drawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int bytesPerLine); + void drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY); + void drawText(int left, int top, const char *str, int len); + void fillRect(int x, int y, int w, int h); + MAExtent getTextSize(const char *str, int len); + int getPixel(Canvas *canvas, int x, int y); + void getImageData(Canvas *canvas, uint8_t *image, const MARect *srcRect, int bytesPerLine); + void setClip(int x, int y, int w, int h); + + MARect *_clip; + int _id; + int _w; + int _h; +}; + +Canvas::~Canvas() { + if (_id != -1) { + EM_ASM_({ + var element = document.getElementById($0); + document.body.removeChild(element); + }, _id); + } +} + +bool Canvas::create(int width, int height) { + _id = ++nextId; + _w = width; + _h = height; + EM_ASM_({ + var canvas = document.createElement("canvas"); + canvas.id = "_canvas" + $0; + canvas.width = $1; + canvas.height = $2; + canvas.style.zIndex = 1; + canvas.style.position = "absolute"; + canvas.style.border = "1px solid"; + document.body.appendChild(canvas); + }, _id, width, height); + return true; +}; + +void Canvas::drawArc(int xc, int yc, double r, double start, double end, double aspect) { +} + +void Canvas::drawEllipse(int xc, int yc, int rx, int ry, bool fill) { +} + +void Canvas::drawLine(int startX, int startY, int endX, int endY) { +} + +void Canvas::drawPixel(int posX, int posY) { +} + +void Canvas::drawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int bytesPerLine) { +} + +void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) { +} + +void Canvas::drawText(int left, int top, const char *str, int len) { +} + +void Canvas::fillRect(int x, int y, int w, int h) { +} + +MAExtent Canvas::getTextSize(const char *str, int len) { + int width = 0; + int height = 0; + + return (MAExtent)((width << 16) + height); +} + +int Canvas::getPixel(Canvas *canvas, int x, int y) { + return 0; +} + +void Canvas::getImageData(Canvas *canvas, uint8_t *image, const MARect *srcRect, int bytesPerLine) { +} + +void Canvas::setClip(int x, int y, int w, int h) { + delete _clip; + if (x != 0 || y != 0 || _w != w || _h != h) { + _clip = new MARect(); + _clip->left = x; + _clip->top = y; + _clip->width = x + w; + _clip->height = y + h; + } else { + _clip = nullptr; + } +} + +// +// maapi implementation +// + +MAHandle maCreatePlaceholder(void) { + MAHandle maHandle = (MAHandle) new Canvas(); + return maHandle; +} + +int maSetColor(int c) { + drawColor = GET_FROM_RGB888(c); + return c; +} + +void maSetClipRect(int left, int top, int width, int height) { + if (drawTarget) { + drawTarget->setClip(left, top, width, height); + } +} + +MAExtent maGetTextSize(const char *str) { + MAExtent result; + if (str && str[0] && drawTarget) { + result = drawTarget->getTextSize(str, strlen(str)); + } else { + result = 0; + } + return result; +} + +MAExtent maGetScrSize(void) { + short width = get_canvas_width(); + short height = get_canvas_height(); + return (MAExtent)((width << 16) + height); +} + +void maDestroyPlaceholder(MAHandle maHandle) { + Canvas *holder = (Canvas *)maHandle; + delete holder; +} + +void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int stride) { + Canvas *holder = (Canvas *)maHandle; + if (srcRect->width == 1 && srcRect->height == 1) { + *((int *)dst) = drawTarget->getPixel(holder, srcRect->left, srcRect->top); + } else { + drawTarget->getImageData(holder, (uint8_t *)dst, srcRect, stride); + } +} + +MAHandle maSetDrawTarget(MAHandle maHandle) { + MAHandle result = (MAHandle) drawTarget; + if (maHandle == (MAHandle) HANDLE_SCREEN || + maHandle == (MAHandle) HANDLE_SCREEN_BUFFER) { + drawTarget = screen; + } else { + drawTarget = (Canvas *)maHandle; + } + delete drawTarget->_clip; + drawTarget->_clip = nullptr; + return result; +} + +int maCreateDrawableImage(MAHandle maHandle, int width, int height) { + Canvas *drawable = (Canvas *)maHandle; + return drawable->create(width, height) ? RES_OK : -1; +} + +// +// drawing +// + +void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d *dstPoint, int transformMode) { + Canvas *src = (Canvas *)maHandle; + if (drawTarget && drawTarget != src) { + drawTarget->drawRegion(src, srcRect, dstPoint->x, dstPoint->y); + } +} + +void maPlot(int posX, int posY) { + if (drawTarget) { + drawTarget->drawPixel(posX, posY); + } +} + +void maLine(int startX, int startY, int endX, int endY) { + if (drawTarget) { + drawTarget->drawLine(startX, startY, endX, endY); + } +} + +void maFillRect(int left, int top, int width, int height) { + if (drawTarget) { + drawTarget->fillRect(left, top, width, height); + } +} + +void maArc(int xc, int yc, double r, double start, double end, double aspect) { + if (drawTarget) { + drawTarget->drawArc(xc, yc, r, start, end, aspect); + } +} + +void maEllipse(int xc, int yc, int rx, int ry, int fill) { + if (drawTarget) { + drawTarget->drawEllipse(xc, yc, rx, ry, fill); + } +} + +void maDrawText(int left, int top, const char *str, int length) { + if (str && str[0] && drawTarget) { + drawTarget->drawText(left, top, str, length); + } +} + +void maDrawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int stride) { + drawTarget->drawRGB(dstPoint, src, srcRect, opacity, stride); +} + +// +// font +// +MAHandle maFontLoadDefault(int type, int style, int size) { + bool italic = (style & FONT_STYLE_ITALIC); + bool bold = (style & FONT_STYLE_BOLD); + if (font) { + delete font; + } + font = new Font(size, bold, italic); + return (MAHandle)font; +} + +MAHandle maFontSetCurrent(MAHandle maHandle) { + font = ((Font *)maHandle); + return maHandle; +} + +int maFontDelete(MAHandle maHandle) { + if (maHandle != -1) { + Font *handleFont = (Font *)maHandle; + if (font == handleFont) { + font = nullptr; + } + delete handleFont; + } + return RES_FONT_OK; +} + +// +// not implemented +// +void maShowVirtualKeyboard(void) { +} + +void maHideVirtualKeyboard(void) { +} + +void maUpdateScreen(void) { +} diff --git a/src/platform/emcc/main.cpp b/src/platform/emcc/main.cpp new file mode 100644 index 00000000..d139523d --- /dev/null +++ b/src/platform/emcc/main.cpp @@ -0,0 +1,66 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2022 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#include "config.h" + +#include "include/osd.h" +#include "common/sbapp.h" +#include "common/device.h" +#include "platform/emcc/runtime.h" + +void appLog(const char *format, ...) { + va_list args; + va_start(args, format); + unsigned size = vsnprintf(NULL, 0, format, args); + va_end(args); + + if (size) { + char *buf = (char *)malloc(size + 3); + buf[0] = '\0'; + va_start(args, format); + vsnprintf(buf, size + 1, format, args); + va_end(args); + buf[size] = '\0'; + + int i = size - 1; + while (i >= 0 && isspace(buf[i])) { + buf[i--] = '\0'; + } + strcat(buf, "\r\n"); + fputs(buf, stderr); + free(buf); + } +} + +void init() { + opt_command[0] = '\0'; + opt_file_permitted = 0; + opt_graphics = 1; + opt_ide = 0; + opt_modpath[0] = '\0'; + opt_nosave = 1; + opt_pref_height = 0; + opt_pref_width = 0; + opt_quiet = 1; + opt_verbose = 0; + opt_autolocal = 0; + os_graf_mx = 1024; + os_graf_my = 768; +} + +int main(int argc, char* argv[]) { + logEntered(); + + init(); + Runtime *runtime = new Runtime(); + runtime->runShell(); + delete runtime; + + logLeaving(); + return 0; +} diff --git a/src/platform/emcc/runtime.cpp b/src/platform/emcc/runtime.cpp new file mode 100644 index 00000000..5d6092a4 --- /dev/null +++ b/src/platform/emcc/runtime.cpp @@ -0,0 +1,144 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2022 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#include "config.h" + +#include +#include + +#include "include/osd.h" +#include "common/sys.h" +#include "common/smbas.h" +#include "common/device.h" +#include "common/keymap.h" +#include "common/inet.h" +#include "common/pproc.h" +#include "lib/maapi.h" +#include "ui/utils.h" +#include "ui/audio.h" +#include "platform/emcc/runtime.h" +#include "platform/emcc/main_bas.h" + +#define MAIN_BAS "__main_bas__" + +Runtime *runtime; + +Runtime::Runtime() : + System() { + logEntered(); + runtime = this; + + MAExtent screenSize = maGetScrSize(); + _output = new AnsiWidget(EXTENT_X(screenSize), EXTENT_Y(screenSize)); + _output->construct(); + + //_eventQueue = new Stack(); + _state = kActiveState; +} + +Runtime::~Runtime() { + logEntered(); +} + +void Runtime::alert(const char *title, const char *message) { +} + +int Runtime::ask(const char *title, const char *prompt, bool cancel) { + int result = 0; + return result; +} + +void Runtime::browseFile(const char *url) { +} + +void Runtime::enableCursor(bool enabled) { +} + +char *Runtime::getClipboardText() { + return nullptr; +} + +char *Runtime::loadResource(const char *fileName) { + logEntered(); + char *buffer = System::loadResource(fileName); + if (buffer == nullptr && strcmp(fileName, MAIN_BAS) == 0) { + buffer = (char *)malloc(main_bas_len + 1); + memcpy(buffer, main_bas, main_bas_len); + buffer[main_bas_len] = '\0'; + } + return buffer; +} + +void Runtime::optionsBox(StringList *items) { +} + +MAEvent Runtime::processEvents(int waitFlag) { + MAEvent event; + event.type = 0; + event.key = 0; + event.nativeKey = 0; + + if (keymap_kbhit()) { + event.type = EVENT_TYPE_KEY_PRESSED; + event.key = keymap_kbpeek(); + } + return event; +} + +void Runtime::runShell() { + logEntered(); + + audio_open(); + runMain(MAIN_BAS); + audio_close(); + _state = kDoneState; + logLeaving(); +} + +void Runtime::setClipboardText(const char *text) { +} + +void Runtime::setWindowRect(int x, int y, int width, int height) { +} + +void Runtime::setWindowTitle(const char *title) { +} + +void Runtime::showCursor(CursorType cursorType) { +} + +// +// ma event handling +// +int maGetEvent(MAEvent *event) { + int result = 0; + return result; +} + +void maWait(int timeout) { + +} + +// +// System platform methods +// +void System::editSource(strlib::String loadPath, bool restoreOnExit) { + // empty +} + +bool System::getPen3() { + return false; +} + +int osd_devinit() { + return 1; +} + +int osd_devrestore() { + return 1; +} diff --git a/src/platform/emcc/runtime.h b/src/platform/emcc/runtime.h new file mode 100644 index 00000000..12c67278 --- /dev/null +++ b/src/platform/emcc/runtime.h @@ -0,0 +1,42 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2022 Chris Warren-Smith. +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#pragma once + +#include "ui/ansiwidget.h" +#include "ui/system.h" + +struct Runtime : public System { + Runtime(); + virtual ~Runtime(); + + void addShortcut(const char *) {} + void alert(const char *title, const char *message); + int ask(const char *title, const char *prompt, bool cancel=true); + void browseFile(const char *url); + char *getClipboardText(); + int getFontSize() { return _output->getFontSize(); } + void enableCursor(bool enabled); + int handle(int event); + char *loadResource(const char *fileName); + void optionsBox(StringList *items); + void onRunCompleted() {} + void saveWindowRect() {} + MAEvent processEvents(int waitFlag); + bool run(const char *bas) { return execute(bas); } + void runShell(); + void resize(int w, int h); + void setClipboardText(const char *text); + void setFontSize(int size); + void setLoadBreak(const char *url) {} + void setLoadPath(const char *url) {} + void setWindowRect(int x, int y, int width, int height); + void setWindowTitle(const char *title); + void share(const char *path) {} + void showCursor(CursorType cursorType); +}; diff --git a/src/ui/utils.h b/src/ui/utils.h index c99af5a2..e5aa52eb 100644 --- a/src/ui/utils.h +++ b/src/ui/utils.h @@ -28,12 +28,12 @@ #include #define deviceLog(...) __android_log_print(ANDROID_LOG_INFO, \ "smallbasic", __VA_ARGS__) -#elif defined(_SDL) || defined(_FLTK) +#elif defined(_SDL) || defined(_FLTK) || defined(_EMCC) void appLog(const char *format, ...); #define deviceLog(...) appLog(__VA_ARGS__) #endif -#if defined(_DEBUG) +#if defined(_DEBUG) || defined(_EMCC) #define trace(...) deviceLog(__VA_ARGS__) #else #define trace(...) From d0c5a9150e5360c464133e03b38666b4c5246c0c Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 13 Mar 2022 15:21:32 +1000 Subject: [PATCH 27/47] EMCC: Emscripten version wip --- src/platform/emcc/Makefile.am | 4 +- src/platform/emcc/{display.cpp => canvas.cpp} | 205 ++++++++++++------ src/platform/emcc/runtime.cpp | 4 + 3 files changed, 145 insertions(+), 68 deletions(-) rename src/platform/emcc/{display.cpp => canvas.cpp} (58%) diff --git a/src/platform/emcc/Makefile.am b/src/platform/emcc/Makefile.am index 4a8f262a..b155edc5 100644 --- a/src/platform/emcc/Makefile.am +++ b/src/platform/emcc/Makefile.am @@ -39,10 +39,10 @@ sbasic_html_SOURCES = \ ../../ui/strlib.cpp \ ../../ui/audio.cpp \ runtime.cpp \ - display.cpp \ + canvas.cpp \ main.cpp -sbasic_html_LDFLAGS = --bind --shell-file shell.html +sbasic_html_LDFLAGS = -O3 -lembind --shell-file shell.html -s ASYNCIFY=1 sbasic_html_LDADD = -L$(top_srcdir)/src/common -lsb_common @PACKAGE_LIBS@ diff --git a/src/platform/emcc/display.cpp b/src/platform/emcc/canvas.cpp similarity index 58% rename from src/platform/emcc/display.cpp rename to src/platform/emcc/canvas.cpp index 042f182f..95b032d2 100644 --- a/src/platform/emcc/display.cpp +++ b/src/platform/emcc/canvas.cpp @@ -12,59 +12,125 @@ #include #include "ui/utils.h" #include "ui/rgb.h" +#include "ui/strlib.h" #include "lib/maapi.h" +#include "platform/emcc/canvas.h" -struct Canvas; -struct Font; static int nextId = 1000; pixel_t drawColor; Canvas *screen; Canvas *drawTarget; Font *font; -EM_JS(int, get_canvas_width, (), { +const char *colors[] = { + "#000", // 0 black + "#000080", // 1 blue + "#008000", // 2 green + "#008080", // 3 cyan + "#800000", // 4 red + "#800080", // 5 magenta + "#800800", // 6 yellow + "#c0c0c0", // 7 white + "#808080", // 8 gray + "#0000ff", // 9 light blue + "#00ff00", // 10 light green + "#00ffff", // 11 light cyan + "#ff0000", // 12 light red + "#ff00ff", // 13 light magenta + "#ffff00", // 14 light yellow + "#fff" // 15 bright white +}; + +const uint32_t colors_i[] = { + 0x000000, // 0 black + 0x000080, // 1 blue + 0x008000, // 2 green + 0x008080, // 3 cyan + 0x800000, // 4 red + 0x800080, // 5 magenta + 0x808000, // 6 yellow + 0xC0C0C0, // 7 white + 0x808080, // 8 gray + 0x0000FF, // 9 light blue + 0x00FF00, // 10 light green + 0x00FFFF, // 11 light cyan + 0xFF0000, // 12 light red + 0xFF00FF, // 13 light magenta + 0xFFFF00, // 14 light yellow + 0xFFFFFF // 15 bright white +}; + +EM_JS(int, draw_pixel, (int id, int x, int y, int r, int g, int b), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + var pxId = ctx.createImageData(1, 1); + var pxData = pxId.data; + pxData[0] = r; + pxData[1] = g; + pxData[2] = b; + pxData[3] = 255; + ctx.putImageData(pxId, x, y); + }); + +EM_JS(int, draw_text, (int id, int x, int y, const char *str, int len, const char *fg), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + var s = new String(str).substring(0, len); + var face = (i ? "italic" : "") + " " + (b ? "bold" : ""); + var width = ctx.measureText(s).width; + var y1 = y * fontHeight; + ctx.font=face + " " + fontSize + "pt monospace"; + ctx.fillStyle = fg; + ctx.fillText(s, x, y1); + }); + +EM_JS(int, get_screen_width, (), { return document.getElementById("canvas").width; }); -EM_JS(int, get_canvas_height, (), { +EM_JS(int, get_screen_height, (), { return document.getElementById("canvas").height; }); -struct Font { - Font(int size, bool bold, bool italic) : - _size(size), - _bold(bold), - _italic(italic) { +EM_JS(int, get_text_size, (int id, const char *str, int len), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + var s = new String(str).substring(0, len); + var metrics = ctx.measureText(s); + var result = (metrics.width << 16) + (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent); + return result; + }); + +strlib::String get_color() { + strlib::String result; + long c = drawColor; + if (c < 0) { + c = -c; + int r = (c>>16) & 0xFF; + int g = (c>>8) & 0xFF; + int b = (c) & 0xFF; + char buf[8]; + sprintf(buf, "#%x%x%x", b, g, r); + result.append(buf); + } else { + result.append((colors[c > 15 ? 15 : c])); } + return result; +} - int _size; - bool _bold; - bool _italic; -}; +Canvas::Canvas() : + _clip(nullptr), + _id(-1), + _w(0), + _h(0) { +} -struct Canvas { - Canvas() : _clip(nullptr), _id(-1), _w(0), _h(0) {} - ~Canvas(); - - bool create(int width, int height); - void drawArc(int xc, int yc, double r, double start, double end, double aspect); - void drawEllipse(int xc, int yc, int rx, int ry, bool fill); - void drawLine(int startX, int startY, int endX, int endY); - void drawPixel(int posX, int posY); - void drawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int bytesPerLine); - void drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY); - void drawText(int left, int top, const char *str, int len); - void fillRect(int x, int y, int w, int h); - MAExtent getTextSize(const char *str, int len); - int getPixel(Canvas *canvas, int x, int y); - void getImageData(Canvas *canvas, uint8_t *image, const MARect *srcRect, int bytesPerLine); - void setClip(int x, int y, int w, int h); - - MARect *_clip; - int _id; - int _w; - int _h; -}; +Canvas::Canvas(int width, int height) : + _clip(nullptr), + _id(-1), + _w(width), + _h(height) { +} Canvas::~Canvas() { if (_id != -1) { @@ -81,7 +147,7 @@ bool Canvas::create(int width, int height) { _h = height; EM_ASM_({ var canvas = document.createElement("canvas"); - canvas.id = "_canvas" + $0; + canvas.id = "canvas_" + $0; canvas.width = $1; canvas.height = $2; canvas.style.zIndex = 1; @@ -93,41 +159,41 @@ bool Canvas::create(int width, int height) { }; void Canvas::drawArc(int xc, int yc, double r, double start, double end, double aspect) { + EM_ASM_({ + var c = document.getElementById($0 == -1 ? "canvas" : "canvas_" + $0); + var ctx = c.getContext("2d"); + ctx.beginPath(); + ctx.arc(100, 75, 50, 0, 2 * Math.PI); + ctx.stroke(); + }, _id, xc, yc, r, start, end, aspect); } void Canvas::drawEllipse(int xc, int yc, int rx, int ry, bool fill) { + logEntered(); } void Canvas::drawLine(int startX, int startY, int endX, int endY) { -} - -void Canvas::drawPixel(int posX, int posY) { + logEntered(); } void Canvas::drawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int bytesPerLine) { + logEntered(); } void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) { -} - -void Canvas::drawText(int left, int top, const char *str, int len) { + logEntered(); } void Canvas::fillRect(int x, int y, int w, int h) { + logEntered(); } MAExtent Canvas::getTextSize(const char *str, int len) { - int width = 0; - int height = 0; - - return (MAExtent)((width << 16) + height); -} - -int Canvas::getPixel(Canvas *canvas, int x, int y) { - return 0; + return (MAExtent)get_text_size(_id, str, len); } void Canvas::getImageData(Canvas *canvas, uint8_t *image, const MARect *srcRect, int bytesPerLine) { + logEntered(); } void Canvas::setClip(int x, int y, int w, int h) { @@ -146,7 +212,6 @@ void Canvas::setClip(int x, int y, int w, int h) { // // maapi implementation // - MAHandle maCreatePlaceholder(void) { MAHandle maHandle = (MAHandle) new Canvas(); return maHandle; @@ -174,9 +239,10 @@ MAExtent maGetTextSize(const char *str) { } MAExtent maGetScrSize(void) { - short width = get_canvas_width(); - short height = get_canvas_height(); - return (MAExtent)((width << 16) + height); + if (screen == nullptr) { + screen = new Canvas(get_screen_width(), get_screen_height()); + } + return (MAExtent)((screen->_w << 16) + screen->_h); } void maDestroyPlaceholder(MAHandle maHandle) { @@ -186,14 +252,11 @@ void maDestroyPlaceholder(MAHandle maHandle) { void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int stride) { Canvas *holder = (Canvas *)maHandle; - if (srcRect->width == 1 && srcRect->height == 1) { - *((int *)dst) = drawTarget->getPixel(holder, srcRect->left, srcRect->top); - } else { - drawTarget->getImageData(holder, (uint8_t *)dst, srcRect, stride); - } + drawTarget->getImageData(holder, (uint8_t *)dst, srcRect, stride); } MAHandle maSetDrawTarget(MAHandle maHandle) { + logEntered(); MAHandle result = (MAHandle) drawTarget; if (maHandle == (MAHandle) HANDLE_SCREEN || maHandle == (MAHandle) HANDLE_SCREEN_BUFFER) { @@ -211,10 +274,14 @@ int maCreateDrawableImage(MAHandle maHandle, int width, int height) { return drawable->create(width, height) ? RES_OK : -1; } +void maUpdateScreen(void) { + logEntered(); + trace("%d \n", screen->_id); +} + // // drawing // - void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d *dstPoint, int transformMode) { Canvas *src = (Canvas *)maHandle; if (drawTarget && drawTarget != src) { @@ -224,7 +291,16 @@ void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d void maPlot(int posX, int posY) { if (drawTarget) { - drawTarget->drawPixel(posX, posY); + int c = drawColor; + if (c < 0) { + c = -c; + } else { + c = colors_i[c > 15 ? 15 : c]; + } + int r = (c & 0xff0000) >> 16; + int g = (c & 0xff00) >> 8; + int b = (c & 0xff); + draw_pixel(drawTarget->_id, posX, posY, r, g, b); } } @@ -254,7 +330,7 @@ void maEllipse(int xc, int yc, int rx, int ry, int fill) { void maDrawText(int left, int top, const char *str, int length) { if (str && str[0] && drawTarget) { - drawTarget->drawText(left, top, str, length); + draw_text(drawTarget->_id, left, top, str, length, get_color()); } } @@ -299,6 +375,3 @@ void maShowVirtualKeyboard(void) { void maHideVirtualKeyboard(void) { } - -void maUpdateScreen(void) { -} diff --git a/src/platform/emcc/runtime.cpp b/src/platform/emcc/runtime.cpp index 5d6092a4..1b6a397f 100644 --- a/src/platform/emcc/runtime.cpp +++ b/src/platform/emcc/runtime.cpp @@ -36,6 +36,8 @@ Runtime::Runtime() : MAExtent screenSize = maGetScrSize(); _output = new AnsiWidget(EXTENT_X(screenSize), EXTENT_Y(screenSize)); _output->construct(); + _output->setTextColor(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND); + _output->setFontSize(11); //_eventQueue = new Stack(); _state = kActiveState; @@ -78,6 +80,8 @@ void Runtime::optionsBox(StringList *items) { } MAEvent Runtime::processEvents(int waitFlag) { + emscripten_sleep(10); + MAEvent event; event.type = 0; event.key = 0; From ce01149975d0bb9a0305c98ea45ed7d0e9085309 Mon Sep 17 00:00:00 2001 From: chrisws Date: Sun, 13 Mar 2022 18:47:45 +1000 Subject: [PATCH 28/47] EMCC: Emscripten version wip --- src/platform/emcc/canvas.cpp | 177 ++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 88 deletions(-) diff --git a/src/platform/emcc/canvas.cpp b/src/platform/emcc/canvas.cpp index 95b032d2..8d1f394b 100644 --- a/src/platform/emcc/canvas.cpp +++ b/src/platform/emcc/canvas.cpp @@ -60,7 +60,49 @@ const uint32_t colors_i[] = { 0xFFFFFF // 15 bright white }; -EM_JS(int, draw_pixel, (int id, int x, int y, int r, int g, int b), { +EM_JS(int, get_screen_height, (), { + return document.getElementById("canvas").height; + }); + +EM_JS(int, get_screen_width, (), { + return document.getElementById("canvas").width; + }); + +EM_JS(int, get_text_size, (int id, const char *str, int len), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + var s = new String(str).substring(0, len); + var metrics = ctx.measureText(s); + var result = (metrics.width << 16) + (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent); + return result; + }); + +EM_JS(void, draw_arc, (int id, int xc, int yc, double r, double start, double end, double aspect), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + ctx.beginPath(); + ctx.arc(100, 75, 50, 0, 2 * Math.PI); + ctx.stroke(); + }); + +EM_JS(void, draw_ellipse, (int id, int xc, int yc, int rx, int ry, int fill), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + + }); + +EM_JS(void, draw_line, (int id, int x1, int y1, int x2, int y2, const char *color), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.lineWidth = 1; + ctx.strokeStyle = UTF8ToString(color); + ctx.stroke(); + }); + +EM_JS(void, draw_pixel, (int id, int x, int y, int r, int g, int b), { var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); var pxId = ctx.createImageData(1, 1); @@ -72,33 +114,22 @@ EM_JS(int, draw_pixel, (int id, int x, int y, int r, int g, int b), { ctx.putImageData(pxId, x, y); }); -EM_JS(int, draw_text, (int id, int x, int y, const char *str, int len, const char *fg), { +EM_JS(void, draw_rect_filled, (int id, int left, int top, int width, int height), { var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); - var s = new String(str).substring(0, len); - var face = (i ? "italic" : "") + " " + (b ? "bold" : ""); - var width = ctx.measureText(s).width; - var y1 = y * fontHeight; - ctx.font=face + " " + fontSize + "pt monospace"; - ctx.fillStyle = fg; - ctx.fillText(s, x, y1); - }); - -EM_JS(int, get_screen_width, (), { - return document.getElementById("canvas").width; - }); -EM_JS(int, get_screen_height, (), { - return document.getElementById("canvas").height; }); -EM_JS(int, get_text_size, (int id, const char *str, int len), { +EM_JS(void, draw_text, (int id, int x, int y, const char *str, int len, const char *color, const char *face), { var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); - var s = new String(str).substring(0, len); - var metrics = ctx.measureText(s); - var result = (metrics.width << 16) + (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent); - return result; + var s = UTF8ToString(str).substring(0, len); + var width = ctx.measureText(s).width; + var fontHeight = 15; + var y1 = y * fontHeight; + ctx.font = UTF8ToString(face); + ctx.fillStyle = UTF8ToString(color); + ctx.fillText(s, x, y1); }); strlib::String get_color() { @@ -151,6 +182,7 @@ bool Canvas::create(int width, int height) { canvas.width = $1; canvas.height = $2; canvas.style.zIndex = 1; + canvas.style.display = "none"; canvas.style.position = "absolute"; canvas.style.border = "1px solid"; document.body.appendChild(canvas); @@ -158,44 +190,6 @@ bool Canvas::create(int width, int height) { return true; }; -void Canvas::drawArc(int xc, int yc, double r, double start, double end, double aspect) { - EM_ASM_({ - var c = document.getElementById($0 == -1 ? "canvas" : "canvas_" + $0); - var ctx = c.getContext("2d"); - ctx.beginPath(); - ctx.arc(100, 75, 50, 0, 2 * Math.PI); - ctx.stroke(); - }, _id, xc, yc, r, start, end, aspect); -} - -void Canvas::drawEllipse(int xc, int yc, int rx, int ry, bool fill) { - logEntered(); -} - -void Canvas::drawLine(int startX, int startY, int endX, int endY) { - logEntered(); -} - -void Canvas::drawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int bytesPerLine) { - logEntered(); -} - -void Canvas::drawRegion(Canvas *src, const MARect *srcRect, int destX, int destY) { - logEntered(); -} - -void Canvas::fillRect(int x, int y, int w, int h) { - logEntered(); -} - -MAExtent Canvas::getTextSize(const char *str, int len) { - return (MAExtent)get_text_size(_id, str, len); -} - -void Canvas::getImageData(Canvas *canvas, uint8_t *image, const MARect *srcRect, int bytesPerLine) { - logEntered(); -} - void Canvas::setClip(int x, int y, int w, int h) { delete _clip; if (x != 0 || y != 0 || _w != w || _h != h) { @@ -231,7 +225,7 @@ void maSetClipRect(int left, int top, int width, int height) { MAExtent maGetTextSize(const char *str) { MAExtent result; if (str && str[0] && drawTarget) { - result = drawTarget->getTextSize(str, strlen(str)); + result = (MAExtent)get_text_size(drawTarget->_id, str, strlen(str)); } else { result = 0; } @@ -250,11 +244,6 @@ void maDestroyPlaceholder(MAHandle maHandle) { delete holder; } -void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int stride) { - Canvas *holder = (Canvas *)maHandle; - drawTarget->getImageData(holder, (uint8_t *)dst, srcRect, stride); -} - MAHandle maSetDrawTarget(MAHandle maHandle) { logEntered(); MAHandle result = (MAHandle) drawTarget; @@ -282,10 +271,21 @@ void maUpdateScreen(void) { // // drawing // -void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d *dstPoint, int transformMode) { - Canvas *src = (Canvas *)maHandle; - if (drawTarget && drawTarget != src) { - drawTarget->drawRegion(src, srcRect, dstPoint->x, dstPoint->y); +void maArc(int xc, int yc, double r, double start, double end, double aspect) { + if (drawTarget) { + draw_arc(drawTarget->_id, xc, yc, r, start, end, aspect); + } +} + +void maEllipse(int xc, int yc, int rx, int ry, int fill) { + if (drawTarget) { + draw_ellipse(drawTarget->_id, xc, yc, rx, ry, fill); + } +} + +void maLine(int startX, int startY, int endX, int endY) { + if (drawTarget) { + draw_line(drawTarget->_id, startX, startY, endX, endY, get_color()); } } @@ -304,38 +304,39 @@ void maPlot(int posX, int posY) { } } -void maLine(int startX, int startY, int endX, int endY) { - if (drawTarget) { - drawTarget->drawLine(startX, startY, endX, endY); - } -} - void maFillRect(int left, int top, int width, int height) { if (drawTarget) { - drawTarget->fillRect(left, top, width, height); + draw_rect_filled(drawTarget->_id, left, top, width, height); } } -void maArc(int xc, int yc, double r, double start, double end, double aspect) { - if (drawTarget) { - drawTarget->drawArc(xc, yc, r, start, end, aspect); +void maDrawText(int left, int top, const char *str, int length) { + if (str && str[0] && drawTarget) { + strlib::String face; + if (font->_italic) { + face.append("italic "); + } + if (font->_bold) { + face.append("bold "); + } + face.append(font->_size).append("pt monospace"); + draw_text(drawTarget->_id, left, top, str, length, get_color(), face); } } -void maEllipse(int xc, int yc, int rx, int ry, int fill) { - if (drawTarget) { - drawTarget->drawEllipse(xc, yc, rx, ry, fill); +void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d *dstPoint, int transformMode) { + Canvas *src = (Canvas *)maHandle; + if (drawTarget && drawTarget != src) { + //draw_region(drawTarget->_id, src, srcRect, dstPoint->x, dstPoint->y); } } -void maDrawText(int left, int top, const char *str, int length) { - if (str && str[0] && drawTarget) { - draw_text(drawTarget->_id, left, top, str, length, get_color()); - } +void maDrawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int stride) { } -void maDrawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int stride) { - drawTarget->drawRGB(dstPoint, src, srcRect, opacity, stride); +void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int stride) { + //Canvas *holder = (Canvas *)maHandle; + //drawTarget->getImageData(holder, (uint8_t *)dst, srcRect, stride); } // From 6b48346ce42ceae4238598d76c7d06ef5c66339a Mon Sep 17 00:00:00 2001 From: chrisws Date: Wed, 16 Mar 2022 19:51:50 +1000 Subject: [PATCH 29/47] EMCC: Emscripten version wip --- src/platform/emcc/canvas.cpp | 39 ++++---- src/platform/emcc/runtime.cpp | 162 ++++++++++++++++++++++++++++------ src/platform/emcc/runtime.h | 13 ++- src/platform/emcc/shell.html | 137 ++++++++++++++++++++++++++++ src/ui/rgb.h | 2 +- 5 files changed, 305 insertions(+), 48 deletions(-) create mode 100644 src/platform/emcc/shell.html diff --git a/src/platform/emcc/canvas.cpp b/src/platform/emcc/canvas.cpp index 8d1f394b..f3013bbb 100644 --- a/src/platform/emcc/canvas.cpp +++ b/src/platform/emcc/canvas.cpp @@ -7,9 +7,8 @@ // #include "config.h" -#include #include -#include +#include #include "ui/utils.h" #include "ui/rgb.h" #include "ui/strlib.h" @@ -114,7 +113,14 @@ EM_JS(void, draw_pixel, (int id, int x, int y, int r, int g, int b), { ctx.putImageData(pxId, x, y); }); -EM_JS(void, draw_rect_filled, (int id, int left, int top, int width, int height), { +EM_JS(void, draw_rect_filled, (int id, int x, int y, int w, int h, const char *color), { + var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var ctx = canvas.getContext("2d"); + ctx.fillStyle = UTF8ToString(color); + ctx.fillRect(x, y, w, h); + }); + +EM_JS(void, draw_region, (int id, int x, int y, int w, int h), { var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); @@ -137,14 +143,15 @@ strlib::String get_color() { long c = drawColor; if (c < 0) { c = -c; - int r = (c>>16) & 0xFF; - int g = (c>>8) & 0xFF; - int b = (c) & 0xFF; + } + if (c >= 0 && c <= 15) { + result.append(colors[c]); + } else { + uint8_t sR, sG, sB; + GET_RGB(c, sR, sG, sB); char buf[8]; - sprintf(buf, "#%x%x%x", b, g, r); + sprintf(buf, "#%x%x%x", sR, sG, sB); result.append(buf); - } else { - result.append((colors[c > 15 ? 15 : c])); } return result; } @@ -179,14 +186,12 @@ bool Canvas::create(int width, int height) { EM_ASM_({ var canvas = document.createElement("canvas"); canvas.id = "canvas_" + $0; - canvas.width = $1; - canvas.height = $2; canvas.style.zIndex = 1; canvas.style.display = "none"; canvas.style.position = "absolute"; - canvas.style.border = "1px solid"; + canvas.className = "emscripten"; document.body.appendChild(canvas); - }, _id, width, height); + }, _id); return true; }; @@ -305,8 +310,9 @@ void maPlot(int posX, int posY) { } void maFillRect(int left, int top, int width, int height) { + logEntered(); if (drawTarget) { - draw_rect_filled(drawTarget->_id, left, top, width, height); + draw_rect_filled(drawTarget->_id, left, top, width, height, get_color()); } } @@ -325,6 +331,7 @@ void maDrawText(int left, int top, const char *str, int length) { } void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d *dstPoint, int transformMode) { + logEntered(); Canvas *src = (Canvas *)maHandle; if (drawTarget && drawTarget != src) { //draw_region(drawTarget->_id, src, srcRect, dstPoint->x, dstPoint->y); @@ -332,11 +339,11 @@ void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d } void maDrawRGB(const MAPoint2d *dstPoint, const void *src, const MARect *srcRect, int opacity, int stride) { + logEntered(); } void maGetImageData(MAHandle maHandle, void *dst, const MARect *srcRect, int stride) { - //Canvas *holder = (Canvas *)maHandle; - //drawTarget->getImageData(holder, (uint8_t *)dst, srcRect, stride); + logEntered(); } // diff --git a/src/platform/emcc/runtime.cpp b/src/platform/emcc/runtime.cpp index 1b6a397f..081aba2d 100644 --- a/src/platform/emcc/runtime.cpp +++ b/src/platform/emcc/runtime.cpp @@ -9,38 +9,80 @@ #include "config.h" #include -#include - +#include #include "include/osd.h" -#include "common/sys.h" #include "common/smbas.h" -#include "common/device.h" -#include "common/keymap.h" -#include "common/inet.h" -#include "common/pproc.h" #include "lib/maapi.h" #include "ui/utils.h" #include "ui/audio.h" +#include "ui/theme.h" #include "platform/emcc/runtime.h" #include "platform/emcc/main_bas.h" #define MAIN_BAS "__main_bas__" +#define WAIT_INTERVAL 10 Runtime *runtime; +MAEvent *getMotionEvent(int type, const EmscriptenMouseEvent *event) { + MAEvent *result = new MAEvent(); + result->type = type; + result->point.x = event->clientX; + result->point.y = event->clientY; + return result; +} + +MAEvent *getKeyPressedEvent(int keycode, int nativeKey) { + MAEvent *result = new MAEvent(); + result->type = EVENT_TYPE_KEY_PRESSED; + result->key = keycode; + result->nativeKey = nativeKey; + return result; +} + +EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *e, void *userData) { + // printf("%s, screen: (%ld,%ld), client: (%ld,%ld),%s%s%s%s button: %hu, buttons: %hu, movement: (%ld,%ld), target: (%ld, %ld)\n", + // emscripten_event_type_to_string(eventType), e->screenX, e->screenY, e->clientX, e->clientY, + // e->ctrlKey ? " CTRL" : "", e->shiftKey ? " SHIFT" : "", e->altKey ? " ALT" : "", e->metaKey ? " META" : "", + // e->button, e->buttons, e->movementX, e->movementY, e->targetX, e->targetY); + + switch (eventType) { + case EMSCRIPTEN_EVENT_DBLCLICK: + case EMSCRIPTEN_EVENT_CLICK: + // + break; + case EMSCRIPTEN_EVENT_MOUSEDOWN: + runtime->pushEvent(getMotionEvent(EVENT_TYPE_POINTER_PRESSED, e)); + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + runtime->pushEvent(getMotionEvent(EVENT_TYPE_POINTER_DRAGGED, e)); + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + runtime->pushEvent(getMotionEvent(EVENT_TYPE_POINTER_RELEASED, e)); + break; + } + return 0; +} + Runtime::Runtime() : System() { logEntered(); runtime = this; + emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + emscripten_set_mousedown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + emscripten_set_dblclick_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + MAExtent screenSize = maGetScrSize(); _output = new AnsiWidget(EXTENT_X(screenSize), EXTENT_Y(screenSize)); _output->construct(); _output->setTextColor(DEFAULT_FOREGROUND, DEFAULT_BACKGROUND); _output->setFontSize(11); - - //_eventQueue = new Stack(); + _eventQueue = new Stack(); _state = kActiveState; + g_themeId = 3; } Runtime::~Runtime() { @@ -79,17 +121,69 @@ char *Runtime::loadResource(const char *fileName) { void Runtime::optionsBox(StringList *items) { } +void Runtime::pause(int timeout) { + if (timeout == -1) { + if (hasEvent()) { + MAEvent *event = popEvent(); + processEvent(*event); + delete event; + } + } else { + int slept = 0; + while (1) { + if (isBreak()) { + break; + } else if (hasEvent()) { + MAEvent *event = popEvent(); + processEvent(*event); + delete event; + } + emscripten_sleep(WAIT_INTERVAL); + slept += WAIT_INTERVAL; + if (timeout > 0 && slept > timeout) { + break; + } + } + } + +} + +void Runtime::processEvent(MAEvent &event) { + switch (event.type) { + case EVENT_TYPE_KEY_PRESSED: + //handleKeyEvent(event); + break; + default: + handleEvent(event); + break; + } +} + MAEvent Runtime::processEvents(int waitFlag) { - emscripten_sleep(10); + switch (waitFlag) { + case 1: + // wait for an event + _output->flush(true); + while (!hasEvent()) { + emscripten_sleep(WAIT_INTERVAL); + } + break; + case 2: + _output->flush(false); + pause(WAIT_INTERVAL); + break; + default: + emscripten_sleep(WAIT_INTERVAL); + } MAEvent event; - event.type = 0; - event.key = 0; - event.nativeKey = 0; - - if (keymap_kbhit()) { - event.type = EVENT_TYPE_KEY_PRESSED; - event.key = keymap_kbpeek(); + if (hasEvent()) { + MAEvent *nextEvent = popEvent(); + processEvent(*nextEvent); + event = *nextEvent; + delete nextEvent; + } else { + event.type = 0; } return event; } @@ -107,12 +201,6 @@ void Runtime::runShell() { void Runtime::setClipboardText(const char *text) { } -void Runtime::setWindowRect(int x, int y, int width, int height) { -} - -void Runtime::setWindowTitle(const char *title) { -} - void Runtime::showCursor(CursorType cursorType) { } @@ -120,12 +208,21 @@ void Runtime::showCursor(CursorType cursorType) { // ma event handling // int maGetEvent(MAEvent *event) { - int result = 0; + int result; + if (runtime->hasEvent()) { + MAEvent *nextEvent = runtime->popEvent(); + event->point = nextEvent->point; + event->type = nextEvent->type; + delete nextEvent; + result = 1; + } else { + result = 0; + } return result; } void maWait(int timeout) { - + runtime->pause(timeout); } // @@ -139,10 +236,19 @@ bool System::getPen3() { return false; } -int osd_devinit() { - return 1; +void System::completeKeyword(int index) { + // empty } -int osd_devrestore() { +// +// sbasic implementation +// +int osd_devinit(void) { + runtime->setRunning(true); return 1; } + +int osd_devrestore(void) { + runtime->setRunning(false); + return 0; +} diff --git a/src/platform/emcc/runtime.h b/src/platform/emcc/runtime.h index 12c67278..e095d1f7 100644 --- a/src/platform/emcc/runtime.h +++ b/src/platform/emcc/runtime.h @@ -8,7 +8,6 @@ #pragma once -#include "ui/ansiwidget.h" #include "ui/system.h" struct Runtime : public System { @@ -27,7 +26,12 @@ struct Runtime : public System { void optionsBox(StringList *items); void onRunCompleted() {} void saveWindowRect() {} + bool hasEvent() { return _eventQueue && _eventQueue->size() > 0; } + void pause(int timeout); + void pushEvent(MAEvent *event) { _eventQueue->push(event); } + MAEvent *popEvent() { return _eventQueue->pop(); } MAEvent processEvents(int waitFlag); + void processEvent(MAEvent &event); bool run(const char *bas) { return execute(bas); } void runShell(); void resize(int w, int h); @@ -35,8 +39,11 @@ struct Runtime : public System { void setFontSize(int size); void setLoadBreak(const char *url) {} void setLoadPath(const char *url) {} - void setWindowRect(int x, int y, int width, int height); - void setWindowTitle(const char *title); + void setWindowRect(int x, int y, int width, int height) {} + void setWindowTitle(const char *title) {} void share(const char *path) {} void showCursor(CursorType cursorType); + +private: + Stack *_eventQueue; }; diff --git a/src/platform/emcc/shell.html b/src/platform/emcc/shell.html new file mode 100644 index 00000000..179ff599 --- /dev/null +++ b/src/platform/emcc/shell.html @@ -0,0 +1,137 @@ + + + + + + SmallBASIC + + + +
+
+
+
+ SmallBASIC ... +
+
+
Downloading...
+
+ +
+ + + + {{{ SCRIPT }}} + + + diff --git a/src/ui/rgb.h b/src/ui/rgb.h index 24134896..7049015a 100644 --- a/src/ui/rgb.h +++ b/src/ui/rgb.h @@ -39,7 +39,7 @@ inline void v_get_argb(int64_t c, uint8_t &a, uint8_t &r, uint8_t &g, uint8_t &b #define v_get_argb_px(a, r, g, b) (a << 24 | (r << 16) | (g << 8) | (b)) -#if defined(_SDL) +#if defined(_SDL) || defined(_EMCC) // SDL_PACKEDORDER_XRGB // A = byte 3 // R = byte 2 From 5c512b2ed0ad7d553dfc888d22392fb8c0dfcecc Mon Sep 17 00:00:00 2001 From: chrisws Date: Sat, 19 Mar 2022 09:19:47 +1000 Subject: [PATCH 30/47] EMCC: Emscripten version wip --- src/platform/emcc/canvas.cpp | 86 +++++++++++++++++++---------------- src/platform/emcc/main.cpp | 2 + src/platform/emcc/runtime.cpp | 7 ++- src/platform/emcc/shell.html | 22 ++++----- 4 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/platform/emcc/canvas.cpp b/src/platform/emcc/canvas.cpp index f3013bbb..ed33b3a7 100644 --- a/src/platform/emcc/canvas.cpp +++ b/src/platform/emcc/canvas.cpp @@ -60,24 +60,27 @@ const uint32_t colors_i[] = { }; EM_JS(int, get_screen_height, (), { - return document.getElementById("canvas").height; + return window.innerHeight; }); EM_JS(int, get_screen_width, (), { - return document.getElementById("canvas").width; + return window.innerWidth; }); -EM_JS(int, get_text_size, (int id, const char *str, int len), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); +EM_JS(int, get_text_size, (int id, const char *str, const char *face), { + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); - var s = new String(str).substring(0, len); + ctx.font = UTF8ToString(face); + + var s = UTF8ToString(str); var metrics = ctx.measureText(s); - var result = (metrics.width << 16) + (metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent); + var height = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; + var result = (metrics.width << 16) + height; return result; }); EM_JS(void, draw_arc, (int id, int xc, int yc, double r, double start, double end, double aspect), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(100, 75, 50, 0, 2 * Math.PI); @@ -85,13 +88,13 @@ EM_JS(void, draw_arc, (int id, int xc, int yc, double r, double start, double en }); EM_JS(void, draw_ellipse, (int id, int xc, int yc, int rx, int ry, int fill), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); }); EM_JS(void, draw_line, (int id, int x1, int y1, int x2, int y2, const char *color), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(x1, y1); @@ -102,7 +105,7 @@ EM_JS(void, draw_line, (int id, int x1, int y1, int x2, int y2, const char *colo }); EM_JS(void, draw_pixel, (int id, int x, int y, int r, int g, int b), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); var pxId = ctx.createImageData(1, 1); var pxData = pxId.data; @@ -114,28 +117,28 @@ EM_JS(void, draw_pixel, (int id, int x, int y, int r, int g, int b), { }); EM_JS(void, draw_rect_filled, (int id, int x, int y, int w, int h, const char *color), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); ctx.fillStyle = UTF8ToString(color); ctx.fillRect(x, y, w, h); }); -EM_JS(void, draw_region, (int id, int x, int y, int w, int h), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); - var ctx = canvas.getContext("2d"); - +EM_JS(void, draw_region, (int id, int srcId, int x, int y, int w, int h, int dx, int dy), { + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var destination = canvas.getContext("2d"); + var source = document.getElementById(srcId == -1 ? "canvas" : "canvas_" + srcId); + destination.drawImage(source, x, y, w, h, dx, dy, w, h); }); EM_JS(void, draw_text, (int id, int x, int y, const char *str, int len, const char *color, const char *face), { - var c = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); + var canvas = document.getElementById(id == -1 ? "canvas" : "canvas_" + id); var ctx = canvas.getContext("2d"); - var s = UTF8ToString(str).substring(0, len); - var width = ctx.measureText(s).width; - var fontHeight = 15; - var y1 = y * fontHeight; ctx.font = UTF8ToString(face); + + var s = UTF8ToString(str).substring(0, len); + var metrics = ctx.measureText(s); ctx.fillStyle = UTF8ToString(color); - ctx.fillText(s, x, y1); + ctx.fillText(s, x, y + metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent); }); strlib::String get_color() { @@ -156,6 +159,18 @@ strlib::String get_color() { return result; } +strlib::String get_face() { + strlib::String result; + if (font->_italic) { + result.append("italic "); + } + if (font->_bold) { + result.append("bold "); + } + result.append(font->_size).append("px monospace"); + return result; +} + Canvas::Canvas() : _clip(nullptr), _id(-1), @@ -186,12 +201,14 @@ bool Canvas::create(int width, int height) { EM_ASM_({ var canvas = document.createElement("canvas"); canvas.id = "canvas_" + $0; + canvas.width = $1; + canvas.height = $2; canvas.style.zIndex = 1; canvas.style.display = "none"; canvas.style.position = "absolute"; canvas.className = "emscripten"; document.body.appendChild(canvas); - }, _id); + }, _id, _w, _h); return true; }; @@ -229,8 +246,8 @@ void maSetClipRect(int left, int top, int width, int height) { MAExtent maGetTextSize(const char *str) { MAExtent result; - if (str && str[0] && drawTarget) { - result = (MAExtent)get_text_size(drawTarget->_id, str, strlen(str)); + if (str && str[0]) { + result = (MAExtent)get_text_size(drawTarget ? drawTarget->_id : -1, str, get_face()); } else { result = 0; } @@ -245,6 +262,7 @@ MAExtent maGetScrSize(void) { } void maDestroyPlaceholder(MAHandle maHandle) { + logEntered(); Canvas *holder = (Canvas *)maHandle; delete holder; } @@ -268,11 +286,6 @@ int maCreateDrawableImage(MAHandle maHandle, int width, int height) { return drawable->create(width, height) ? RES_OK : -1; } -void maUpdateScreen(void) { - logEntered(); - trace("%d \n", screen->_id); -} - // // drawing // @@ -318,15 +331,7 @@ void maFillRect(int left, int top, int width, int height) { void maDrawText(int left, int top, const char *str, int length) { if (str && str[0] && drawTarget) { - strlib::String face; - if (font->_italic) { - face.append("italic "); - } - if (font->_bold) { - face.append("bold "); - } - face.append(font->_size).append("pt monospace"); - draw_text(drawTarget->_id, left, top, str, length, get_color(), face); + draw_text(drawTarget->_id, left, top, str, length, get_color(), get_face()); } } @@ -334,7 +339,7 @@ void maDrawImageRegion(MAHandle maHandle, const MARect *srcRect, const MAPoint2d logEntered(); Canvas *src = (Canvas *)maHandle; if (drawTarget && drawTarget != src) { - //draw_region(drawTarget->_id, src, srcRect, dstPoint->x, dstPoint->y); + draw_region(drawTarget->_id, src->_id, srcRect->left, srcRect->top, srcRect->width, srcRect->height, dstPoint->x, dstPoint->y); } } @@ -383,3 +388,6 @@ void maShowVirtualKeyboard(void) { void maHideVirtualKeyboard(void) { } + +void maUpdateScreen(void) { +} diff --git a/src/platform/emcc/main.cpp b/src/platform/emcc/main.cpp index d139523d..580ddec7 100644 --- a/src/platform/emcc/main.cpp +++ b/src/platform/emcc/main.cpp @@ -51,6 +51,8 @@ void init() { opt_autolocal = 0; os_graf_mx = 1024; os_graf_my = 768; + os_graphics = 1; + os_color_depth = 16; } int main(int argc, char* argv[]) { diff --git a/src/platform/emcc/runtime.cpp b/src/platform/emcc/runtime.cpp index 081aba2d..4b0594f6 100644 --- a/src/platform/emcc/runtime.cpp +++ b/src/platform/emcc/runtime.cpp @@ -82,11 +82,16 @@ Runtime::Runtime() : _output->setFontSize(11); _eventQueue = new Stack(); _state = kActiveState; - g_themeId = 3; + g_themeId = 0; } Runtime::~Runtime() { logEntered(); + delete _output; + delete _eventQueue; + runtime = NULL; + _output = NULL; + _eventQueue = NULL; } void Runtime::alert(const char *title, const char *message) { diff --git a/src/platform/emcc/shell.html b/src/platform/emcc/shell.html index 179ff599..9bc4fc98 100644 --- a/src/platform/emcc/shell.html +++ b/src/platform/emcc/shell.html @@ -6,7 +6,9 @@ SmallBASIC