Implement real MsgBox() for VBScript table scripts (browser confirm/alert)
libwinevbs's __LIBWINEVBS__ build of Global_MsgBox() only logged the prompt text and never set a return value, since it has no host UI to show a dialog through. Any table script that branches on MsgBox(...) = vbYes (a common pattern - e.g. core.vbs's own trough ball-count dialog) always took the "no" branch, since the result stayed at its zero-initialized default. Adds a msgbox callback to libwinevbs_callbacks_t, wired on the vpinball-wasm side to a real window.confirm()/alert() call. Both are synchronous browser APIs that block JS execution and return a value immediately, matching what VBScript's MsgBox() needs (its result is used by the calling statement right away) - the only way to answer it asynchronously would require Asyncify or a busy-wait poll loop, which this project has deliberately avoided elsewhere. Confirmed fixed by hands-on testing: a table's ball-count confirm dialog now correctly returns Yes/No based on the user's click, instead of always silently taking the "no" branch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
diff --git a/src/core/VPApp.cpp b/src/core/VPApp.cpp
|
||||
index 7175dd2..f05a324 100644
|
||||
--- a/src/core/VPApp.cpp
|
||||
+++ b/src/core/VPApp.cpp
|
||||
@@ -36,6 +36,10 @@
|
||||
#include <libwinevbs/libwinevbs.h>
|
||||
#endif
|
||||
|
||||
+#ifdef __EMSCRIPTEN__
|
||||
+#include <emscripten.h>
|
||||
+#endif
|
||||
+
|
||||
#include "parts/ball.h"
|
||||
#include "parts/timer.h"
|
||||
#include "parts/flipper.h"
|
||||
@@ -273,6 +277,34 @@ int VPApp::GetLogicalNumberOfProcessors() const
|
||||
return m_logicalNumberOfProcessors;
|
||||
}
|
||||
|
||||
+#ifdef __EMSCRIPTEN__
|
||||
+// VBScript's MsgBox() must return synchronously (its VARIANT result is used
|
||||
+// right after the call, in the same script statement) - window.confirm()/
|
||||
+// alert() are the only browser APIs that block JS execution and return a
|
||||
+// value synchronously, so they're used here directly rather than routing
|
||||
+// through any async/JS-callback mechanism the host page might otherwise want
|
||||
+// to customize the look of (which would require Asyncify to suspend the
|
||||
+// call, since single-threaded builds have no other way to block for it).
|
||||
+// type is the raw VBScript "buttons" argument (MB_OK=0, MB_OKCANCEL=1,
|
||||
+// MB_ABORTRETRYIGNORE=2, MB_YESNOCANCEL=3, MB_YESNO=4, MB_RETRYCANCEL=5).
|
||||
+// The returned id matches vbOK(1)/vbCancel(2)/vbYes(6)/vbNo(7).
|
||||
+static int EmscriptenMsgBox(const char* prompt, int type, const char* title)
|
||||
+{
|
||||
+ const string message = (title && title[0]) ? (string(title) + "\n\n" + prompt) : string(prompt);
|
||||
+ switch (type & 0x0F)
|
||||
+ {
|
||||
+ case 1: // MB_OKCANCEL
|
||||
+ return EM_ASM_INT({ return confirm(UTF8ToString($0)) ? 1 : 0; }, message.c_str()) ? 1 /* IDOK */ : 2 /* IDCANCEL */;
|
||||
+ case 3: // MB_YESNOCANCEL - Cancel isn't distinguishable from No via confirm(), best effort
|
||||
+ case 4: // MB_YESNO
|
||||
+ return EM_ASM_INT({ return confirm(UTF8ToString($0)) ? 1 : 0; }, message.c_str()) ? 6 /* IDYES */ : 7 /* IDNO */;
|
||||
+ default:
|
||||
+ EM_ASM_({ alert(UTF8ToString($0)); }, message.c_str());
|
||||
+ return 1; // IDOK
|
||||
+ }
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
void VPApp::InitInstance()
|
||||
{
|
||||
std::filesystem::path iniFileName = m_commandLineCustomSettingsFileName;
|
||||
@@ -325,6 +357,9 @@ void VPApp::InitInstance()
|
||||
delete[] buffer;
|
||||
}
|
||||
};
|
||||
+#ifdef __EMSCRIPTEN__
|
||||
+ callbacks.msgbox = &EmscriptenMsgBox;
|
||||
+#endif
|
||||
libwinevbs_init(&callbacks);
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user