From e36d4b9cec687d46766bc2cbb4ac0a8a905edb1c Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Fri, 3 Apr 2026 19:29:09 -0500 Subject: [PATCH] New form scope bug fixing. --- apps/dvxbasic/compiler/parser.c | 30 +++++++++++----------------- apps/dvxbasic/formrt/formrt.c | 17 ++++++++++++---- apps/dvxbasic/ide/ideDesigner.c | 18 ++++++++++++++++- apps/dvxbasic/ide/ideMain.c | 33 +++++++++++++++++++------------ apps/dvxbasic/ide/ideProperties.c | 2 +- apps/dvxbasic/ide/ideToolbox.c | 2 +- core/dvxApp.c | 28 ++++++++++++++++++++++++++ core/dvxApp.h | 6 ++++++ 8 files changed, 97 insertions(+), 39 deletions(-) diff --git a/apps/dvxbasic/compiler/parser.c b/apps/dvxbasic/compiler/parser.c index 8598cda..7bb53b9 100644 --- a/apps/dvxbasic/compiler/parser.c +++ b/apps/dvxbasic/compiler/parser.c @@ -1993,8 +1993,10 @@ static void parseBeginForm(BasParserT *p) { basSymTabEnterFormScope(&p->sym, formName); - // Emit a forward JMP that will skip form init code (patched at ENDFORM). - // If no init code is emitted, the JMP is patched to jump to right here (noop). + // Emit a forward JMP to skip over form init code. All module-level + // bytecode inside the form scope (array DIMs, UDT init, executable + // statements, JMPs over SUB bodies) goes into the init block that + // runs at form load time, not at program startup. basEmit8(&p->cg, OP_JMP); p->formInitJmpAddr = basCodePos(&p->cg); basEmit16(&p->cg, 0); // placeholder — patched at ENDFORM @@ -2017,24 +2019,14 @@ static void parseEndForm(BasParserT *p) { int32_t varCount = basSymTabLeaveFormScope(&p->sym); - // Determine if any form init code was emitted - int32_t initStart = p->formInitCodeStart; - int32_t initAddr = -1; - int32_t initLen = 0; - int32_t curPos = basCodePos(&p->cg); + // Close the form init block: add OP_RET and patch the JMP + basEmit8(&p->cg, OP_RET); + int32_t initAddr = p->formInitCodeStart; + int32_t initLen = basCodePos(&p->cg) - p->formInitCodeStart; - if (curPos > initStart) { - // Init code was emitted — add OP_RET to end the init block - basEmit8(&p->cg, OP_RET); - initAddr = initStart; - initLen = basCodePos(&p->cg) - initStart; - } - - // Patch the JMP to skip over init code (land here) - if (p->formInitJmpAddr >= 0) { - int16_t offset = (int16_t)(basCodePos(&p->cg) - (p->formInitJmpAddr + 2)); - basPatch16(&p->cg, p->formInitJmpAddr, offset); - } + // Patch the JMP to skip over the entire init block + int16_t offset = (int16_t)(basCodePos(&p->cg) - (p->formInitJmpAddr + 2)); + basPatch16(&p->cg, p->formInitJmpAddr, offset); p->formInitJmpAddr = -1; p->formInitCodeStart = -1; diff --git a/apps/dvxbasic/formrt/formrt.c b/apps/dvxbasic/formrt/formrt.c index 8438a2a..cc59295 100644 --- a/apps/dvxbasic/formrt/formrt.c +++ b/apps/dvxbasic/formrt/formrt.c @@ -552,8 +552,7 @@ void basFormRtHideForm(void *ctx, void *formRef) { return; } - form->window->visible = false; - dvxInvalidateWindow(rt->ctx, form->window); + dvxHideWindow(rt->ctx, form->window); } @@ -724,8 +723,18 @@ BasFormT *basFormRtLoadFrm(BasFormRtT *rt, const char *source, int32_t sourceLen continue; } - // "VERSION x.xx" -- skip version declaration + // "VERSION DVX x.xx" (native) or "VERSION x.xx" (VB import) if (strncasecmp(trimmed, "VERSION ", 8) == 0) { + const char *ver = trimmed + 8; + + if (strncasecmp(ver, "DVX ", 4) != 0) { + double vbVer = atof(ver); + + if (vbVer > 2.0) { + return NULL; // VB4+ form, not compatible + } + } + continue; } @@ -1076,7 +1085,7 @@ void basFormRtShowForm(void *ctx, void *formRef, bool modal) { return; } - form->window->visible = true; + dvxShowWindow(rt->ctx, form->window); dvxRaiseWindow(rt->ctx, form->window); if (form->frmAutoSize) { diff --git a/apps/dvxbasic/ide/ideDesigner.c b/apps/dvxbasic/ide/ideDesigner.c index d558a69..b8d2f15 100644 --- a/apps/dvxbasic/ide/ideDesigner.c +++ b/apps/dvxbasic/ide/ideDesigner.c @@ -401,6 +401,22 @@ bool dsgnLoadFrm(DsgnStateT *ds, const char *source, int32_t sourceLen) { } if (strncasecmp(trimmed, "VERSION ", 8) == 0) { + // Accept "VERSION DVX x.xx" (native) and "VERSION x.xx" (VB import). + // Reject VB forms with version > 1.xx (VB4+/VB6 use features we + // don't support like OLE controls and binary properties). + const char *ver = trimmed + 8; + + if (strncasecmp(ver, "DVX ", 4) == 0) { + // Native DVX BASIC form — always accepted + } else { + // VB form — check version number + double vbVer = atof(ver); + + if (vbVer > 2.0) { + return false; // VB4+ form, not compatible + } + } + continue; } @@ -1014,7 +1030,7 @@ int32_t dsgnSaveFrm(const DsgnStateT *ds, char *buf, int32_t bufSize) { int32_t pos = 0; - pos += snprintf(buf + pos, bufSize - pos, "VERSION 1.00\n"); + pos += snprintf(buf + pos, bufSize - pos, "VERSION DVX 1.00\n"); pos += snprintf(buf + pos, bufSize - pos, "Begin Form %s\n", ds->form->name); pos += snprintf(buf + pos, bufSize - pos, " Caption = \"%s\"\n", ds->form->caption); pos += snprintf(buf + pos, bufSize - pos, " Layout = %s\n", ds->form->layout); diff --git a/apps/dvxbasic/ide/ideMain.c b/apps/dvxbasic/ide/ideMain.c index 61cbadb..010b68e 100644 --- a/apps/dvxbasic/ide/ideMain.c +++ b/apps/dvxbasic/ide/ideMain.c @@ -902,9 +902,8 @@ static void compileAndRun(void) { clearOutput(); setStatus("Compiling..."); - - // Force a display update so the status is visible - dvxInvalidateWindow(sAc, sWin); + dvxSetBusy(sAc, true); + dvxUpdate(sAc); // Build source: either concatenate project files or use editor contents char *concatBuf = NULL; @@ -921,6 +920,7 @@ static void compileAndRun(void) { if (!concatBuf) { setStatus("Out of memory."); + dvxSetBusy(sAc, false); return; } @@ -1076,6 +1076,8 @@ static void compileAndRun(void) { arrput(sProject.sourceMap, mapEntry); sProject.sourceMapCount = (int32_t)arrlen(sProject.sourceMap); } + + dvxUpdate(sAc); } concatBuf[pos] = '\0'; @@ -1099,6 +1101,7 @@ static void compileAndRun(void) { if (!parser) { free(concatBuf); setStatus("Out of memory."); + dvxSetBusy(sAc, false); return; } @@ -1125,6 +1128,7 @@ static void compileAndRun(void) { } setStatus("Compilation failed."); + dvxSetBusy(sAc, false); basParserFree(parser); free(parser); free(concatBuf); @@ -1139,9 +1143,12 @@ static void compileAndRun(void) { if (!mod) { setStatus("Failed to build module."); + dvxSetBusy(sAc, false); return; } + dvxSetBusy(sAc, false); + // Cache the compiled module for Ctrl+F5 if (sCachedModule) { basModuleFree(sCachedModule); @@ -1187,11 +1194,11 @@ static void runModule(BasModuleT *mod) { bool hadCodeWin = sCodeWin && sCodeWin->visible; bool hadPrjWin = sProjectWin && sProjectWin->visible; - if (sFormWin) { sFormWin->visible = false; dvxInvalidateWindow(sAc, sFormWin); } - if (sToolboxWin) { sToolboxWin->visible = false; dvxInvalidateWindow(sAc, sToolboxWin); } - if (sPropsWin) { sPropsWin->visible = false; dvxInvalidateWindow(sAc, sPropsWin); } - if (sCodeWin) { sCodeWin->visible = false; dvxInvalidateWindow(sAc, sCodeWin); } - if (sProjectWin) { sProjectWin->visible = false; dvxInvalidateWindow(sAc, sProjectWin); } + if (sFormWin) { dvxHideWindow(sAc, sFormWin); } + if (sToolboxWin) { dvxHideWindow(sAc, sToolboxWin); } + if (sPropsWin) { dvxHideWindow(sAc, sPropsWin); } + if (sCodeWin) { dvxHideWindow(sAc, sCodeWin); } + if (sProjectWin) { dvxHideWindow(sAc, sProjectWin); } // Create VM BasVmT *vm = basVmCreate(); @@ -1291,11 +1298,11 @@ static void runModule(BasModuleT *mod) { basVmDestroy(vm); // Restore IDE windows - if (hadFormWin && sFormWin) { sFormWin->visible = true; dvxInvalidateWindow(sAc, sFormWin); } - if (hadToolbox && sToolboxWin) { sToolboxWin->visible = true; dvxInvalidateWindow(sAc, sToolboxWin); } - if (hadProps && sPropsWin) { sPropsWin->visible = true; dvxInvalidateWindow(sAc, sPropsWin); } - if (hadCodeWin && sCodeWin) { sCodeWin->visible = true; dvxInvalidateWindow(sAc, sCodeWin); } - if (hadPrjWin && sProjectWin) { sProjectWin->visible = true; dvxInvalidateWindow(sAc, sProjectWin); } + if (hadFormWin && sFormWin) { dvxShowWindow(sAc, sFormWin); } + if (hadToolbox && sToolboxWin) { dvxShowWindow(sAc, sToolboxWin); } + if (hadProps && sPropsWin) { dvxShowWindow(sAc, sPropsWin); } + if (hadCodeWin && sCodeWin) { dvxShowWindow(sAc, sCodeWin); } + if (hadPrjWin && sProjectWin) { dvxShowWindow(sAc, sProjectWin); } // Repaint to clear destroyed runtime forms and restore designer dvxUpdate(sAc); diff --git a/apps/dvxbasic/ide/ideProperties.c b/apps/dvxbasic/ide/ideProperties.c index 5327e5d..6fba58f 100644 --- a/apps/dvxbasic/ide/ideProperties.c +++ b/apps/dvxbasic/ide/ideProperties.c @@ -87,7 +87,7 @@ static void onTreeReorder(WidgetT *w); // ============================================================ static void onPrpClose(WindowT *win) { - win->visible = false; + dvxHideWindow(sPrpCtx, win); } diff --git a/apps/dvxbasic/ide/ideToolbox.c b/apps/dvxbasic/ide/ideToolbox.c index 1966be9..73bddd5 100644 --- a/apps/dvxbasic/ide/ideToolbox.c +++ b/apps/dvxbasic/ide/ideToolbox.c @@ -68,7 +68,7 @@ static void onToolClick(WidgetT *w) { static void onTbxClose(WindowT *win) { - win->visible = false; + dvxHideWindow(sDs->ctx, win); } diff --git a/core/dvxApp.c b/core/dvxApp.c index f893890..ab7057b 100644 --- a/core/dvxApp.c +++ b/core/dvxApp.c @@ -3925,6 +3925,34 @@ WindowT *dvxCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, } +// ============================================================ +// dvxHideWindow +// ============================================================ + +void dvxHideWindow(AppContextT *ctx, WindowT *win) { + if (!win || !win->visible) { + return; + } + + dirtyListAdd(&ctx->dirty, win->x, win->y, win->w, win->h); + win->visible = false; +} + + +// ============================================================ +// dvxShowWindow +// ============================================================ + +void dvxShowWindow(AppContextT *ctx, WindowT *win) { + if (!win || win->visible) { + return; + } + + win->visible = true; + dvxInvalidateWindow(ctx, win); +} + + // ============================================================ // dvxDestroyWindow // ============================================================ diff --git a/core/dvxApp.h b/core/dvxApp.h index 3b10f31..435158a 100644 --- a/core/dvxApp.h +++ b/core/dvxApp.h @@ -223,6 +223,12 @@ void dvxInvalidateRect(AppContextT *ctx, WindowT *win, int32_t x, int32_t y, int // Mark the entire window content area as dirty. void dvxInvalidateWindow(AppContextT *ctx, WindowT *win); +// Hide a window without destroying it (marks exposed region dirty). +void dvxHideWindow(AppContextT *ctx, WindowT *win); + +// Show a previously hidden window (marks region dirty for repaint). +void dvxShowWindow(AppContextT *ctx, WindowT *win); + // Minimize a window (show as icon at bottom of screen) void dvxMinimizeWindow(AppContextT *ctx, WindowT *win);