diff --git a/.gitignore b/.gitignore index 18fb1c4..a0cc7a4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ zig-cache zig-out .direnv .zig-cache +examples/comprehensive/examples \ No newline at end of file diff --git a/build.zig b/build.zig index cc7750f..96454eb 100644 --- a/build.zig +++ b/build.zig @@ -96,11 +96,18 @@ pub fn build(b: *Build) !void { // Function to generate API documentation fn generate_docs(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarget, flags_module: *Module) void { // Create a temporary object for documentation generation - const webui_lib = b.addObject(.{ + const webui_lib = b.addObject(if (builtin.zig_version.minor == 14) .{ .name = "webui_lib", .root_source_file = b.path(b.pathJoin(&.{ "src", "webui.zig" })), .target = target, .optimize = optimize, + } else .{ + .name = "webui_lib", + .root_module = b.addModule("webui_lib", .{ + .root_source_file = b.path(b.pathJoin(&.{ "src", "webui.zig" })), + .target = target, + .optimize = optimize, + }), }); webui_lib.root_module.addImport("flags", flags_module); @@ -153,11 +160,18 @@ fn build_examples(b: *Build, optimize: OptimizeMode, target: Build.ResolvedTarge const path = b.pathJoin(&.{ "examples", example_name, "main.zig" }); // Create an executable for each example - const exe = b.addExecutable(.{ + const exe = b.addExecutable(if (builtin.zig_version.minor == 14) .{ .name = example_name, .root_source_file = b.path(path), .target = target, .optimize = optimize, + } else .{ + .name = example_name, + .root_module = b.addModule(example_name, .{ + .root_source_file = b.path(path), + .target = target, + .optimize = optimize, + }), }); // Add the webui module and link against the library diff --git a/build.zig.zon b/build.zig.zon index 289ed09..0728827 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,12 +1,12 @@ -.{ +.{ .name = .zig_webui, .version = "2.5.0-beta.4", .fingerprint = 0x95965ed3cdfb8c33, .minimum_zig_version = "0.14.0", .dependencies = .{ .webui = .{ - .hash = "webui-2.5.0-beta.4-pxqD5bE4NwCVDCBguzzRmyOubiadAaVRNM0XMxqUv_GS", - .url = "https://github.com/webui-dev/webui/archive/e673cee5a26b4c475395ce8e2f5fb92608bf3574.tar.gz", + .hash = "webui-2.5.0-beta.4-pxqD5bY8NwAOad4eidLvvpn4UbMNNCtWnGlhPj5BScgZ", + .url = "https://github.com/webui-dev/webui/archive/ffb235a6e1ad0904a284836c205832bb47ec3ebe.tar.gz", }, }, .paths = .{ diff --git a/examples/comprehensive/index.html b/examples/comprehensive/index.html new file mode 100644 index 0000000..b53f225 --- /dev/null +++ b/examples/comprehensive/index.html @@ -0,0 +1,883 @@ + + + + + + WebUI Comprehensive Showcase + + + +
+

🚀 WebUI Comprehensive Showcase

+

Demonstrating the full capabilities of the Zig WebUI library

+
+
🌐 Loading...
+
👥 0 Users
+
💬 0 Messages
+
📁 0 Files
+
+
+ +
+ +
+ +
+

👤 User Management

+
+ + + +
+
User management ready
+
+ + +
+

💬 Messaging

+
+ + + + + +
+
Messaging system ready
+
+ + +
+

⚙️ Data Processing

+
+ + + +
+
Data processing ready
+
+ + +
+

⚡ Command Execution

+
+ + + +
+
Command execution ready
+
+ + +
+

📊 Performance Testing

+
+ + + +
+
+
+
+
Performance testing ready
+
+ + +
+

⚙️ Settings

+
+ + + + +
+
Settings management ready
+
+
+ + +
+

🖥️ System Information

+
+ + + +
+
Click "Get System Info" to load system details
+
+ + +
+

📝 Activity Log

+
+ +
+
+ + +
+

🌐 Global Controls

+
+ + + + +
+
Global controls ready
+
+
+ + + + +
+ + + + diff --git a/examples/comprehensive/main.zig b/examples/comprehensive/main.zig new file mode 100644 index 0000000..77ceef5 --- /dev/null +++ b/examples/comprehensive/main.zig @@ -0,0 +1,483 @@ +//! WebUI Zig - Comprehensive Feature Showcase +//! This example demonstrates multiple WebUI features working together +const std = @import("std"); +const webui = @import("webui"); + +const html = @embedFile("index.html"); + +var allocator = std.heap.page_allocator; + +// Settings storage +var settings_map: std.HashMap([]const u8, []const u8, std.hash_map.StringContext, 80) = undefined; + +// Application state +const AppState = struct { + users_online: u32 = 0, + messages_sent: u32 = 0, + files_uploaded: u32 = 0, + + fn init() AppState { + return AppState{}; + } +}; + +// Track unique users +var online_users: std.HashMap([]const u8, bool, std.hash_map.StringContext, 80) = undefined; + +var app_state: AppState = undefined; + +pub fn main() !void { + // Initialize settings storage + settings_map = std.HashMap([]const u8, []const u8, std.hash_map.StringContext, 80).init(allocator); + defer { + // Clean up all allocated setting keys and values + var iterator = settings_map.iterator(); + while (iterator.next()) |entry| { + allocator.free(entry.key_ptr.*); + allocator.free(entry.value_ptr.*); + } + settings_map.deinit(); + } + + // Initialize online users tracking + online_users = std.HashMap([]const u8, bool, std.hash_map.StringContext, 80).init(allocator); + defer { + // Clean up all allocated user keys + var user_iterator = online_users.iterator(); + while (user_iterator.next()) |entry| { + allocator.free(entry.key_ptr.*); + } + online_users.deinit(); + } + + app_state = AppState.init(); + // Configure WebUI + webui.setConfig(.multi_client, true); + webui.setConfig(.folder_monitor, true); + webui.setTimeout(0); // No timeout + + // Create main window + var main_window = webui.newWindow(); + + // Configure window + main_window.setSize(1200, 800); + main_window.setCenter(); + main_window.setPublic(true); + main_window.setIcon("...", "image/svg+xml"); + + // Set up file handling + try main_window.setRootFolder("public"); + main_window.setFileHandler(customFileHandler); + + // Bind comprehensive functions + _ = try main_window.binding("get_app_status", getAppStatus); + _ = try main_window.binding("user_action", userAction); + _ = try main_window.binding("send_notification", sendNotification); + _ = try main_window.binding("process_data", processData); + _ = try main_window.binding("execute_command", executeCommand); + _ = try main_window.binding("get_system_info", getSystemInfo); + _ = try main_window.binding("test_performance", testPerformance); + _ = try main_window.binding("manage_settings", manageSettings); + _ = try main_window.binding("upload_file", uploadFile); + + // Set runtime for enhanced JavaScript support + main_window.setRuntime(.NodeJS); + + // Create public directory + std.fs.cwd().makeDir("examples/comprehensive/public") catch {}; + + // Show window + try main_window.show(html); + + std.debug.print("Comprehensive WebUI showcase started\n", .{}); + std.debug.print("Port: {}\n", .{main_window.getPort() catch 0}); + std.debug.print("URL: {s}\n", .{main_window.getUrl() catch "unknown"}); + + // Wait for window to close + webui.wait(); + + // Clean up + webui.clean(); +} + +fn customFileHandler(filename: []const u8) ?[]const u8 { + // Handle API endpoints + if (std.mem.startsWith(u8, filename, "/api/")) { + return handleApiRequest(filename); + } + + // Handle static files + if (std.mem.eql(u8, filename, "/status")) { + const status_html = + \\HTTP/1.1 200 OK + \\Content-Type: text/html + \\ + \\

WebUI Status

Server is running!

+ ; + return status_html; + } + + return null; // Let default handler take over +} + +fn handleApiRequest(path: []const u8) ?[]const u8 { + if (std.mem.eql(u8, path, "/api/stats")) { + var buffer: [512]u8 = undefined; + + const json = std.fmt.bufPrint(buffer[0..], + \\HTTP/1.1 200 OK + \\Content-Type: application/json + \\Access-Control-Allow-Origin: * + \\ + \\{{"users":{}, "messages":{}, "files":{}}} + , .{ app_state.users_online, app_state.messages_sent, app_state.files_uploaded }) catch return null; + + // Allocate persistent memory for response + const response = allocator.dupe(u8, json) catch return null; + return response; + } + + return null; +} + +fn getAppStatus(e: *webui.Event) void { + const win = e.getWindow(); + const port = win.getPort() catch 0; + const url = win.getUrl() catch "unknown"; + + var buffer: [1024]u8 = undefined; + const json = std.fmt.bufPrintZ(buffer[0..], + \\{{"status":"running","users":{},"messages":{},"files":{},"port":{},"url":"{s}","clientId":{},"timestamp":{}}} + , .{ app_state.users_online, app_state.messages_sent, app_state.files_uploaded, port, url, e.client_id, std.time.timestamp() }) catch "{\"error\":\"format_error\"}"; + + std.debug.print("App Status - Users: {}, Messages: {}, Files: {}\n", .{ app_state.users_online, app_state.messages_sent, app_state.files_uploaded }); + + e.returnString(json); +} + +fn userAction(e: *webui.Event, action: [:0]const u8, data: [:0]const u8) void { + std.debug.print("User action: {s} with data: {s}\n", .{ action, data }); + + var response: [512]u8 = undefined; + var result: [:0]const u8 = ""; + + if (std.mem.eql(u8, action, "login")) { + // Check if user is already online + if (online_users.contains(data)) { + result = std.fmt.bufPrintZ(response[0..], "User '{s}' is already online. Online users: {}", .{ data, app_state.users_online }) catch "Error"; + } else { + // Add new user + const username_copy = allocator.dupe(u8, data) catch { + result = "Error: Memory allocation failed"; + e.returnString(result); + return; + }; + online_users.put(username_copy, true) catch { + allocator.free(username_copy); + result = "Error: Failed to track user"; + e.returnString(result); + return; + }; + app_state.users_online += 1; + result = std.fmt.bufPrintZ(response[0..], "User '{s}' logged in. Online users: {}", .{ data, app_state.users_online }) catch "Error"; + } + } else if (std.mem.eql(u8, action, "logout")) { + // Check if user is online + if (online_users.fetchRemove(data)) |kv| { + allocator.free(kv.key); + if (app_state.users_online > 0) app_state.users_online -= 1; + result = std.fmt.bufPrintZ(response[0..], "User '{s}' logged out. Online users: {}", .{ data, app_state.users_online }) catch "Error"; + } else { + result = std.fmt.bufPrintZ(response[0..], "User '{s}' was not online. Online users: {}", .{ data, app_state.users_online }) catch "Error"; + } + } else if (std.mem.eql(u8, action, "message")) { + app_state.messages_sent += 1; + result = std.fmt.bufPrintZ(response[0..], "Message sent. Total messages: {}", .{app_state.messages_sent}) catch "Error"; + } else if (std.mem.eql(u8, action, "upload")) { + // For simulation purposes, just acknowledge the upload request + const filename = if (data.len > 0) data else "demo_file.txt"; + result = std.fmt.bufPrintZ(response[0..], "Upload request received for '{s}'. Use the file input for actual upload.", .{filename}) catch "Error"; + } else { + result = "Unknown action"; + } + + e.returnString(result); +} + +fn sendNotification(e: *webui.Event, message: [:0]const u8, level: [:0]const u8) void { + const win = e.getWindow(); + + // Send notification to all clients + var js_code: [512]u8 = undefined; + const script = std.fmt.bufPrintZ(js_code[0..], "showNotification('{s}', '{s}');", .{ message, level }) catch return; + + win.run(script); + + e.returnString("Notification sent to all clients"); + std.debug.print("Notification sent: {s} ({s})\n", .{ message, level }); +} + +fn processData(e: *webui.Event, operation: [:0]const u8, input_data: [:0]const u8) void { + std.debug.print("Processing data: {s} on {s}\n", .{ operation, input_data }); + + var result: [1024]u8 = undefined; + var output: [:0]const u8 = ""; + + if (std.mem.eql(u8, operation, "reverse")) { + // Reverse string + var reversed: [512]u8 = undefined; + var i: usize = 0; + while (i < input_data.len and i < 512) : (i += 1) { + reversed[i] = input_data[input_data.len - 1 - i]; + } + output = std.fmt.bufPrintZ(result[0..], "Reversed: {s}", .{reversed[0..i]}) catch "Error"; + } else if (std.mem.eql(u8, operation, "uppercase")) { + // Convert to uppercase + var upper: [512]u8 = undefined; + for (input_data, 0..) |c, i| { + if (i >= 512) break; + upper[i] = std.ascii.toUpper(c); + } + output = std.fmt.bufPrintZ(result[0..], "Uppercase: {s}", .{upper[0..@min(input_data.len, 512)]}) catch "Error"; + } else if (std.mem.eql(u8, operation, "hash")) { + // Simple hash (sum of bytes) + var hash: u32 = 0; + for (input_data) |c| { + hash = hash *% 31 +% c; + } + output = std.fmt.bufPrintZ(result[0..], "Hash: {X}", .{hash}) catch "Error"; + } else { + output = "Unknown operation"; + } + + e.returnString(output); +} + +fn executeCommand(e: *webui.Event, command: [:0]const u8, args: [:0]const u8) void { + std.debug.print("Execute command: {s} {s}\n", .{ command, args }); + + var response: [512]u8 = undefined; + var result: [:0]const u8 = ""; + + if (std.mem.eql(u8, command, "echo")) { + result = std.fmt.bufPrintZ(response[0..], "Echo: {s}", .{args}) catch "Error"; + } else if (std.mem.eql(u8, command, "time")) { + const timestamp = std.time.timestamp(); + result = std.fmt.bufPrintZ(response[0..], "Current time: {}", .{timestamp}) catch "Error"; + } else if (std.mem.eql(u8, command, "random")) { + var prng = std.Random.DefaultPrng.init(@intCast(std.time.timestamp())); + const random_num = prng.random().int(u32); + result = std.fmt.bufPrintZ(response[0..], "Random number: {}", .{random_num}) catch "Error"; + } else if (std.mem.eql(u8, command, "memory")) { + // Simple memory info (simulated) + result = std.fmt.bufPrintZ(response[0..], "Memory usage: {}MB", .{50 + @rem(std.time.timestamp(), 100)}) catch "Error"; + } else { + result = "Unknown command"; + } + + e.returnString(result); +} + +fn getSystemInfo(e: *webui.Event) void { + const builtin = @import("builtin"); + + var buffer: [1024]u8 = undefined; + const info = std.fmt.bufPrintZ(buffer[0..], + \\{{"os":"{s}","arch":"{s}","zigVersion":"{s}","webuiVersion":"2.5.0","timestamp":{}}} + , .{ @tagName(builtin.os.tag), @tagName(builtin.cpu.arch), @import("builtin").zig_version_string, std.time.timestamp() }) catch "{}"; + + e.returnString(info); +} + +fn testPerformance(e: *webui.Event, iterations: i64, operation: [:0]const u8) void { + const start_time = std.time.nanoTimestamp(); + + var i: i64 = 0; + var result: u64 = 0; + + if (std.mem.eql(u8, operation, "math")) { + while (i < iterations) : (i += 1) { + result = result +% (@as(u64, @intCast(i)) * @as(u64, @intCast(i))); + } + } else if (std.mem.eql(u8, operation, "string")) { + var buffer: [64]u8 = undefined; + while (i < iterations) : (i += 1) { + _ = std.fmt.bufPrint(buffer[0..], "test{}", .{i}) catch continue; + } + } + + const end_time = std.time.nanoTimestamp(); + const duration_ms = @as(f64, @floatFromInt(end_time - start_time)) / 1_000_000.0; + + var response: [256]u8 = undefined; + const msg = std.fmt.bufPrintZ(response[0..], "Performance test completed: {} iterations of {s} in {d:.2}ms", .{ iterations, operation, duration_ms }) catch "Error"; + + e.returnString(msg); + std.debug.print("Performance test: {} iterations in {d:.2}ms\n", .{ iterations, duration_ms }); +} + +fn manageSettings(e: *webui.Event, action: [:0]const u8, key: [:0]const u8, value: [:0]const u8) void { + std.debug.print("Settings: {s} {s} = {s}\n", .{ action, key, value }); + + var response: [512]u8 = undefined; + var result: [:0]const u8 = ""; + + if (std.mem.eql(u8, action, "set")) { + // Store setting in memory + const key_copy = allocator.dupe(u8, key) catch { + result = "Error: Memory allocation failed for key"; + e.returnString(result); + return; + }; + const value_copy = allocator.dupe(u8, value) catch { + allocator.free(key_copy); + result = "Error: Memory allocation failed for value"; + e.returnString(result); + return; + }; + + // Remove old value if exists + if (settings_map.get(key)) |old_value| { + allocator.free(old_value); + } + + settings_map.put(key_copy, value_copy) catch { + allocator.free(key_copy); + allocator.free(value_copy); + result = "Error: Failed to store setting"; + e.returnString(result); + return; + }; + + result = std.fmt.bufPrintZ(response[0..], "Setting '{s}' set to '{s}'", .{ key, value }) catch "Error"; + } else if (std.mem.eql(u8, action, "get")) { + // Get setting from memory + if (settings_map.get(key)) |stored_value| { + result = std.fmt.bufPrintZ(response[0..], "Setting '{s}' = '{s}'", .{ key, stored_value }) catch "Error"; + } else { + result = std.fmt.bufPrintZ(response[0..], "Setting '{s}' not found (no value set)", .{key}) catch "Error"; + } + } else if (std.mem.eql(u8, action, "delete")) { + // Delete setting from memory + if (settings_map.fetchRemove(key)) |kv| { + allocator.free(kv.key); + allocator.free(kv.value); + result = std.fmt.bufPrintZ(response[0..], "Setting '{s}' deleted", .{key}) catch "Error"; + } else { + result = std.fmt.bufPrintZ(response[0..], "Setting '{s}' not found (nothing to delete)", .{key}) catch "Error"; + } + } else { + result = "Unknown settings action"; + } + + e.returnString(result); +} + +fn uploadFile(e: *webui.Event, filename: [:0]const u8, content: [:0]const u8) void { + std.debug.print("Upload file: {s} (size: {})\n", .{ filename, content.len }); + + var response: [512]u8 = undefined; + var result: [:0]const u8 = ""; + + // Validate filename + if (filename.len == 0) { + result = "Error: Filename cannot be empty"; + e.returnString(result); + return; + } + + // Ensure public directory exists + std.fs.cwd().makePath("examples/comprehensive/public") catch |err| { + std.debug.print("Warning: Failed to create public directory: {}\n", .{err}); + // Continue anyway, maybe directory already exists + }; + + // Sanitize filename to avoid filesystem issues while preserving Unicode characters (including Chinese) + var safe_filename: [512]u8 = undefined; + var safe_len: usize = 0; + + // Use Zig's UTF-8 iterator to properly handle Unicode characters + const utf8_view = std.unicode.Utf8View.init(filename) catch { + // If filename is not valid UTF-8, use a default name + const default_name = "uploaded_file.txt"; + @memcpy(safe_filename[0..default_name.len], default_name); + safe_len = default_name.len; + safe_filename[safe_len] = 0; + return; // Exit early with default name + }; + + var iter = utf8_view.iterator(); + while (iter.nextCodepoint()) |codepoint| { + if (safe_len >= 500) break; // Leave space for null terminator + + // Check if this is a filesystem-dangerous character (ASCII only) + if (codepoint < 128) { + const ascii_char = @as(u8, @intCast(codepoint)); + if (ascii_char == '/' or ascii_char == '\\' or ascii_char == ':' or + ascii_char == '*' or ascii_char == '?' or ascii_char == '"' or + ascii_char == '<' or ascii_char == '>' or ascii_char == '|' or + ascii_char == 0) + { + // Replace dangerous characters with underscore + safe_filename[safe_len] = '_'; + safe_len += 1; + } else { + // Safe ASCII character + safe_filename[safe_len] = ascii_char; + safe_len += 1; + } + } else { + // Unicode character (including Chinese) - encode back to UTF-8 + var utf8_buffer: [4]u8 = undefined; + const utf8_len = std.unicode.utf8Encode(codepoint, &utf8_buffer) catch { + // If encoding fails, replace with underscore + safe_filename[safe_len] = '_'; + safe_len += 1; + continue; + }; + + // Check if we have enough space + if (safe_len + utf8_len <= 500) { + @memcpy(safe_filename[safe_len .. safe_len + utf8_len], utf8_buffer[0..utf8_len]); + safe_len += utf8_len; + } else { + break; // No more space + } + } + } + safe_filename[safe_len] = 0; // null terminate + + // Create file path in public directory + const file_path = std.fmt.allocPrint(allocator, "examples/comprehensive/public/{s}", .{safe_filename[0..safe_len :0]}) catch { + result = "Error: Failed to create file path"; + e.returnString(result); + return; + }; + defer allocator.free(file_path); + + std.debug.print("Creating file at: {s}\n", .{file_path}); + + // Create file + const file = std.fs.cwd().createFile(file_path, .{}) catch |err| { + std.debug.print("Failed to create file {s}: {}\n", .{ file_path, err }); + result = std.fmt.bufPrintZ(response[0..], "Error: Failed to create file '{s}' ({s})", .{ filename, @errorName(err) }) catch "Error"; + e.returnString(result); + return; + }; + defer file.close(); + + // Write content to file + file.writeAll(content) catch |err| { + std.debug.print("Failed to write to file {s}: {}\n", .{ file_path, err }); + result = std.fmt.bufPrintZ(response[0..], "Error: Failed to write to file '{s}' ({s})", .{ filename, @errorName(err) }) catch "Error"; + e.returnString(result); + return; + }; + + // Update counters + app_state.files_uploaded += 1; + + // Return success message + result = std.fmt.bufPrintZ(response[0..], "File '{s}' uploaded successfully as '{s}'. Size: {} bytes. Total files: {}", .{ filename, safe_filename[0..safe_len :0], content.len, app_state.files_uploaded }) catch "Error"; + e.returnString(result); +} diff --git a/examples/event_handling/index.html b/examples/event_handling/index.html new file mode 100644 index 0000000..878b6ca --- /dev/null +++ b/examples/event_handling/index.html @@ -0,0 +1,745 @@ + + + + + + Event Handling & Context Management + + + +
+

