diff --git a/build.zig.zon b/build.zig.zon index 3302fd6..9f25ba6 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,8 +5,8 @@ .minimum_zig_version = "0.14.0", .dependencies = .{ .webui = .{ - .hash = "webui-2.5.0-beta.4-pxqD5YoPNwCJ9uGbFj8HOnmOUW6QgvHZtLqmpZN5kfmw", - .url = "https://github.com/webui-dev/webui/archive/9dd20b5c98b53c1e03f94e80415ddfec5c37a1fa.tar.gz", + .hash = "webui-2.5.0-beta.4-pxqD5Y4GNwCmkPZG76ANjcJbSUoP7R4qu3FsZEPRcB9J", + .url = "https://github.com/webui-dev/webui/archive/91364686ca444ddc66aac9de5f5ca2fc7f7afdfe.tar.gz", }, }, .paths = .{ diff --git a/examples/call_js_from_zig/main.zig b/examples/call_js_from_zig/main.zig index 72ffc54..c43fa25 100644 --- a/examples/call_js_from_zig/main.zig +++ b/examples/call_js_from_zig/main.zig @@ -8,11 +8,11 @@ pub fn main() !void { var nwin = webui.newWindow(); // Bind HTML elements with C functions - _ = nwin.bind("my_function_count", my_function_count); - _ = nwin.bind("my_function_exit", my_function_exit); + _ = try nwin.bind("my_function_count", my_function_count); + _ = try nwin.bind("my_function_exit", my_function_exit); // Show the window - _ = nwin.show(html); + try nwin.show(html); // _ = nwin.showBrowser(html, .Chrome); // Wait until all windows get closed @@ -31,13 +31,13 @@ fn my_function_count(e: *webui.Event) void { const win = e.getWindow(); // Run JavaScript - if (!win.script("return GetCount();", 0, &response)) { + win.script("return GetCount();", 0, &response) catch { if (!win.isShown()) { std.debug.print("window closed\n", .{}); } else { std.debug.print("js error:{s}\n", .{response}); } - } + }; const res_buf = response[0..std.mem.len(@as([*:0]u8, @ptrCast(&response)))]; diff --git a/examples/call_zig_from_js/main.zig b/examples/call_zig_from_js/main.zig index 1e21718..a5b67e6 100644 --- a/examples/call_zig_from_js/main.zig +++ b/examples/call_zig_from_js/main.zig @@ -8,19 +8,28 @@ const html = @embedFile("index.html"); pub fn main() !void { var nwin = webui.newWindow(); - _ = nwin.bind("my_function_string", my_function_string); - _ = nwin.bind("my_function_integer", my_function_integer); - _ = nwin.bind("my_function_boolean", my_function_boolean); - _ = nwin.bind("my_function_with_response", my_function_with_response); - _ = nwin.bind("my_function_raw_binary", my_function_raw_binary); + _ = try nwin.binding("my_function_string", getString); + // _ = try nwin.bind("my_function_string", my_function_string); + _ = try nwin.binding("my_function_integer", getInteger); + // _ = try nwin.bind("my_function_integer", my_function_integer); + _ = try nwin.bind("my_function_boolean", my_function_boolean); + _ = try nwin.bind("my_function_with_response", my_function_with_response); + _ = try nwin.bind("my_function_raw_binary", my_function_raw_binary); - _ = nwin.show(html); + try nwin.show(html); webui.wait(); webui.clean(); } +fn getString(str1: [:0]const u8, str2: [:0]const u8) void { + // Hello + std.debug.print("my_function_string 1: {s}\n", .{str1}); + // World + std.debug.print("my_function_string 2: {s}\n", .{str2}); +} + fn my_function_string(e: *webui.Event) void { // JavaScript: // my_function_string('Hello', 'World`); @@ -35,6 +44,12 @@ fn my_function_string(e: *webui.Event) void { std.debug.print("my_function_string 2: {s}\n", .{str_2}); } +fn getInteger(n1: i64, n2: i64, n3: i64, f1: f64) void { + std.debug.print("number is {},{},{},{}", .{ + n1, n2, n3, f1, + }); +} + fn my_function_integer(e: *webui.Event) void { // JavaScript: // my_function_integer(123, 456, 789, 12345.6789); @@ -95,11 +110,11 @@ fn my_function_raw_binary(e: *webui.Event) void { // Or e.getStringAt(0); const raw_1 = e.getString(); - const raw_2 = e.getStringAt(1); + const raw_2 = e.getRawAt(1); // Or e.getSizeAt(0); - const len_1 = e.getSize(); - const len_2 = e.getSizeAt(1); + const len_1 = e.getSize() catch return; + const len_2 = e.getSizeAt(1) catch return; // Print raw_1 std.debug.print("my_function_raw_binary 1 ({} bytes): ", .{len_1}); diff --git a/examples/custom_spa_server_on_free_port/main.zig b/examples/custom_spa_server_on_free_port/main.zig index 478f766..1709fc3 100644 --- a/examples/custom_spa_server_on_free_port/main.zig +++ b/examples/custom_spa_server_on_free_port/main.zig @@ -1,4 +1,5 @@ //! Custom web Server - Free Port - Example +// Note: if you want to run this example, you nedd a python, zig will wrap a child process to launch python server const std = @import("std"); const webui = @import("webui"); @@ -12,9 +13,9 @@ pub fn main() !void { var nwin = webui.newWindow(); // Bind all events - _ = nwin.bind("", events); + _ = try nwin.bind("", events); // Bind a JS call to a Zig fn - _ = nwin.bind("gotoPage", goto_page); + _ = try nwin.bind("gotoPage", goto_page); // The `webui.js` script will be available at: // @@ -27,7 +28,7 @@ pub fn main() !void { const webui_port: u64 = webui.getFreePort(); std.debug.print("Free Port for webui.js: {d} \n", .{webui_port}); // now use the port: - _ = nwin.setPort(webui_port); + try nwin.setPort(webui_port); const backend_port = webui.getFreePort(); std.debug.print("Free Port for custom web server: {d} \n", .{backend_port}); @@ -45,7 +46,7 @@ pub fn main() !void { // Show a new window served by our custom web server (spawned above): var buf: [64]u8 = undefined; home_url = try std.fmt.bufPrintZ(&buf, "http://localhost:{d}/index.html", .{backend_port}); - _ = nwin.show(home_url); + try nwin.show(home_url); // Wait until all windows get closed webui.wait(); diff --git a/examples/custom_web_server/main.zig b/examples/custom_web_server/main.zig index 4126f1a..62e8328 100644 --- a/examples/custom_web_server/main.zig +++ b/examples/custom_web_server/main.zig @@ -7,20 +7,20 @@ pub fn main() !void { var nwin = webui.newWindow(); // Bind all events - _ = nwin.bind("", events); + _ = try nwin.bind("", events); // Bind HTML elements with C functions - _ = nwin.bind("my_backend_func", my_backend_func); + _ = try nwin.bind("my_backend_func", my_backend_func); // Set the web-server/WebSocket port that WebUI should // use. This means `webui.js` will be available at: // http://localhost:MY_PORT_NUMBER/webui.js - _ = nwin.setPort(8081); + try nwin.setPort(8081); // Show a new window and show our custom web server // Assuming the custom web server is running on port // 8080... - _ = nwin.show("http://localhost:8080/"); + try nwin.show("http://localhost:8080/"); // Wait until all windows get closed webui.wait(); diff --git a/examples/frameless/main.zig b/examples/frameless/main.zig index 53773cd..c466ce4 100644 --- a/examples/frameless/main.zig +++ b/examples/frameless/main.zig @@ -25,9 +25,9 @@ pub fn main() !void { // create a new window var nwin = webui.newWindow(); - _ = nwin.bind("minimize", minimize); - _ = nwin.bind("maximize", maximize); - _ = nwin.bind("close", close); + _ = try nwin.bind("minimize", minimize); + _ = try nwin.bind("maximize", maximize); + _ = try nwin.bind("close", close); nwin.setSize(800, 600); nwin.setFrameless(true); @@ -35,7 +35,7 @@ pub fn main() !void { nwin.setResizable(true); nwin.setCenter(); - _ = nwin.showWv(html); + try nwin.showWv(html); // wait the window exit webui.wait(); diff --git a/examples/minimal/main.zig b/examples/minimal/main.zig index e12b13c..2e608b2 100644 --- a/examples/minimal/main.zig +++ b/examples/minimal/main.zig @@ -6,7 +6,7 @@ pub fn main() !void { var nwin = webui.newWindow(); // show the content - _ = nwin.show(" Hello World ! "); + try nwin.show(" Hello World ! "); // wait the window exit webui.wait(); diff --git a/examples/public_network_access/main.zig b/examples/public_network_access/main.zig index b7668b0..f734626 100644 --- a/examples/public_network_access/main.zig +++ b/examples/public_network_access/main.zig @@ -26,7 +26,7 @@ fn public_window_events(e: *webui.Event) void { fn private_window_events(e: *webui.Event) void { if (e.event_type == .EVENT_CONNECTED) { - const public_win_url: [:0]const u8 = public_window.getUrl(); + const public_win_url: [:0]const u8 = public_window.getUrl() catch return; var buf = std.mem.zeroes([1024]u8); const js = std.fmt.bufPrintZ(&buf, "document.getElementById('urlSpan').innerHTML = '{s}';", .{public_win_url}) catch unreachable; private_window.run(js); @@ -36,7 +36,7 @@ fn private_window_events(e: *webui.Event) void { pub fn main() !void { // Create windows private_window = webui.newWindow(); - public_window = webui.newWindow(); + public_window = webui.newWindow(); // App webui.setTimeout(0); // Wait forever (never timeout) @@ -46,21 +46,21 @@ pub fn main() !void { public_window.setPublic(true); // Bind all events - _ = public_window.bind("", public_window_events); + _ = try public_window.bind("", public_window_events); // Set public window HTML - _ = public_window.showBrowser(public_html, .NoBrowser); + try public_window.showBrowser(public_html, .NoBrowser); // Main Private Window // Run Js - _ = private_window.bind("", private_window_events); + _ = try private_window.bind("", private_window_events); // Bind exit button - _ = private_window.bind("Exit", app_exit); + _ = try private_window.bind("Exit", app_exit); // Show the window - _ = private_window.show(private_html); + _ = try private_window.show(private_html); // Wait until all windows get closed webui.wait(); diff --git a/examples/serve_a_folder/main.zig b/examples/serve_a_folder/main.zig index c06b15d..1326515 100644 --- a/examples/serve_a_folder/main.zig +++ b/examples/serve_a_folder/main.zig @@ -9,17 +9,17 @@ var MySecondWindow: webui = undefined; pub fn main() !void { // Create new windows - MyWindow = webui.newWindowWithId(1); - MySecondWindow = webui.newWindowWithId(2); + MyWindow = try webui.newWindowWithId(1); + MySecondWindow = try webui.newWindowWithId(2); // Bind HTML element IDs with a C functions - _ = MyWindow.bind("SwitchToSecondPage", switch_second_window); - _ = MyWindow.bind("OpenNewWindow", show_second_window); - _ = MyWindow.bind("Exit", exit_app); - _ = MySecondWindow.bind("Exit", exit_app); + _ = try MyWindow.bind("SwitchToSecondPage", switch_second_window); + _ = try MyWindow.bind("OpenNewWindow", show_second_window); + _ = try MyWindow.bind("Exit", exit_app); + _ = try MySecondWindow.bind("Exit", exit_app); // Bind events - _ = MyWindow.bind("", events); + _ = try MyWindow.bind("", events); // Set the `.ts` and `.js` runtime // webui_set_runtime(MyWindow, NodeJS); @@ -38,7 +38,7 @@ pub fn main() !void { // Show a new window // webui_set_root_folder(MyWindow, "_MY_PATH_HERE_"); // webui_show_browser(MyWindow, "index.html", Chrome); - _ = MyWindow.show("index.html"); + try MyWindow.show("index.html"); // Wait until all windows get closed webui.wait(); @@ -85,7 +85,7 @@ fn switch_second_window(e: *webui.Event) void { // time the user clicks on "SwitchToSecondPage" // Switch to `/second.html` in the same opened window. - _ = e.getWindow().show("second.html"); + e.getWindow().show("second.html") catch return; } fn show_second_window(_: *webui.Event) void { @@ -94,7 +94,7 @@ fn show_second_window(_: *webui.Event) void { // Show a new window, and navigate to `/second.html` // if it's already open, then switch in the same window - _ = MySecondWindow.show("second.html"); + MySecondWindow.show("second.html") catch return; } var count: i32 = 0; diff --git a/examples/text_editor/main.zig b/examples/text_editor/main.zig index 93ca735..704566c 100644 --- a/examples/text_editor/main.zig +++ b/examples/text_editor/main.zig @@ -14,14 +14,14 @@ pub fn main() !void { var mainW = webui.newWindow(); // Set the root folder for the UI - _ = mainW.setRootFolder("ui"); + try mainW.setRootFolder("ui"); // Bind HTML elements with the specified ID to C functions - _ = mainW.bind("close_app", close); + _ = try mainW.bind("close_app", close); // Show the window, this will select the best browser to show // and you can use showBrowser to select which browser will be used - _ = mainW.show("index.html"); + try mainW.show("index.html"); // Wait until all windows get closed webui.wait(); diff --git a/examples/web_app_multi_client/main.zig b/examples/web_app_multi_client/main.zig index ebfcd82..d16c150 100644 --- a/examples/web_app_multi_client/main.zig +++ b/examples/web_app_multi_client/main.zig @@ -182,15 +182,15 @@ pub fn main() !void { var win = webui.newWindow(); // Bind HTML with a Zig functions - _ = win.bind("save", save); - _ = win.bind("saveAll", saveAll); - _ = win.bind("exit_app", exit_app); + _ = try win.bind("save", save); + _ = try win.bind("saveAll", saveAll); + _ = try win.bind("exit_app", exit_app); // Bind all events - _ = win.bind("", events); + _ = try win.bind("", events); // Start server only - const url = win.startServer("index.html"); + const url = try win.startServer("index.html"); // Open a new page in the default native web browser webui.openUrl(@as([*c]const u8, @ptrCast(url.ptr))[0..url.len :0]); diff --git a/src/c.zig b/src/c.zig index 8d228d3..beae296 100644 --- a/src/c.zig +++ b/src/c.zig @@ -602,7 +602,7 @@ pub extern fn webui_get_child_process_id(window: usize) callconv(.C) usize; /// @return Returns the window `hwnd` as `void*` /// /// @example HWND hwnd = webui_win32_get_hwnd(myWindow); -pub extern fn webui_win32_get_hwnd(window: usize) callconv(.C) *anyopaque; +pub extern fn webui_win32_get_hwnd(window: usize) callconv(.C) ?*anyopaque; /// @brief Get the network port of a running window. /// This can be useful to determine the HTTP link of `webui.js` @@ -895,6 +895,16 @@ pub extern fn webui_return_string(e: *Event, s: [*:0]const u8) callconv(.C) void /// @example webui_return_bool(e, true); pub extern fn webui_return_bool(e: *Event, b: bool) callconv(.C) void; +/// @brief Get the last WebUI error code. +/// +/// @example int error_num = webui_get_last_error_number(); +pub extern fn webui_get_last_error_number() callconv(.C) c_int; + +/// @brief Get the last WebUI error message. +/// +/// @example const char* error_msg = webui_get_last_error_message(); +pub extern fn webui_get_last_error_message() callconv(.C) [*:0]const u8; + // -- Wrapper's Interface ------------- /// @brief Bind a specific HTML element click event with a function. Empty element means all events. diff --git a/src/webui.zig b/src/webui.zig index f280c38..80c5891 100644 --- a/src/webui.zig +++ b/src/webui.zig @@ -17,35 +17,80 @@ const flags = @import("flags"); pub const c = @import("c.zig"); +pub const WebUIError = error{ + // WebUI does not currently provide any information as to what caused errors + // in most functions, instead we will just return a GenericError + GenericError, + /// this present create window new id failed + CreateWindowError, + /// this present bind element with callback failed + BindError, + /// show window failed + ShowError, + /// start server failed(like show, but no window) + ServerError, + /// encode failed + EncodeError, + /// decode failed + DecodeError, + /// get url failed + UrlError, + /// get process info failed + ProcessError, + /// get HWND failed, this is only occur on MS window + HWNDError, + /// get or set windows listening prot failed + PortError, + /// run javascript failed + ScriptError, + /// allocate memory failed + AllocateFailed, +}; + +/// webui error wrapper +pub const WebUIErrorInfo = struct { + num: i32, + msg: [:0]const u8, +}; + +/// through this func, we can get webui's lastest error number and error message +pub fn getLastError() WebUIErrorInfo { + return .{ + .num = c.webui_get_last_error_number(), + .msg = c.webui_get_last_error_message(), + }; +} + /// The window number. Do not modify. window_handle: usize, /// Creating a new WebUI window object. pub fn newWindow() webui { + const window_handle = c.webui_new_window(); + return .{ - .window_handle = c.webui_new_window(), + .window_handle = window_handle, }; } /// Create a new webui window object using a specified window number. -pub fn newWindowWithId(id: usize) webui { +pub fn newWindowWithId(id: usize) !webui { if (id == 0 or id >= WEBUI_MAX_IDS) { - std.log.err("id {} is illegal", .{id}); - if (comptime builtin.zig_version.minor == 11) { - std.os.exit(1); - } else if (comptime builtin.zig_version.minor >= 12) { - std.posix.exit(1); - } + return WebUIError.CreateWindowError; } + const window_handle = c.webui_new_window_id(id); + return .{ - .window_handle = c.webui_new_window_id(id), + .window_handle = window_handle, }; } /// Get a free window number that can be used with /// `newWindowWithId` pub fn getNewWindowId() usize { - return c.webui_get_new_window_id(); + const window_id = c.webui_get_new_window_id(); + + return window_id; } /// Bind an HTML element and a JavaScript object with a backend function. @@ -57,13 +102,16 @@ pub fn bind( self: webui, element: [:0]const u8, comptime func: fn (e: *Event) void, -) usize { +) !usize { const tmp_struct = struct { fn handle(tmp_e: *Event) callconv(.C) void { func(tmp_e); } }; - return c.webui_bind(self.window_handle, element.ptr, tmp_struct.handle); + const index = c.webui_bind(self.window_handle, element.ptr, tmp_struct.handle); + if (index == 0) return WebUIError.BindError; + + return index; } /// Use this API after using `bind()` to add any user data to it that can be @@ -83,29 +131,33 @@ pub fn getBestBrowser(self: webui) Browser { /// This will refresh all windows in multi-client mode. /// Returns True if showing the window is successed /// `content` is the html which will be shown -pub fn show(self: webui, content: [:0]const u8) bool { - return c.webui_show(self.window_handle, content.ptr); +pub fn show(self: webui, content: [:0]const u8) !void { + const success = c.webui_show(self.window_handle, content.ptr); + if (!success) return WebUIError.ShowError; } /// Same as `show()`. But using a specific web browser /// Returns True if showing the window is successed -pub fn showBrowser(self: webui, content: [:0]const u8, browser: Browser) bool { - return c.webui_show_browser(self.window_handle, content.ptr, browser); +pub fn showBrowser(self: webui, content: [:0]const u8, browser: Browser) !void { + const success = c.webui_show_browser(self.window_handle, content.ptr, browser); + if (!success) return WebUIError.ShowError; } /// Same as `show()`. But start only the web server and return the URL. /// No window will be shown. -pub fn startServer(self: webui, path: [:0]const u8) [:0]const u8 { +pub fn startServer(self: webui, path: [:0]const u8) ![:0]const u8 { const url = c.webui_start_server(self.window_handle, path.ptr); const url_len = std.mem.len(url); + if (url_len == 0) return WebUIError.ServerError; return url[0..url_len :0]; } /// Show a WebView window using embedded HTML, or a file. If the window is already /// opend, it will be refreshed. Note: Win32 need `WebView2Loader.dll`. /// Returns True if if showing the WebView window is successed. -pub fn showWv(self: webui, content: [:0]const u8) bool { - return c.webui_show_wv(self.window_handle, content.ptr); +pub fn showWv(self: webui, content: [:0]const u8) !void { + const success = c.webui_show_wv(self.window_handle, content.ptr); + if (!success) return WebUIError.ShowError; } /// Set the window in Kiosk mode (Full screen) @@ -172,19 +224,23 @@ pub fn exit() void { } /// Set the web-server root folder path for a specific window. -pub fn setRootFolder(self: webui, path: [:0]const u8) bool { - return c.webui_set_root_folder(self.window_handle, path.ptr); +pub fn setRootFolder(self: webui, path: [:0]const u8) !void { + const success = c.webui_set_root_folder(self.window_handle, path.ptr); + // TODO: replace this error + if (!success) return WebUIError.GenericError; } /// Set custom browser folder path. -pub fn setBrowserFolder(self: webui, path: [:0]const u8) bool { - return c.webui_set_browser_folder(self.window_handle, path.ptr); +pub fn setBrowserFolder(self: webui, path: [:0]const u8) void { + c.webui_set_browser_folder(self.window_handle, path.ptr); } /// Set the web-server root folder path for all windows. /// Should be used before `show()`. -pub fn setDefaultRootFolder(path: [:0]const u8) bool { - return c.webui_set_default_root_folder(path.ptr); +pub fn setDefaultRootFolder(path: [:0]const u8) !void { + const success = c.webui_set_default_root_folder(path.ptr); + // TODO: replace this error + if (!success) return WebUIError.GenericError; } /// Set a custom handler to serve files. This custom handler should @@ -254,11 +310,9 @@ pub fn setIcon(self: webui, icon: [:0]const u8, icon_type: [:0]const u8) void { /// Base64 encoding. Use this to safely send text based data to the UI. If /// it fails it will return NULL. /// you need free the return memory with free function -pub fn encode(str: [:0]const u8) ?[]u8 { +pub fn encode(str: [:0]const u8) ![]u8 { const ptr = c.webui_encode(str.ptr); - if (ptr == null) { - return null; - } + if (ptr == null) return WebUIError.EncodeError; const len = std.mem.len(ptr); return ptr[0..len]; } @@ -267,11 +321,9 @@ pub fn encode(str: [:0]const u8) ?[]u8 { /// Use this to safely decode received Base64 text from the UI. /// If it fails it will return NULL. /// you need free the return memory with free function -pub fn decode(str: [:0]const u8) ?[]u8 { +pub fn decode(str: [:0]const u8) ![]u8 { const ptr = c.webui_decode(str.ptr); - if (ptr == null) { - return null; - } + if (ptr == null) return WebUIError.DecodeError; const len = std.mem.len(ptr); return ptr[0..len]; } @@ -285,7 +337,7 @@ pub fn free(buf: []const u8) void { /// it can be safely freed using `free()` at any time. /// In general, you should not use this function pub fn malloc(size: usize) ![]u8 { - const ptr = c.webui_malloc(size) orelse return error.AllocateFailed; + const ptr = c.webui_malloc(size) orelse return WebUIError.AllocateFailed; return @as([*]u8, @ptrCast(ptr))[0..size]; } @@ -340,9 +392,10 @@ pub fn setProxy(self: webui, proxy_server: [:0]const u8) void { } /// Get the full current URL. -pub fn getUrl(self: webui) [:0]const u8 { +pub fn getUrl(self: webui) ![:0]const u8 { const ptr = c.webui_get_url(self.window_handle); const len = std.mem.len(ptr); + if (len == 0) return WebUIError.UrlError; return ptr[0..len :0]; } @@ -379,33 +432,47 @@ pub fn deleteProfile(self: webui) void { } /// Get the ID of the parent process (The web browser may re-create another new process). -pub fn getParentProcessId(self: webui) usize { - return c.webui_get_parent_process_id(self.window_handle); +pub fn getParentProcessId(self: webui) !usize { + const process_id = c.webui_get_parent_process_id(self.window_handle); + if (process_id == 0) return WebUIError.ProcessError; + return process_id; } /// Get the ID of the last child process. -pub fn getChildProcessId(self: webui) usize { - return c.webui_get_child_process_id(self.window_handle); +pub fn getChildProcessId(self: webui) !usize { + const process_id = c.webui_get_child_process_id(self.window_handle); + if (process_id == 0) return WebUIError.ProcessError; + return process_id; } /// Gets Win32 window `HWND`. More reliable with WebView /// than web browser window, as browser PIDs may change on launch. -pub fn win32GetHwnd(self: webui) windows.HWND { +pub fn win32GetHwnd(self: webui) !windows.HWND { + if (builtin.os.tag != .windows) { + @compileError("Note: method win32GetHwnd only can call on MS windows!"); + } const tmp_hwnd = c.webui_win32_get_hwnd(self.window_handle); - return @ptrCast(tmp_hwnd); + if (tmp_hwnd) { + return @ptrCast(tmp_hwnd); + } else { + return WebUIError.HWNDError; + } } /// Get the network port of a running window. /// This can be useful to determine the HTTP link of `webui.js` -pub fn getPort(self: webui) usize { - return c.webui_get_port(self.window_handle); +pub fn getPort(self: webui) !usize { + const port = c.webui_get_port(self.window_handle); + if (port == 0) return WebUIError.PortError; + return port; } /// Set a custom web-server network port to be used by WebUI. /// This can be useful to determine the HTTP link of `webui.js` in case /// you are trying to use WebUI with an external web-server like NGNIX /// Returns True if the port is free and usable by WebUI -pub fn setPort(self: webui, port: usize) bool { - return c.webui_set_port(self.window_handle, port); +pub fn setPort(self: webui, port: usize) !void { + const success = c.webui_set_port(self.window_handle, port); + if (!success) return WebUIError.PortError; } // Get an available usable free network port. @@ -446,11 +513,13 @@ pub fn getMimeType(file: [:0]const u8) [:0]const u8 { /// both in PEM format. /// This works only with `webui-2-secure` library. /// If set empty WebUI will generate a self-signed certificate. -pub fn setTlsCertificate(certificate_pem: [:0]const u8, private_key_pem: [:0]const u8) bool { +pub fn setTlsCertificate(certificate_pem: [:0]const u8, private_key_pem: [:0]const u8) !void { if (comptime !flags.enableTLS) { - @panic("not enable tls"); + @compileError("not enable tls"); } - return c.webui_set_tls_certificate(certificate_pem.ptr, private_key_pem.ptr); + const success = c.webui_set_tls_certificate(certificate_pem.ptr, private_key_pem.ptr); + // TODO: replace this error + if (!success) return WebUIError.GenericError; } /// Run JavaScript without waiting for the response.All clients. @@ -461,14 +530,15 @@ pub fn run(self: webui, script_content: [:0]const u8) void { /// Run JavaScript and get the response back. Work only in single client mode. /// Make sure your local buffer can hold the response. /// Return True if there is no execution error -pub fn script(self: webui, script_content: [:0]const u8, timeout: usize, buffer: []u8) bool { - return c.webui_script( +pub fn script(self: webui, script_content: [:0]const u8, timeout: usize, buffer: []u8) !void { + const success = c.webui_script( self.window_handle, script_content.ptr, timeout, buffer.ptr, buffer.len, ); + if (!success) return WebUIError.ScriptError; } /// Chose between Deno and Nodejs as runtime for .js and .ts files. @@ -590,28 +660,33 @@ pub fn interfaceIsAppRunning() bool { /// Get a unique window ID. pub fn interfaceGetWindowId(self: webui) usize { - return c.webui_interface_get_window_id(self.window_handle); + const window_id = c.webui_interface_get_window_id(self.window_handle); + return window_id; } /// Get an argument as string at a specific index pub fn interfaceGetStringAt(self: webui, event_number: usize, index: usize) [:0]const u8 { const ptr = c.webui_interface_get_string_at(self.window_handle, event_number, index); + // TODO: Error handling here. const len = std.mem.len(ptr); return ptr[0..len :0]; } /// Get an argument as integer at a specific index pub fn interfaceGetIntAt(self: webui, event_number: usize, index: usize) i64 { + // TODO: Error handling here return c.webui_interface_get_int_at(self.window_handle, event_number, index); } /// Get an argument as float at a specific index. pub fn interfaceGetFloatAt(self: webui, event_number: usize, index: usize) f64 { + // TODO: Error handling here return c.webui_interface_get_float_at(self.window_handle, event_number, index); } /// Get an argument as boolean at a specific index pub fn interfaceGetBoolAt(self: webui, event_number: usize, index: usize) bool { + // TODO: Error handling here return c.webui_interface_get_bool_at(self.window_handle, event_number, index); } @@ -621,8 +696,9 @@ pub fn interfaceGetSizeAt(self: webui, event_number: usize, index: usize) usize } // Show a window using embedded HTML, or a file. If the window is already -pub fn interfaceShowClient(self: webui, event_number: usize, content: [:0]const u8) bool { - return c.webui_interface_show_client(self.window_handle, event_number, content.ptr); +pub fn interfaceShowClient(self: webui, event_number: usize, content: [:0]const u8) !void { + const success = c.webui_interface_show_client(self.window_handle, event_number, content.ptr); + if (!success) return WebUIError.ShowError; } // Close a specific client. @@ -651,18 +727,19 @@ pub fn interfaceRunClient(self: webui, event_number: usize, script_content: [:0] } // Run JavaScript and get the response back. Single client. -pub fn interfaceScriptClient(self: webui, event_number: usize, script_content: [:0]const u8, timeout: usize, buffer: []u8) bool { - return c.webui_interface_script_client(self.window_handle, event_number, script_content.ptr, timeout, buffer.ptr, buffer.len); +pub fn interfaceScriptClient(self: webui, event_number: usize, script_content: [:0]const u8, timeout: usize, buffer: []u8) !void { + const success = c.webui_interface_script_client(self.window_handle, event_number, script_content.ptr, timeout, buffer.ptr, buffer.len); + if (!success) return WebUIError.ScriptError; } /// a very convenient function for binding callback. /// you just need to pase a function to get param. /// no need to care webui param api. -pub fn binding(self: webui, element: [:0]const u8, comptime callback: anytype) usize { +pub fn binding(self: webui, element: [:0]const u8, comptime callback: anytype) !usize { const T = @TypeOf(callback); const TInfo = @typeInfo(T); - if (TInfo != .Fn) { + if (TInfo != .@"fn") { const err_msg = std.fmt.comptimePrint( "callback's type ({}), it must be a function!", .{T}, @@ -670,7 +747,7 @@ pub fn binding(self: webui, element: [:0]const u8, comptime callback: anytype) u @compileError(err_msg); } - const fnInfo = TInfo.Fn; + const fnInfo = TInfo.@"fn"; if (fnInfo.return_type != void) { const err_msg = std.fmt.comptimePrint( "callback's return type ({}), it must be void!", @@ -705,7 +782,7 @@ pub fn binding(self: webui, element: [:0]const u8, comptime callback: anytype) u if (param.type) |tt| { const paramTInfo = @typeInfo(tt); switch (paramTInfo) { - .Struct => { + .@"struct" => { if (tt != Event) { const err_msg = std.fmt.comptimePrint( "the struct type is ({}), the struct type you can use only is Event in params!", @@ -715,30 +792,39 @@ pub fn binding(self: webui, element: [:0]const u8, comptime callback: anytype) u } param_tup[i] = e; }, - .Bool => { + .bool => { const res = e.getBoolAt(i); param_tup[i] = res; }, - .Int => { + .int => { const res = e.getIntAt(i); param_tup[i] = @intCast(res); }, - .Float => { + .float => { const res = e.getFloatAt(i); param_tup[i] = res; }, - .Pointer => |pointer| { - if (pointer.size != .Slice or pointer.child != u8 or pointer.is_const == false) { + .pointer => |pointer| { + if (pointer.size != .slice or pointer.child != u8 or pointer.is_const == false) { const err_msg = std.fmt.comptimePrint( "the pointer type is ({}), not support other type for pointer param!", .{tt}, ); @compileError(err_msg); } - const str_ptr = e.getStringAt(i); - const tmp_str_len = e.getSizeAt(i); - const str: []const u8 = str_ptr[0..tmp_str_len]; - param_tup[i] = str; + if (pointer.sentinel()) |sentinel| { + if (sentinel != 0) { + const err_msg = std.fmt.comptimePrint( + "type is ({}), only support these types: Event, Bool, Int, Float, [:0]const u8, []u8!", + .{tt}, + ); + @compileError(err_msg); + } + const str_ptr = e.getStringAt(i); + param_tup[i] = str_ptr; + } else { + @compileError("not support []u8"); + } }, else => { const err_msg = std.fmt.comptimePrint( @@ -770,7 +856,7 @@ fn fnParamsToTuple(comptime params: []const std.builtin.Type.Fn.Param) type { res[i] = Type.StructField{ .type = param.type.?, .alignment = @alignOf(param.type.?), - .default_value = null, + .default_value_ptr = null, .is_comptime = false, .name = std.fmt.comptimePrint("{}", .{i}), }; @@ -778,8 +864,8 @@ fn fnParamsToTuple(comptime params: []const std.builtin.Type.Fn.Param) type { break :blk res; }; return @Type(.{ - .Struct = std.builtin.Type.Struct{ - .layout = .Auto, + .@"struct" = std.builtin.Type.Struct{ + .layout = .auto, .is_tuple = true, .decls = &.{}, .fields = &fields, @@ -948,14 +1034,15 @@ pub const Event = extern struct { script_content: [:0]const u8, timeout: usize, buffer: []u8, - ) bool { - return c.webui_script_client( + ) !void { + const success = c.webui_script_client( self, script_content.ptr, timeout, buffer.ptr, buffer.len, ); + if (!success) return WebUIError.ScriptError; } /// Return the response to JavaScript as integer. @@ -984,21 +1071,20 @@ pub const Event = extern struct { const T = @TypeOf(val); const type_info = @typeInfo(T); - const is_dev = builtin.zig_version.minor > 13; - switch (type_info) { - if (is_dev) .pointer else .Pointer => |pointer| { - if (pointer.size == .Slice and - pointer.child == u8 and - (if (pointer.sentinel) |sentinel| @as(*u8, @ptrCast(sentinel)).* == 0 else false)) - { - e.returnString(val); - } else { - const err_msg = std.fmt.comptimePrint("val's type ({}), only support []const u8 for Pointer!", .{T}); - @compileError(err_msg); + .pointer => |pointer| { + // pointer must be u8 slice + if (pointer.child == u8 or pointer.size == .slice) { + // sentinel element must be 0 + const sentinel = pointer.sentinel(); + if (sentinel != null and sentinel.? == 0) { + return e.returnString(val); + } } + const err_msg = std.fmt.comptimePrint("val's type ({}), only support [:0]const u8 for Pointer!", .{T}); + @compileError(err_msg); }, - if (is_dev) .int else .Int => |int| { + .int => |int| { const bits = int.bits; const is_signed = int.signedness == .signed; if (is_signed and bits <= 64) { @@ -1010,10 +1096,10 @@ pub const Event = extern struct { @compileError(err_msg); } }, - if (is_dev) .bool else .Bool => e.returnBool(val), - if (is_dev) .float else .Float => e.returnFloat(val), + .bool => e.returnBool(val), + .float => e.returnFloat(val), else => { - const err_msg = std.fmt.comptimePrint("val's type ({}), only support int, bool, string([]const u8)!", .{T}); + const err_msg = std.fmt.comptimePrint("val's type ({}), only support int, float, bool, string([]const u8)!", .{T}); @compileError(err_msg); }, } @@ -1057,6 +1143,17 @@ pub const Event = extern struct { const len = std.mem.len(ptr); return ptr[0..len :0]; } + // Get the first argument raw buffer + pub fn getRawAt(e: *Event, index: usize) [*]const u8 { + const ptr = c.webui_get_string_at(e, index); + return @ptrCast(ptr); + } + + // Get the first argument raw buffer + pub fn getRaw(e: *Event) [*]const u8 { + const ptr = c.webui_get_string(e); + return @ptrCast(ptr); + } /// Get an argument as boolean at a specific index pub fn getBoolAt(e: *Event, index: usize) bool { @@ -1069,17 +1166,23 @@ pub const Event = extern struct { } /// Get the size in bytes of an argument at a specific index - pub fn getSizeAt(e: *Event, index: usize) usize { - return c.webui_get_size_at(e, index); + pub fn getSizeAt(e: *Event, index: usize) !usize { + const size = c.webui_get_size_at(e, index); + if (size == 0) return WebUIError.GenericError; + return size; } /// Get size in bytes of the first argument - pub fn getSize(e: *Event) usize { - return c.webui_get_size(e); + pub fn getSize(e: *Event) !usize { + const size = c.webui_get_size(e); + if (size == 0) return WebUIError.GenericError; + return size; } /// Get user data that is set using `SetContext()`. - pub fn getContext(e: *Event) *anyopaque { - return c.webui_get_context(e); + pub fn getContext(e: *Event) !*anyopaque { + const context = c.webui_get_context(e); + if (context == null) return WebUIError.GenericError; + return context; } };