yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
wasm_error_handler.cc
Go to the documentation of this file.
1// clang-format off
2#ifdef __EMSCRIPTEN__
3
5
6#include <emscripten.h>
7#include <emscripten/html5.h>
8#include <map>
9#include <mutex>
10
11namespace yaze {
12namespace platform {
13
14// Static member initialization
15std::atomic<bool> WasmErrorHandler::initialized_{false};
16std::atomic<int> WasmErrorHandler::callback_counter_{0};
17
18// Store confirmation callbacks with timestamps for timeout cleanup
19struct CallbackEntry {
20 std::function<void(bool)> callback;
21 double timestamp; // Time when callback was registered (ms since epoch)
22};
23static std::map<int, CallbackEntry> g_confirm_callbacks;
24static std::mutex g_callback_mutex;
25
26// Callback timeout in milliseconds (5 minutes)
27constexpr double kCallbackTimeoutMs = 5.0 * 60.0 * 1000.0;
28
29// Helper to get current time in milliseconds
30static double GetCurrentTimeMs() {
31 return EM_ASM_DOUBLE({ return Date.now(); });
32}
33
34// Cleanup stale callbacks that have exceeded the timeout
35static void CleanupStaleCallbacks() {
36 std::lock_guard<std::mutex> lock(g_callback_mutex);
37 const double now = GetCurrentTimeMs();
38
39 for (auto it = g_confirm_callbacks.begin(); it != g_confirm_callbacks.end();) {
40 if (now - it->second.timestamp > kCallbackTimeoutMs) {
41 it = g_confirm_callbacks.erase(it);
42 } else {
43 ++it;
44 }
45 }
46}
47
48// JavaScript function to register cleanup handler for page unload
49EM_JS(void, js_register_cleanup_handler, (), {
50 window.addEventListener('beforeunload', function() {
51 // Signal C++ to cleanup stale callbacks
52 if (Module._cleanupConfirmCallbacks) {
53 Module._cleanupConfirmCallbacks();
54 }
55 });
56});
57
58// C++ cleanup function called from JavaScript on page unload
59extern "C" EMSCRIPTEN_KEEPALIVE void cleanupConfirmCallbacks() {
60 std::lock_guard<std::mutex> lock(g_callback_mutex);
61 g_confirm_callbacks.clear();
62}
63
64// JavaScript functions for browser UI interaction
65EM_JS(void, js_show_modal, (const char* title, const char* message, const char* type), {
66 var titleStr = UTF8ToString(title);
67 var messageStr = UTF8ToString(message);
68 var typeStr = UTF8ToString(type);
69 if (typeof window.showYazeModal === 'function') {
70 window.showYazeModal(titleStr, messageStr, typeStr);
71 } else {
72 alert(titleStr + '\n\n' + messageStr);
73 }
74});
75
76EM_JS(void, js_show_toast, (const char* message, const char* type, int duration_ms), {
77 var messageStr = UTF8ToString(message);
78 var typeStr = UTF8ToString(type);
79 if (typeof window.showYazeToast === 'function') {
80 window.showYazeToast(messageStr, typeStr, duration_ms);
81 } else {
82 console.log('[' + typeStr + '] ' + messageStr);
83 }
84});
85
86EM_JS(void, js_show_progress, (const char* task, float progress), {
87 var taskStr = UTF8ToString(task);
88 if (typeof window.showYazeProgress === 'function') {
89 window.showYazeProgress(taskStr, progress);
90 } else {
91 console.log('Progress: ' + taskStr + ' - ' + (progress * 100).toFixed(0) + '%');
92 }
93});
94
95EM_JS(void, js_hide_progress, (), {
96 if (typeof window.hideYazeProgress === 'function') {
97 window.hideYazeProgress();
98 }
99});
100
101EM_JS(void, js_show_confirm, (const char* message, int callback_id), {
102 var messageStr = UTF8ToString(message);
103 if (typeof window.showYazeConfirm === 'function') {
104 window.showYazeConfirm(messageStr, function(result) {
105 Module._handleConfirmCallback(callback_id, result ? 1 : 0);
106 });
107 } else {
108 var result = confirm(messageStr);
109 Module._handleConfirmCallback(callback_id, result ? 1 : 0);
110 }
111});
112
113EM_JS(void, js_inject_styles, (), {
114 if (document.getElementById('yaze-error-handler-styles')) {
115 return;
116 }
117 var link = document.createElement('link');
118 link.id = 'yaze-error-handler-styles';
119 link.rel = 'stylesheet';
120 link.href = 'error_handler.css';
121 link.onerror = function() {
122 var style = document.createElement('style');
123 style.textContent = '.yaze-modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:10000}.yaze-modal-content{background:white;border-radius:8px;padding:24px;max-width:500px;box-shadow:0 4px 6px rgba(0,0,0,0.1)}.yaze-toast{position:fixed;bottom:20px;right:20px;padding:12px 20px;border-radius:4px;color:white;z-index:10001}.yaze-toast-info{background:#3498db}.yaze-toast-success{background:#2ecc71}.yaze-toast-warning{background:#f39c12}.yaze-toast-error{background:#e74c3c}';
124 document.head.appendChild(style);
125 };
126 document.head.appendChild(link);
127});
128
129// C++ callback handler for confirmation dialogs
130extern "C" EMSCRIPTEN_KEEPALIVE void handleConfirmCallback(int callback_id, int result) {
131 std::function<void(bool)> callback;
132 {
133 std::lock_guard<std::mutex> lock(g_callback_mutex);
134 auto it = g_confirm_callbacks.find(callback_id);
135 if (it != g_confirm_callbacks.end()) {
136 callback = it->second.callback;
137 g_confirm_callbacks.erase(it);
138 }
139 }
140 if (callback) {
141 callback(result != 0);
142 }
143}
144
145void WasmErrorHandler::Initialize() {
146 // Use compare_exchange for thread-safe initialization
147 bool expected = false;
148 if (!initialized_.compare_exchange_strong(expected, true)) {
149 return; // Already initialized by another thread
150 }
151 js_inject_styles();
152 EM_ASM({
153 Module._handleConfirmCallback = Module.cwrap('handleConfirmCallback', null, ['number', 'number']);
154 Module._cleanupConfirmCallbacks = Module.cwrap('cleanupConfirmCallbacks', null, []);
155 });
156 js_register_cleanup_handler();
157}
158
159void WasmErrorHandler::ShowError(const std::string& title, const std::string& message) {
160 if (!initialized_.load()) Initialize();
161 js_show_modal(title.c_str(), message.c_str(), "error");
162}
163
164void WasmErrorHandler::ShowWarning(const std::string& title, const std::string& message) {
165 if (!initialized_.load()) Initialize();
166 js_show_modal(title.c_str(), message.c_str(), "warning");
167}
168
169void WasmErrorHandler::ShowInfo(const std::string& title, const std::string& message) {
170 if (!initialized_.load()) Initialize();
171 js_show_modal(title.c_str(), message.c_str(), "info");
172}
173
174void WasmErrorHandler::Toast(const std::string& message, ToastType type, int duration_ms) {
175 if (!initialized_.load()) Initialize();
176 const char* type_str = "info";
177 switch (type) {
178 case ToastType::kSuccess: type_str = "success"; break;
179 case ToastType::kWarning: type_str = "warning"; break;
180 case ToastType::kError: type_str = "error"; break;
181 case ToastType::kInfo:
182 default: type_str = "info"; break;
183 }
184 js_show_toast(message.c_str(), type_str, duration_ms);
185}
186
187void WasmErrorHandler::ShowProgress(const std::string& task, float progress) {
188 if (!initialized_.load()) Initialize();
189 if (progress < 0.0f) progress = 0.0f;
190 if (progress > 1.0f) progress = 1.0f;
191 js_show_progress(task.c_str(), progress);
192}
193
194void WasmErrorHandler::HideProgress() {
195 if (!initialized_.load()) Initialize();
196 js_hide_progress();
197}
198
199void WasmErrorHandler::Confirm(const std::string& message, std::function<void(bool)> callback) {
200 if (!initialized_.load()) Initialize();
201
202 // Cleanup any stale callbacks before adding new one
203 CleanupStaleCallbacks();
204
205 int callback_id = callback_counter_.fetch_add(1) + 1;
206 {
207 std::lock_guard<std::mutex> lock(g_callback_mutex);
208 g_confirm_callbacks[callback_id] = CallbackEntry{callback, GetCurrentTimeMs()};
209 }
210 js_show_confirm(message.c_str(), callback_id);
211}
212
213} // namespace platform
214} // namespace yaze
215
216#endif // __EMSCRIPTEN__
217// clang-format on
EM_JS(void, CallJsAiDriver,(const char *history_json), { if(window.yaze &&window.yaze.ai &&window.yaze.ai.processAgentRequest) { window.yaze.ai.processAgentRequest(UTF8ToString(history_json));} else { console.error("AI Driver not found in window.yaze.ai.processAgentRequest");} })