🎯 Event Handling & Context Management

+ +
+ +
+

👤 User Session

+
+
+ + + +
+
+ + +
+ + +
+

🖱️ Click Tracking

+
+ + + + +
+ +
+
+ + +
+

💬 Multi-Client Messaging

+
+
+

Send Direct Message

+
+ + + + +
+
+ +
+

Broadcast Message

+
+ + +
+
+
+ +
+
Welcome to the messaging system!
+
+
+ + +
+

📊 Event Monitoring

+
+
+

Client Information

+ + +
+ Client Status: Ready +
+
+ +
+

Event Statistics

+
+ Events processed: 0
+ Last event: None
+ Session time: 00:00:00 +
+
+
+
+ + +
+

⚡ Advanced Event Features

+
+ + + + +
+ +
+
+ + +
+ + + + \ No newline at end of file diff --git a/examples/event_handling/main.zig b/examples/event_handling/main.zig new file mode 100644 index 0000000..784a8b7 --- /dev/null +++ b/examples/event_handling/main.zig @@ -0,0 +1,507 @@ +//! WebUI Zig - Event Handling and Context Management Example +//! This example demonstrates advanced event handling, context management, and multi-client support +const std = @import("std"); +const webui = @import("webui"); + +const html = @embedFile("index.html"); + +var allocator = std.heap.page_allocator; + +// Global user context storage for each window/client +var global_user_contexts: ?std.AutoHashMap(usize, *UserContext) = null; + +// Online user structure +const OnlineUser = struct { + client_id: usize, + username: []const u8, +}; + +// Online users list for tracking connected clients +var online_users: ?std.ArrayList(OnlineUser) = null; + +// Initialize global user contexts if not already initialized +fn ensureContextsInitialized() void { + if (global_user_contexts == null) { + global_user_contexts = std.AutoHashMap(usize, *UserContext).init(allocator); + } + if (online_users == null) { + online_users = std.ArrayList(OnlineUser).init(allocator); + } +} + +// Get the global contexts map (ensuring it's initialized) +fn getContextsMap() *std.AutoHashMap(usize, *UserContext) { + ensureContextsInitialized(); + return &global_user_contexts.?; +} + +// User data structure for context +const UserContext = struct { + user_id: u32, + username: []const u8, + session_start: i64, + click_count: u32, + + fn create(user_id: u32, username: []const u8) !*UserContext { + const context = try allocator.create(UserContext); + context.* = UserContext{ + .user_id = user_id, + .username = try allocator.dupe(u8, username), + .session_start = std.time.timestamp(), + .click_count = 0, + }; + return context; + } + + fn destroy(self: *UserContext) void { + allocator.free(self.username); + allocator.destroy(self); + } +}; + +pub fn main() !void { + // Configure for multi-client support + webui.setConfig(.multi_client, true); // Enable multi-client mode + webui.setConfig(.use_cookies, true); // Use cookies for client identification + + // Set public access to allow connections from other devices + // This allows other devices on the same network to connect + + // Create window + var nwin = webui.newWindow(); + + // Allow public network access (other devices can connect) + nwin.setPublic(true); + + // Set event blocking mode + nwin.setEventBlocking(false); // Non-blocking events + + // Bind event handlers with context + _ = try nwin.bind("user_login", userLogin); + _ = try nwin.bind("user_logout", userLogout); + _ = try nwin.bind("track_click", trackClick); + _ = try nwin.bind("get_user_info", getUserInfo); + _ = try nwin.bind("get_online_users", getOnlineUsers); + _ = try nwin.bind("send_message", sendMessage); + _ = try nwin.bind("broadcast_message", broadcastMessage); + _ = try nwin.bind("client_connect", clientConnect); + _ = try nwin.bind("client_disconnect", clientDisconnect); + + // Bind interface handlers for advanced event management + _ = try nwin.interfaceBind("interface_handler", interfaceEventHandler); + + // Bind universal event handler (empty element name = all events) + _ = try nwin.bind("", universalEventHandler); + + // Show window with multi-client support + // Try to show embedded HTML first + nwin.show(html) catch |err| { + // If that fails, try alternative approach + std.debug.print("Warning: Failed to show embedded HTML ({}), trying alternative method...\n", .{err}); + + // Write HTML to temporary file and use startServer + const temp_file = "temp_index.html"; + const file = std.fs.cwd().createFile(temp_file, .{}) catch |file_err| { + std.debug.print("Error: Could not create temporary HTML file: {}\n", .{file_err}); + return; + }; + defer file.close(); + defer std.fs.cwd().deleteFile(temp_file) catch {}; + + file.writeAll(html) catch |write_err| { + std.debug.print("Error: Could not write HTML content: {}\n", .{write_err}); + return; + }; + + // Start server with file + const url = nwin.startServer(temp_file) catch |server_err| { + std.debug.print("Error: Could not start server: {}\n", .{server_err}); + return; + }; + + std.debug.print("📍 Server URL: {s}\n", .{url}); + + // Open in default browser + webui.openUrl(@as([*c]const u8, @ptrCast(url.ptr))[0..url.len :0]); + }; + + // Get and display server information + const port = nwin.getPort() catch |err| blk: { + std.debug.print("Warning: Could not get server port ({})\n", .{err}); + break :blk @as(usize, 0); + }; + + const url = nwin.getUrl() catch |err| blk: { + std.debug.print("Warning: Could not get server URL ({})\n", .{err}); + break :blk @as([:0]const u8, "unknown"); + }; + + // Print multi-client instructions + std.debug.print("\n🌐 Multi-Client Event Handling Server Started!\n", .{}); + std.debug.print("📍 Server URL: {s}\n", .{url}); + std.debug.print("🔌 Server Port: {}\n", .{port}); + std.debug.print("🏠 Local Access: http://localhost:{}\n", .{port}); + std.debug.print("🌍 Network Access: http://[YOUR_IP]:{}\n", .{port}); + std.debug.print("\n💡 To find your IP address:\n", .{}); + std.debug.print(" Windows: ipconfig | findstr IPv4\n", .{}); + std.debug.print(" Mac/Linux: ifconfig | grep inet\n", .{}); + std.debug.print(" Or check in network settings\n", .{}); + std.debug.print("\n" ++ "=" ** 60 ++ "\n", .{}); + std.debug.print("\n📋 How to test multi-client functionality:\n", .{}); + std.debug.print("\n🔗 Multi-Client Connection Methods:\n", .{}); + std.debug.print(" 1. SAME COMPUTER - New Tab/Window:\n", .{}); + std.debug.print(" → Copy: {s}\n", .{url}); + std.debug.print(" → Paste in new browser tab\n", .{}); + std.debug.print("\n 2. SAME COMPUTER - Different Browser:\n", .{}); + std.debug.print(" → Chrome: {s}\n", .{url}); + std.debug.print(" → Firefox: {s}\n", .{url}); + std.debug.print(" → Edge: {s}\n", .{url}); + std.debug.print("\n 3. OTHER DEVICES (Phone/Tablet/PC):\n", .{}); + std.debug.print(" → Find your IP address using commands above\n", .{}); + std.debug.print(" → Replace localhost with your IP\n", .{}); + std.debug.print(" → Example: http://192.168.1.100:{}\n", .{port}); + std.debug.print("\n👥 Suggested Test Users:\n", .{}); + std.debug.print(" 🟦 Window 1: Alice (ID: 1001)\n", .{}); + std.debug.print(" 🟩 Window 2: Bob (ID: 1002)\n", .{}); + std.debug.print(" 🟨 Window 3: Carol (ID: 1003)\n", .{}); + std.debug.print(" 🟪 Window 4: David (ID: 1004)\n", .{}); + std.debug.print("\n🎯 Test Features:\n", .{}); + std.debug.print(" • Login with different users\n", .{}); + std.debug.print(" • Refresh online user list (🔄 button)\n", .{}); + std.debug.print(" • Send direct messages between users\n", .{}); + std.debug.print(" • Test broadcast messages\n", .{}); + std.debug.print(" • Click tracking per user\n\n", .{}); + + std.debug.print("Event handling server started. Multi-client support enabled.\n", .{}); + + // Wait for all windows to close + webui.wait(); + + // Clean up any remaining contexts + cleanupAllContexts(); + webui.clean(); +} + +fn getLocalIPAddress() ![]const u8 { + // Placeholder function - user should check their actual IP address + // Use the commands shown in the console output to find your real IP + return "192.168.x.x"; +} + +fn userLogin(e: *webui.Event) void { + const username = e.getString(); + const user_id = @as(u32, @intCast(e.getIntAt(1))); + + std.debug.print("User login attempt: {} - {s}\n", .{user_id, username}); + + // Create user context + const context = UserContext.create(user_id, username) catch { + e.returnString("Login failed: Unable to create user context"); + return; + }; + + // Set context for this element + const win = e.getWindow(); + // Set context for the calling element and make it available globally + // We'll use a window-wide context storage + win.setContext("user_login", @ptrCast(context)); + + // Store context globally using client ID as key + getContextsMap().put(e.client_id, context) catch { + std.debug.print("Failed to store global context for client {}\n", .{e.client_id}); + }; + + // Add user to online list + ensureContextsInitialized(); + if (online_users) |*users| { + users.append(OnlineUser{ .client_id = e.client_id, .username = context.username }) catch { + std.debug.print("Failed to add user to online list\n", .{}); + }; + } + + // Broadcast user list update to all clients + broadcastUserListUpdate(); + + // Return success response + var response: [257]u8 = undefined; // +1 for null terminator + const msg = std.fmt.bufPrint(response[0..256], + "Welcome {s}! User ID: {}, Session started.", .{username, user_id}) catch ""; + + // Ensure null termination + response[msg.len] = 0; + const null_terminated: [:0]const u8 = response[0..msg.len :0]; + e.returnString(null_terminated); + std.debug.print("User {s} logged in successfully\n", .{username}); +} + +fn userLogout(e: *webui.Event) void { + // Get user context from global storage + const context = getContextsMap().get(e.client_id) orelse { + e.returnString("No active session found"); + return; + }; + + // Calculate session duration + const session_duration = std.time.timestamp() - context.session_start; + + var response: [257]u8 = undefined; // +1 for null terminator + const msg = std.fmt.bufPrint(response[0..256], + "Goodbye {s}! Session duration: {}s, Total clicks: {}", + .{context.username, session_duration, context.click_count}) catch ""; + + // Ensure null termination + response[msg.len] = 0; + const null_terminated: [:0]const u8 = response[0..msg.len :0]; + e.returnString(null_terminated); + std.debug.print("User {s} logged out. Session: {}s\n", .{context.username, session_duration}); + + // Remove from global context storage + _ = getContextsMap().remove(e.client_id); + + // Remove from online users list + if (online_users) |*users| { + var i: usize = 0; + while (i < users.items.len) { + if (users.items[i].client_id == e.client_id) { + _ = users.orderedRemove(i); + break; + } + i += 1; + } + } + + // Broadcast user list update to all clients + broadcastUserListUpdate(); + + // Clean up context + context.destroy(); +} + +fn trackClick(e: *webui.Event) void { + const button_name = e.getString(); + + // Get user context from global storage + const context = getContextsMap().get(e.client_id) orelse { + e.returnString("No active session - please login first"); + return; + }; + + context.click_count += 1; + + var response: [257]u8 = undefined; // +1 for null terminator + const msg = std.fmt.bufPrint(response[0..256], + "Button '{s}' clicked by {s}. Total clicks: {}", + .{button_name, context.username, context.click_count}) catch ""; + + // Ensure null termination + response[msg.len] = 0; + const null_terminated: [:0]const u8 = response[0..msg.len :0]; + e.returnString(null_terminated); + std.debug.print("Click tracked: {s} by {s} ({})\n", .{button_name, context.username, context.click_count}); +} + +fn getUserInfo(e: *webui.Event) void { + // Get user context from global storage + const context = getContextsMap().get(e.client_id) orelse { + e.returnString("{\"error\":\"No active session\"}"); + return; + }; + + const session_time = std.time.timestamp() - context.session_start; + + var response: [513]u8 = undefined; // +1 for null terminator + const json = std.fmt.bufPrint(response[0..512], + "{{\"userId\":{},\"username\":\"{s}\",\"sessionTime\":{},\"clickCount\":{},\"clientId\":{}}}", + .{context.user_id, context.username, session_time, context.click_count, e.client_id}) catch ""; + + // Ensure null termination + response[json.len] = 0; + const null_terminated: [:0]const u8 = response[0..json.len :0]; + e.returnString(null_terminated); +} + +fn sendMessage(e: *webui.Event) void { + const message = e.getString(); + const target_client = e.getIntAt(1); + + // Get sender context from global storage + const context = getContextsMap().get(e.client_id) orelse { + e.returnString("Authentication required"); + return; + }; + + // In a real application, you would send to specific client + // For this example, we'll just log the message + std.debug.print("Message from {s} to client {}: {s}\n", .{context.username, target_client, message}); + + // Send message to specific client (simulated) + const win = e.getWindow(); + var js_code: [513]u8 = undefined; // +1 for null terminator + const script = std.fmt.bufPrint(js_code[0..512], + "receiveMessage('{s}', '{s}');", .{context.username, message}) catch return; + + // Ensure null termination + js_code[script.len] = 0; + const null_terminated: [:0]const u8 = js_code[0..script.len :0]; + win.run(null_terminated); + + e.returnString("Message sent"); +} + +fn broadcastUserListUpdate() void { + // Create JSON list of online users + var users_json: [1024]u8 = undefined; + var json_content: []const u8 = undefined; + + if (online_users) |users| { + var stream = std.io.fixedBufferStream(&users_json); + var writer = stream.writer(); + + writer.writeAll("[") catch return; + for (users.items, 0..) |user, i| { + if (i > 0) writer.writeAll(",") catch return; + writer.print("{{\"clientId\":{},\"username\":\"{s}\"}}", .{user.client_id, user.username}) catch return; + } + writer.writeAll("]") catch return; + + json_content = stream.getWritten(); + } else { + json_content = "[]"; + } + + // Broadcast to all windows (this is a simplified approach) + // In a real implementation, you'd iterate through all active windows + std.debug.print("Broadcasting user list update: {s}\n", .{json_content}); + + // Note: This is a simplified version. In a full implementation, + // you would need to track all active windows and send to each one. +} + +fn getOnlineUsers(e: *webui.Event) void { + // Return list of online users as JSON + var users_json: [1024]u8 = undefined; + var json_content: []const u8 = undefined; + + if (online_users) |users| { + var stream = std.io.fixedBufferStream(&users_json); + var writer = stream.writer(); + + writer.writeAll("[") catch { + e.returnString("[]"); + return; + }; + for (users.items, 0..) |user, i| { + if (i > 0) writer.writeAll(",") catch continue; + writer.print("{{\"clientId\":{},\"username\":\"{s}\"}}", .{user.client_id, user.username}) catch continue; + } + writer.writeAll("]") catch { + e.returnString("[]"); + return; + }; + + json_content = stream.getWritten(); + } else { + json_content = "[]"; + } + + var response: [1025]u8 = undefined; + std.mem.copyForwards(u8, response[0..json_content.len], json_content); + response[json_content.len] = 0; + const null_terminated: [:0]const u8 = response[0..json_content.len :0]; + e.returnString(null_terminated); +} + +fn broadcastMessage(e: *webui.Event) void { + const message = e.getString(); + + // Get sender context from global storage + const context = getContextsMap().get(e.client_id) orelse { + e.returnString("Authentication required"); + return; + }; + + std.debug.print("Broadcast from {s}: {s}\n", .{context.username, message}); + + // Broadcast to all clients + const win = e.getWindow(); + var js_code: [513]u8 = undefined; // +1 for null terminator + const script = std.fmt.bufPrint(js_code[0..512], + "receiveBroadcast('{s}', '{s}');", .{context.username, message}) catch return; + + // Ensure null termination + js_code[script.len] = 0; + const null_terminated: [:0]const u8 = js_code[0..script.len :0]; + win.run(null_terminated); + + e.returnString("Message broadcasted to all clients"); +} + +fn clientConnect(e: *webui.Event) void { + std.debug.print("Client connected: ID={}, Connection={}\n", .{e.client_id, e.connection_id}); + + // Send welcome message to the specific client + e.runClient("showNotification('Connected to server');"); + + var response: [129]u8 = undefined; // +1 for null terminator + const msg = std.fmt.bufPrint(response[0..128], + "Client {} connected", .{e.client_id}) catch ""; + + // Ensure null termination + response[msg.len] = 0; + const null_terminated: [:0]const u8 = response[0..msg.len :0]; + e.returnString(null_terminated); +} + +fn clientDisconnect(e: *webui.Event) void { + std.debug.print("Client disconnected: ID={}, Connection={}\n", .{e.client_id, e.connection_id}); + + // Clean up any client-specific resources + e.closeClient(); +} + +fn universalEventHandler(e: *webui.Event) void { + // This handler catches all events + std.debug.print("Universal handler - Event: {s}, Client: {}, Type: {}\n", + .{e.element, e.client_id, e.event_type}); + + // You could implement global logging, analytics, or security checks here +} + +fn interfaceEventHandler( + window_handle: usize, + event_type: webui.EventKind, + element: []u8, + event_number: usize, + bind_id: usize, +) void { + std.debug.print("Interface handler - Window: {}, Type: {}, Element: {s}, Event: {}, Bind: {}\n", + .{window_handle, event_type, element, event_number, bind_id}); + + // Advanced event processing using interface API + const win = webui{ .window_handle = window_handle }; + + // Get event data using interface methods + const arg_count = win.interfaceGetSizeAt(event_number, 0); + if (arg_count > 0) { + const first_arg = win.interfaceGetStringAt(event_number, 0); + std.debug.print("First argument: {s}\n", .{first_arg}); + } + + // Set response using interface + win.interfaceSetResponse(event_number, "Interface handler processed event"); +} + +fn cleanupAllContexts() void { + // In a real application, you would maintain a list of all contexts + // and clean them up here + // Clean up all remaining user contexts + if (global_user_contexts) |*contexts| { + var iterator = contexts.iterator(); + while (iterator.next()) |entry| { + entry.value_ptr.*.destroy(); + } + contexts.clearAndFree(); + } + + std.debug.print("Cleaning up all user contexts...\n", .{}); +} diff --git a/examples/js_execution/index.html b/examples/js_execution/index.html new file mode 100644 index 0000000..d692f24 --- /dev/null +++ b/examples/js_execution/index.html @@ -0,0 +1,307 @@ + + + + + + JavaScript Execution Example + + + +
+

🚀 JavaScript Execution & Communication

+ +
+

Simple JavaScript Execution

+ +
Ready to execute JavaScript...
+
+ +
+

JavaScript with Response

+
+ + + + +
+
Select operation and click Calculate...
+
+ +
+

Complex JavaScript Processing

+
+ + +
+
Enter text and click Process...
+
+ +
+

Send Data to JavaScript

+
+ + + +
+
No data received yet...
+
+ +
+

Raw Binary Data

+
+ + bytes + +
+
Raw data display area...
+
+ +
+

Navigation

+
+ + + +
+ +
+ +
+

DOM Manipulation

+
+ + +
+
This content can be updated from Zig
+
+ +
+

JSON Data Exchange

+
+ + +
+
JSON processing result will appear here...
+
+
+ + + + \ No newline at end of file diff --git a/examples/js_execution/main.zig b/examples/js_execution/main.zig new file mode 100644 index 0000000..8e686dc --- /dev/null +++ b/examples/js_execution/main.zig @@ -0,0 +1,282 @@ +//! WebUI Zig - JavaScript Execution and Communication Example +//! This example demonstrates running JavaScript from Zig and advanced communication +const std = @import("std"); +const webui = @import("webui"); + +const html = @embedFile("index.html"); + +var allocator = std.heap.page_allocator; + +pub fn main() !void { + // Create window + var nwin = webui.newWindow(); + + // Set runtime for JavaScript execution + nwin.setRuntime(.NodeJS); + + // Bind functions for JavaScript execution + _ = try nwin.binding("run_simple_js", runSimpleJs); + _ = try nwin.binding("run_js_with_response", runJsWithResponse); + _ = try nwin.binding("run_complex_js", runComplexJs); + _ = try nwin.binding("send_data_to_js", sendDataToJs); + _ = try nwin.binding("send_raw_data", sendRawData); + _ = try nwin.binding("navigate_to_url", navigateToUrl); + _ = try nwin.binding("get_page_content", getPageContent); + _ = try nwin.binding("manipulate_dom", manipulateDom); + _ = try nwin.binding("handle_json_data", handleJsonData); + + // Show window + try nwin.show(html); + + // Wait for window to close + webui.wait(); + + // Clean up + webui.clean(); +} + +fn runSimpleJs(e: *webui.Event) void { + const win = e.getWindow(); + + // Run JavaScript without waiting for response + win.run("alert('Hello from Zig!'); console.log('JavaScript executed from Zig');"); + + e.returnString("Simple JavaScript executed"); + std.debug.print("Executed simple JavaScript\n", .{}); +} + +fn runJsWithResponse(e: *webui.Event, operation: [:0]const u8, a: i64, b: i64) void { + const win = e.getWindow(); + + // Prepare JavaScript code based on operation + var js_code: [256]u8 = undefined; + const script = switch (std.mem.eql(u8, operation, "add")) { + true => std.fmt.bufPrint(js_code[0..], "return {} + {};", .{a, b}), + false => switch (std.mem.eql(u8, operation, "multiply")) { + true => std.fmt.bufPrint(js_code[0..], "return {} * {};", .{a, b}), + false => switch (std.mem.eql(u8, operation, "power")) { + true => std.fmt.bufPrint(js_code[0..], "return Math.pow({}, {});", .{a, b}), + false => std.fmt.bufPrint(js_code[0..], "return 'Unknown operation';", .{}), + }, + }, + } catch return; + + // Execute JavaScript and get response + var response_buffer: [64]u8 = undefined; + // Ensure script is null-terminated by creating a proper null-terminated string + var null_terminated_script: [257]u8 = undefined; // +1 for null terminator + @memcpy(null_terminated_script[0..script.len], script); + null_terminated_script[script.len] = 0; + const script_z: [:0]const u8 = null_terminated_script[0..script.len :0]; + + win.script(script_z, 5, response_buffer[0..]) catch { + e.returnString("JavaScript execution failed"); + return; + }; + + // Find the null terminator + var response_len: usize = 0; + for (response_buffer) |c| { + if (c == 0) break; + response_len += 1; + } + + e.returnString(response_buffer[0..response_len :0]); + std.debug.print("JavaScript result: {s}\n", .{response_buffer[0..response_len]}); +} + +fn runComplexJs(e: *webui.Event, data: [:0]const u8) void { + const win = e.getWindow(); + + // Complex JavaScript that processes data and returns result + var js_code: [512]u8 = undefined; + const script = std.fmt.bufPrint(js_code[0..], + \\const data = '{s}'; + \\const result = {{ + \\ original: data, + \\ length: data.length, + \\ uppercase: data.toUpperCase(), + \\ words: data.split(' ').length, + \\ reversed: data.split('').reverse().join(''), + \\ timestamp: new Date().toISOString() + \\}}; + \\return JSON.stringify(result); + , .{data}) catch return; + + var response_buffer: [1024]u8 = undefined; + // Ensure script is null-terminated + var null_terminated_script: [513]u8 = undefined; // +1 for null terminator + @memcpy(null_terminated_script[0..script.len], script); + null_terminated_script[script.len] = 0; + const script_z: [:0]const u8 = null_terminated_script[0..script.len :0]; + + win.script(script_z, 10, response_buffer[0..]) catch { + e.returnString("Complex JavaScript execution failed"); + return; + }; + + // Find response length + var response_len: usize = 0; + for (response_buffer) |c| { + if (c == 0) break; + response_len += 1; + } + + e.returnString(response_buffer[0..response_len :0]); + std.debug.print("Complex JavaScript result: {s}\n", .{response_buffer[0..response_len]}); +} + +fn sendDataToJs(e: *webui.Event, message: [:0]const u8, value: i64) void { + const win = e.getWindow(); + + // Create data structure to send + var data: [256]u8 = undefined; + const json_data = std.fmt.bufPrint(data[0..], + "{{\"message\":\"{s}\",\"value\":{},\"timestamp\":{}}}", + .{message, value, std.time.timestamp()}) catch return; + + // Send data to JavaScript function + var js_call: [512]u8 = undefined; + const js_code = std.fmt.bufPrint(js_call[0..], + "receiveDataFromZig({s});", .{json_data}) catch return; + + // Ensure js_code is null-terminated + var null_terminated_js: [513]u8 = undefined; // +1 for null terminator + @memcpy(null_terminated_js[0..js_code.len], js_code); + null_terminated_js[js_code.len] = 0; + const js_code_z: [:0]const u8 = null_terminated_js[0..js_code.len :0]; + + win.run(js_code_z); + + e.returnString("Data sent to JavaScript"); + std.debug.print("Sent data to JavaScript: {s}\n", .{json_data}); +} + +fn sendRawData(e: *webui.Event, size: i64) void { + const win = e.getWindow(); + + // Create raw binary data + const data_size: usize = @intCast(@min(size, 1024)); + const raw_data = allocator.alloc(u8, data_size) catch return; + defer allocator.free(raw_data); + + // Fill with sample data + for (raw_data, 0..) |*byte, i| { + byte.* = @intCast(i % 256); + } + + // Send raw data to JavaScript + win.sendRaw("receiveRawData", raw_data); + + var response: [64]u8 = undefined; + const msg = std.fmt.bufPrint(response[0..], + "Sent {} bytes of raw data", .{data_size}) catch return; + + // Ensure msg is null-terminated + var null_terminated_msg: [65]u8 = undefined; // +1 for null terminator + @memcpy(null_terminated_msg[0..msg.len], msg); + null_terminated_msg[msg.len] = 0; + const msg_z: [:0]const u8 = null_terminated_msg[0..msg.len :0]; + + e.returnString(msg_z); + std.debug.print("Sent raw data: {} bytes\n", .{data_size}); +} + +fn navigateToUrl(e: *webui.Event, url: [:0]const u8) void { + const win = e.getWindow(); + + if (std.mem.startsWith(u8, url, "http://") or std.mem.startsWith(u8, url, "https://")) { + win.navigate(url); + e.returnString("Navigation initiated"); + std.debug.print("Navigating to: {s}\n", .{url}); + } else { + e.returnString("Invalid URL - must start with http:// or https://"); + } +} + +fn getPageContent(e: *webui.Event) void { + const win = e.getWindow(); + + const js_code = + \\return JSON.stringify({ + \\ title: document.title, + \\ url: window.location.href, + \\ userAgent: navigator.userAgent, + \\ cookies: document.cookie, + \\ elements: document.querySelectorAll('*').length + \\}); + ; + + var response_buffer: [2048]u8 = undefined; + win.script(js_code, 5, response_buffer[0..]) catch { + e.returnString("Failed to get page content"); + return; + }; + + // Find response length + var response_len: usize = 0; + for (response_buffer) |c| { + if (c == 0) break; + response_len += 1; + } + + e.returnString(response_buffer[0..response_len :0]); + std.debug.print("Page content: {s}\n", .{response_buffer[0..response_len]}); +} + +fn manipulateDom(e: *webui.Event, element_id: [:0]const u8, new_text: [:0]const u8) void { + const win = e.getWindow(); + + var js_code: [512]u8 = undefined; + const script = std.fmt.bufPrint(js_code[0..], + \\const element = document.getElementById('{s}'); + \\if (element) {{ + \\ element.innerHTML = '{s}'; + \\ element.style.background = 'rgba(0,255,0,0.2)'; + \\ setTimeout(() => element.style.background = '', 2000); + \\ return 'Element updated successfully'; + \\}} else {{ + \\ return 'Element not found'; + \\}} + , .{element_id, new_text}) catch return; + + var response_buffer: [128]u8 = undefined; + // Ensure script is null-terminated + var null_terminated_script: [513]u8 = undefined; // +1 for null terminator + @memcpy(null_terminated_script[0..script.len], script); + null_terminated_script[script.len] = 0; + const script_z: [:0]const u8 = null_terminated_script[0..script.len :0]; + + win.script(script_z, 5, response_buffer[0..]) catch { + e.returnString("DOM manipulation failed"); + return; + }; + + // Find response length + var response_len: usize = 0; + for (response_buffer) |c| { + if (c == 0) break; + response_len += 1; + } + + e.returnString(response_buffer[0..response_len :0]); + std.debug.print("DOM manipulation result: {s}\n", .{response_buffer[0..response_len]}); +} + +fn handleJsonData(e: *webui.Event, json_str: [:0]const u8) void { + // In a real application, you would parse the JSON properly + // For this example, we'll just echo back some processed info + + var response: [512]u8 = undefined; + const result = std.fmt.bufPrint(response[0..], + "Received JSON data: {s} (length: {})", .{json_str, json_str.len}) catch return; + + // Ensure result is null-terminated + var null_terminated_result: [513]u8 = undefined; // +1 for null terminator + @memcpy(null_terminated_result[0..result.len], result); + null_terminated_result[result.len] = 0; + const result_z: [:0]const u8 = null_terminated_result[0..result.len :0]; + + e.returnString(result_z); + std.debug.print("Processed JSON: {s}\n", .{json_str}); +} \ No newline at end of file diff --git a/examples/web_app_multi_client/main.zig b/examples/web_app_multi_client/main.zig index d16c150..d92d386 100644 --- a/examples/web_app_multi_client/main.zig +++ b/examples/web_app_multi_client/main.zig @@ -1,5 +1,6 @@ const std = @import("std"); const webui = @import("webui"); +const builtin = @import("builtin"); // general purpose allocator var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -40,10 +41,15 @@ fn saveAll(e: *webui.Event) void { public_input = allocator.dupe(u8, publicInput) catch unreachable; // general new js - const js = std.fmt.allocPrintZ( + const js = if (builtin.zig_version.minor == 14) std.fmt.allocPrintZ( allocator, "document.getElementById(\"publicInput\").value = \"{s}\";", .{publicInput}, + ) catch unreachable else std.fmt.allocPrintSentinel( + allocator, + "document.getElementById(\"publicInput\").value = \"{s}\";", + .{publicInput}, + 0, ) catch unreachable; // free js defer allocator.free(js); diff --git a/examples/window_management/index.html b/examples/window_management/index.html new file mode 100644 index 0000000..49d29fa --- /dev/null +++ b/examples/window_management/index.html @@ -0,0 +1,202 @@ + + + + + + Window Management Example + + + +
+

🖼️ Window Management Example

+ +
+

Basic Window Controls

+ + +
+ +
+

Window Modes

+ +
+ +
+

Browser Features

+ +
+ +
+

Window Size Control

+
+ + + + + +
+
+ +
+

Multiple Windows

+ +
+ +
+

Window Information

+ +
Click "Get Window Info" to see details
+
+ +
+

Result Display

+
+ Ready... +
+
+ +
+

💡 Browser Mode Tips

+
+

Keyboard Shortcuts:

+
    +
  • F11 - Toggle fullscreen
  • +
  • Ctrl + Shift + I - Developer tools
  • +
  • Ctrl + R - Refresh page
  • +
  • Alt + F4 - Close window (Windows)
  • +
  • Cmd + W - Close window (macOS)
  • +
  • Esc - Exit fullscreen/kiosk mode
  • +
+

Features:

+
    +
  • Kiosk Mode: WebUI native kiosk mode
  • +
  • Fullscreen: Standard fullscreen toggle
  • +
  • Window Info: Real-time browser and window details
  • +
+

Note: All features use WebUI native APIs.

+
+
+
+ + + + \ No newline at end of file diff --git a/examples/window_management/main.zig b/examples/window_management/main.zig new file mode 100644 index 0000000..a686886 --- /dev/null +++ b/examples/window_management/main.zig @@ -0,0 +1,196 @@ +//! WebUI Zig - Window Management Example +//! This example demonstrates various window management features +const std = @import("std"); +const webui = @import("webui"); + +const html = @embedFile("index.html"); + +pub fn main() !void { + // 配置WebUI全局设置以避免安全警告 + webui.setConfig(.show_wait_connection, true); + webui.setConfig(.use_cookies, true); + webui.setConfig(.multi_client, false); // 单客户端模式更安全 + + // Create multiple windows + var main_window = webui.newWindow(); + var second_window = webui.newWindow(); + + // Configure main window + main_window.setSize(800, 600); + main_window.setPosition(100, 100); + main_window.setCenter(); + main_window.setIcon("...", "image/svg+xml"); + + // Configure second window + second_window.setSize(400, 300); + second_window.setPosition(500, 200); + second_window.setKiosk(false); + second_window.setResizable(true); + + // Bind window control functions + _ = try main_window.binding("close_window", closeWindow); + _ = try main_window.binding("toggle_kiosk", toggleKiosk); + _ = try main_window.binding("get_window_info", getWindowInfo); + _ = try main_window.binding("open_second_window", openSecondWindow); + _ = try main_window.binding("set_window_size", setWindowSize); + _ = try main_window.binding("center_window", centerWindow); + _ = try main_window.binding("show_browser_info", showBrowserInfo); + + // Bind second window controls + _ = try second_window.binding("close_second", closeSecondWindow); + + + // Show main window + // 使用普通浏览器模式 + try main_window.show(html); + + // Wait for all windows to close + webui.wait(); + + // Clean up + webui.clean(); +} + +var main_win: ?webui = null; +var second_win: ?webui = null; +var is_kiosk = false; + +fn closeWindow(e: *webui.Event) void { + const win = e.getWindow(); + win.close(); + std.debug.print("Window closed\n", .{}); +} + +fn toggleKiosk(e: *webui.Event) void { + // 使用WebUI原生API设置kiosk模式 + const win = e.getWindow(); + is_kiosk = !is_kiosk; + win.setKiosk(is_kiosk); + + const response = if (is_kiosk) "Kiosk mode enabled" else "Kiosk mode disabled"; + e.returnString(response); + std.debug.print("Kiosk mode toggled: {}\n", .{is_kiosk}); +} + +fn getWindowInfo(e: *webui.Event) void { + const win = e.getWindow(); + + const port = win.getPort() catch 0; + const url = win.getUrl() catch ""; + const is_shown = win.isShown(); + + var buffer: [512]u8 = undefined; + const info = std.fmt.bufPrint(buffer[0..], + "Port: {}, URL: {s}, Shown: {}", .{port, url, is_shown}) catch ""; + + // 确保有足够空间容纳null终止符 + if (info.len < buffer.len) { + buffer[info.len] = 0; + e.returnString(buffer[0..info.len :0]); + } else { + // 如果缓冲区太小,返回错误信息 + const error_msg = "Buffer too small"; + e.returnString(error_msg); + } + std.debug.print("Window info: {s}\n", .{info}); +} + +fn openSecondWindow(e: *webui.Event) void { + // 检查第二个窗口是否存在且仍在运行 + if (second_win == null or (second_win != null and !second_win.?.isShown())) { + // 如果窗口已经被外部关闭,重置为null + if (second_win != null and !second_win.?.isShown()) { + std.debug.print("Detected second window was closed externally, resetting\n", .{}); + second_win = null; + } + + second_win = webui.newWindow(); + if (second_win) |*win| { + // 重新绑定第二个窗口的关闭函数 + _ = win.binding("close_second", closeSecondWindow) catch { + std.debug.print("Failed to bind close_second function\n", .{}); + e.returnString("Failed to bind second window functions"); + return; + }; + + // 绑定空的断开连接事件处理器,用于清理状态 + _ = win.binding("", handleSecondWindowDisconnect) catch { + std.debug.print("Failed to bind disconnect handler\n", .{}); + }; + + win.setSize(400, 300); + win.show("

Second Window

") catch { + std.debug.print("Failed to show second window\n", .{}); + e.returnString("Failed to show second window"); + return; + }; + e.returnString("Second window opened successfully"); + } else { + e.returnString("Failed to create second window"); + } + } else { + e.returnString("Second window already open"); + } +} + +fn closeSecondWindow(e: *webui.Event) void { + const win = e.getWindow(); + win.close(); + second_win = null; + std.debug.print("Second window closed\n", .{}); +} + +fn handleSecondWindowDisconnect(e: *webui.Event) void { + // 当第二个窗口断开连接时(包括用户点击x关闭),清理状态 + _ = e; // 标记参数已使用 + second_win = null; + std.debug.print("Second window disconnected\n", .{}); +} + +fn setWindowSize(e: *webui.Event, width: i64, height: i64) void { + const win = e.getWindow(); + win.setSize(@intCast(width), @intCast(height)); + + var buffer: [128]u8 = undefined; + const response = std.fmt.bufPrint(buffer[0..], + "Size set to {}x{}", .{width, height}) catch ""; + + // 确保有足够空间容纳null终止符 + if (response.len < buffer.len) { + buffer[response.len] = 0; + e.returnString(buffer[0..response.len :0]); + } else { + // 如果缓冲区太小,返回错误信息 + const error_msg = "Buffer too small"; + e.returnString(error_msg); + } + std.debug.print("Window size set to {}x{}\n", .{width, height}); +} + +fn centerWindow(e: *webui.Event) void { + const win = e.getWindow(); + win.setCenter(); + e.returnString("Window centered"); + std.debug.print("Window centered\n", .{}); +} + +fn showBrowserInfo(e: *webui.Event) void { + // 显示浏览器和窗口信息 + e.runClient( + \\const info = { + \\ userAgent: navigator.userAgent, + \\ screenWidth: screen.width, + \\ screenHeight: screen.height, + \\ windowWidth: window.innerWidth, + \\ windowHeight: window.innerHeight, + \\ isFullscreen: !!document.fullscreenElement + \\}; + \\document.getElementById('info-display').innerHTML = + \\ 'Browser: ' + info.userAgent.split(' ').pop() + '
' + + \\ 'Screen: ' + info.screenWidth + 'x' + info.screenHeight + '
' + + \\ 'Window: ' + info.windowWidth + 'x' + info.windowHeight + '
' + + \\ 'Fullscreen: ' + info.isFullscreen; + ); + std.debug.print("Browser info displayed\n", .{}); + e.returnString("Browser information updated"); +} diff --git a/src/c.zig b/src/c.zig index 4c1a43c..4f5d47f 100644 --- a/src/c.zig +++ b/src/c.zig @@ -13,7 +13,7 @@ const Runtime = webui.Runtime; /// @return Returns the window number. /// /// @example const my_window: usize = webui_new_window(); -pub extern fn webui_new_window() callconv(.C) usize; +pub extern fn webui_new_window() callconv(.c) usize; /// @brief Create a new webui window object using a specified window number. /// @@ -22,14 +22,14 @@ pub extern fn webui_new_window() callconv(.C) usize; /// @return Returns the same window number if success. /// /// @example const my_window: usize = webui_new_window_id(123); -pub extern fn webui_new_window_id(window_number: usize) callconv(.C) usize; +pub extern fn webui_new_window_id(window_number: usize) callconv(.c) usize; /// @brief Get a free window number that can be used with `webui_new_window_id()` /// /// @return Returns the first available free window number. Starting from 1. /// /// @example const my_window: usize = webui_get_new_window_id(); -pub extern fn webui_get_new_window_id() callconv(.C) usize; +pub extern fn webui_get_new_window_id() callconv(.c) usize; /// @brief Bind an HTML element and a Javascript object with a backend function. Empty /// element name means all events. @@ -44,8 +44,8 @@ pub extern fn webui_get_new_window_id() callconv(.C) usize; pub extern fn webui_bind( window: usize, element: [*:0]const u8, - func: *const fn (e: *Event) callconv(.C) void, -) callconv(.C) usize; + func: *const fn (e: *Event) callconv(.c) void, +) callconv(.c) usize; /// @brief Use this API after using `webui_bind()` to add any user data to it that can be /// read later using `webui_get_context()`. @@ -66,7 +66,7 @@ pub extern fn webui_set_context( window: usize, element: [*:0]const u8, context: *anyopaque, -) callconv(.C) void; +) callconv(.c) void; /// @brief Get user data that is set using `webui_set_context()`. /// @@ -84,7 +84,7 @@ pub extern fn webui_set_context( /// } pub extern fn webui_get_context( e: *Event, -) callconv(.C) ?*anyopaque; +) callconv(.c) ?*anyopaque; /// @brief Get the recommended web browser ID to use. If you /// are already using one, this function will return the same ID. @@ -94,7 +94,7 @@ pub extern fn webui_get_context( /// @return Returns a web browser ID. /// /// @example const browser_id: usize = webui_get_best_browser(my_window); -pub extern fn webui_get_best_browser(window: usize) callconv(.C) Browser; +pub extern fn webui_get_best_browser(window: usize) callconv(.c) Browser; /// @brief Show a window using embedded HTML, or a file. If the window is already /// open, it will be refreshed. This will refresh all windows in multi-client mode. @@ -106,7 +106,7 @@ pub extern fn webui_get_best_browser(window: usize) callconv(.C) Browser; /// /// @example webui_show(my_window, "..."); | /// webui_show(my_window, "index.html"); | webui_show(my_window, "http://..."); -pub extern fn webui_show(window: usize, content: [*:0]const u8) callconv(.C) bool; +pub extern fn webui_show(window: usize, content: [*:0]const u8) callconv(.c) bool; /// @brief Show a window using embedded HTML, or a file. If the window is already /// open, it will be refreshed. Single client. @@ -118,7 +118,7 @@ pub extern fn webui_show(window: usize, content: [*:0]const u8) callconv(.C) boo /// /// @example webui_show_client(e, "..."); | /// webui_show_client(e, "index.html"); | webui_show_client(e, "http://..."); -pub extern fn webui_show_client(e: *Event, content: [*:0]const u8) callconv(.C) bool; +pub extern fn webui_show_client(e: *Event, content: [*:0]const u8) callconv(.c) bool; /// @brief Same as `webui_show()`. But using a specific web browser. /// @@ -134,7 +134,7 @@ pub extern fn webui_show_browser( window: usize, content: [*:0]const u8, browser: Browser, -) callconv(.C) bool; +) callconv(.c) bool; /// @brief Same as `webui_show()`. But start only the web server and return the URL. /// No window will be shown. @@ -148,7 +148,7 @@ pub extern fn webui_show_browser( pub extern fn webui_start_server( window: usize, content: [*:0]const u8, -) callconv(.C) [*:0]const u8; +) callconv(.c) [*:0]const u8; /// @brief Show a WebView window using embedded HTML, or a file. If the window is already /// open, it will be refreshed. Note: Win32 need `WebView2Loader.dll`. @@ -160,7 +160,7 @@ pub extern fn webui_start_server( /// /// @example webui_show_wv(my_window, "..."); | webui_show_wv(my_window, /// "index.html"); | webui_show_wv(my_window, "http://..."); -pub extern fn webui_show_wv(window: usize, content: [*:0]const u8) callconv(.C) bool; +pub extern fn webui_show_wv(window: usize, content: [*:0]const u8) callconv(.c) bool; /// @brief Set the window in Kiosk mode (Full screen). /// @@ -168,7 +168,7 @@ pub extern fn webui_show_wv(window: usize, content: [*:0]const u8) callconv(.C) /// @param status True or False /// /// @example webui_set_kiosk(my_window, true); -pub extern fn webui_set_kiosk(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_kiosk(window: usize, status: bool) callconv(.c) void; /// @brief Add a user-defined web browser's CLI parameters. /// @@ -176,7 +176,7 @@ pub extern fn webui_set_kiosk(window: usize, status: bool) callconv(.C) void; /// @param params Command line parameters /// /// @example webui_set_custom_parameters(myWindow, "--remote-debugging-port=9222"); -pub extern fn webui_set_custom_parameters(window: usize, params: [*:0]const u8) callconv(.C) void; +pub extern fn webui_set_custom_parameters(window: usize, params: [*:0]const u8) callconv(.c) void; /// @brief Set the window with high-contrast support. Useful when you want to /// build a better high-contrast theme with CSS. @@ -185,7 +185,7 @@ pub extern fn webui_set_custom_parameters(window: usize, params: [*:0]const u8) /// @param status True or False /// /// @example webui_set_high_contrast(my_window, true); -pub extern fn webui_set_high_contrast(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_high_contrast(window: usize, status: bool) callconv(.c) void; /// @brief Sets whether the window frame is resizable or fixed. /// Works only on WebView window. @@ -194,26 +194,26 @@ pub extern fn webui_set_high_contrast(window: usize, status: bool) callconv(.C) /// @param status True or False /// /// @example webui_set_resizable(myWindow, true); -pub extern fn webui_set_resizable(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_resizable(window: usize, status: bool) callconv(.c) void; /// @brief Get OS high contrast preference. /// /// @return Returns True if OS is using high contrast theme /// /// @example const hc: bool = webui_is_high_contrast(); -pub extern fn webui_is_high_contrast() callconv(.C) bool; +pub extern fn webui_is_high_contrast() callconv(.c) bool; /// @brief Check if a web browser is installed. /// /// @return Returns True if the specified browser is available. /// /// @example const status: bool = webui_browser_exist(.Chrome); -pub extern fn webui_browser_exist(browser: Browser) callconv(.C) bool; +pub extern fn webui_browser_exist(browser: Browser) callconv(.c) bool; /// @brief Wait until all opened windows get closed. /// /// @example webui_wait(); -pub extern fn webui_wait() callconv(.C) void; +pub extern fn webui_wait() callconv(.c) void; /// @brief Close a specific window only. The window object will still exist. /// All clients. @@ -221,40 +221,40 @@ pub extern fn webui_wait() callconv(.C) void; /// @param The window number /// /// @example webui_close(my_window); -pub extern fn webui_close(window: usize) callconv(.C) void; +pub extern fn webui_close(window: usize) callconv(.c) void; /// @brief Minimize a WebView window. /// /// @param window The window number /// /// @example webui_minimize(myWindow); -pub extern fn webui_minimize(window: usize) callconv(.C) void; +pub extern fn webui_minimize(window: usize) callconv(.c) void; /// @brief Maximize a WebView window. /// /// @param window The window number /// /// @example webui_maximize(myWindow); -pub extern fn webui_maximize(window: usize) callconv(.C) void; +pub extern fn webui_maximize(window: usize) callconv(.c) void; /// @brief Close a specific client. /// /// @param e The event struct /// /// @example webui_close_client(e); -pub extern fn webui_close_client(e: *Event) callconv(.C) void; +pub extern fn webui_close_client(e: *Event) callconv(.c) void; /// @brief Close a specific window and free all memory resources. /// /// @param window The window number /// /// @example webui_destroy(my_window); -pub extern fn webui_destroy(window: usize) callconv(.C) void; +pub extern fn webui_destroy(window: usize) callconv(.c) void; /// @brief Close all open windows. `webui_wait()` will return (Break). /// /// @example webui_exit(); -pub extern fn webui_exit() callconv(.C) void; +pub extern fn webui_exit() callconv(.c) void; /// @brief Set the web-server root folder path for a specific window. /// @@ -262,14 +262,14 @@ pub extern fn webui_exit() callconv(.C) void; /// @param path The local folder full path /// /// @example webui_set_root_folder(my_window, "/home/Foo/Bar/"); -pub extern fn webui_set_root_folder(window: usize, path: [*:0]const u8) callconv(.C) bool; +pub extern fn webui_set_root_folder(window: usize, path: [*:0]const u8) callconv(.c) bool; /// @brief Set custom browser folder path. /// /// @param path The browser folder path /// /// @example webui_set_browser_folder("/home/Foo/Bar/"); -pub extern fn webui_set_browser_folder(path: [*:0]const u8) callconv(.C) void; +pub extern fn webui_set_browser_folder(path: [*:0]const u8) callconv(.c) void; /// @brief Set the web-server root folder path for all windows. Should be used /// before `webui_show()`. @@ -277,7 +277,7 @@ pub extern fn webui_set_browser_folder(path: [*:0]const u8) callconv(.C) void; /// @param path The local folder full path /// /// @example webui_set_default_root_folder("/home/Foo/Bar/"); -pub extern fn webui_set_default_root_folder(path: [*:0]const u8) callconv(.C) bool; +pub extern fn webui_set_default_root_folder(path: [*:0]const u8) callconv(.c) bool; /// @brief Set a custom handler to serve files. This custom handler should /// return full HTTP header and body. @@ -290,8 +290,8 @@ pub extern fn webui_set_default_root_folder(path: [*:0]const u8) callconv(.C) bo /// @example webui_set_file_handler(my_window, myHandlerFunction); pub extern fn webui_set_file_handler( window: usize, - handler: *const fn (filename: [*:0]const u8, length: *c_int) callconv(.C) ?*const anyopaque, -) callconv(.C) void; + handler: *const fn (filename: [*:0]const u8, length: *c_int) callconv(.c) ?*const anyopaque, +) callconv(.c) void; /// @brief Set a custom handler to serve files. This custom handler should /// return full HTTP header and body. @@ -308,8 +308,8 @@ pub extern fn webui_set_file_handler_window( window: usize, filename: [*:0]const u8, length: *c_int, - ) callconv(.C) ?*const anyopaque, -) callconv(.C) void; + ) callconv(.c) ?*const anyopaque, +) callconv(.c) void; /// /// @brief Use this API to set a file handler response if your backend need async @@ -325,14 +325,14 @@ pub extern fn webui_interface_set_response_file_handler( window: usize, response: *const anyopaque, length: usize, -) callconv(.C) void; +) callconv(.c) void; /// @brief Check if the specified window is still running. /// /// @param window The window number /// /// @example webui_is_shown(my_window); -pub extern fn webui_is_shown(window: usize) callconv(.C) bool; +pub extern fn webui_is_shown(window: usize) callconv(.c) bool; /// @brief Set the maximum time in seconds to wait for the window to connect. /// This effect `show()` and `wait()`. Value of `0` means wait forever. @@ -340,7 +340,7 @@ pub extern fn webui_is_shown(window: usize) callconv(.C) bool; /// @param second The timeout in seconds /// /// @example webui_set_timeout(30); -pub extern fn webui_set_timeout(second: usize) callconv(.C) void; +pub extern fn webui_set_timeout(second: usize) callconv(.c) void; /// @brief Set the default embedded HTML favicon. /// @@ -353,7 +353,7 @@ pub extern fn webui_set_icon( window: usize, icon: [*:0]const u8, icon_type: [*:0]const u8, -) callconv(.C) void; +) callconv(.c) void; /// @brief Encode text to Base64. The returned buffer need to be freed. /// @@ -362,7 +362,7 @@ pub extern fn webui_set_icon( /// @return Returns the base64 encoded string /// /// @example const base64: [*:0]u8 = webui_encode("Foo Bar"); -pub extern fn webui_encode(str: [*:0]const u8) callconv(.C) ?[*:0]u8; +pub extern fn webui_encode(str: [*:0]const u8) callconv(.c) ?[*:0]u8; /// @brief Decode a Base64 encoded text. The returned buffer need to be freed. /// @@ -371,14 +371,14 @@ pub extern fn webui_encode(str: [*:0]const u8) callconv(.C) ?[*:0]u8; /// @return Returns the base64 decoded string /// /// @example const str: [*:0]u8 = webui_decode("SGVsbG8="); -pub extern fn webui_decode(str: [*:0]const u8) callconv(.C) ?[*:0]u8; +pub extern fn webui_decode(str: [*:0]const u8) callconv(.c) ?[*:0]u8; /// @brief Safely free a buffer allocated by WebUI using `webui_malloc()`. /// /// @param ptr The buffer to be freed /// /// @example webui_free(my_buffer); -pub extern fn webui_free(ptr: *anyopaque) callconv(.C) void; +pub extern fn webui_free(ptr: *anyopaque) callconv(.c) void; /// @brief Copy raw data. /// @@ -391,7 +391,7 @@ pub extern fn webui_memcpy( dest: *anyopaque, src: *anyopaque, count: usize, -) callconv(.C) void; +) callconv(.c) void; /// @brief Safely allocate memory using the WebUI memory management system. It /// can be safely freed using `webui_free()` at any time. @@ -399,7 +399,7 @@ pub extern fn webui_memcpy( /// @param size The size of memory in bytes /// /// @example var my_buffer: [*:0]u8 = @ptrCast(@alignCast(webui_malloc(1024))); -pub extern fn webui_malloc(size: usize) callconv(.C) ?*anyopaque; +pub extern fn webui_malloc(size: usize) callconv(.c) ?*anyopaque; /// @brief Safely send raw data to the UI. All clients. /// @@ -413,9 +413,9 @@ pub extern fn webui_malloc(size: usize) callconv(.C) ?*anyopaque; pub extern fn webui_send_raw( window: usize, function: [*:0]const u8, - raw: [*]const anyopaque, + raw: *const anyopaque, size: usize, -) callconv(.C) void; +) callconv(.c) void; /// @brief Safely send raw data to the UI. Single client. /// @@ -429,9 +429,9 @@ pub extern fn webui_send_raw( pub extern fn webui_send_raw_client( e: *Event, function: [*:0]const u8, - raw: [*]const anyopaque, + raw: *const anyopaque, size: usize, -) callconv(.C) void; +) callconv(.c) void; /// @brief Set a window in hidden mode. Should be called before `webui_show()`. /// @@ -439,7 +439,7 @@ pub extern fn webui_send_raw_client( /// @param status The status: True or False /// /// @example webui_set_hide(my_window, true); -pub extern fn webui_set_hide(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_hide(window: usize, status: bool) callconv(.c) void; /// @brief Set the window size. /// @@ -448,7 +448,7 @@ pub extern fn webui_set_hide(window: usize, status: bool) callconv(.C) void; /// @param height The window height /// /// @example webui_set_size(my_window, 800, 600); -pub extern fn webui_set_size(window: usize, width: u32, height: u32) callconv(.C) void; +pub extern fn webui_set_size(window: usize, width: u32, height: u32) callconv(.c) void; /// @brief Set the window minimum size. /// @@ -461,7 +461,7 @@ pub extern fn webui_set_minimum_size( window: usize, width: u32, height: u32, -) callconv(.C) void; +) callconv(.c) void; /// @brief Set the window position. /// @@ -470,7 +470,7 @@ pub extern fn webui_set_minimum_size( /// @param y The window Y /// /// @example webui_set_position(my_window, 100, 100); -pub extern fn webui_set_position(window: usize, x: u32, y: u32) callconv(.C) void; +pub extern fn webui_set_position(window: usize, x: u32, y: u32) callconv(.c) void; /// @brief Centers the window on the screen. Works better with /// WebView. Call this function before `webui_show()` for better results. @@ -478,7 +478,7 @@ pub extern fn webui_set_position(window: usize, x: u32, y: u32) callconv(.C) voi /// @param window The window number /// /// @example webui_set_center(myWindow); -pub extern fn webui_set_center(window: usize) callconv(.C) void; +pub extern fn webui_set_center(window: usize) callconv(.c) void; /// @brief Set the web browser profile to use. An empty `name` and `path` means /// the default user profile. Need to be called before `webui_show()`. @@ -493,7 +493,7 @@ pub extern fn webui_set_profile( window: usize, name: [*:0]const u8, path: [*:0]const u8, -) callconv(.C) void; +) callconv(.c) void; /// @brief Set the web browser proxy server to use. Need to be called before `webui_show()`. /// @@ -504,7 +504,7 @@ pub extern fn webui_set_profile( pub extern fn webui_set_proxy( window: usize, proxy_server: [*:0]const u8, -) callconv(.C) void; +) callconv(.c) void; /// @brief Get current URL of a running window. /// @@ -513,14 +513,14 @@ pub extern fn webui_set_proxy( /// @return Returns the full URL string /// /// @example const url: [*:0]const u8 = webui_get_url(my_window); -pub extern fn webui_get_url(window: usize) callconv(.C) [*:0]const u8; +pub extern fn webui_get_url(window: usize) callconv(.c) [*:0]const u8; /// @brief Open an URL in the native default web browser. /// /// @param url The URL to open /// /// @example webui_open_url("https://webui.me"); -pub extern fn webui_open_url(url: [*:0]const u8) callconv(.C) void; +pub extern fn webui_open_url(url: [*:0]const u8) callconv(.c) void; /// @brief Allow a specific window address to be accessible from a public network. /// @@ -528,7 +528,7 @@ pub extern fn webui_open_url(url: [*:0]const u8) callconv(.C) void; /// @param status True or False /// /// @example webui_set_public(my_window, true); -pub extern fn webui_set_public(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_public(window: usize, status: bool) callconv(.c) void; /// @brief Navigate to a specific URL. All clients. /// @@ -536,7 +536,7 @@ pub extern fn webui_set_public(window: usize, status: bool) callconv(.C) void; /// @param url Full HTTP URL /// /// @example webui_navigate(my_window, "http://domain.com"); -pub extern fn webui_navigate(window: usize, url: [*:0]const u8) callconv(.C) void; +pub extern fn webui_navigate(window: usize, url: [*:0]const u8) callconv(.c) void; /// @brief Navigate to a specific URL. Single client. /// @@ -544,14 +544,14 @@ pub extern fn webui_navigate(window: usize, url: [*:0]const u8) callconv(.C) voi /// @param url Full HTTP URL /// /// @example webui_navigate_client(e, "http://domain.com"); -pub extern fn webui_navigate_client(e: *Event, url: [*:0]const u8) callconv(.C) void; +pub extern fn webui_navigate_client(e: *Event, url: [*:0]const u8) callconv(.c) void; /// @brief Free all memory resources. Should be called only at the end. /// /// @example /// webui_wait(); /// webui_clean(); -pub extern fn webui_clean() callconv(.C) void; +pub extern fn webui_clean() callconv(.c) void; /// @brief Delete all local web-browser profiles folder. It should be called at the /// end. @@ -560,7 +560,7 @@ pub extern fn webui_clean() callconv(.C) void; /// webui_wait(); /// webui_delete_all_profiles(); /// webui_clean(); -pub extern fn webui_delete_all_profiles() callconv(.C) void; +pub extern fn webui_delete_all_profiles() callconv(.c) void; /// @brief Delete a specific window web-browser local folder profile. /// @@ -573,7 +573,7 @@ pub extern fn webui_delete_all_profiles() callconv(.C) void; /// /// @note This can break functionality of other windows if using the same /// web-browser. -pub extern fn webui_delete_profile(window: usize) callconv(.C) void; +pub extern fn webui_delete_profile(window: usize) callconv(.c) void; /// @brief Get the ID of the parent process (The web browser may re-create /// another new process). @@ -583,7 +583,7 @@ pub extern fn webui_delete_profile(window: usize) callconv(.C) void; /// @return Returns the parent process ID as integer /// /// @example const id: usize = webui_get_parent_process_id(my_window); -pub extern fn webui_get_parent_process_id(window: usize) callconv(.C) usize; +pub extern fn webui_get_parent_process_id(window: usize) callconv(.c) usize; /// @brief Get the ID of the last child process. /// @@ -592,7 +592,7 @@ pub extern fn webui_get_parent_process_id(window: usize) callconv(.C) usize; /// @return Returns the child process ID as integer /// /// @example const id: usize = webui_get_child_process_id(my_window); -pub extern fn webui_get_child_process_id(window: usize) callconv(.C) usize; +pub extern fn webui_get_child_process_id(window: usize) callconv(.c) usize; /// @brief Gets Win32 window `HWND`. More reliable with WebView /// than web browser window, as browser PIDs may change on launch. @@ -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` @@ -612,7 +612,7 @@ pub extern fn webui_win32_get_hwnd(window: usize) callconv(.C) ?*anyopaque; /// @return Returns the network port of the window /// /// @example const port: usize = webui_get_port(my_window); -pub extern fn webui_get_port(window: usize) callconv(.C) usize; +pub extern fn webui_get_port(window: usize) callconv(.c) usize; /// @brief Set a custom web-server/websocket network port to be used by WebUI. /// This can be useful to determine the HTTP link of `webui.js` in case @@ -624,14 +624,14 @@ pub extern fn webui_get_port(window: usize) callconv(.C) usize; /// @return Returns True if the port is free and usable by WebUI /// /// @example const ret: bool = webui_set_port(my_window, 8080); -pub extern fn webui_set_port(window: usize, port: usize) callconv(.C) bool; +pub extern fn webui_set_port(window: usize, port: usize) callconv(.c) bool; /// @brief Get an available usable free network port. /// /// @return Returns a free port /// /// @example const port: usize = webui_get_free_port(); -pub extern fn webui_get_free_port() callconv(.C) usize; +pub extern fn webui_get_free_port() callconv(.c) usize; /// @brief Control the WebUI behaviour. It's recommended to be called at the beginning. /// @@ -639,7 +639,7 @@ pub extern fn webui_get_free_port() callconv(.C) usize; /// @param status The status of the option, `true` or `false` /// /// @example webui_set_config(.show_wait_connection, false); -pub extern fn webui_set_config(option: Config, status: bool) callconv(.C) void; +pub extern fn webui_set_config(option: Config, status: bool) callconv(.c) void; /// @brief Control if UI events coming from this window should be processed /// one at a time in a single blocking thread `True`, or process every event in @@ -650,7 +650,7 @@ pub extern fn webui_set_config(option: Config, status: bool) callconv(.C) void; /// @param status The blocking status `true` or `false` /// /// @example webui_set_event_blocking(my_window, true); -pub extern fn webui_set_event_blocking(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_event_blocking(window: usize, status: bool) callconv(.c) void; /// @brief Make a WebView window frameless. /// @@ -658,7 +658,7 @@ pub extern fn webui_set_event_blocking(window: usize, status: bool) callconv(.C) /// @param status The frameless status `true` or `false` /// /// @example webui_set_frameless(myWindow, true); -pub extern fn webui_set_frameless(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_frameless(window: usize, status: bool) callconv(.c) void; /// @brief Make a WebView window transparent. /// @@ -666,14 +666,14 @@ pub extern fn webui_set_frameless(window: usize, status: bool) callconv(.C) void /// @param status The transparency status `true` or `false` /// /// @example webui_set_transparent(myWindow, true); -pub extern fn webui_set_transparent(window: usize, status: bool) callconv(.C) void; +pub extern fn webui_set_transparent(window: usize, status: bool) callconv(.c) void; /// @brief Get the HTTP mime type of a file. /// /// @return Returns the HTTP mime string /// /// @example const mime: [*:0]const u8 = webui_get_mime_type("foo.png"); -pub extern fn webui_get_mime_type(file: [*:0]const u8) callconv(.C) [*:0]const u8; +pub extern fn webui_get_mime_type(file: [*:0]const u8) callconv(.c) [*:0]const u8; // -- SSL/TLS ------------------------- @@ -691,7 +691,7 @@ pub extern fn webui_get_mime_type(file: [*:0]const u8) callconv(.C) [*:0]const u pub extern fn webui_set_tls_certificate( certificate_pem: [*:0]const u8, private_key_pem: [*:0]const u8, -) callconv(.C) bool; +) callconv(.c) bool; // -- JavaScript ---------------------- @@ -701,7 +701,7 @@ pub extern fn webui_set_tls_certificate( /// @param script The JavaScript to be run /// /// @example webui_run(my_window, "alert('Hello');"); -pub extern fn webui_run(window: usize, script: [*:0]const u8) callconv(.C) void; +pub extern fn webui_run(window: usize, script: [*:0]const u8) callconv(.c) void; /// @brief Run JavaScript without waiting for the response. Single client. /// @@ -709,7 +709,7 @@ pub extern fn webui_run(window: usize, script: [*:0]const u8) callconv(.C) void; /// @param script The JavaScript to be run /// /// @example webui_run_client(e, "alert('Hello');"); -pub extern fn webui_run_client(e: *Event, script: [*:0]const u8) callconv(.C) void; +pub extern fn webui_run_client(e: *Event, script: [*:0]const u8) callconv(.c) void; /// @brief Run JavaScript and get the response back. Work only in single client mode. /// Make sure your local buffer can hold the response. @@ -729,7 +729,7 @@ pub extern fn webui_script( timeout: usize, buffer: [*]u8, buffer_length: usize, -) callconv(.C) bool; +) callconv(.c) bool; /// @brief Run JavaScript and get the response back. Single Client. /// Make sure your local buffer can hold the response. @@ -749,7 +749,7 @@ pub extern fn webui_script_client( timeout: usize, buffer: [*]u8, buffer_length: usize, -) callconv(.C) bool; +) callconv(.c) bool; /// @brief Choose between Deno and Nodejs as runtime for .js and .ts files. /// @@ -757,7 +757,7 @@ pub extern fn webui_script_client( /// @param runtime .Deno | .Bun | .Nodejs | .None /// /// @example webui_set_runtime(my_window, .Deno); -pub extern fn webui_set_runtime(window: usize, runtime: Runtime) callconv(.C) void; +pub extern fn webui_set_runtime(window: usize, runtime: Runtime) callconv(.c) void; /// @brief Get how many arguments there are in an event. /// @@ -766,7 +766,7 @@ pub extern fn webui_set_runtime(window: usize, runtime: Runtime) callconv(.C) vo /// @return Returns the arguments count. /// /// @example const count: usize = webui_get_count(e); -pub extern fn webui_get_count(e: *Event) callconv(.C) usize; +pub extern fn webui_get_count(e: *Event) callconv(.c) usize; /// @brief Get an argument as integer at a specific index. /// @@ -776,7 +776,7 @@ pub extern fn webui_get_count(e: *Event) callconv(.C) usize; /// @return Returns argument as integer /// /// @example const my_num: i64 = webui_get_int_at(e, 0); -pub extern fn webui_get_int_at(e: *Event, index: usize) callconv(.C) i64; +pub extern fn webui_get_int_at(e: *Event, index: usize) callconv(.c) i64; /// @brief Get the first argument as integer. /// @@ -785,7 +785,7 @@ pub extern fn webui_get_int_at(e: *Event, index: usize) callconv(.C) i64; /// @return Returns argument as integer /// /// @example const my_num: i64 = webui_get_int(e); -pub extern fn webui_get_int(e: *Event) callconv(.C) i64; +pub extern fn webui_get_int(e: *Event) callconv(.c) i64; /// @brief Get an argument as float at a specific index. /// @@ -795,7 +795,7 @@ pub extern fn webui_get_int(e: *Event) callconv(.C) i64; /// @return Returns argument as float /// /// @example const my_num: f64 = webui_get_float_at(e, 0); -pub extern fn webui_get_float_at(e: *Event, index: usize) callconv(.C) f64; +pub extern fn webui_get_float_at(e: *Event, index: usize) callconv(.c) f64; /// @brief Get the first argument as float. /// @@ -804,7 +804,7 @@ pub extern fn webui_get_float_at(e: *Event, index: usize) callconv(.C) f64; /// @return Returns argument as float /// /// @example const my_num: f64 = webui_get_float(e); -pub extern fn webui_get_float(e: *Event) callconv(.C) f64; +pub extern fn webui_get_float(e: *Event) callconv(.c) f64; /// @brief Get an argument as string at a specific index. /// @@ -814,7 +814,7 @@ pub extern fn webui_get_float(e: *Event) callconv(.C) f64; /// @return Returns argument as string /// /// @example const my_str: [*:0]const u8 = webui_get_string_at(e, 0); -pub extern fn webui_get_string_at(e: *Event, index: usize) callconv(.C) [*:0]const u8; +pub extern fn webui_get_string_at(e: *Event, index: usize) callconv(.c) [*:0]const u8; /// @brief Get the first argument as string. /// @@ -823,7 +823,7 @@ pub extern fn webui_get_string_at(e: *Event, index: usize) callconv(.C) [*:0]con /// @return Returns argument as string /// /// @example const my_str: [*:0]const u8 = webui_get_string(e); -pub extern fn webui_get_string(e: *Event) callconv(.C) [*:0]const u8; +pub extern fn webui_get_string(e: *Event) callconv(.c) [*:0]const u8; /// @brief Get an argument as boolean at a specific index. /// @@ -833,7 +833,7 @@ pub extern fn webui_get_string(e: *Event) callconv(.C) [*:0]const u8; /// @return Returns argument as boolean /// /// @example const my_bool: bool = webui_get_bool_at(e, 0); -pub extern fn webui_get_bool_at(e: *Event, index: usize) callconv(.C) bool; +pub extern fn webui_get_bool_at(e: *Event, index: usize) callconv(.c) bool; /// @brief Get the first argument as boolean. /// @@ -842,7 +842,7 @@ pub extern fn webui_get_bool_at(e: *Event, index: usize) callconv(.C) bool; /// @return Returns argument as boolean /// /// @example const my_bool: bool = webui_get_bool(e); -pub extern fn webui_get_bool(e: *Event) callconv(.C) bool; +pub extern fn webui_get_bool(e: *Event) callconv(.c) bool; /// @brief Get the size in bytes of an argument at a specific index. /// @@ -852,7 +852,7 @@ pub extern fn webui_get_bool(e: *Event) callconv(.C) bool; /// @return Returns size in bytes /// /// @example const arg_len: usize = webui_get_size_at(e, 0); -pub extern fn webui_get_size_at(e: *Event, index: usize) callconv(.C) usize; +pub extern fn webui_get_size_at(e: *Event, index: usize) callconv(.c) usize; /// @brief Get size in bytes of the first argument. /// @@ -861,7 +861,7 @@ pub extern fn webui_get_size_at(e: *Event, index: usize) callconv(.C) usize; /// @return Returns size in bytes /// /// @example const arg_len: usize = webui_get_size(e); -pub extern fn webui_get_size(e: *Event) callconv(.C) usize; +pub extern fn webui_get_size(e: *Event) callconv(.c) usize; /// @brief Return the response to JavaScript as integer. /// @@ -869,7 +869,7 @@ pub extern fn webui_get_size(e: *Event) callconv(.C) usize; /// @param n The integer to be send to JavaScript /// /// @example webui_return_int(e, 123); -pub extern fn webui_return_int(e: *Event, n: i64) callconv(.C) void; +pub extern fn webui_return_int(e: *Event, n: i64) callconv(.c) void; /// @brief Return the response to JavaScript as float. /// @@ -877,7 +877,7 @@ pub extern fn webui_return_int(e: *Event, n: i64) callconv(.C) void; /// @param f The float number to be send to JavaScript /// /// @example webui_return_float(e, 123.456); -pub extern fn webui_return_float(e: *Event, f: f64) callconv(.C) void; +pub extern fn webui_return_float(e: *Event, f: f64) callconv(.c) void; /// @brief Return the response to JavaScript as string. /// @@ -885,7 +885,7 @@ pub extern fn webui_return_float(e: *Event, f: f64) callconv(.C) void; /// @param n The string to be send to JavaScript /// /// @example webui_return_string(e, "Response..."); -pub extern fn webui_return_string(e: *Event, s: [*:0]const u8) callconv(.C) void; +pub extern fn webui_return_string(e: *Event, s: [*:0]const u8) callconv(.c) void; /// @brief Return the response to JavaScript as boolean. /// @@ -893,17 +893,17 @@ pub extern fn webui_return_string(e: *Event, s: [*:0]const u8) callconv(.C) void /// @param n The boolean to be send to JavaScript /// /// @example webui_return_bool(e, true); -pub extern fn webui_return_bool(e: *Event, b: bool) callconv(.C) void; +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; +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; +pub extern fn webui_get_last_error_message() callconv(.c) [*:0]const u8; // -- Wrapper's Interface ------------- @@ -925,8 +925,8 @@ pub extern fn webui_interface_bind( element: [*:0]u8, event_number: usize, bind_id: usize, - ) void, -) callconv(.C) usize; + ) callconv(.c) void, +) callconv(.c) usize; /// @brief When using `webui_interface_bind()`, you may need this function to easily set a response. /// @@ -939,14 +939,14 @@ pub extern fn webui_interface_set_response( window: usize, event_number: usize, response: [*:0]const u8, -) callconv(.C) void; +) callconv(.c) void; /// @brief Check if the app is still running. /// /// @return Returns True if app is running /// /// @example const status: bool = webui_interface_is_app_running(); -pub extern fn webui_interface_is_app_running() callconv(.C) bool; +pub extern fn webui_interface_is_app_running() callconv(.c) bool; /// @brief Get a unique window ID. /// @@ -955,7 +955,7 @@ pub extern fn webui_interface_is_app_running() callconv(.C) bool; /// @return Returns the unique window ID as integer /// /// @example const id: usize = webui_interface_get_window_id(my_window); -pub extern fn webui_interface_get_window_id(window: usize) callconv(.C) usize; +pub extern fn webui_interface_get_window_id(window: usize) callconv(.c) usize; /// @brief Get an argument as string at a specific index. /// @@ -970,7 +970,7 @@ pub extern fn webui_interface_get_string_at( window: usize, event_number: usize, index: usize, -) callconv(.C) [*:0]const u8; +) callconv(.c) [*:0]const u8; /// @brief Get an argument as integer at a specific index. /// @@ -985,7 +985,7 @@ pub extern fn webui_interface_get_int_at( window: usize, event_number: usize, index: usize, -) callconv(.C) i64; +) callconv(.c) i64; /// @brief Get an argument as float at a specific index. /// @@ -1000,7 +1000,7 @@ pub extern fn webui_interface_get_float_at( window: usize, event_number: usize, index: usize, -) callconv(.C) f64; +) callconv(.c) f64; /// @brief Get an argument as boolean at a specific index. /// @@ -1015,7 +1015,7 @@ pub extern fn webui_interface_get_bool_at( window: usize, event_number: usize, index: usize, -) callconv(.C) bool; +) callconv(.c) bool; /// @brief Get the size in bytes of an argument at a specific index. /// @@ -1030,7 +1030,7 @@ pub extern fn webui_interface_get_size_at( window: usize, event_number: usize, index: usize, -) callconv(.C) usize; +) callconv(.c) usize; /// @brief Show a window using embedded HTML, or a file. If the window is already /// open, it will be refreshed. Single client. @@ -1047,7 +1047,7 @@ pub extern fn webui_interface_show_client( window: usize, event_number: usize, content: [*:0]const u8, -) callconv(.C) bool; +) callconv(.c) bool; /// @brief Close a specific client. /// @@ -1058,7 +1058,7 @@ pub extern fn webui_interface_show_client( pub extern fn webui_interface_close_client( window: usize, event_number: usize, -) callconv(.C) void; +) callconv(.c) void; /// @brief Safely send raw data to the UI. Single client. /// @@ -1076,7 +1076,7 @@ pub extern fn webui_interface_send_raw_client( function: [*:0]const u8, raw: [*c]const u8, size: usize, -) callconv(.C) void; +) callconv(.c) void; /// @brief Navigate to a specific URL. Single client. /// @@ -1089,7 +1089,7 @@ pub extern fn webui_interface_navigate_client( window: usize, event_number: usize, url: [*:0]const u8, -) callconv(.C) void; +) callconv(.c) void; /// @brief Run JavaScript without waiting for the response. Single client. /// @@ -1102,7 +1102,7 @@ pub extern fn webui_interface_run_client( window: usize, event_number: usize, script: [*:0]const u8, -) callconv(.C) void; +) callconv(.c) void; /// @brief Run JavaScript and get the response back. Single client. /// Make sure your local buffer can hold the response. @@ -1124,4 +1124,4 @@ pub extern fn webui_interface_script_client( timeout: usize, buffer: [*c]u8, buffer_length: usize, -) callconv(.C) bool; +) callconv(.c) bool; diff --git a/src/webui.zig b/src/webui.zig index f4fd648..e32c848 100644 --- a/src/webui.zig +++ b/src/webui.zig @@ -104,7 +104,7 @@ pub fn bind( comptime func: fn (e: *Event) void, ) !usize { const tmp_struct = struct { - fn handle(tmp_e: *Event) callconv(.C) void { + fn handle(tmp_e: *Event) callconv(.c) void { func(tmp_e); } }; @@ -231,8 +231,8 @@ pub fn setRootFolder(self: webui, path: [:0]const u8) !void { } /// Set custom browser folder path. -pub fn setBrowserFolder(self: webui, path: [:0]const u8) void { - c.webui_set_browser_folder(self.window_handle, path.ptr); +pub fn setBrowserFolder(path: [:0]const u8) void { + c.webui_set_browser_folder(path.ptr); } /// Set the web-server root folder path for all windows. @@ -248,7 +248,7 @@ pub fn setDefaultRootFolder(path: [:0]const u8) !void { /// This deactivates any previous handler set with `setFileHandlerWindow`. pub fn setFileHandler(self: webui, comptime handler: fn (filename: []const u8) ?[]const u8) void { const tmp_struct = struct { - fn handle(tmp_filename: [*:0]const u8, length: *c_int) callconv(.C) ?*const anyopaque { + fn handle(tmp_filename: [*:0]const u8, length: *c_int) callconv(.c) ?*const anyopaque { const len = std.mem.len(tmp_filename); const content = handler(tmp_filename[0..len]); if (content) |val| { @@ -267,7 +267,7 @@ pub fn setFileHandler(self: webui, comptime handler: fn (filename: []const u8) ? /// This deactivates any previous handler set with `setFileHandler`. pub fn setFileHandlerWindow(self: webui, comptime handler: fn (window_handle: usize, filename: []const u8) ?[]const u8) void { const tmp_struct = struct { - fn handle(window: usize, tmp_filename: [*:0]const u8, length: *c_int) callconv(.C) ?*const anyopaque { + fn handle(window: usize, tmp_filename: [*:0]const u8, length: *c_int) callconv(.c) ?*const anyopaque { const len = std.mem.len(tmp_filename); const content = handler(window, tmp_filename[0..len]); if (content) |val| { @@ -312,9 +312,12 @@ pub fn setIcon(self: webui, icon: [:0]const u8, icon_type: [:0]const u8) void { /// you need free the return memory with free function pub fn encode(str: [:0]const u8) ![]u8 { const ptr = c.webui_encode(str.ptr); - if (ptr == null) return WebUIError.EncodeError; - const len = std.mem.len(ptr); - return ptr[0..len]; + if (ptr) |valid_ptr| { + const len = std.mem.len(valid_ptr); + return valid_ptr[0..len]; + } else { + return WebUIError.EncodeError; + } } /// Base64 decoding. @@ -323,9 +326,12 @@ pub fn encode(str: [:0]const u8) ![]u8 { /// you need free the return memory with free function pub fn decode(str: [:0]const u8) ![]u8 { const ptr = c.webui_decode(str.ptr); - if (ptr == null) return WebUIError.DecodeError; - const len = std.mem.len(ptr); - return ptr[0..len]; + if (ptr) |valid_ptr| { + const len = std.mem.len(valid_ptr); + return valid_ptr[0..len]; + } else { + return WebUIError.DecodeError; + } } /// Safely free a buffer allocated by WebUI using @@ -349,7 +355,7 @@ pub fn memcpy(dst: []u8, src: []const u8) void { /// Safely send raw data to the UI. All clients. pub fn sendRaw(self: webui, js_func: [:0]const u8, raw: []u8) void { - c.webui_send_raw(self.window_handle, js_func.ptr, @ptrCast(raw.ptr), raw.len); + c.webui_send_raw(self.window_handle, js_func.ptr, raw.ptr, raw.len); } /// Set a window in hidden mode. @@ -631,7 +637,7 @@ pub fn interfaceBind( event_number: usize, bind_id: usize, ) void, -) void { +) !usize { const tmp_struct = struct { fn handle( tmp_window: usize, @@ -639,12 +645,14 @@ pub fn interfaceBind( tmp_element: [*:0]u8, tmp_event_number: usize, tmp_bind_id: usize, - ) callconv(.C) void { + ) callconv(.c) void { const len = std.mem.len(tmp_element); callback(tmp_window, tmp_event_type, tmp_element[0..len], tmp_event_number, tmp_bind_id); } }; - c.webui_interface_bind(self.window_handle, element.ptr, tmp_struct.handle); + const index = c.webui_interface_bind(self.window_handle, element.ptr, tmp_struct.handle); + if (index == 0) return WebUIError.BindError; + return index; } /// When using `interfaceBind()`, @@ -1056,7 +1064,7 @@ pub const Event = extern struct { c.webui_send_raw_client( self, function.ptr, - @ptrCast(buffer.ptr), + buffer.ptr, buffer.len, ); }