sparkbun 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/sparkbun.cjs +18 -0
- package/dist-linux-arm64/bsdiff +0 -0
- package/dist-linux-arm64/bspatch +0 -0
- package/dist-linux-arm64/libElectrobunCore.so +0 -0
- package/dist-linux-arm64/libNativeWrapper.so +0 -0
- package/dist-linux-arm64/libasar.so +0 -0
- package/dist-linux-x64/bsdiff +0 -0
- package/dist-linux-x64/bspatch +0 -0
- package/dist-linux-x64/libElectrobunCore.so +0 -0
- package/dist-linux-x64/libNativeWrapper.so +0 -0
- package/dist-linux-x64/libasar.so +0 -0
- package/dist-macos-arm64/bsdiff +0 -0
- package/dist-macos-arm64/bspatch +0 -0
- package/dist-macos-arm64/libElectrobunCore.dylib +0 -0
- package/dist-macos-arm64/libNativeWrapper.dylib +0 -0
- package/dist-macos-arm64/libasar.dylib +0 -0
- package/dist-macos-arm64/libwebgpu_dawn.dylib +0 -0
- package/dist-macos-arm64/preload-full.js +885 -0
- package/dist-macos-arm64/preload-sandboxed.js +111 -0
- package/dist-macos-arm64/process_helper +0 -0
- package/dist-win-x64/ElectrobunCore.dll +0 -0
- package/dist-win-x64/WebView2Loader.dll +0 -0
- package/dist-win-x64/bsdiff.exe +0 -0
- package/dist-win-x64/bspatch.exe +0 -0
- package/dist-win-x64/libNativeWrapper.dll +0 -0
- package/dist-win-x64/zig-asar/arm64/libasar.dll +0 -0
- package/dist-win-x64/zig-asar/x64/libasar.dll +0 -0
- package/package.json +47 -0
- package/scripts/build-and-upload-artifacts.js +207 -0
- package/scripts/gen-webgpu-ffi.mjs +162 -0
- package/scripts/install-windows-deps.ps1 +80 -0
- package/scripts/package-release.js +237 -0
- package/scripts/push-version.js +84 -0
- package/scripts/update-bun-version.ts +122 -0
- package/scripts/update-cef-version.ts +145 -0
- package/src/browser/builtinrpcSchema.ts +19 -0
- package/src/browser/global.d.ts +36 -0
- package/src/browser/index.ts +234 -0
- package/src/browser/webviewtag.ts +88 -0
- package/src/browser/wgputag.ts +48 -0
- package/src/bun/SparkBunConfig.ts +497 -0
- package/src/bun/__tests__/ffi-contract.test.ts +105 -0
- package/src/bun/core/ApplicationMenu.ts +70 -0
- package/src/bun/core/BrowserView.ts +416 -0
- package/src/bun/core/BrowserWindow.ts +396 -0
- package/src/bun/core/BuildConfig.ts +71 -0
- package/src/bun/core/ContextMenu.ts +75 -0
- package/src/bun/core/GpuWindow.ts +289 -0
- package/src/bun/core/Paths.ts +5 -0
- package/src/bun/core/Socket.ts +22 -0
- package/src/bun/core/Tray.ts +197 -0
- package/src/bun/core/Updater.ts +1131 -0
- package/src/bun/core/Utils.ts +487 -0
- package/src/bun/core/WGPUView.ts +167 -0
- package/src/bun/core/menuRoles.ts +181 -0
- package/src/bun/events/ApplicationEvents.ts +22 -0
- package/src/bun/events/event.ts +27 -0
- package/src/bun/events/eventEmitter.ts +45 -0
- package/src/bun/events/trayEvents.ts +11 -0
- package/src/bun/events/webviewEvents.ts +39 -0
- package/src/bun/events/windowEvents.ts +23 -0
- package/src/bun/index.ts +120 -0
- package/src/bun/preload/.generated/compiled.ts +2 -0
- package/src/bun/preload/build.ts +65 -0
- package/src/bun/preload/dragRegions.ts +41 -0
- package/src/bun/preload/encryption.ts +86 -0
- package/src/bun/preload/events.ts +171 -0
- package/src/bun/preload/globals.d.ts +45 -0
- package/src/bun/preload/index-sandboxed.ts +28 -0
- package/src/bun/preload/index.ts +77 -0
- package/src/bun/preload/internalRpc.ts +80 -0
- package/src/bun/preload/overlaySync.ts +107 -0
- package/src/bun/preload/webviewTag.ts +451 -0
- package/src/bun/preload/wgpuTag.ts +246 -0
- package/src/bun/proc/linux.md +43 -0
- package/src/bun/proc/native.ts +3253 -0
- package/src/bun/webGPU.ts +346 -0
- package/src/bun/webgpuAdapter.ts +3011 -0
- package/src/cli/bun.lockb +0 -0
- package/src/cli/index.ts +4653 -0
- package/src/cli/package-lock.json +81 -0
- package/src/cli/package.json +11 -0
- package/src/cli/templates/embedded.ts +2 -0
- package/src/core/build.zig +16 -0
- package/src/core/main.zig +3378 -0
- package/src/extractor/build.zig +22 -0
- package/src/installer/installer-template.ts +216 -0
- package/src/launcher/main.ts +221 -0
- package/src/native/build/libNativeWrapper.so +0 -0
- package/src/native/linux/build/nativeWrapper.o +0 -0
- package/src/native/linux/cef_loader.cpp +110 -0
- package/src/native/linux/cef_loader.h +28 -0
- package/src/native/linux/cef_process_helper_linux.cpp +160 -0
- package/src/native/linux/nativeWrapper.cpp +11768 -0
- package/src/native/macos/cef_process_helper_mac.cc +160 -0
- package/src/native/macos/nativeWrapper.mm +9172 -0
- package/src/native/shared/accelerator_parser.h +72 -0
- package/src/native/shared/app_paths.h +110 -0
- package/src/native/shared/asar.h +35 -0
- package/src/native/shared/cache_migration.h +244 -0
- package/src/native/shared/callbacks.h +57 -0
- package/src/native/shared/cef_response_filter.h +189 -0
- package/src/native/shared/chromium_flags.h +181 -0
- package/src/native/shared/config.h +66 -0
- package/src/native/shared/download_event.h +197 -0
- package/src/native/shared/ffi_helpers.h +139 -0
- package/src/native/shared/glob_match.h +59 -0
- package/src/native/shared/json_menu_parser.h +223 -0
- package/src/native/shared/mime_types.h +101 -0
- package/src/native/shared/navigation_rules.h +98 -0
- package/src/native/shared/partition_context.h +137 -0
- package/src/native/shared/pending_resize_queue.h +45 -0
- package/src/native/shared/permissions.h +118 -0
- package/src/native/shared/permissions_cef.h +74 -0
- package/src/native/shared/preload_script.h +71 -0
- package/src/native/shared/shutdown_guard.h +134 -0
- package/src/native/shared/thread_safe_map.h +138 -0
- package/src/native/shared/webview_storage.h +91 -0
- package/src/native/win/cef_process_helper_win.cpp +143 -0
- package/src/native/win/dcomp_compositor.h +352 -0
- package/src/native/win/nativeWrapper.cpp +12434 -0
- package/src/npmbin/index.js +34 -0
- package/src/shared/bun-version.ts +3 -0
- package/src/shared/cef-version.ts +5 -0
- package/src/shared/naming.test.ts +327 -0
- package/src/shared/naming.ts +188 -0
- package/src/shared/platform.ts +48 -0
- package/src/shared/rpc.ts +541 -0
- package/src/shared/sparkbun-version.ts +2 -0
- package/src/types/three.d.ts +1 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,3378 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const builtin = @import("builtin");
|
|
3
|
+
|
|
4
|
+
const allocator = std.heap.c_allocator;
|
|
5
|
+
|
|
6
|
+
const StartEventLoopFn = *const fn ([*:0]const u8, [*:0]const u8, [*:0]const u8) callconv(.C) void;
|
|
7
|
+
const ForceExitFn = *const fn (c_int) callconv(.C) void;
|
|
8
|
+
const WindowPtr = ?*anyopaque;
|
|
9
|
+
const WebviewPtr = ?*anyopaque;
|
|
10
|
+
const WgpuViewPtr = ?*anyopaque;
|
|
11
|
+
const TrayPtr = ?*anyopaque;
|
|
12
|
+
const WindowCloseHandler = *const fn (u32) callconv(.C) void;
|
|
13
|
+
const WindowMoveHandler = *const fn (u32, f64, f64) callconv(.C) void;
|
|
14
|
+
const WindowResizeHandler = *const fn (u32, f64, f64, f64, f64) callconv(.C) void;
|
|
15
|
+
const WindowFocusHandler = *const fn (u32) callconv(.C) void;
|
|
16
|
+
const WindowBlurHandler = *const fn (u32) callconv(.C) void;
|
|
17
|
+
const WindowKeyHandler = *const fn (u32, u32, u32, u32, u32) callconv(.C) void;
|
|
18
|
+
const DecideNavigationHandler = *const fn (u32, [*:0]const u8) callconv(.C) u32;
|
|
19
|
+
const WebviewEventHandler = *const fn (u32, [*:0]const u8, [*:0]const u8) callconv(.C) void;
|
|
20
|
+
const WebviewPostMessageHandler = *const fn (u32, [*:0]const u8) callconv(.C) void;
|
|
21
|
+
const StatusItemHandler = *const fn (u32, [*:0]const u8) callconv(.C) void;
|
|
22
|
+
const GlobalShortcutHandler = *const fn ([*:0]const u8) callconv(.C) void;
|
|
23
|
+
const QuitRequestedHandler = *const fn () callconv(.C) void;
|
|
24
|
+
const URLOpenHandler = *const fn ([*:0]const u8) callconv(.C) void;
|
|
25
|
+
const AppReopenHandler = *const fn () callconv(.C) void;
|
|
26
|
+
const Aes256Gcm = std.crypto.aead.aes_gcm.Aes256Gcm;
|
|
27
|
+
const WebviewSecretKey = [Aes256Gcm.key_length]u8;
|
|
28
|
+
const websocket_magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
29
|
+
const websocket_payload_limit: usize = 1024 * 1024 * 500;
|
|
30
|
+
const websocket_port_range_start: u16 = 50000;
|
|
31
|
+
const websocket_port_range_end: u16 = 65535;
|
|
32
|
+
|
|
33
|
+
const TrayState = struct {
|
|
34
|
+
title: [:0]u8,
|
|
35
|
+
image: [:0]u8,
|
|
36
|
+
menu_config: ?[:0]u8,
|
|
37
|
+
is_template: bool,
|
|
38
|
+
width: u32,
|
|
39
|
+
height: u32,
|
|
40
|
+
handler: ?StatusItemHandler,
|
|
41
|
+
ptr: TrayPtr,
|
|
42
|
+
visible: bool,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const WindowState = struct {
|
|
46
|
+
ptr: WindowPtr,
|
|
47
|
+
transparent: bool,
|
|
48
|
+
close_handler: ?WindowCloseHandler,
|
|
49
|
+
move_handler: ?WindowMoveHandler,
|
|
50
|
+
resize_handler: ?WindowResizeHandler,
|
|
51
|
+
focus_handler: ?WindowFocusHandler,
|
|
52
|
+
blur_handler: ?WindowBlurHandler,
|
|
53
|
+
key_handler: ?WindowKeyHandler,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const WebviewRendererKind = enum {
|
|
57
|
+
native,
|
|
58
|
+
cef,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const PendingHostMessage = struct {
|
|
62
|
+
webview_id: u32,
|
|
63
|
+
message: [:0]u8,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const WebviewState = struct {
|
|
67
|
+
ptr: WebviewPtr,
|
|
68
|
+
window_id: u32,
|
|
69
|
+
host_webview_id: ?u32,
|
|
70
|
+
renderer: WebviewRendererKind,
|
|
71
|
+
secret_key: WebviewSecretKey,
|
|
72
|
+
socket_handle: ?std.posix.socket_t,
|
|
73
|
+
transport_ready: bool,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const WgpuViewState = struct {
|
|
77
|
+
ptr: WgpuViewPtr,
|
|
78
|
+
window_id: u32,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const WebviewRuntimeState = struct {
|
|
82
|
+
rpc_port: u32 = 0,
|
|
83
|
+
preload_script: ?[:0]u8 = null,
|
|
84
|
+
preload_script_sandboxed: ?[:0]u8 = null,
|
|
85
|
+
configured: bool = false,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const HostTransportState = struct {
|
|
89
|
+
started: bool = false,
|
|
90
|
+
port: u32 = 0,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const DefaultWebviewCallbacks = struct {
|
|
94
|
+
navigation_callback: ?DecideNavigationHandler = null,
|
|
95
|
+
webview_event_handler: ?WebviewEventHandler = null,
|
|
96
|
+
event_bridge_handler: ?WebviewPostMessageHandler = null,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const HostMessageWakeupState = struct {
|
|
100
|
+
initialized: bool = false,
|
|
101
|
+
read_fd: c_int = -1,
|
|
102
|
+
write_fd: c_int = -1,
|
|
103
|
+
signaled: bool = false,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const NativeWrapperState = struct {
|
|
107
|
+
lib: std.DynLib,
|
|
108
|
+
path: []u8,
|
|
109
|
+
start_event_loop: StartEventLoopFn,
|
|
110
|
+
force_exit: ForceExitFn,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
var last_error: ?[:0]u8 = null;
|
|
114
|
+
var native_wrapper_loaded = false;
|
|
115
|
+
var native_wrapper_state: NativeWrapperState = undefined;
|
|
116
|
+
var next_tray_id: u32 = 1;
|
|
117
|
+
var next_window_id: u32 = 1;
|
|
118
|
+
var next_webview_id: u32 = 1;
|
|
119
|
+
var next_wgpu_view_id: u32 = 1;
|
|
120
|
+
var tray_registry = std.AutoHashMap(u32, TrayState).init(allocator);
|
|
121
|
+
var window_registry = std.AutoHashMap(u32, WindowState).init(allocator);
|
|
122
|
+
var window_registry_mutex: std.Thread.Mutex = .{};
|
|
123
|
+
var webview_registry = std.AutoHashMap(u32, WebviewState).init(allocator);
|
|
124
|
+
var webview_registry_mutex: std.Thread.Mutex = .{};
|
|
125
|
+
var wgpu_view_registry = std.AutoHashMap(u32, WgpuViewState).init(allocator);
|
|
126
|
+
var wgpu_view_registry_mutex: std.Thread.Mutex = .{};
|
|
127
|
+
var pending_host_messages = std.ArrayList(PendingHostMessage).init(allocator);
|
|
128
|
+
var pending_host_messages_mutex: std.Thread.Mutex = .{};
|
|
129
|
+
var webview_runtime_state = WebviewRuntimeState{};
|
|
130
|
+
var host_transport_state = HostTransportState{};
|
|
131
|
+
var host_transport_mutex: std.Thread.Mutex = .{};
|
|
132
|
+
var default_webview_callbacks = DefaultWebviewCallbacks{};
|
|
133
|
+
var managed_quit_requested_handler: ?QuitRequestedHandler = null;
|
|
134
|
+
var exit_on_last_window_closed: bool = true;
|
|
135
|
+
var host_message_wakeup_state = HostMessageWakeupState{};
|
|
136
|
+
var host_message_wakeup_mutex: std.Thread.Mutex = .{};
|
|
137
|
+
|
|
138
|
+
const empty_rect_json: [*:0]const u8 = "{\"x\":0,\"y\":0,\"width\":0,\"height\":0}";
|
|
139
|
+
|
|
140
|
+
fn clearLastError() void {
|
|
141
|
+
if (last_error) |message| {
|
|
142
|
+
allocator.free(message);
|
|
143
|
+
last_error = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fn setLastError(comptime fmt: []const u8, args: anytype) void {
|
|
148
|
+
clearLastError();
|
|
149
|
+
last_error = std.fmt.allocPrintZ(allocator, fmt, args) catch null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn dupeZ(input: [*:0]const u8) ![:0]u8 {
|
|
153
|
+
return allocator.dupeZ(u8, std.mem.span(input));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fn replaceOwnedZ(target: *[:0]u8, input: [*:0]const u8) bool {
|
|
157
|
+
const next = dupeZ(input) catch |err| {
|
|
158
|
+
setLastError("Failed to allocate string: {s}", .{@errorName(err)});
|
|
159
|
+
return false;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
allocator.free(target.*);
|
|
163
|
+
target.* = next;
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fn replaceOptionalOwnedZ(target: *?[:0]u8, input: [*:0]const u8) bool {
|
|
168
|
+
const next = dupeZ(input) catch |err| {
|
|
169
|
+
setLastError("Failed to allocate string: {s}", .{@errorName(err)});
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if (target.*) |current| {
|
|
174
|
+
allocator.free(current);
|
|
175
|
+
}
|
|
176
|
+
target.* = next;
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fn freeTrayState(state: *TrayState) void {
|
|
181
|
+
allocator.free(state.title);
|
|
182
|
+
allocator.free(state.image);
|
|
183
|
+
if (state.menu_config) |menu_config| {
|
|
184
|
+
allocator.free(menu_config);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export fn electrobun_core_last_error() [*:0]const u8 {
|
|
189
|
+
if (last_error) |message| {
|
|
190
|
+
return message.ptr;
|
|
191
|
+
}
|
|
192
|
+
return "";
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fn parseWebviewRenderer(renderer: [*:0]const u8) WebviewRendererKind {
|
|
196
|
+
if (std.mem.eql(u8, std.mem.span(renderer), "cef")) {
|
|
197
|
+
return .cef;
|
|
198
|
+
}
|
|
199
|
+
return .native;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn rememberDefaultWebviewCallbacks(
|
|
203
|
+
navigation_callback: ?DecideNavigationHandler,
|
|
204
|
+
webview_event_handler: ?WebviewEventHandler,
|
|
205
|
+
event_bridge_handler: ?WebviewPostMessageHandler,
|
|
206
|
+
) void {
|
|
207
|
+
if (navigation_callback) |handler| {
|
|
208
|
+
default_webview_callbacks.navigation_callback = handler;
|
|
209
|
+
}
|
|
210
|
+
if (webview_event_handler) |handler| {
|
|
211
|
+
default_webview_callbacks.webview_event_handler = handler;
|
|
212
|
+
}
|
|
213
|
+
if (event_bridge_handler) |handler| {
|
|
214
|
+
default_webview_callbacks.event_bridge_handler = handler;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
fn ensureHostMessageWakeupInitialized() bool {
|
|
219
|
+
if (builtin.os.tag == .windows) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
host_message_wakeup_mutex.lock();
|
|
224
|
+
defer host_message_wakeup_mutex.unlock();
|
|
225
|
+
|
|
226
|
+
if (host_message_wakeup_state.initialized) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const fds = std.posix.pipe() catch return false;
|
|
231
|
+
host_message_wakeup_state.read_fd = @intCast(fds[0]);
|
|
232
|
+
host_message_wakeup_state.write_fd = @intCast(fds[1]);
|
|
233
|
+
host_message_wakeup_state.initialized = true;
|
|
234
|
+
host_message_wakeup_state.signaled = false;
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fn signalHostMessageWakeup() void {
|
|
239
|
+
if (!ensureHostMessageWakeupInitialized()) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
host_message_wakeup_mutex.lock();
|
|
244
|
+
defer host_message_wakeup_mutex.unlock();
|
|
245
|
+
|
|
246
|
+
if (host_message_wakeup_state.signaled) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const byte: [1]u8 = .{1};
|
|
251
|
+
const write_fd: std.posix.fd_t = @intCast(host_message_wakeup_state.write_fd);
|
|
252
|
+
_ = std.posix.write(write_fd, &byte) catch return;
|
|
253
|
+
host_message_wakeup_state.signaled = true;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
fn enqueuePendingHostMessage(webview_id: u32, message: [*:0]const u8) void {
|
|
257
|
+
const owned_message = dupeZ(message) catch return;
|
|
258
|
+
|
|
259
|
+
pending_host_messages_mutex.lock();
|
|
260
|
+
defer pending_host_messages_mutex.unlock();
|
|
261
|
+
|
|
262
|
+
pending_host_messages.append(.{
|
|
263
|
+
.webview_id = webview_id,
|
|
264
|
+
.message = owned_message,
|
|
265
|
+
}) catch {
|
|
266
|
+
allocator.free(owned_message);
|
|
267
|
+
return;
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
signalHostMessageWakeup();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fn hostBridgeQueueTrampoline(webview_id: u32, message: [*:0]const u8) callconv(.C) void {
|
|
274
|
+
enqueuePendingHostMessage(webview_id, message);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export fn popNextQueuedHostMessage(out_webview_id: *u32) ?[*:0]u8 {
|
|
278
|
+
clearLastError();
|
|
279
|
+
|
|
280
|
+
pending_host_messages_mutex.lock();
|
|
281
|
+
defer pending_host_messages_mutex.unlock();
|
|
282
|
+
|
|
283
|
+
if (pending_host_messages.items.len == 0) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const entry = pending_host_messages.orderedRemove(0);
|
|
288
|
+
if (pending_host_messages.items.len == 0) {
|
|
289
|
+
host_message_wakeup_mutex.lock();
|
|
290
|
+
host_message_wakeup_state.signaled = false;
|
|
291
|
+
host_message_wakeup_mutex.unlock();
|
|
292
|
+
}
|
|
293
|
+
out_webview_id.* = entry.webview_id;
|
|
294
|
+
return entry.message.ptr;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export fn freeCoreString(value: ?[*:0]u8) void {
|
|
298
|
+
if (value) |ptr_value| {
|
|
299
|
+
const slice = std.mem.sliceTo(ptr_value, 0);
|
|
300
|
+
allocator.free(@constCast(slice));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export fn getHostMessageWakeupReadFD() c_int {
|
|
305
|
+
clearLastError();
|
|
306
|
+
if (!ensureHostMessageWakeupInitialized()) {
|
|
307
|
+
return -1;
|
|
308
|
+
}
|
|
309
|
+
return host_message_wakeup_state.read_fd;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fn ensureWebviewRuntimeConfigured() bool {
|
|
313
|
+
if (!webview_runtime_state.configured) {
|
|
314
|
+
setLastError("Webview runtime is not configured", .{});
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (webview_runtime_state.preload_script == null or webview_runtime_state.preload_script_sandboxed == null) {
|
|
319
|
+
setLastError("Webview runtime preload scripts are not configured", .{});
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fn parseWebviewSecretKey(secret_key: [*:0]const u8) ?WebviewSecretKey {
|
|
327
|
+
const input = std.mem.trim(u8, std.mem.span(secret_key), " \t\r\n");
|
|
328
|
+
var parsed: WebviewSecretKey = [_]u8{0} ** Aes256Gcm.key_length;
|
|
329
|
+
var iterator = std.mem.splitScalar(u8, input, ',');
|
|
330
|
+
var index: usize = 0;
|
|
331
|
+
|
|
332
|
+
while (iterator.next()) |part| {
|
|
333
|
+
const trimmed = std.mem.trim(u8, part, " \t\r\n");
|
|
334
|
+
if (trimmed.len == 0) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (index >= parsed.len) {
|
|
338
|
+
setLastError("Webview secret key must contain exactly {d} bytes", .{Aes256Gcm.key_length});
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
parsed[index] = std.fmt.parseInt(u8, trimmed, 10) catch |err| {
|
|
342
|
+
setLastError("Failed to parse webview secret key byte: {s}", .{@errorName(err)});
|
|
343
|
+
return null;
|
|
344
|
+
};
|
|
345
|
+
index += 1;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (index != parsed.len) {
|
|
349
|
+
setLastError("Webview secret key must contain exactly {d} bytes", .{Aes256Gcm.key_length});
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return parsed;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
fn closeSocketHandle(handle: std.posix.socket_t) void {
|
|
357
|
+
var stream = std.net.Stream{ .handle = handle };
|
|
358
|
+
stream.close();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
fn clearWebviewSocketHandleIfCurrent(webview_id: u32, handle: std.posix.socket_t) void {
|
|
362
|
+
webview_registry_mutex.lock();
|
|
363
|
+
defer webview_registry_mutex.unlock();
|
|
364
|
+
|
|
365
|
+
const state = webview_registry.getPtr(webview_id) orelse return;
|
|
366
|
+
if (state.socket_handle != null and state.socket_handle.? == handle) {
|
|
367
|
+
state.socket_handle = null;
|
|
368
|
+
state.transport_ready = false;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
fn closeAndClearWebviewSocketHandle(webview_id: u32) void {
|
|
373
|
+
var handle_to_close: ?std.posix.socket_t = null;
|
|
374
|
+
|
|
375
|
+
webview_registry_mutex.lock();
|
|
376
|
+
if (webview_registry.getPtr(webview_id)) |state| {
|
|
377
|
+
handle_to_close = state.socket_handle;
|
|
378
|
+
state.socket_handle = null;
|
|
379
|
+
state.transport_ready = false;
|
|
380
|
+
}
|
|
381
|
+
webview_registry_mutex.unlock();
|
|
382
|
+
|
|
383
|
+
if (handle_to_close) |handle| {
|
|
384
|
+
closeSocketHandle(handle);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
fn attachWebviewSocketHandle(webview_id: u32, handle: std.posix.socket_t) bool {
|
|
389
|
+
var handle_to_close: ?std.posix.socket_t = null;
|
|
390
|
+
|
|
391
|
+
webview_registry_mutex.lock();
|
|
392
|
+
const state = webview_registry.getPtr(webview_id) orelse {
|
|
393
|
+
webview_registry_mutex.unlock();
|
|
394
|
+
return false;
|
|
395
|
+
};
|
|
396
|
+
if (state.socket_handle) |previous_handle| {
|
|
397
|
+
if (previous_handle != handle) {
|
|
398
|
+
handle_to_close = previous_handle;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
state.socket_handle = handle;
|
|
402
|
+
state.transport_ready = false;
|
|
403
|
+
webview_registry_mutex.unlock();
|
|
404
|
+
|
|
405
|
+
if (handle_to_close) |previous_handle| {
|
|
406
|
+
closeSocketHandle(previous_handle);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const WebviewTransportContext = struct {
|
|
413
|
+
secret_key: WebviewSecretKey,
|
|
414
|
+
socket_handle: ?std.posix.socket_t,
|
|
415
|
+
transport_ready: bool,
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
fn lookupWebviewTransportContext(webview_id: u32) ?WebviewTransportContext {
|
|
419
|
+
const state = lookupWebviewState(webview_id) orelse return null;
|
|
420
|
+
return .{
|
|
421
|
+
.secret_key = state.secret_key,
|
|
422
|
+
.socket_handle = state.socket_handle,
|
|
423
|
+
.transport_ready = state.transport_ready,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
fn markWebviewTransportReady(webview_id: u32, handle: std.posix.socket_t) void {
|
|
428
|
+
webview_registry_mutex.lock();
|
|
429
|
+
defer webview_registry_mutex.unlock();
|
|
430
|
+
|
|
431
|
+
const state = webview_registry.getPtr(webview_id) orelse return;
|
|
432
|
+
if (state.socket_handle != null and state.socket_handle.? == handle) {
|
|
433
|
+
state.transport_ready = true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
fn decodeBase64Alloc(input: []const u8) ![]u8 {
|
|
438
|
+
const decoded_len = try std.base64.standard.Decoder.calcSizeForSlice(input);
|
|
439
|
+
const decoded = try allocator.alloc(u8, decoded_len);
|
|
440
|
+
errdefer allocator.free(decoded);
|
|
441
|
+
try std.base64.standard.Decoder.decode(decoded, input);
|
|
442
|
+
return decoded;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
fn encodeBase64Alloc(input: []const u8) ![]u8 {
|
|
446
|
+
const encoded_len = std.base64.standard.Encoder.calcSize(input.len);
|
|
447
|
+
const encoded = try allocator.alloc(u8, encoded_len);
|
|
448
|
+
_ = std.base64.standard.Encoder.encode(encoded, input);
|
|
449
|
+
return encoded;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
fn parseRequestHeaderValue(headers: []const u8, header_name: []const u8) ?[]const u8 {
|
|
453
|
+
var lines = std.mem.splitSequence(u8, headers, "\r\n");
|
|
454
|
+
_ = lines.next();
|
|
455
|
+
|
|
456
|
+
while (lines.next()) |line| {
|
|
457
|
+
if (line.len == 0) {
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
const separator_index = std.mem.indexOfScalar(u8, line, ':') orelse continue;
|
|
461
|
+
const name = line[0..separator_index];
|
|
462
|
+
if (!std.ascii.eqlIgnoreCase(name, header_name)) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
return std.mem.trim(u8, line[separator_index + 1 ..], " \t");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
fn parseWebviewIdFromTarget(target: []const u8) ?u32 {
|
|
472
|
+
const query_index = std.mem.indexOfScalar(u8, target, '?') orelse return null;
|
|
473
|
+
const path = target[0..query_index];
|
|
474
|
+
if (!std.mem.eql(u8, path, "/socket")) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const query = target[query_index + 1 ..];
|
|
479
|
+
var pairs = std.mem.splitScalar(u8, query, '&');
|
|
480
|
+
while (pairs.next()) |pair| {
|
|
481
|
+
const separator_index = std.mem.indexOfScalar(u8, pair, '=') orelse continue;
|
|
482
|
+
const name = pair[0..separator_index];
|
|
483
|
+
if (!std.mem.eql(u8, name, "webviewId")) {
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
const value = pair[separator_index + 1 ..];
|
|
487
|
+
return std.fmt.parseInt(u32, value, 10) catch null;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return null;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
fn writeSimpleHttpResponse(stream: std.net.Stream, status: []const u8, body: []const u8) !void {
|
|
494
|
+
try stream.writer().print(
|
|
495
|
+
"HTTP/1.1 {s}\r\nContent-Type: text/plain\r\nContent-Length: {d}\r\nConnection: close\r\n\r\n{s}",
|
|
496
|
+
.{ status, body.len, body },
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
fn writeWebSocketHandshake(stream: std.net.Stream, websocket_key: []const u8) !void {
|
|
501
|
+
var sha1 = std.crypto.hash.Sha1.init(.{});
|
|
502
|
+
sha1.update(websocket_key);
|
|
503
|
+
sha1.update(websocket_magic);
|
|
504
|
+
|
|
505
|
+
var digest: [std.crypto.hash.Sha1.digest_length]u8 = undefined;
|
|
506
|
+
sha1.final(&digest);
|
|
507
|
+
|
|
508
|
+
var accept_buffer: [std.base64.standard.Encoder.calcSize(digest.len)]u8 = undefined;
|
|
509
|
+
const accept_value = std.base64.standard.Encoder.encode(&accept_buffer, &digest);
|
|
510
|
+
|
|
511
|
+
try stream.writer().print(
|
|
512
|
+
"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {s}\r\n\r\n",
|
|
513
|
+
.{accept_value},
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const PendingStreamReader = struct {
|
|
518
|
+
stream: std.net.Stream,
|
|
519
|
+
pending: []const u8,
|
|
520
|
+
index: usize = 0,
|
|
521
|
+
|
|
522
|
+
fn readExact(self: *PendingStreamReader, dest: []u8) !void {
|
|
523
|
+
var written: usize = 0;
|
|
524
|
+
|
|
525
|
+
if (self.index < self.pending.len) {
|
|
526
|
+
const available = self.pending.len - self.index;
|
|
527
|
+
const to_copy = @min(dest.len, available);
|
|
528
|
+
@memcpy(dest[0..to_copy], self.pending[self.index .. self.index + to_copy]);
|
|
529
|
+
self.index += to_copy;
|
|
530
|
+
written += to_copy;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (written == dest.len) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
try self.stream.reader().readNoEof(dest[written..]);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const WebSocketFrame = struct {
|
|
542
|
+
opcode: u8,
|
|
543
|
+
payload: []u8,
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
fn readWebSocketFrame(reader: *PendingStreamReader) !WebSocketFrame {
|
|
547
|
+
var header: [2]u8 = undefined;
|
|
548
|
+
reader.readExact(&header) catch |err| switch (err) {
|
|
549
|
+
error.EndOfStream => return error.ConnectionClosed,
|
|
550
|
+
else => return err,
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
const fin = (header[0] & 0x80) != 0;
|
|
554
|
+
const opcode = header[0] & 0x0F;
|
|
555
|
+
const masked = (header[1] & 0x80) != 0;
|
|
556
|
+
var payload_len: usize = header[1] & 0x7F;
|
|
557
|
+
|
|
558
|
+
if (!fin) {
|
|
559
|
+
return error.UnsupportedWebSocketFrame;
|
|
560
|
+
}
|
|
561
|
+
if (!masked) {
|
|
562
|
+
return error.InvalidWebSocketFrame;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (payload_len == 126) {
|
|
566
|
+
var extended: [2]u8 = undefined;
|
|
567
|
+
try reader.readExact(&extended);
|
|
568
|
+
payload_len = (@as(usize, extended[0]) << 8) | @as(usize, extended[1]);
|
|
569
|
+
} else if (payload_len == 127) {
|
|
570
|
+
var extended: [8]u8 = undefined;
|
|
571
|
+
try reader.readExact(&extended);
|
|
572
|
+
var parsed_len: u64 = 0;
|
|
573
|
+
for (extended) |byte| {
|
|
574
|
+
parsed_len = (parsed_len << 8) | @as(u64, byte);
|
|
575
|
+
}
|
|
576
|
+
payload_len = std.math.cast(usize, parsed_len) orelse return error.WebSocketFrameTooLarge;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (payload_len > websocket_payload_limit) {
|
|
580
|
+
return error.WebSocketFrameTooLarge;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
var mask: [4]u8 = undefined;
|
|
584
|
+
try reader.readExact(&mask);
|
|
585
|
+
|
|
586
|
+
const payload = try allocator.alloc(u8, payload_len);
|
|
587
|
+
errdefer allocator.free(payload);
|
|
588
|
+
try reader.readExact(payload);
|
|
589
|
+
|
|
590
|
+
for (payload, 0..) |*byte, index| {
|
|
591
|
+
byte.* ^= mask[index % mask.len];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return .{
|
|
595
|
+
.opcode = opcode,
|
|
596
|
+
.payload = payload,
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
fn writeWebSocketFrame(stream: std.net.Stream, opcode: u8, payload: []const u8) !void {
|
|
601
|
+
var header: [10]u8 = undefined;
|
|
602
|
+
var header_len: usize = 0;
|
|
603
|
+
|
|
604
|
+
header[header_len] = 0x80 | (opcode & 0x0F);
|
|
605
|
+
header_len += 1;
|
|
606
|
+
|
|
607
|
+
if (payload.len <= 125) {
|
|
608
|
+
header[header_len] = @intCast(payload.len);
|
|
609
|
+
header_len += 1;
|
|
610
|
+
} else if (payload.len <= std.math.maxInt(u16)) {
|
|
611
|
+
header[header_len] = 126;
|
|
612
|
+
header_len += 1;
|
|
613
|
+
header[header_len] = @intCast((payload.len >> 8) & 0xFF);
|
|
614
|
+
header[header_len + 1] = @intCast(payload.len & 0xFF);
|
|
615
|
+
header_len += 2;
|
|
616
|
+
} else {
|
|
617
|
+
header[header_len] = 127;
|
|
618
|
+
header_len += 1;
|
|
619
|
+
var shift: u6 = 56;
|
|
620
|
+
while (true) {
|
|
621
|
+
header[header_len] = @intCast((@as(u64, @intCast(payload.len)) >> shift) & 0xFF);
|
|
622
|
+
header_len += 1;
|
|
623
|
+
if (shift == 0) {
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
shift -= 8;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
try stream.writer().writeAll(header[0..header_len]);
|
|
631
|
+
if (payload.len > 0) {
|
|
632
|
+
try stream.writer().writeAll(payload);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
fn encryptHostTransportPacket(message_json: []const u8, secret_key: WebviewSecretKey) ![]u8 {
|
|
637
|
+
var nonce: [Aes256Gcm.nonce_length]u8 = undefined;
|
|
638
|
+
std.crypto.random.bytes(&nonce);
|
|
639
|
+
|
|
640
|
+
const ciphertext = try allocator.alloc(u8, message_json.len);
|
|
641
|
+
defer allocator.free(ciphertext);
|
|
642
|
+
|
|
643
|
+
var tag: [Aes256Gcm.tag_length]u8 = undefined;
|
|
644
|
+
Aes256Gcm.encrypt(ciphertext, &tag, message_json, "", nonce, secret_key);
|
|
645
|
+
|
|
646
|
+
const encrypted_data_b64 = try encodeBase64Alloc(ciphertext);
|
|
647
|
+
defer allocator.free(encrypted_data_b64);
|
|
648
|
+
const nonce_b64 = try encodeBase64Alloc(&nonce);
|
|
649
|
+
defer allocator.free(nonce_b64);
|
|
650
|
+
const tag_b64 = try encodeBase64Alloc(&tag);
|
|
651
|
+
defer allocator.free(tag_b64);
|
|
652
|
+
|
|
653
|
+
return try std.json.stringifyAlloc(allocator, .{
|
|
654
|
+
.encryptedData = encrypted_data_b64,
|
|
655
|
+
.iv = nonce_b64,
|
|
656
|
+
.tag = tag_b64,
|
|
657
|
+
}, .{});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
fn decryptHostTransportPacket(message_json: []const u8, secret_key: WebviewSecretKey) ![]u8 {
|
|
661
|
+
var parsed = try std.json.parseFromSlice(std.json.Value, allocator, message_json, .{});
|
|
662
|
+
defer parsed.deinit();
|
|
663
|
+
|
|
664
|
+
if (parsed.value != .object) {
|
|
665
|
+
return error.InvalidTransportPacket;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const encrypted_data_value = parsed.value.object.get("encryptedData") orelse return error.InvalidTransportPacket;
|
|
669
|
+
const iv_value = parsed.value.object.get("iv") orelse return error.InvalidTransportPacket;
|
|
670
|
+
const tag_value = parsed.value.object.get("tag") orelse return error.InvalidTransportPacket;
|
|
671
|
+
|
|
672
|
+
if (encrypted_data_value != .string or iv_value != .string or tag_value != .string) {
|
|
673
|
+
return error.InvalidTransportPacket;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const encrypted_data = try decodeBase64Alloc(encrypted_data_value.string);
|
|
677
|
+
defer allocator.free(encrypted_data);
|
|
678
|
+
const nonce_bytes = try decodeBase64Alloc(iv_value.string);
|
|
679
|
+
defer allocator.free(nonce_bytes);
|
|
680
|
+
const tag_bytes = try decodeBase64Alloc(tag_value.string);
|
|
681
|
+
defer allocator.free(tag_bytes);
|
|
682
|
+
|
|
683
|
+
if (nonce_bytes.len != Aes256Gcm.nonce_length or tag_bytes.len != Aes256Gcm.tag_length) {
|
|
684
|
+
return error.InvalidTransportPacket;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
var nonce: [Aes256Gcm.nonce_length]u8 = undefined;
|
|
688
|
+
@memcpy(nonce[0..], nonce_bytes);
|
|
689
|
+
|
|
690
|
+
var tag: [Aes256Gcm.tag_length]u8 = undefined;
|
|
691
|
+
@memcpy(tag[0..], tag_bytes);
|
|
692
|
+
|
|
693
|
+
const plaintext = try allocator.alloc(u8, encrypted_data.len);
|
|
694
|
+
errdefer allocator.free(plaintext);
|
|
695
|
+
try Aes256Gcm.decrypt(plaintext, encrypted_data, tag, "", nonce, secret_key);
|
|
696
|
+
return plaintext;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
fn dispatchHostTransportMessage(webview_id: u32, encrypted_packet: []const u8) void {
|
|
700
|
+
const context = lookupWebviewTransportContext(webview_id) orelse return;
|
|
701
|
+
|
|
702
|
+
const plaintext = decryptHostTransportPacket(encrypted_packet, context.secret_key) catch return;
|
|
703
|
+
defer allocator.free(plaintext);
|
|
704
|
+
|
|
705
|
+
if (context.socket_handle) |socket_handle| {
|
|
706
|
+
markWebviewTransportReady(webview_id, socket_handle);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const message_z = allocator.dupeZ(u8, plaintext) catch return;
|
|
710
|
+
defer allocator.free(message_z);
|
|
711
|
+
|
|
712
|
+
enqueuePendingHostMessage(webview_id, message_z.ptr);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
fn handleHostTransportConnection(connection: std.net.Server.Connection) void {
|
|
716
|
+
var stream = connection.stream;
|
|
717
|
+
defer stream.close();
|
|
718
|
+
|
|
719
|
+
var read_buffer: [16 * 1024]u8 = undefined;
|
|
720
|
+
var server = std.http.Server.init(connection, &read_buffer);
|
|
721
|
+
const request = server.receiveHead() catch return;
|
|
722
|
+
|
|
723
|
+
const request_headers = server.read_buffer[0..request.head_end];
|
|
724
|
+
const websocket_key = parseRequestHeaderValue(request_headers, "Sec-WebSocket-Key") orelse {
|
|
725
|
+
writeSimpleHttpResponse(stream, "400 Bad Request", "Missing Sec-WebSocket-Key") catch {};
|
|
726
|
+
return;
|
|
727
|
+
};
|
|
728
|
+
const webview_id = parseWebviewIdFromTarget(request.head.target) orelse {
|
|
729
|
+
writeSimpleHttpResponse(stream, "400 Bad Request", "Missing webviewId") catch {};
|
|
730
|
+
return;
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
if (lookupWebviewState(webview_id) == null) {
|
|
734
|
+
writeSimpleHttpResponse(stream, "404 Not Found", "Unknown webviewId") catch {};
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
writeWebSocketHandshake(stream, websocket_key) catch return;
|
|
739
|
+
|
|
740
|
+
if (!attachWebviewSocketHandle(webview_id, stream.handle)) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
var reader = PendingStreamReader{
|
|
745
|
+
.stream = stream,
|
|
746
|
+
.pending = server.read_buffer[request.head_end..server.read_buffer_len],
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
while (true) {
|
|
750
|
+
const frame = readWebSocketFrame(&reader) catch |err| switch (err) {
|
|
751
|
+
error.ConnectionClosed => break,
|
|
752
|
+
else => break,
|
|
753
|
+
};
|
|
754
|
+
defer allocator.free(frame.payload);
|
|
755
|
+
|
|
756
|
+
switch (frame.opcode) {
|
|
757
|
+
0x1 => dispatchHostTransportMessage(webview_id, frame.payload),
|
|
758
|
+
0x8 => {
|
|
759
|
+
writeWebSocketFrame(stream, 0x8, "") catch {};
|
|
760
|
+
break;
|
|
761
|
+
},
|
|
762
|
+
0x9 => writeWebSocketFrame(stream, 0xA, frame.payload) catch break,
|
|
763
|
+
else => {},
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
clearWebviewSocketHandleIfCurrent(webview_id, stream.handle);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
fn hostTransportAcceptLoop(server: std.net.Server) void {
|
|
771
|
+
var listener = server;
|
|
772
|
+
while (true) {
|
|
773
|
+
const connection = listener.accept() catch break;
|
|
774
|
+
const thread = std.Thread.spawn(.{}, handleHostTransportConnection, .{connection}) catch {
|
|
775
|
+
connection.stream.close();
|
|
776
|
+
continue;
|
|
777
|
+
};
|
|
778
|
+
thread.detach();
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
host_transport_mutex.lock();
|
|
782
|
+
host_transport_state.started = false;
|
|
783
|
+
host_transport_state.port = 0;
|
|
784
|
+
host_transport_mutex.unlock();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
fn startHostTransportServer(requested_port: u32) bool {
|
|
788
|
+
host_transport_mutex.lock();
|
|
789
|
+
defer host_transport_mutex.unlock();
|
|
790
|
+
|
|
791
|
+
if (host_transport_state.started) {
|
|
792
|
+
webview_runtime_state.rpc_port = host_transport_state.port;
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
var current_port: u16 = if (requested_port == 0)
|
|
797
|
+
websocket_port_range_start
|
|
798
|
+
else
|
|
799
|
+
std.math.cast(u16, requested_port) orelse {
|
|
800
|
+
setLastError("Requested websocket port is out of range: {d}", .{requested_port});
|
|
801
|
+
return false;
|
|
802
|
+
};
|
|
803
|
+
const port_limit: u16 = if (requested_port == 0)
|
|
804
|
+
websocket_port_range_end
|
|
805
|
+
else
|
|
806
|
+
current_port;
|
|
807
|
+
|
|
808
|
+
while (current_port <= port_limit) : (current_port += 1) {
|
|
809
|
+
const address = std.net.Address.parseIp("127.0.0.1", current_port) catch |err| {
|
|
810
|
+
setLastError("Failed to parse websocket listen address: {s}", .{@errorName(err)});
|
|
811
|
+
return false;
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
const server = std.net.Address.listen(address, .{ .reuse_address = true }) catch |err| switch (err) {
|
|
815
|
+
error.AddressInUse => continue,
|
|
816
|
+
else => {
|
|
817
|
+
setLastError("Failed to start websocket server: {s}", .{@errorName(err)});
|
|
818
|
+
return false;
|
|
819
|
+
},
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
const actual_port = server.listen_address.getPort();
|
|
823
|
+
const thread = std.Thread.spawn(.{}, hostTransportAcceptLoop, .{server}) catch |err| {
|
|
824
|
+
server.stream.close();
|
|
825
|
+
setLastError("Failed to spawn websocket server thread: {s}", .{@errorName(err)});
|
|
826
|
+
return false;
|
|
827
|
+
};
|
|
828
|
+
thread.detach();
|
|
829
|
+
|
|
830
|
+
host_transport_state.started = true;
|
|
831
|
+
host_transport_state.port = actual_port;
|
|
832
|
+
webview_runtime_state.rpc_port = actual_port;
|
|
833
|
+
return true;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
setLastError("Unable to find an available websocket port", .{});
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
fn buildElectrobunPreload(
|
|
841
|
+
webview_id: u32,
|
|
842
|
+
window_id: u32,
|
|
843
|
+
secret_key: [*:0]const u8,
|
|
844
|
+
sandbox: bool,
|
|
845
|
+
) ?[:0]u8 {
|
|
846
|
+
if (!ensureWebviewRuntimeConfigured()) {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (sandbox) {
|
|
851
|
+
const sandboxed_preload_script = webview_runtime_state.preload_script_sandboxed.?;
|
|
852
|
+
return std.fmt.allocPrintZ(
|
|
853
|
+
allocator,
|
|
854
|
+
\\window.__electrobunWebviewId = {d};
|
|
855
|
+
\\window.__electrobunWindowId = {d};
|
|
856
|
+
\\window.__electrobunEventBridge = window.__electrobunEventBridge || window.webkit?.messageHandlers?.eventBridge || window.eventBridge || window.chrome?.webview?.hostObjects?.eventBridge;
|
|
857
|
+
\\window.__electrobunInternalBridge = window.__electrobunInternalBridge || window.webkit?.messageHandlers?.internalBridge || window.internalBridge || window.chrome?.webview?.hostObjects?.internalBridge;
|
|
858
|
+
\\{s}
|
|
859
|
+
,
|
|
860
|
+
.{ webview_id, window_id, sandboxed_preload_script },
|
|
861
|
+
) catch |err| {
|
|
862
|
+
setLastError("Failed to build sandboxed preload script: {s}", .{@errorName(err)});
|
|
863
|
+
return null;
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const preload_script = webview_runtime_state.preload_script.?;
|
|
868
|
+
return std.fmt.allocPrintZ(
|
|
869
|
+
allocator,
|
|
870
|
+
\\window.__electrobunWebviewId = {d};
|
|
871
|
+
\\window.__electrobunWindowId = {d};
|
|
872
|
+
\\window.__electrobunHostSocketPort = {d};
|
|
873
|
+
\\window.__electrobunRpcSocketPort = {d};
|
|
874
|
+
\\window.__electrobunSecretKeyBytes = [{s}];
|
|
875
|
+
\\window.__electrobunEventBridge = window.__electrobunEventBridge || window.webkit?.messageHandlers?.eventBridge || window.eventBridge || window.chrome?.webview?.hostObjects?.eventBridge;
|
|
876
|
+
\\window.__electrobunInternalBridge = window.__electrobunInternalBridge || window.webkit?.messageHandlers?.internalBridge || window.internalBridge || window.chrome?.webview?.hostObjects?.internalBridge;
|
|
877
|
+
\\window.__electrobunHostBridge = window.__electrobunHostBridge || window.__electrobunBunBridge || window.webkit?.messageHandlers?.hostBridge || window.webkit?.messageHandlers?.bunBridge || window.hostBridge || window.bunBridge || window.chrome?.webview?.hostObjects?.hostBridge || window.chrome?.webview?.hostObjects?.bunBridge;
|
|
878
|
+
\\window.__electrobunBunBridge = window.__electrobunBunBridge || window.webkit?.messageHandlers?.bunBridge || window.bunBridge || window.chrome?.webview?.hostObjects?.bunBridge;
|
|
879
|
+
\\{s}
|
|
880
|
+
,
|
|
881
|
+
.{
|
|
882
|
+
webview_id,
|
|
883
|
+
window_id,
|
|
884
|
+
webview_runtime_state.rpc_port,
|
|
885
|
+
webview_runtime_state.rpc_port,
|
|
886
|
+
std.mem.span(secret_key),
|
|
887
|
+
preload_script,
|
|
888
|
+
},
|
|
889
|
+
) catch |err| {
|
|
890
|
+
setLastError("Failed to build preload script: {s}", .{@errorName(err)});
|
|
891
|
+
return null;
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
export fn configureWebviewRuntime(
|
|
896
|
+
rpc_port: u32,
|
|
897
|
+
preload_script: [*:0]const u8,
|
|
898
|
+
preload_script_sandboxed: [*:0]const u8,
|
|
899
|
+
) bool {
|
|
900
|
+
clearLastError();
|
|
901
|
+
|
|
902
|
+
webview_runtime_state.rpc_port = rpc_port;
|
|
903
|
+
|
|
904
|
+
if (!replaceOptionalOwnedZ(&webview_runtime_state.preload_script, preload_script)) {
|
|
905
|
+
return false;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (!replaceOptionalOwnedZ(
|
|
909
|
+
&webview_runtime_state.preload_script_sandboxed,
|
|
910
|
+
preload_script_sandboxed,
|
|
911
|
+
)) {
|
|
912
|
+
return false;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (!startHostTransportServer(rpc_port)) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
webview_runtime_state.configured = true;
|
|
920
|
+
return true;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
fn nativeWrapperFileName() []const u8 {
|
|
924
|
+
return switch (builtin.os.tag) {
|
|
925
|
+
.windows => "libNativeWrapper.dll",
|
|
926
|
+
.macos => "libNativeWrapper.dylib",
|
|
927
|
+
else => "libNativeWrapper.so",
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
fn resolveNativeWrapperPath() ![]u8 {
|
|
932
|
+
const exe_path = try std.fs.selfExePathAlloc(allocator);
|
|
933
|
+
defer allocator.free(exe_path);
|
|
934
|
+
|
|
935
|
+
const exe_dir = std.fs.path.dirname(exe_path) orelse return error.InvalidExePath;
|
|
936
|
+
return std.fs.path.join(allocator, &.{ exe_dir, nativeWrapperFileName() });
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
fn ensureNativeWrapperLoaded() bool {
|
|
940
|
+
if (native_wrapper_loaded) {
|
|
941
|
+
return true;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const native_wrapper_path = resolveNativeWrapperPath() catch |err| {
|
|
945
|
+
setLastError("Failed to resolve native wrapper path: {s}", .{@errorName(err)});
|
|
946
|
+
return false;
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
var native_wrapper = std.DynLib.open(native_wrapper_path) catch |err| {
|
|
950
|
+
setLastError("Failed to open native wrapper at {s}: {s}", .{
|
|
951
|
+
native_wrapper_path,
|
|
952
|
+
@errorName(err),
|
|
953
|
+
});
|
|
954
|
+
allocator.free(native_wrapper_path);
|
|
955
|
+
return false;
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
const start_event_loop = native_wrapper.lookup(
|
|
959
|
+
StartEventLoopFn,
|
|
960
|
+
"startEventLoop",
|
|
961
|
+
) orelse {
|
|
962
|
+
setLastError("Native wrapper is missing startEventLoop", .{});
|
|
963
|
+
native_wrapper.close();
|
|
964
|
+
allocator.free(native_wrapper_path);
|
|
965
|
+
return false;
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const force_exit = native_wrapper.lookup(
|
|
969
|
+
ForceExitFn,
|
|
970
|
+
"forceExit",
|
|
971
|
+
) orelse {
|
|
972
|
+
setLastError("Native wrapper is missing forceExit", .{});
|
|
973
|
+
native_wrapper.close();
|
|
974
|
+
allocator.free(native_wrapper_path);
|
|
975
|
+
return false;
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
native_wrapper_state = .{
|
|
979
|
+
.lib = native_wrapper,
|
|
980
|
+
.path = native_wrapper_path,
|
|
981
|
+
.start_event_loop = start_event_loop,
|
|
982
|
+
.force_exit = force_exit,
|
|
983
|
+
};
|
|
984
|
+
native_wrapper_loaded = true;
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
fn lookupNativeSymbol(comptime T: type, comptime name: [:0]const u8) ?T {
|
|
989
|
+
if (!ensureNativeWrapperLoaded()) {
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
return native_wrapper_state.lib.lookup(T, name) orelse {
|
|
994
|
+
setLastError("Native wrapper is missing {s}", .{name});
|
|
995
|
+
return null;
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
fn createNativeTrayForState(tray_id: u32, state: *TrayState) bool {
|
|
1000
|
+
const CreateNativeTrayFn = *const fn (
|
|
1001
|
+
u32,
|
|
1002
|
+
[*:0]const u8,
|
|
1003
|
+
[*:0]const u8,
|
|
1004
|
+
bool,
|
|
1005
|
+
u32,
|
|
1006
|
+
u32,
|
|
1007
|
+
?StatusItemHandler,
|
|
1008
|
+
) callconv(.C) TrayPtr;
|
|
1009
|
+
const SetNativeTrayMenuFn = *const fn (TrayPtr, [*:0]const u8) callconv(.C) void;
|
|
1010
|
+
|
|
1011
|
+
const create_native_tray = lookupNativeSymbol(CreateNativeTrayFn, "createTray") orelse return false;
|
|
1012
|
+
const tray_ptr = create_native_tray(
|
|
1013
|
+
tray_id,
|
|
1014
|
+
state.title.ptr,
|
|
1015
|
+
state.image.ptr,
|
|
1016
|
+
state.is_template,
|
|
1017
|
+
state.width,
|
|
1018
|
+
state.height,
|
|
1019
|
+
state.handler,
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
if (tray_ptr == null) {
|
|
1023
|
+
setLastError("Failed to create tray", .{});
|
|
1024
|
+
return false;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
state.ptr = tray_ptr;
|
|
1028
|
+
state.visible = true;
|
|
1029
|
+
|
|
1030
|
+
if (state.menu_config) |menu_config| {
|
|
1031
|
+
const set_native_tray_menu = lookupNativeSymbol(SetNativeTrayMenuFn, "setTrayMenu") orelse return false;
|
|
1032
|
+
set_native_tray_menu(tray_ptr, menu_config.ptr);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
return true;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
fn hideNativeTray(state: *TrayState) void {
|
|
1039
|
+
const RemoveNativeTrayFn = *const fn (TrayPtr) callconv(.C) void;
|
|
1040
|
+
|
|
1041
|
+
if (state.ptr) |tray_ptr| {
|
|
1042
|
+
const remove_native_tray = lookupNativeSymbol(RemoveNativeTrayFn, "removeTray") orelse {
|
|
1043
|
+
state.ptr = null;
|
|
1044
|
+
state.visible = false;
|
|
1045
|
+
return;
|
|
1046
|
+
};
|
|
1047
|
+
remove_native_tray(tray_ptr);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
state.ptr = null;
|
|
1051
|
+
state.visible = false;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
fn lookupWindowState(window_id: u32) ?WindowState {
|
|
1055
|
+
window_registry_mutex.lock();
|
|
1056
|
+
defer window_registry_mutex.unlock();
|
|
1057
|
+
return window_registry.get(window_id);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
fn lookupWindowPtr(window_id: u32) WindowPtr {
|
|
1061
|
+
const state = lookupWindowState(window_id) orelse return null;
|
|
1062
|
+
return state.ptr;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
fn requireWindowPtr(window_id: u32) WindowPtr {
|
|
1066
|
+
const window_ptr = lookupWindowPtr(window_id);
|
|
1067
|
+
if (window_ptr == null) {
|
|
1068
|
+
setLastError("Window {d} not found", .{window_id});
|
|
1069
|
+
}
|
|
1070
|
+
return window_ptr;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
fn lookupWebviewState(webview_id: u32) ?WebviewState {
|
|
1074
|
+
webview_registry_mutex.lock();
|
|
1075
|
+
defer webview_registry_mutex.unlock();
|
|
1076
|
+
return webview_registry.get(webview_id);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
fn lookupWebviewPtr(webview_id: u32) WebviewPtr {
|
|
1080
|
+
const state = lookupWebviewState(webview_id) orelse return null;
|
|
1081
|
+
return state.ptr;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
fn lookupWebviewHostId(webview_id: u32) ?u32 {
|
|
1085
|
+
const state = lookupWebviewState(webview_id) orelse return null;
|
|
1086
|
+
return state.host_webview_id;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
fn requireWebviewPtr(webview_id: u32) WebviewPtr {
|
|
1090
|
+
const webview_ptr = lookupWebviewPtr(webview_id);
|
|
1091
|
+
if (webview_ptr == null) {
|
|
1092
|
+
setLastError("Webview {d} not found", .{webview_id});
|
|
1093
|
+
}
|
|
1094
|
+
return webview_ptr;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
fn lookupWgpuViewState(wgpu_view_id: u32) ?WgpuViewState {
|
|
1098
|
+
wgpu_view_registry_mutex.lock();
|
|
1099
|
+
defer wgpu_view_registry_mutex.unlock();
|
|
1100
|
+
return wgpu_view_registry.get(wgpu_view_id);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
fn lookupWgpuViewPtr(wgpu_view_id: u32) WgpuViewPtr {
|
|
1104
|
+
const state = lookupWgpuViewState(wgpu_view_id) orelse return null;
|
|
1105
|
+
return state.ptr;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
fn requireWgpuViewPtr(wgpu_view_id: u32) WgpuViewPtr {
|
|
1109
|
+
const wgpu_view_ptr = lookupWgpuViewPtr(wgpu_view_id);
|
|
1110
|
+
if (wgpu_view_ptr == null) {
|
|
1111
|
+
setLastError("WGPUView {d} not found", .{wgpu_view_id});
|
|
1112
|
+
}
|
|
1113
|
+
return wgpu_view_ptr;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
fn hasOpenWindows() bool {
|
|
1117
|
+
window_registry_mutex.lock();
|
|
1118
|
+
defer window_registry_mutex.unlock();
|
|
1119
|
+
return window_registry.count() > 0;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
fn collectWebviewIdsForWindow(window_id: u32, list: *std.ArrayList(u32)) void {
|
|
1123
|
+
webview_registry_mutex.lock();
|
|
1124
|
+
defer webview_registry_mutex.unlock();
|
|
1125
|
+
|
|
1126
|
+
var iterator = webview_registry.iterator();
|
|
1127
|
+
while (iterator.next()) |entry| {
|
|
1128
|
+
if (entry.value_ptr.window_id == window_id) {
|
|
1129
|
+
list.append(entry.key_ptr.*) catch return;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
fn collectWgpuViewIdsForWindow(window_id: u32, list: *std.ArrayList(u32)) void {
|
|
1135
|
+
wgpu_view_registry_mutex.lock();
|
|
1136
|
+
defer wgpu_view_registry_mutex.unlock();
|
|
1137
|
+
|
|
1138
|
+
var iterator = wgpu_view_registry.iterator();
|
|
1139
|
+
while (iterator.next()) |entry| {
|
|
1140
|
+
if (entry.value_ptr.window_id == window_id) {
|
|
1141
|
+
list.append(entry.key_ptr.*) catch return;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
fn loadManagedHTMLForWebview(webview_id: u32, html: [*:0]const u8) void {
|
|
1147
|
+
const state = lookupWebviewState(webview_id) orelse return;
|
|
1148
|
+
|
|
1149
|
+
if (state.renderer == .cef) {
|
|
1150
|
+
const SetWebviewHTMLContentFn = *const fn (u32, [*:0]const u8) callconv(.C) void;
|
|
1151
|
+
const set_webview_html_content = lookupNativeSymbol(SetWebviewHTMLContentFn, "setWebviewHTMLContent") orelse return;
|
|
1152
|
+
set_webview_html_content(webview_id, html);
|
|
1153
|
+
loadURLInWebView(webview_id, "views://internal/index.html");
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
loadHTMLInWebView(webview_id, html);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
fn allocateOwnedJavascriptString(
|
|
1161
|
+
comptime fmt: []const u8,
|
|
1162
|
+
args: anytype,
|
|
1163
|
+
) ?[:0]u8 {
|
|
1164
|
+
return std.fmt.allocPrintZ(allocator, fmt, args) catch |err| {
|
|
1165
|
+
setLastError("Failed to allocate javascript string: {s}", .{@errorName(err)});
|
|
1166
|
+
return null;
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
fn quoteJavascriptString(value: [*:0]const u8) ?[]u8 {
|
|
1171
|
+
return std.json.stringifyAlloc(allocator, std.mem.span(value), .{}) catch |err| {
|
|
1172
|
+
setLastError("Failed to encode javascript string: {s}", .{@errorName(err)});
|
|
1173
|
+
return null;
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
fn buildHostWebviewEventJavascript(
|
|
1178
|
+
webview_id: u32,
|
|
1179
|
+
event_name: [*:0]const u8,
|
|
1180
|
+
detail: [*:0]const u8,
|
|
1181
|
+
) ?[:0]u8 {
|
|
1182
|
+
const encoded_event_name = quoteJavascriptString(event_name) orelse return null;
|
|
1183
|
+
defer allocator.free(encoded_event_name);
|
|
1184
|
+
|
|
1185
|
+
const event_name_slice = std.mem.span(event_name);
|
|
1186
|
+
if (std.mem.eql(u8, event_name_slice, "new-window-open") or std.mem.eql(u8, event_name_slice, "host-message")) {
|
|
1187
|
+
return allocateOwnedJavascriptString(
|
|
1188
|
+
"document.querySelector('#electrobun-webview-{d}').emit({s}, {s});",
|
|
1189
|
+
.{ webview_id, encoded_event_name, std.mem.span(detail) },
|
|
1190
|
+
);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
const encoded_detail = quoteJavascriptString(detail) orelse return null;
|
|
1194
|
+
defer allocator.free(encoded_detail);
|
|
1195
|
+
|
|
1196
|
+
return allocateOwnedJavascriptString(
|
|
1197
|
+
"document.querySelector('#electrobun-webview-{d}').emit({s}, {s});",
|
|
1198
|
+
.{ webview_id, encoded_event_name, encoded_detail },
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
fn buildInternalMessageJavascript(message_json: [*:0]const u8) ?[:0]u8 {
|
|
1203
|
+
return allocateOwnedJavascriptString(
|
|
1204
|
+
"window.__electrobun.receiveInternalMessageFromHost({s});",
|
|
1205
|
+
.{std.mem.span(message_json)},
|
|
1206
|
+
);
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
fn jsonString(value: std.json.Value) ?[]const u8 {
|
|
1210
|
+
return switch (value) {
|
|
1211
|
+
.string => |string_value| string_value,
|
|
1212
|
+
.number_string => |string_value| string_value,
|
|
1213
|
+
else => null,
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
fn jsonBool(value: std.json.Value) ?bool {
|
|
1218
|
+
return switch (value) {
|
|
1219
|
+
.bool => |bool_value| bool_value,
|
|
1220
|
+
else => null,
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
fn jsonU32(value: std.json.Value) ?u32 {
|
|
1225
|
+
return switch (value) {
|
|
1226
|
+
.integer => |integer_value| blk: {
|
|
1227
|
+
if (integer_value < 0 or integer_value > std.math.maxInt(u32)) break :blk null;
|
|
1228
|
+
break :blk @intCast(integer_value);
|
|
1229
|
+
},
|
|
1230
|
+
.float => |float_value| blk: {
|
|
1231
|
+
if (!std.math.isFinite(float_value)) break :blk null;
|
|
1232
|
+
if (@floor(float_value) != float_value) break :blk null;
|
|
1233
|
+
if (float_value < 0 or float_value > std.math.maxInt(u32)) break :blk null;
|
|
1234
|
+
break :blk @intFromFloat(float_value);
|
|
1235
|
+
},
|
|
1236
|
+
.number_string => |string_value| std.fmt.parseInt(u32, string_value, 10) catch null,
|
|
1237
|
+
else => null,
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
fn jsonF64(value: std.json.Value) ?f64 {
|
|
1242
|
+
return switch (value) {
|
|
1243
|
+
.integer => |integer_value| @floatFromInt(integer_value),
|
|
1244
|
+
.float => |float_value| float_value,
|
|
1245
|
+
.number_string => |string_value| std.fmt.parseFloat(f64, string_value) catch null,
|
|
1246
|
+
else => null,
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
fn duplicateSentinelString(value: []const u8) ?[:0]u8 {
|
|
1251
|
+
return allocator.dupeZ(u8, value) catch null;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
const InternalRect = struct {
|
|
1255
|
+
x: f64,
|
|
1256
|
+
y: f64,
|
|
1257
|
+
width: f64,
|
|
1258
|
+
height: f64,
|
|
1259
|
+
};
|
|
1260
|
+
|
|
1261
|
+
fn parseInternalRect(value: std.json.Value) ?InternalRect {
|
|
1262
|
+
if (value != .object) {
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
const object = value.object;
|
|
1267
|
+
const x = jsonF64(object.get("x") orelse return null) orelse return null;
|
|
1268
|
+
const y = jsonF64(object.get("y") orelse return null) orelse return null;
|
|
1269
|
+
const width = jsonF64(object.get("width") orelse return null) orelse return null;
|
|
1270
|
+
const height = jsonF64(object.get("height") orelse return null) orelse return null;
|
|
1271
|
+
return .{ .x = x, .y = y, .width = width, .height = height };
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
fn sendInternalBridgeResponse(
|
|
1275
|
+
host_webview_id: u32,
|
|
1276
|
+
request_id: []const u8,
|
|
1277
|
+
success: bool,
|
|
1278
|
+
payload: anytype,
|
|
1279
|
+
) void {
|
|
1280
|
+
const encoded_id = std.json.stringifyAlloc(allocator, request_id, .{}) catch return;
|
|
1281
|
+
defer allocator.free(encoded_id);
|
|
1282
|
+
|
|
1283
|
+
const payload_json = std.json.stringifyAlloc(allocator, payload, .{}) catch return;
|
|
1284
|
+
defer allocator.free(payload_json);
|
|
1285
|
+
|
|
1286
|
+
const response_json = std.fmt.allocPrintZ(
|
|
1287
|
+
allocator,
|
|
1288
|
+
"{{\"type\":\"response\",\"id\":{s},\"success\":{s},\"payload\":{s}}}",
|
|
1289
|
+
.{ encoded_id, if (success) "true" else "false", payload_json },
|
|
1290
|
+
) catch return;
|
|
1291
|
+
defer allocator.free(response_json);
|
|
1292
|
+
|
|
1293
|
+
_ = sendInternalMessageToWebview(host_webview_id, response_json.ptr);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
fn dispatchStoredWebviewEvent(payload: std.json.Value) void {
|
|
1297
|
+
if (payload != .object) {
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const handler = default_webview_callbacks.webview_event_handler orelse return;
|
|
1302
|
+
const payload_object = payload.object;
|
|
1303
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1304
|
+
const event_name = jsonString(payload_object.get("eventName") orelse return) orelse return;
|
|
1305
|
+
const detail = jsonString(payload_object.get("detail") orelse return) orelse return;
|
|
1306
|
+
|
|
1307
|
+
const event_name_z = allocator.dupeZ(u8, event_name) catch return;
|
|
1308
|
+
defer allocator.free(event_name_z);
|
|
1309
|
+
const detail_z = allocator.dupeZ(u8, detail) catch return;
|
|
1310
|
+
defer allocator.free(detail_z);
|
|
1311
|
+
|
|
1312
|
+
handler(webview_id, event_name_z.ptr, detail_z.ptr);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
fn createManagedWebviewFromInternalRequest(params: std.json.Value) ?u32 {
|
|
1316
|
+
if (params != .object) {
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const callbacks = default_webview_callbacks;
|
|
1321
|
+
if (callbacks.webview_event_handler == null or callbacks.event_bridge_handler == null) {
|
|
1322
|
+
return null;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
const params_object = params.object;
|
|
1326
|
+
const host_webview_id = jsonU32(params_object.get("hostWebviewId") orelse return null) orelse return null;
|
|
1327
|
+
const window_id = jsonU32(params_object.get("windowId") orelse return null) orelse return null;
|
|
1328
|
+
const renderer = jsonString(params_object.get("renderer") orelse return null) orelse return null;
|
|
1329
|
+
const partition = jsonString(params_object.get("partition") orelse return null) orelse "persist:default";
|
|
1330
|
+
const frame = parseInternalRect(params_object.get("frame") orelse return null) orelse return null;
|
|
1331
|
+
const sandbox = jsonBool(params_object.get("sandbox") orelse .{ .bool = false }) orelse false;
|
|
1332
|
+
const transparent = jsonBool(params_object.get("transparent") orelse .{ .bool = false }) orelse false;
|
|
1333
|
+
const passthrough = jsonBool(params_object.get("passthrough") orelse .{ .bool = false }) orelse false;
|
|
1334
|
+
const url_value = params_object.get("url");
|
|
1335
|
+
const html_value = params_object.get("html");
|
|
1336
|
+
const preload_value = params_object.get("preload");
|
|
1337
|
+
const navigation_rules_value = params_object.get("navigationRules");
|
|
1338
|
+
|
|
1339
|
+
var secret_key: WebviewSecretKey = undefined;
|
|
1340
|
+
std.crypto.random.bytes(&secret_key);
|
|
1341
|
+
|
|
1342
|
+
var secret_key_buffer = std.ArrayList(u8).init(allocator);
|
|
1343
|
+
defer secret_key_buffer.deinit();
|
|
1344
|
+
for (secret_key, 0..) |byte, index| {
|
|
1345
|
+
if (index > 0) {
|
|
1346
|
+
secret_key_buffer.append(',') catch return null;
|
|
1347
|
+
}
|
|
1348
|
+
secret_key_buffer.writer().print("{d}", .{byte}) catch return null;
|
|
1349
|
+
}
|
|
1350
|
+
secret_key_buffer.append(0) catch return null;
|
|
1351
|
+
const secret_key_z = secret_key_buffer.toOwnedSliceSentinel(0) catch return null;
|
|
1352
|
+
defer allocator.free(secret_key_z);
|
|
1353
|
+
|
|
1354
|
+
const url = if (url_value) |value| jsonString(value) orelse "" else "";
|
|
1355
|
+
const preload = if (preload_value) |value| jsonString(value) orelse "" else "";
|
|
1356
|
+
const renderer_z = duplicateSentinelString(renderer) orelse return null;
|
|
1357
|
+
defer allocator.free(renderer_z);
|
|
1358
|
+
const partition_z = duplicateSentinelString(partition) orelse return null;
|
|
1359
|
+
defer allocator.free(partition_z);
|
|
1360
|
+
const url_z = duplicateSentinelString(if (html_value != null and html_value.? != .null) "" else url) orelse return null;
|
|
1361
|
+
defer allocator.free(url_z);
|
|
1362
|
+
const preload_z = duplicateSentinelString(preload) orelse return null;
|
|
1363
|
+
defer allocator.free(preload_z);
|
|
1364
|
+
|
|
1365
|
+
const rules_json = if (navigation_rules_value) |value|
|
|
1366
|
+
if (value == .null)
|
|
1367
|
+
null
|
|
1368
|
+
else
|
|
1369
|
+
std.json.stringifyAlloc(allocator, value, .{}) catch return null
|
|
1370
|
+
else
|
|
1371
|
+
null;
|
|
1372
|
+
defer if (rules_json) |allocated| allocator.free(allocated);
|
|
1373
|
+
|
|
1374
|
+
const webview_id = createWebview(
|
|
1375
|
+
window_id,
|
|
1376
|
+
host_webview_id,
|
|
1377
|
+
renderer_z.ptr,
|
|
1378
|
+
url_z.ptr,
|
|
1379
|
+
frame.x,
|
|
1380
|
+
frame.y,
|
|
1381
|
+
frame.width,
|
|
1382
|
+
frame.height,
|
|
1383
|
+
false,
|
|
1384
|
+
partition_z.ptr,
|
|
1385
|
+
callbacks.navigation_callback,
|
|
1386
|
+
callbacks.webview_event_handler,
|
|
1387
|
+
callbacks.event_bridge_handler,
|
|
1388
|
+
null,
|
|
1389
|
+
null,
|
|
1390
|
+
secret_key_z.ptr,
|
|
1391
|
+
preload_z.ptr,
|
|
1392
|
+
"",
|
|
1393
|
+
sandbox,
|
|
1394
|
+
transparent,
|
|
1395
|
+
passthrough,
|
|
1396
|
+
);
|
|
1397
|
+
|
|
1398
|
+
if (webview_id == 0) {
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
if (html_value) |value| {
|
|
1403
|
+
const html = jsonString(value) orelse "";
|
|
1404
|
+
if (html.len > 0) {
|
|
1405
|
+
const html_z = duplicateSentinelString(html) orelse return webview_id;
|
|
1406
|
+
defer allocator.free(html_z);
|
|
1407
|
+
loadManagedHTMLForWebview(webview_id, html_z.ptr);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
if (rules_json) |allocated| {
|
|
1412
|
+
const rules_json_z = duplicateSentinelString(allocated) orelse return webview_id;
|
|
1413
|
+
defer allocator.free(rules_json_z);
|
|
1414
|
+
setWebviewNavigationRules(webview_id, rules_json_z.ptr);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
return webview_id;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
fn createManagedWgpuViewFromInternalRequest(params: std.json.Value) ?u32 {
|
|
1421
|
+
if (params != .object) {
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
const params_object = params.object;
|
|
1426
|
+
const window_id = jsonU32(params_object.get("windowId") orelse return null) orelse return null;
|
|
1427
|
+
const frame = parseInternalRect(params_object.get("frame") orelse return null) orelse return null;
|
|
1428
|
+
const transparent = jsonBool(params_object.get("transparent") orelse .{ .bool = false }) orelse false;
|
|
1429
|
+
const passthrough = jsonBool(params_object.get("passthrough") orelse .{ .bool = false }) orelse false;
|
|
1430
|
+
|
|
1431
|
+
const wgpu_view_id = createWGPUView(
|
|
1432
|
+
window_id,
|
|
1433
|
+
frame.x,
|
|
1434
|
+
frame.y,
|
|
1435
|
+
frame.width,
|
|
1436
|
+
frame.height,
|
|
1437
|
+
false,
|
|
1438
|
+
transparent,
|
|
1439
|
+
passthrough,
|
|
1440
|
+
);
|
|
1441
|
+
if (wgpu_view_id == 0) {
|
|
1442
|
+
return null;
|
|
1443
|
+
}
|
|
1444
|
+
return wgpu_view_id;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
fn handleInternalRequest(request_object: std.json.ObjectMap) void {
|
|
1448
|
+
const method = jsonString(request_object.get("method") orelse return) orelse return;
|
|
1449
|
+
const request_id = jsonString(request_object.get("id") orelse return) orelse return;
|
|
1450
|
+
const host_webview_id = jsonU32(request_object.get("hostWebviewId") orelse return) orelse return;
|
|
1451
|
+
const params = request_object.get("params") orelse .null;
|
|
1452
|
+
|
|
1453
|
+
if (std.mem.eql(u8, method, "webviewTagInit")) {
|
|
1454
|
+
const webview_id = createManagedWebviewFromInternalRequest(params) orelse {
|
|
1455
|
+
sendInternalBridgeResponse(host_webview_id, request_id, false, "Failed to create webview tag");
|
|
1456
|
+
return;
|
|
1457
|
+
};
|
|
1458
|
+
sendInternalBridgeResponse(host_webview_id, request_id, true, webview_id);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
if (std.mem.eql(u8, method, "wgpuTagInit")) {
|
|
1463
|
+
const wgpu_view_id = createManagedWgpuViewFromInternalRequest(params) orelse {
|
|
1464
|
+
sendInternalBridgeResponse(host_webview_id, request_id, false, "Failed to create WGPU view");
|
|
1465
|
+
return;
|
|
1466
|
+
};
|
|
1467
|
+
sendInternalBridgeResponse(host_webview_id, request_id, true, wgpu_view_id);
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
if (std.mem.eql(u8, method, "webviewTagCanGoBack")) {
|
|
1472
|
+
if (params != .object) return;
|
|
1473
|
+
const webview_id = jsonU32(params.object.get("id") orelse return) orelse return;
|
|
1474
|
+
sendInternalBridgeResponse(host_webview_id, request_id, true, webviewCanGoBack(webview_id));
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
if (std.mem.eql(u8, method, "webviewTagCanGoForward")) {
|
|
1479
|
+
if (params != .object) return;
|
|
1480
|
+
const webview_id = jsonU32(params.object.get("id") orelse return) orelse return;
|
|
1481
|
+
sendInternalBridgeResponse(host_webview_id, request_id, true, webviewCanGoForward(webview_id));
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
sendInternalBridgeResponse(host_webview_id, request_id, false, "Unknown internal request");
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
fn handleInternalMessage(message_id: []const u8, payload: std.json.Value) void {
|
|
1489
|
+
if (std.mem.eql(u8, message_id, "webviewEvent")) {
|
|
1490
|
+
dispatchStoredWebviewEvent(payload);
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
if (payload != .object) {
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
const payload_object = payload.object;
|
|
1499
|
+
|
|
1500
|
+
if (std.mem.eql(u8, message_id, "webviewTagResize")) {
|
|
1501
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1502
|
+
const frame = parseInternalRect(payload_object.get("frame") orelse return) orelse return;
|
|
1503
|
+
const masks = if (payload_object.get("masks")) |value| jsonString(value) orelse "[]" else "[]";
|
|
1504
|
+
const masks_z = duplicateSentinelString(masks) orelse return;
|
|
1505
|
+
defer allocator.free(masks_z);
|
|
1506
|
+
resizeWebview(webview_id, frame.x, frame.y, frame.width, frame.height, masks_z.ptr);
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
if (std.mem.eql(u8, message_id, "wgpuTagResize")) {
|
|
1511
|
+
const wgpu_view_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1512
|
+
const frame = parseInternalRect(payload_object.get("frame") orelse return) orelse return;
|
|
1513
|
+
const masks = if (payload_object.get("masks")) |value| jsonString(value) orelse "[]" else "[]";
|
|
1514
|
+
const masks_z = duplicateSentinelString(masks) orelse return;
|
|
1515
|
+
defer allocator.free(masks_z);
|
|
1516
|
+
resizeWGPUView(wgpu_view_id, frame.x, frame.y, frame.width, frame.height, masks_z.ptr);
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if (std.mem.eql(u8, message_id, "webviewTagUpdateSrc")) {
|
|
1521
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1522
|
+
const url = jsonString(payload_object.get("url") orelse return) orelse return;
|
|
1523
|
+
const url_z = duplicateSentinelString(url) orelse return;
|
|
1524
|
+
defer allocator.free(url_z);
|
|
1525
|
+
loadURLInWebView(webview_id, url_z.ptr);
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
if (std.mem.eql(u8, message_id, "webviewTagUpdateHtml")) {
|
|
1530
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1531
|
+
const html = jsonString(payload_object.get("html") orelse return) orelse return;
|
|
1532
|
+
const html_z = duplicateSentinelString(html) orelse return;
|
|
1533
|
+
defer allocator.free(html_z);
|
|
1534
|
+
loadManagedHTMLForWebview(webview_id, html_z.ptr);
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
if (std.mem.eql(u8, message_id, "webviewTagUpdatePreload")) {
|
|
1539
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1540
|
+
const preload = jsonString(payload_object.get("preload") orelse return) orelse return;
|
|
1541
|
+
const preload_z = duplicateSentinelString(preload) orelse return;
|
|
1542
|
+
defer allocator.free(preload_z);
|
|
1543
|
+
updatePreloadScriptToWebView(webview_id, "electrobun_custom_preload_script", preload_z.ptr, true);
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
if (std.mem.eql(u8, message_id, "webviewTagGoBack")) {
|
|
1548
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1549
|
+
webviewGoBack(webview_id);
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
if (std.mem.eql(u8, message_id, "webviewTagGoForward")) {
|
|
1554
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1555
|
+
webviewGoForward(webview_id);
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
if (std.mem.eql(u8, message_id, "webviewTagReload")) {
|
|
1560
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1561
|
+
webviewReload(webview_id);
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
if (std.mem.eql(u8, message_id, "webviewTagRemove")) {
|
|
1566
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1567
|
+
webviewRemove(webview_id);
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
if (std.mem.eql(u8, message_id, "startWindowMove")) {
|
|
1572
|
+
const window_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1573
|
+
beginWindowMove(window_id);
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (std.mem.eql(u8, message_id, "stopWindowMove")) {
|
|
1578
|
+
endWindowMove();
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
if (std.mem.eql(u8, message_id, "webviewTagSetTransparent")) {
|
|
1583
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1584
|
+
const transparent = jsonBool(payload_object.get("transparent") orelse return) orelse return;
|
|
1585
|
+
webviewSetTransparent(webview_id, transparent);
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
if (std.mem.eql(u8, message_id, "wgpuTagSetTransparent")) {
|
|
1590
|
+
const wgpu_view_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1591
|
+
const transparent = jsonBool(payload_object.get("transparent") orelse return) orelse return;
|
|
1592
|
+
setWGPUViewTransparent(wgpu_view_id, transparent);
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
if (std.mem.eql(u8, message_id, "webviewTagSetPassthrough")) {
|
|
1597
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1598
|
+
const passthrough = jsonBool(payload_object.get("enablePassthrough") orelse return) orelse return;
|
|
1599
|
+
webviewSetPassthrough(webview_id, passthrough);
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
if (std.mem.eql(u8, message_id, "wgpuTagSetPassthrough")) {
|
|
1604
|
+
const wgpu_view_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1605
|
+
const passthrough = jsonBool(payload_object.get("passthrough") orelse return) orelse return;
|
|
1606
|
+
setWGPUViewPassthrough(wgpu_view_id, passthrough);
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
if (std.mem.eql(u8, message_id, "webviewTagSetHidden")) {
|
|
1611
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1612
|
+
const hidden = jsonBool(payload_object.get("hidden") orelse return) orelse return;
|
|
1613
|
+
webviewSetHidden(webview_id, hidden);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
if (std.mem.eql(u8, message_id, "wgpuTagSetHidden")) {
|
|
1618
|
+
const wgpu_view_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1619
|
+
const hidden = jsonBool(payload_object.get("hidden") orelse return) orelse return;
|
|
1620
|
+
setWGPUViewHidden(wgpu_view_id, hidden);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
if (std.mem.eql(u8, message_id, "wgpuTagRemove")) {
|
|
1625
|
+
const wgpu_view_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1626
|
+
removeWGPUView(wgpu_view_id);
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
if (std.mem.eql(u8, message_id, "wgpuTagRunTest")) {
|
|
1631
|
+
const wgpu_view_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1632
|
+
runWGPUViewTest(wgpu_view_id);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
if (std.mem.eql(u8, message_id, "webviewTagSetNavigationRules")) {
|
|
1637
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1638
|
+
const rules_value = payload_object.get("rules") orelse return;
|
|
1639
|
+
const rules_json = std.json.stringifyAlloc(allocator, rules_value, .{}) catch return;
|
|
1640
|
+
defer allocator.free(rules_json);
|
|
1641
|
+
const rules_json_z = duplicateSentinelString(rules_json) orelse return;
|
|
1642
|
+
defer allocator.free(rules_json_z);
|
|
1643
|
+
setWebviewNavigationRules(webview_id, rules_json_z.ptr);
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
if (std.mem.eql(u8, message_id, "webviewTagFindInPage")) {
|
|
1648
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1649
|
+
const search_text = jsonString(payload_object.get("searchText") orelse return) orelse return;
|
|
1650
|
+
const forward = jsonBool(payload_object.get("forward") orelse return) orelse return;
|
|
1651
|
+
const match_case = jsonBool(payload_object.get("matchCase") orelse return) orelse return;
|
|
1652
|
+
const search_text_z = duplicateSentinelString(search_text) orelse return;
|
|
1653
|
+
defer allocator.free(search_text_z);
|
|
1654
|
+
webviewFindInPage(webview_id, search_text_z.ptr, forward, match_case);
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
if (std.mem.eql(u8, message_id, "webviewTagStopFind")) {
|
|
1659
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1660
|
+
webviewStopFind(webview_id);
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
if (std.mem.eql(u8, message_id, "webviewTagOpenDevTools")) {
|
|
1665
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1666
|
+
webviewOpenDevTools(webview_id);
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
if (std.mem.eql(u8, message_id, "webviewTagCloseDevTools")) {
|
|
1671
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1672
|
+
webviewCloseDevTools(webview_id);
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
if (std.mem.eql(u8, message_id, "webviewTagToggleDevTools")) {
|
|
1677
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1678
|
+
webviewToggleDevTools(webview_id);
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
if (std.mem.eql(u8, message_id, "webviewTagExecuteJavascript")) {
|
|
1683
|
+
const webview_id = jsonU32(payload_object.get("id") orelse return) orelse return;
|
|
1684
|
+
const js = jsonString(payload_object.get("js") orelse return) orelse return;
|
|
1685
|
+
const js_z = duplicateSentinelString(js) orelse return;
|
|
1686
|
+
defer allocator.free(js_z);
|
|
1687
|
+
evaluateJavaScriptWithNoCompletion(webview_id, js_z.ptr);
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
fn handleInternalBridgePacket(packet: std.json.Value) void {
|
|
1693
|
+
if (packet != .object) {
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
const object = packet.object;
|
|
1698
|
+
const packet_type = jsonString(object.get("type") orelse return) orelse return;
|
|
1699
|
+
|
|
1700
|
+
if (std.mem.eql(u8, packet_type, "request")) {
|
|
1701
|
+
handleInternalRequest(object);
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
if (std.mem.eql(u8, packet_type, "message")) {
|
|
1706
|
+
const message_id = jsonString(object.get("id") orelse return) orelse return;
|
|
1707
|
+
const payload = object.get("payload") orelse .null;
|
|
1708
|
+
handleInternalMessage(message_id, payload);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
fn processInternalBridgeBatch(message_json: []const u8) void {
|
|
1713
|
+
var parsed = std.json.parseFromSlice(std.json.Value, allocator, message_json, .{}) catch return;
|
|
1714
|
+
defer parsed.deinit();
|
|
1715
|
+
|
|
1716
|
+
if (parsed.value == .array) {
|
|
1717
|
+
for (parsed.value.array.items) |item| {
|
|
1718
|
+
const item_json = jsonString(item) orelse continue;
|
|
1719
|
+
var nested = std.json.parseFromSlice(std.json.Value, allocator, item_json, .{}) catch continue;
|
|
1720
|
+
defer nested.deinit();
|
|
1721
|
+
handleInternalBridgePacket(nested.value);
|
|
1722
|
+
}
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
handleInternalBridgePacket(parsed.value);
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
fn internalBridgeCoreTrampoline(_webview_id: u32, message: [*:0]const u8) callconv(.C) void {
|
|
1730
|
+
_ = _webview_id;
|
|
1731
|
+
processInternalBridgeBatch(std.mem.span(message));
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
fn windowCloseTrampoline(window_id: u32) callconv(.C) void {
|
|
1735
|
+
var close_handler: ?WindowCloseHandler = null;
|
|
1736
|
+
var child_webview_ids = std.ArrayList(u32).init(allocator);
|
|
1737
|
+
defer child_webview_ids.deinit();
|
|
1738
|
+
var child_wgpu_view_ids = std.ArrayList(u32).init(allocator);
|
|
1739
|
+
defer child_wgpu_view_ids.deinit();
|
|
1740
|
+
|
|
1741
|
+
window_registry_mutex.lock();
|
|
1742
|
+
if (window_registry.fetchRemove(window_id)) |removed| {
|
|
1743
|
+
close_handler = removed.value.close_handler;
|
|
1744
|
+
}
|
|
1745
|
+
window_registry_mutex.unlock();
|
|
1746
|
+
|
|
1747
|
+
collectWebviewIdsForWindow(window_id, &child_webview_ids);
|
|
1748
|
+
for (child_webview_ids.items) |webview_id| {
|
|
1749
|
+
webviewRemove(webview_id);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
collectWgpuViewIdsForWindow(window_id, &child_wgpu_view_ids);
|
|
1753
|
+
for (child_wgpu_view_ids.items) |wgpu_view_id| {
|
|
1754
|
+
removeWGPUView(wgpu_view_id);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
if (close_handler) |handler| {
|
|
1758
|
+
handler(window_id);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
if (exit_on_last_window_closed and !hasOpenWindows()) {
|
|
1762
|
+
if (managed_quit_requested_handler) |handler| {
|
|
1763
|
+
handler();
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
fn windowMoveTrampoline(window_id: u32, x: f64, y: f64) callconv(.C) void {
|
|
1769
|
+
const state = lookupWindowState(window_id) orelse return;
|
|
1770
|
+
if (state.move_handler) |handler| {
|
|
1771
|
+
handler(window_id, x, y);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
fn windowResizeTrampoline(window_id: u32, x: f64, y: f64, width: f64, height: f64) callconv(.C) void {
|
|
1776
|
+
const state = lookupWindowState(window_id) orelse return;
|
|
1777
|
+
if (state.resize_handler) |handler| {
|
|
1778
|
+
handler(window_id, x, y, width, height);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
fn windowFocusTrampoline(window_id: u32) callconv(.C) void {
|
|
1783
|
+
const state = lookupWindowState(window_id) orelse return;
|
|
1784
|
+
if (state.focus_handler) |handler| {
|
|
1785
|
+
handler(window_id);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
fn windowBlurTrampoline(window_id: u32) callconv(.C) void {
|
|
1790
|
+
const state = lookupWindowState(window_id) orelse return;
|
|
1791
|
+
if (state.blur_handler) |handler| {
|
|
1792
|
+
handler(window_id);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
fn windowKeyTrampoline(window_id: u32, key_code: u32, modifiers: u32, is_down: u32, is_repeat: u32) callconv(.C) void {
|
|
1797
|
+
const state = lookupWindowState(window_id) orelse return;
|
|
1798
|
+
if (state.key_handler) |handler| {
|
|
1799
|
+
handler(window_id, key_code, modifiers, is_down, is_repeat);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
fn managedQuitRequestedTrampoline() callconv(.C) void {
|
|
1804
|
+
if (managed_quit_requested_handler) |handler| {
|
|
1805
|
+
handler();
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
export fn electrobun_core_run_main_thread(
|
|
1810
|
+
identifier: [*:0]const u8,
|
|
1811
|
+
name: [*:0]const u8,
|
|
1812
|
+
channel: [*:0]const u8,
|
|
1813
|
+
exit_code: c_int,
|
|
1814
|
+
) c_int {
|
|
1815
|
+
clearLastError();
|
|
1816
|
+
|
|
1817
|
+
if (!ensureNativeWrapperLoaded()) {
|
|
1818
|
+
return 1;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
native_wrapper_state.start_event_loop(identifier, name, channel);
|
|
1822
|
+
native_wrapper_state.force_exit(exit_code);
|
|
1823
|
+
return 0;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
export fn getWindowStyle(
|
|
1827
|
+
borderless: bool,
|
|
1828
|
+
titled: bool,
|
|
1829
|
+
closable: bool,
|
|
1830
|
+
miniaturizable: bool,
|
|
1831
|
+
resizable: bool,
|
|
1832
|
+
unified_title_and_toolbar: bool,
|
|
1833
|
+
full_screen: bool,
|
|
1834
|
+
full_size_content_view: bool,
|
|
1835
|
+
utility_window: bool,
|
|
1836
|
+
doc_modal_window: bool,
|
|
1837
|
+
nonactivating_panel: bool,
|
|
1838
|
+
hud_window: bool,
|
|
1839
|
+
) u32 {
|
|
1840
|
+
const GetWindowStyleFn = *const fn (
|
|
1841
|
+
bool,
|
|
1842
|
+
bool,
|
|
1843
|
+
bool,
|
|
1844
|
+
bool,
|
|
1845
|
+
bool,
|
|
1846
|
+
bool,
|
|
1847
|
+
bool,
|
|
1848
|
+
bool,
|
|
1849
|
+
bool,
|
|
1850
|
+
bool,
|
|
1851
|
+
bool,
|
|
1852
|
+
bool,
|
|
1853
|
+
) callconv(.C) u32;
|
|
1854
|
+
|
|
1855
|
+
const get_window_style = lookupNativeSymbol(GetWindowStyleFn, "getWindowStyle") orelse return 0;
|
|
1856
|
+
return get_window_style(
|
|
1857
|
+
borderless,
|
|
1858
|
+
titled,
|
|
1859
|
+
closable,
|
|
1860
|
+
miniaturizable,
|
|
1861
|
+
resizable,
|
|
1862
|
+
unified_title_and_toolbar,
|
|
1863
|
+
full_screen,
|
|
1864
|
+
full_size_content_view,
|
|
1865
|
+
utility_window,
|
|
1866
|
+
doc_modal_window,
|
|
1867
|
+
nonactivating_panel,
|
|
1868
|
+
hud_window,
|
|
1869
|
+
);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
export fn createWindow(
|
|
1873
|
+
x: f64,
|
|
1874
|
+
y: f64,
|
|
1875
|
+
width: f64,
|
|
1876
|
+
height: f64,
|
|
1877
|
+
style_mask: u32,
|
|
1878
|
+
title_bar_style: [*:0]const u8,
|
|
1879
|
+
transparent: bool,
|
|
1880
|
+
title: [*:0]const u8,
|
|
1881
|
+
hidden: bool,
|
|
1882
|
+
activate: bool,
|
|
1883
|
+
traffic_light_offset_x: f64,
|
|
1884
|
+
traffic_light_offset_y: f64,
|
|
1885
|
+
close_handler: ?WindowCloseHandler,
|
|
1886
|
+
move_handler: ?WindowMoveHandler,
|
|
1887
|
+
resize_handler: ?WindowResizeHandler,
|
|
1888
|
+
focus_handler: ?WindowFocusHandler,
|
|
1889
|
+
blur_handler: ?WindowBlurHandler,
|
|
1890
|
+
key_handler: ?WindowKeyHandler,
|
|
1891
|
+
) u32 {
|
|
1892
|
+
clearLastError();
|
|
1893
|
+
|
|
1894
|
+
const CreateWindowFn = *const fn (
|
|
1895
|
+
u32,
|
|
1896
|
+
f64,
|
|
1897
|
+
f64,
|
|
1898
|
+
f64,
|
|
1899
|
+
f64,
|
|
1900
|
+
u32,
|
|
1901
|
+
[*:0]const u8,
|
|
1902
|
+
bool,
|
|
1903
|
+
f64,
|
|
1904
|
+
f64,
|
|
1905
|
+
?WindowCloseHandler,
|
|
1906
|
+
?WindowMoveHandler,
|
|
1907
|
+
?WindowResizeHandler,
|
|
1908
|
+
?WindowFocusHandler,
|
|
1909
|
+
?WindowBlurHandler,
|
|
1910
|
+
?WindowKeyHandler,
|
|
1911
|
+
) callconv(.C) WindowPtr;
|
|
1912
|
+
const SetWindowTitleFn = *const fn (WindowPtr, [*:0]const u8) callconv(.C) void;
|
|
1913
|
+
const ShowWindowFn = *const fn (WindowPtr, bool) callconv(.C) void;
|
|
1914
|
+
|
|
1915
|
+
const create_window = lookupNativeSymbol(
|
|
1916
|
+
CreateWindowFn,
|
|
1917
|
+
"createWindowWithFrameAndStyleFromWorker",
|
|
1918
|
+
) orelse return 0;
|
|
1919
|
+
const set_window_title = lookupNativeSymbol(SetWindowTitleFn, "setWindowTitle") orelse return 0;
|
|
1920
|
+
const show_window = lookupNativeSymbol(ShowWindowFn, "showWindow") orelse return 0;
|
|
1921
|
+
|
|
1922
|
+
window_registry_mutex.lock();
|
|
1923
|
+
const start_id = next_window_id;
|
|
1924
|
+
var window_id = next_window_id;
|
|
1925
|
+
|
|
1926
|
+
while (window_id == 0 or window_registry.contains(window_id)) {
|
|
1927
|
+
window_id +%= 1;
|
|
1928
|
+
if (window_id == 0) {
|
|
1929
|
+
window_id = 1;
|
|
1930
|
+
}
|
|
1931
|
+
if (window_id == start_id) {
|
|
1932
|
+
window_registry_mutex.unlock();
|
|
1933
|
+
setLastError("Failed to allocate window id", .{});
|
|
1934
|
+
return 0;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
next_window_id = window_id +% 1;
|
|
1939
|
+
if (next_window_id == 0) {
|
|
1940
|
+
next_window_id = 1;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
window_registry.put(window_id, .{
|
|
1944
|
+
.ptr = null,
|
|
1945
|
+
.transparent = transparent,
|
|
1946
|
+
.close_handler = close_handler,
|
|
1947
|
+
.move_handler = move_handler,
|
|
1948
|
+
.resize_handler = resize_handler,
|
|
1949
|
+
.focus_handler = focus_handler,
|
|
1950
|
+
.blur_handler = blur_handler,
|
|
1951
|
+
.key_handler = key_handler,
|
|
1952
|
+
}) catch |err| {
|
|
1953
|
+
window_registry_mutex.unlock();
|
|
1954
|
+
setLastError("Failed to store window state: {s}", .{@errorName(err)});
|
|
1955
|
+
return 0;
|
|
1956
|
+
};
|
|
1957
|
+
window_registry_mutex.unlock();
|
|
1958
|
+
|
|
1959
|
+
const window_ptr = create_window(
|
|
1960
|
+
window_id,
|
|
1961
|
+
x,
|
|
1962
|
+
y,
|
|
1963
|
+
width,
|
|
1964
|
+
height,
|
|
1965
|
+
style_mask,
|
|
1966
|
+
title_bar_style,
|
|
1967
|
+
transparent,
|
|
1968
|
+
traffic_light_offset_x,
|
|
1969
|
+
traffic_light_offset_y,
|
|
1970
|
+
windowCloseTrampoline,
|
|
1971
|
+
windowMoveTrampoline,
|
|
1972
|
+
windowResizeTrampoline,
|
|
1973
|
+
windowFocusTrampoline,
|
|
1974
|
+
windowBlurTrampoline,
|
|
1975
|
+
windowKeyTrampoline,
|
|
1976
|
+
);
|
|
1977
|
+
|
|
1978
|
+
if (window_ptr == null) {
|
|
1979
|
+
window_registry_mutex.lock();
|
|
1980
|
+
_ = window_registry.remove(window_id);
|
|
1981
|
+
window_registry_mutex.unlock();
|
|
1982
|
+
setLastError("Failed to create window", .{});
|
|
1983
|
+
return 0;
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
window_registry_mutex.lock();
|
|
1987
|
+
const state = window_registry.getPtr(window_id) orelse {
|
|
1988
|
+
window_registry_mutex.unlock();
|
|
1989
|
+
setLastError("Window {d} disappeared during creation", .{window_id});
|
|
1990
|
+
return 0;
|
|
1991
|
+
};
|
|
1992
|
+
state.ptr = window_ptr;
|
|
1993
|
+
window_registry_mutex.unlock();
|
|
1994
|
+
|
|
1995
|
+
set_window_title(window_ptr, title);
|
|
1996
|
+
if (!hidden) {
|
|
1997
|
+
show_window(window_ptr, activate);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
return window_id;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
export fn getWindowPointer(window_id: u32) WindowPtr {
|
|
2004
|
+
clearLastError();
|
|
2005
|
+
return lookupWindowPtr(window_id);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
export fn setWindowTitle(window_id: u32, title: [*:0]const u8) void {
|
|
2009
|
+
const SetWindowTitleFn = *const fn (WindowPtr, [*:0]const u8) callconv(.C) void;
|
|
2010
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2011
|
+
const set_window_title = lookupNativeSymbol(SetWindowTitleFn, "setWindowTitle") orelse return;
|
|
2012
|
+
set_window_title(window, title);
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
export fn minimizeWindow(window_id: u32) void {
|
|
2016
|
+
const MinimizeWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2017
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2018
|
+
const minimize_window = lookupNativeSymbol(MinimizeWindowFn, "minimizeWindow") orelse return;
|
|
2019
|
+
minimize_window(window);
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
export fn restoreWindow(window_id: u32) void {
|
|
2023
|
+
const RestoreWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2024
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2025
|
+
const restore_window = lookupNativeSymbol(RestoreWindowFn, "restoreWindow") orelse return;
|
|
2026
|
+
restore_window(window);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
export fn isWindowMinimized(window_id: u32) bool {
|
|
2030
|
+
const IsWindowMinimizedFn = *const fn (WindowPtr) callconv(.C) bool;
|
|
2031
|
+
const window = lookupWindowPtr(window_id) orelse return false;
|
|
2032
|
+
const is_window_minimized = lookupNativeSymbol(IsWindowMinimizedFn, "isWindowMinimized") orelse return false;
|
|
2033
|
+
return is_window_minimized(window);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
export fn maximizeWindow(window_id: u32) void {
|
|
2037
|
+
const MaximizeWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2038
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2039
|
+
const maximize_window = lookupNativeSymbol(MaximizeWindowFn, "maximizeWindow") orelse return;
|
|
2040
|
+
maximize_window(window);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
export fn unmaximizeWindow(window_id: u32) void {
|
|
2044
|
+
const UnmaximizeWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2045
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2046
|
+
const unmaximize_window = lookupNativeSymbol(UnmaximizeWindowFn, "unmaximizeWindow") orelse return;
|
|
2047
|
+
unmaximize_window(window);
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
export fn isWindowMaximized(window_id: u32) bool {
|
|
2051
|
+
const IsWindowMaximizedFn = *const fn (WindowPtr) callconv(.C) bool;
|
|
2052
|
+
const window = lookupWindowPtr(window_id) orelse return false;
|
|
2053
|
+
const is_window_maximized = lookupNativeSymbol(IsWindowMaximizedFn, "isWindowMaximized") orelse return false;
|
|
2054
|
+
return is_window_maximized(window);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
export fn setWindowFullScreen(window_id: u32, full_screen: bool) void {
|
|
2058
|
+
const SetWindowFullScreenFn = *const fn (WindowPtr, bool) callconv(.C) void;
|
|
2059
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2060
|
+
const set_window_full_screen = lookupNativeSymbol(SetWindowFullScreenFn, "setWindowFullScreen") orelse return;
|
|
2061
|
+
set_window_full_screen(window, full_screen);
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
export fn isWindowFullScreen(window_id: u32) bool {
|
|
2065
|
+
const IsWindowFullScreenFn = *const fn (WindowPtr) callconv(.C) bool;
|
|
2066
|
+
const window = lookupWindowPtr(window_id) orelse return false;
|
|
2067
|
+
const is_window_full_screen = lookupNativeSymbol(IsWindowFullScreenFn, "isWindowFullScreen") orelse return false;
|
|
2068
|
+
return is_window_full_screen(window);
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
export fn setWindowAlwaysOnTop(window_id: u32, always_on_top: bool) void {
|
|
2072
|
+
const SetWindowAlwaysOnTopFn = *const fn (WindowPtr, bool) callconv(.C) void;
|
|
2073
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2074
|
+
const set_window_always_on_top = lookupNativeSymbol(SetWindowAlwaysOnTopFn, "setWindowAlwaysOnTop") orelse return;
|
|
2075
|
+
set_window_always_on_top(window, always_on_top);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
export fn isWindowAlwaysOnTop(window_id: u32) bool {
|
|
2079
|
+
const IsWindowAlwaysOnTopFn = *const fn (WindowPtr) callconv(.C) bool;
|
|
2080
|
+
const window = lookupWindowPtr(window_id) orelse return false;
|
|
2081
|
+
const is_window_always_on_top = lookupNativeSymbol(IsWindowAlwaysOnTopFn, "isWindowAlwaysOnTop") orelse return false;
|
|
2082
|
+
return is_window_always_on_top(window);
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
export fn setWindowVisibleOnAllWorkspaces(window_id: u32, visible_on_all_workspaces: bool) void {
|
|
2086
|
+
const SetWindowVisibleOnAllWorkspacesFn = *const fn (WindowPtr, bool) callconv(.C) void;
|
|
2087
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2088
|
+
const set_window_visible_on_all_workspaces = lookupNativeSymbol(
|
|
2089
|
+
SetWindowVisibleOnAllWorkspacesFn,
|
|
2090
|
+
"setWindowVisibleOnAllWorkspaces",
|
|
2091
|
+
) orelse return;
|
|
2092
|
+
set_window_visible_on_all_workspaces(window, visible_on_all_workspaces);
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
export fn isWindowVisibleOnAllWorkspaces(window_id: u32) bool {
|
|
2096
|
+
const IsWindowVisibleOnAllWorkspacesFn = *const fn (WindowPtr) callconv(.C) bool;
|
|
2097
|
+
const window = lookupWindowPtr(window_id) orelse return false;
|
|
2098
|
+
const is_window_visible_on_all_workspaces = lookupNativeSymbol(
|
|
2099
|
+
IsWindowVisibleOnAllWorkspacesFn,
|
|
2100
|
+
"isWindowVisibleOnAllWorkspaces",
|
|
2101
|
+
) orelse return false;
|
|
2102
|
+
return is_window_visible_on_all_workspaces(window);
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
export fn setWindowButtonPosition(window_id: u32, x: f64, y: f64) void {
|
|
2106
|
+
const SetWindowButtonPositionFn = *const fn (WindowPtr, f64, f64) callconv(.C) void;
|
|
2107
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2108
|
+
const set_window_button_position = lookupNativeSymbol(
|
|
2109
|
+
SetWindowButtonPositionFn,
|
|
2110
|
+
"setWindowButtonPosition",
|
|
2111
|
+
) orelse return;
|
|
2112
|
+
set_window_button_position(window, x, y);
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
export fn showWindow(window_id: u32, activate: bool) void {
|
|
2116
|
+
const ShowWindowFn = *const fn (WindowPtr, bool) callconv(.C) void;
|
|
2117
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2118
|
+
const show_window = lookupNativeSymbol(ShowWindowFn, "showWindow") orelse return;
|
|
2119
|
+
show_window(window, activate);
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
export fn activateWindow(window_id: u32) void {
|
|
2123
|
+
const ActivateWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2124
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2125
|
+
const activate_window = lookupNativeSymbol(ActivateWindowFn, "activateWindow") orelse return;
|
|
2126
|
+
activate_window(window);
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
export fn hideWindow(window_id: u32) void {
|
|
2130
|
+
const HideWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2131
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2132
|
+
const hide_window = lookupNativeSymbol(HideWindowFn, "hideWindow") orelse return;
|
|
2133
|
+
hide_window(window);
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
export fn closeWindow(window_id: u32) void {
|
|
2137
|
+
const CloseWindowFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2138
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2139
|
+
const close_window = lookupNativeSymbol(CloseWindowFn, "closeWindow") orelse return;
|
|
2140
|
+
close_window(window);
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
export fn setWindowPosition(window_id: u32, x: f64, y: f64) void {
|
|
2144
|
+
const SetWindowPositionFn = *const fn (WindowPtr, f64, f64) callconv(.C) void;
|
|
2145
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2146
|
+
const set_window_position = lookupNativeSymbol(SetWindowPositionFn, "setWindowPosition") orelse return;
|
|
2147
|
+
set_window_position(window, x, y);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
export fn setWindowSize(window_id: u32, width: f64, height: f64) void {
|
|
2151
|
+
const SetWindowSizeFn = *const fn (WindowPtr, f64, f64) callconv(.C) void;
|
|
2152
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2153
|
+
const set_window_size = lookupNativeSymbol(SetWindowSizeFn, "setWindowSize") orelse return;
|
|
2154
|
+
set_window_size(window, width, height);
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
export fn setWindowFrame(window_id: u32, x: f64, y: f64, width: f64, height: f64) void {
|
|
2158
|
+
const SetWindowFrameFn = *const fn (WindowPtr, f64, f64, f64, f64) callconv(.C) void;
|
|
2159
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2160
|
+
const set_window_frame = lookupNativeSymbol(SetWindowFrameFn, "setWindowFrame") orelse return;
|
|
2161
|
+
set_window_frame(window, x, y, width, height);
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
export fn getWindowFrame(
|
|
2165
|
+
window_id: u32,
|
|
2166
|
+
x: *f64,
|
|
2167
|
+
y: *f64,
|
|
2168
|
+
width: *f64,
|
|
2169
|
+
height: *f64,
|
|
2170
|
+
) void {
|
|
2171
|
+
const GetWindowFrameFn = *const fn (WindowPtr, *f64, *f64, *f64, *f64) callconv(.C) void;
|
|
2172
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2173
|
+
const get_window_frame = lookupNativeSymbol(GetWindowFrameFn, "getWindowFrame") orelse return;
|
|
2174
|
+
get_window_frame(window, x, y, width, height);
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
export fn beginWindowMove(window_id: u32) void {
|
|
2178
|
+
clearLastError();
|
|
2179
|
+
const StartWindowMoveFn = *const fn (WindowPtr) callconv(.C) void;
|
|
2180
|
+
const window = requireWindowPtr(window_id) orelse return;
|
|
2181
|
+
const start_window_move = lookupNativeSymbol(StartWindowMoveFn, "startWindowMove") orelse return;
|
|
2182
|
+
start_window_move(window);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
export fn endWindowMove() void {
|
|
2186
|
+
clearLastError();
|
|
2187
|
+
const StopWindowMoveFn = *const fn () callconv(.C) void;
|
|
2188
|
+
const stop_window_move = lookupNativeSymbol(StopWindowMoveFn, "stopWindowMove") orelse return;
|
|
2189
|
+
stop_window_move();
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
export fn createWebview(
|
|
2193
|
+
window_id: u32,
|
|
2194
|
+
host_webview_id: u32,
|
|
2195
|
+
renderer: [*:0]const u8,
|
|
2196
|
+
url: [*:0]const u8,
|
|
2197
|
+
x: f64,
|
|
2198
|
+
y: f64,
|
|
2199
|
+
width: f64,
|
|
2200
|
+
height: f64,
|
|
2201
|
+
auto_resize: bool,
|
|
2202
|
+
partition_identifier: [*:0]const u8,
|
|
2203
|
+
navigation_callback: ?DecideNavigationHandler,
|
|
2204
|
+
webview_event_handler: ?WebviewEventHandler,
|
|
2205
|
+
event_bridge_handler: ?WebviewPostMessageHandler,
|
|
2206
|
+
_host_bridge_handler: ?WebviewPostMessageHandler,
|
|
2207
|
+
_internal_bridge_handler: ?WebviewPostMessageHandler,
|
|
2208
|
+
secret_key: [*:0]const u8,
|
|
2209
|
+
custom_preload_script: [*:0]const u8,
|
|
2210
|
+
views_root: [*:0]const u8,
|
|
2211
|
+
sandbox: bool,
|
|
2212
|
+
start_transparent: bool,
|
|
2213
|
+
start_passthrough: bool,
|
|
2214
|
+
) u32 {
|
|
2215
|
+
clearLastError();
|
|
2216
|
+
_ = _host_bridge_handler;
|
|
2217
|
+
_ = _internal_bridge_handler;
|
|
2218
|
+
rememberDefaultWebviewCallbacks(
|
|
2219
|
+
navigation_callback,
|
|
2220
|
+
webview_event_handler,
|
|
2221
|
+
event_bridge_handler,
|
|
2222
|
+
);
|
|
2223
|
+
|
|
2224
|
+
const SetNextWebviewFlagsFn = *const fn (bool, bool) callconv(.C) void;
|
|
2225
|
+
const InitWebviewFn = *const fn (
|
|
2226
|
+
u32,
|
|
2227
|
+
WindowPtr,
|
|
2228
|
+
[*:0]const u8,
|
|
2229
|
+
[*:0]const u8,
|
|
2230
|
+
f64,
|
|
2231
|
+
f64,
|
|
2232
|
+
f64,
|
|
2233
|
+
f64,
|
|
2234
|
+
bool,
|
|
2235
|
+
[*:0]const u8,
|
|
2236
|
+
?DecideNavigationHandler,
|
|
2237
|
+
?WebviewEventHandler,
|
|
2238
|
+
?WebviewPostMessageHandler,
|
|
2239
|
+
?WebviewPostMessageHandler,
|
|
2240
|
+
?WebviewPostMessageHandler,
|
|
2241
|
+
[*:0]const u8,
|
|
2242
|
+
[*:0]const u8,
|
|
2243
|
+
[*:0]const u8,
|
|
2244
|
+
bool,
|
|
2245
|
+
bool,
|
|
2246
|
+
) callconv(.C) WebviewPtr;
|
|
2247
|
+
|
|
2248
|
+
const window_state = lookupWindowState(window_id) orelse {
|
|
2249
|
+
setLastError("Window {d} not found", .{window_id});
|
|
2250
|
+
return 0;
|
|
2251
|
+
};
|
|
2252
|
+
const window = window_state.ptr orelse {
|
|
2253
|
+
setLastError("Window {d} not found", .{window_id});
|
|
2254
|
+
return 0;
|
|
2255
|
+
};
|
|
2256
|
+
const set_next_webview_flags = lookupNativeSymbol(SetNextWebviewFlagsFn, "setNextWebviewFlags") orelse return 0;
|
|
2257
|
+
const init_webview = lookupNativeSymbol(InitWebviewFn, "initWebview") orelse return 0;
|
|
2258
|
+
const parsed_secret_key = parseWebviewSecretKey(secret_key) orelse return 0;
|
|
2259
|
+
|
|
2260
|
+
webview_registry_mutex.lock();
|
|
2261
|
+
const start_id = next_webview_id;
|
|
2262
|
+
var webview_id = next_webview_id;
|
|
2263
|
+
|
|
2264
|
+
while (webview_id == 0 or webview_registry.contains(webview_id)) {
|
|
2265
|
+
webview_id +%= 1;
|
|
2266
|
+
if (webview_id == 0) {
|
|
2267
|
+
webview_id = 1;
|
|
2268
|
+
}
|
|
2269
|
+
if (webview_id == start_id) {
|
|
2270
|
+
webview_registry_mutex.unlock();
|
|
2271
|
+
setLastError("Failed to allocate webview id", .{});
|
|
2272
|
+
return 0;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
next_webview_id = webview_id +% 1;
|
|
2277
|
+
if (next_webview_id == 0) {
|
|
2278
|
+
next_webview_id = 1;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
webview_registry.put(webview_id, .{
|
|
2282
|
+
.ptr = null,
|
|
2283
|
+
.window_id = window_id,
|
|
2284
|
+
.host_webview_id = if (host_webview_id == 0) null else host_webview_id,
|
|
2285
|
+
.renderer = parseWebviewRenderer(renderer),
|
|
2286
|
+
.secret_key = parsed_secret_key,
|
|
2287
|
+
.socket_handle = null,
|
|
2288
|
+
.transport_ready = false,
|
|
2289
|
+
}) catch |err| {
|
|
2290
|
+
webview_registry_mutex.unlock();
|
|
2291
|
+
setLastError("Failed to store webview state: {s}", .{@errorName(err)});
|
|
2292
|
+
return 0;
|
|
2293
|
+
};
|
|
2294
|
+
webview_registry_mutex.unlock();
|
|
2295
|
+
|
|
2296
|
+
const electrobun_preload_script = buildElectrobunPreload(
|
|
2297
|
+
webview_id,
|
|
2298
|
+
window_id,
|
|
2299
|
+
secret_key,
|
|
2300
|
+
sandbox,
|
|
2301
|
+
) orelse {
|
|
2302
|
+
webview_registry_mutex.lock();
|
|
2303
|
+
_ = webview_registry.remove(webview_id);
|
|
2304
|
+
webview_registry_mutex.unlock();
|
|
2305
|
+
return 0;
|
|
2306
|
+
};
|
|
2307
|
+
defer allocator.free(electrobun_preload_script);
|
|
2308
|
+
|
|
2309
|
+
set_next_webview_flags(start_transparent, start_passthrough);
|
|
2310
|
+
|
|
2311
|
+
const webview_ptr = init_webview(
|
|
2312
|
+
webview_id,
|
|
2313
|
+
window,
|
|
2314
|
+
renderer,
|
|
2315
|
+
url,
|
|
2316
|
+
x,
|
|
2317
|
+
y,
|
|
2318
|
+
width,
|
|
2319
|
+
height,
|
|
2320
|
+
auto_resize,
|
|
2321
|
+
partition_identifier,
|
|
2322
|
+
navigation_callback,
|
|
2323
|
+
webview_event_handler,
|
|
2324
|
+
event_bridge_handler,
|
|
2325
|
+
hostBridgeQueueTrampoline,
|
|
2326
|
+
internalBridgeCoreTrampoline,
|
|
2327
|
+
electrobun_preload_script.ptr,
|
|
2328
|
+
custom_preload_script,
|
|
2329
|
+
views_root,
|
|
2330
|
+
window_state.transparent,
|
|
2331
|
+
sandbox,
|
|
2332
|
+
);
|
|
2333
|
+
|
|
2334
|
+
if (webview_ptr == null) {
|
|
2335
|
+
webview_registry_mutex.lock();
|
|
2336
|
+
_ = webview_registry.remove(webview_id);
|
|
2337
|
+
webview_registry_mutex.unlock();
|
|
2338
|
+
setLastError("Failed to create webview", .{});
|
|
2339
|
+
return 0;
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2342
|
+
webview_registry_mutex.lock();
|
|
2343
|
+
const state = webview_registry.getPtr(webview_id) orelse {
|
|
2344
|
+
webview_registry_mutex.unlock();
|
|
2345
|
+
setLastError("Webview {d} disappeared during creation", .{webview_id});
|
|
2346
|
+
return 0;
|
|
2347
|
+
};
|
|
2348
|
+
state.ptr = webview_ptr;
|
|
2349
|
+
webview_registry_mutex.unlock();
|
|
2350
|
+
|
|
2351
|
+
return webview_id;
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
export fn getWebviewPointer(webview_id: u32) WebviewPtr {
|
|
2355
|
+
clearLastError();
|
|
2356
|
+
return lookupWebviewPtr(webview_id);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
export fn resizeWebview(
|
|
2360
|
+
webview_id: u32,
|
|
2361
|
+
x: f64,
|
|
2362
|
+
y: f64,
|
|
2363
|
+
width: f64,
|
|
2364
|
+
height: f64,
|
|
2365
|
+
masks_json: [*:0]const u8,
|
|
2366
|
+
) void {
|
|
2367
|
+
clearLastError();
|
|
2368
|
+
const ResizeWebviewFn = *const fn (WebviewPtr, f64, f64, f64, f64, [*:0]const u8) callconv(.C) void;
|
|
2369
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2370
|
+
const resize_webview = lookupNativeSymbol(ResizeWebviewFn, "resizeWebview") orelse return;
|
|
2371
|
+
resize_webview(webview, x, y, width, height, masks_json);
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
export fn loadURLInWebView(webview_id: u32, url: [*:0]const u8) void {
|
|
2375
|
+
clearLastError();
|
|
2376
|
+
const LoadURLInWebViewFn = *const fn (WebviewPtr, [*:0]const u8) callconv(.C) void;
|
|
2377
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2378
|
+
const load_url_in_webview = lookupNativeSymbol(LoadURLInWebViewFn, "loadURLInWebView") orelse return;
|
|
2379
|
+
load_url_in_webview(webview, url);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
export fn loadHTMLInWebView(webview_id: u32, html: [*:0]const u8) void {
|
|
2383
|
+
clearLastError();
|
|
2384
|
+
const LoadHTMLInWebViewFn = *const fn (WebviewPtr, [*:0]const u8) callconv(.C) void;
|
|
2385
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2386
|
+
const load_html_in_webview = lookupNativeSymbol(LoadHTMLInWebViewFn, "loadHTMLInWebView") orelse return;
|
|
2387
|
+
load_html_in_webview(webview, html);
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
export fn updatePreloadScriptToWebView(
|
|
2391
|
+
webview_id: u32,
|
|
2392
|
+
script_identifier: [*:0]const u8,
|
|
2393
|
+
script: [*:0]const u8,
|
|
2394
|
+
all_frames: bool,
|
|
2395
|
+
) void {
|
|
2396
|
+
clearLastError();
|
|
2397
|
+
const UpdatePreloadScriptToWebViewFn = *const fn (WebviewPtr, [*:0]const u8, [*:0]const u8, bool) callconv(.C) void;
|
|
2398
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2399
|
+
const update_preload_script_to_webview = lookupNativeSymbol(
|
|
2400
|
+
UpdatePreloadScriptToWebViewFn,
|
|
2401
|
+
"updatePreloadScriptToWebView",
|
|
2402
|
+
) orelse return;
|
|
2403
|
+
update_preload_script_to_webview(webview, script_identifier, script, all_frames);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
export fn webviewCanGoBack(webview_id: u32) bool {
|
|
2407
|
+
clearLastError();
|
|
2408
|
+
const WebviewCanGoBackFn = *const fn (WebviewPtr) callconv(.C) bool;
|
|
2409
|
+
const webview = lookupWebviewPtr(webview_id) orelse return false;
|
|
2410
|
+
const webview_can_go_back = lookupNativeSymbol(WebviewCanGoBackFn, "webviewCanGoBack") orelse return false;
|
|
2411
|
+
return webview_can_go_back(webview);
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
export fn webviewCanGoForward(webview_id: u32) bool {
|
|
2415
|
+
clearLastError();
|
|
2416
|
+
const WebviewCanGoForwardFn = *const fn (WebviewPtr) callconv(.C) bool;
|
|
2417
|
+
const webview = lookupWebviewPtr(webview_id) orelse return false;
|
|
2418
|
+
const webview_can_go_forward = lookupNativeSymbol(WebviewCanGoForwardFn, "webviewCanGoForward") orelse return false;
|
|
2419
|
+
return webview_can_go_forward(webview);
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
export fn webviewGoBack(webview_id: u32) void {
|
|
2423
|
+
clearLastError();
|
|
2424
|
+
const WebviewGoBackFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2425
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2426
|
+
const webview_go_back = lookupNativeSymbol(WebviewGoBackFn, "webviewGoBack") orelse return;
|
|
2427
|
+
webview_go_back(webview);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
export fn webviewGoForward(webview_id: u32) void {
|
|
2431
|
+
clearLastError();
|
|
2432
|
+
const WebviewGoForwardFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2433
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2434
|
+
const webview_go_forward = lookupNativeSymbol(WebviewGoForwardFn, "webviewGoForward") orelse return;
|
|
2435
|
+
webview_go_forward(webview);
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
export fn webviewReload(webview_id: u32) void {
|
|
2439
|
+
clearLastError();
|
|
2440
|
+
const WebviewReloadFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2441
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2442
|
+
const webview_reload = lookupNativeSymbol(WebviewReloadFn, "webviewReload") orelse return;
|
|
2443
|
+
webview_reload(webview);
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
export fn webviewRemove(webview_id: u32) void {
|
|
2447
|
+
clearLastError();
|
|
2448
|
+
const WebviewRemoveFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2449
|
+
|
|
2450
|
+
webview_registry_mutex.lock();
|
|
2451
|
+
const removed = webview_registry.fetchRemove(webview_id);
|
|
2452
|
+
webview_registry_mutex.unlock();
|
|
2453
|
+
|
|
2454
|
+
const webview = if (removed) |entry| entry.value.ptr else null;
|
|
2455
|
+
const socket_handle = if (removed) |entry| entry.value.socket_handle else null;
|
|
2456
|
+
if (socket_handle) |handle| {
|
|
2457
|
+
closeSocketHandle(handle);
|
|
2458
|
+
}
|
|
2459
|
+
if (webview == null) {
|
|
2460
|
+
return;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
const webview_remove = lookupNativeSymbol(WebviewRemoveFn, "webviewRemove") orelse return;
|
|
2464
|
+
webview_remove(webview);
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
export fn setWebviewHTMLContent(webview_id: u32, html: [*:0]const u8) void {
|
|
2468
|
+
clearLastError();
|
|
2469
|
+
const SetWebviewHTMLContentFn = *const fn (u32, [*:0]const u8) callconv(.C) void;
|
|
2470
|
+
const set_webview_html_content = lookupNativeSymbol(SetWebviewHTMLContentFn, "setWebviewHTMLContent") orelse return;
|
|
2471
|
+
set_webview_html_content(webview_id, html);
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
export fn webviewSetTransparent(webview_id: u32, transparent: bool) void {
|
|
2475
|
+
clearLastError();
|
|
2476
|
+
const WebviewSetTransparentFn = *const fn (WebviewPtr, bool) callconv(.C) void;
|
|
2477
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2478
|
+
const webview_set_transparent = lookupNativeSymbol(WebviewSetTransparentFn, "webviewSetTransparent") orelse return;
|
|
2479
|
+
webview_set_transparent(webview, transparent);
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
export fn webviewSetPassthrough(webview_id: u32, passthrough: bool) void {
|
|
2483
|
+
clearLastError();
|
|
2484
|
+
const WebviewSetPassthroughFn = *const fn (WebviewPtr, bool) callconv(.C) void;
|
|
2485
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2486
|
+
const webview_set_passthrough = lookupNativeSymbol(WebviewSetPassthroughFn, "webviewSetPassthrough") orelse return;
|
|
2487
|
+
webview_set_passthrough(webview, passthrough);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
export fn webviewSetHidden(webview_id: u32, hidden: bool) void {
|
|
2491
|
+
clearLastError();
|
|
2492
|
+
const WebviewSetHiddenFn = *const fn (WebviewPtr, bool) callconv(.C) void;
|
|
2493
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2494
|
+
const webview_set_hidden = lookupNativeSymbol(WebviewSetHiddenFn, "webviewSetHidden") orelse return;
|
|
2495
|
+
webview_set_hidden(webview, hidden);
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
export fn setWebviewNavigationRules(webview_id: u32, rules_json: [*:0]const u8) void {
|
|
2499
|
+
clearLastError();
|
|
2500
|
+
const SetWebviewNavigationRulesFn = *const fn (WebviewPtr, [*:0]const u8) callconv(.C) void;
|
|
2501
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2502
|
+
const set_webview_navigation_rules = lookupNativeSymbol(
|
|
2503
|
+
SetWebviewNavigationRulesFn,
|
|
2504
|
+
"setWebviewNavigationRules",
|
|
2505
|
+
) orelse return;
|
|
2506
|
+
set_webview_navigation_rules(webview, rules_json);
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
export fn webviewFindInPage(
|
|
2510
|
+
webview_id: u32,
|
|
2511
|
+
search_text: [*:0]const u8,
|
|
2512
|
+
forward: bool,
|
|
2513
|
+
match_case: bool,
|
|
2514
|
+
) void {
|
|
2515
|
+
clearLastError();
|
|
2516
|
+
const WebviewFindInPageFn = *const fn (WebviewPtr, [*:0]const u8, bool, bool) callconv(.C) void;
|
|
2517
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2518
|
+
const webview_find_in_page = lookupNativeSymbol(WebviewFindInPageFn, "webviewFindInPage") orelse return;
|
|
2519
|
+
webview_find_in_page(webview, search_text, forward, match_case);
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
export fn webviewStopFind(webview_id: u32) void {
|
|
2523
|
+
clearLastError();
|
|
2524
|
+
const WebviewStopFindFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2525
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2526
|
+
const webview_stop_find = lookupNativeSymbol(WebviewStopFindFn, "webviewStopFind") orelse return;
|
|
2527
|
+
webview_stop_find(webview);
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
export fn evaluateJavaScriptWithNoCompletion(webview_id: u32, js: [*:0]const u8) void {
|
|
2531
|
+
clearLastError();
|
|
2532
|
+
const EvaluateJavaScriptWithNoCompletionFn = *const fn (WebviewPtr, [*:0]const u8) callconv(.C) void;
|
|
2533
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2534
|
+
const evaluate_javascript_with_no_completion = lookupNativeSymbol(
|
|
2535
|
+
EvaluateJavaScriptWithNoCompletionFn,
|
|
2536
|
+
"evaluateJavaScriptWithNoCompletion",
|
|
2537
|
+
) orelse return;
|
|
2538
|
+
evaluate_javascript_with_no_completion(webview, js);
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
export fn dispatchHostWebviewEvent(
|
|
2542
|
+
webview_id: u32,
|
|
2543
|
+
event_name: [*:0]const u8,
|
|
2544
|
+
detail: [*:0]const u8,
|
|
2545
|
+
) bool {
|
|
2546
|
+
clearLastError();
|
|
2547
|
+
|
|
2548
|
+
const host_webview_id = lookupWebviewHostId(webview_id) orelse return false;
|
|
2549
|
+
const js = buildHostWebviewEventJavascript(webview_id, event_name, detail) orelse return false;
|
|
2550
|
+
defer allocator.free(js);
|
|
2551
|
+
|
|
2552
|
+
evaluateJavaScriptWithNoCompletion(host_webview_id, js.ptr);
|
|
2553
|
+
return true;
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
export fn clearWebviewHostTransport(webview_id: u32) void {
|
|
2557
|
+
clearLastError();
|
|
2558
|
+
closeAndClearWebviewSocketHandle(webview_id);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
export fn sendHostMessageToWebviewViaTransport(webview_id: u32, message_json: [*:0]const u8) bool {
|
|
2562
|
+
clearLastError();
|
|
2563
|
+
|
|
2564
|
+
const context = lookupWebviewTransportContext(webview_id) orelse return false;
|
|
2565
|
+
if (!context.transport_ready) {
|
|
2566
|
+
return false;
|
|
2567
|
+
}
|
|
2568
|
+
const socket_handle = context.socket_handle orelse return false;
|
|
2569
|
+
|
|
2570
|
+
const encrypted_packet = encryptHostTransportPacket(std.mem.span(message_json), context.secret_key) catch |err| {
|
|
2571
|
+
setLastError("Failed to encrypt host transport packet: {s}", .{@errorName(err)});
|
|
2572
|
+
return false;
|
|
2573
|
+
};
|
|
2574
|
+
defer allocator.free(encrypted_packet);
|
|
2575
|
+
|
|
2576
|
+
const stream = std.net.Stream{ .handle = socket_handle };
|
|
2577
|
+
writeWebSocketFrame(stream, 0x1, encrypted_packet) catch {
|
|
2578
|
+
clearWebviewSocketHandleIfCurrent(webview_id, socket_handle);
|
|
2579
|
+
return false;
|
|
2580
|
+
};
|
|
2581
|
+
|
|
2582
|
+
return true;
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
export fn sendInternalMessageToWebview(webview_id: u32, message_json: [*:0]const u8) bool {
|
|
2586
|
+
clearLastError();
|
|
2587
|
+
|
|
2588
|
+
const js = buildInternalMessageJavascript(message_json) orelse return false;
|
|
2589
|
+
defer allocator.free(js);
|
|
2590
|
+
|
|
2591
|
+
evaluateJavaScriptWithNoCompletion(webview_id, js.ptr);
|
|
2592
|
+
return true;
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
export fn webviewOpenDevTools(webview_id: u32) void {
|
|
2596
|
+
clearLastError();
|
|
2597
|
+
const WebviewOpenDevToolsFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2598
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2599
|
+
const webview_open_devtools = lookupNativeSymbol(WebviewOpenDevToolsFn, "webviewOpenDevTools") orelse return;
|
|
2600
|
+
webview_open_devtools(webview);
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
export fn webviewCloseDevTools(webview_id: u32) void {
|
|
2604
|
+
clearLastError();
|
|
2605
|
+
const WebviewCloseDevToolsFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2606
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2607
|
+
const webview_close_devtools = lookupNativeSymbol(WebviewCloseDevToolsFn, "webviewCloseDevTools") orelse return;
|
|
2608
|
+
webview_close_devtools(webview);
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
export fn webviewToggleDevTools(webview_id: u32) void {
|
|
2612
|
+
clearLastError();
|
|
2613
|
+
const WebviewToggleDevToolsFn = *const fn (WebviewPtr) callconv(.C) void;
|
|
2614
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2615
|
+
const webview_toggle_devtools = lookupNativeSymbol(WebviewToggleDevToolsFn, "webviewToggleDevTools") orelse return;
|
|
2616
|
+
webview_toggle_devtools(webview);
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
export fn webviewSetPageZoom(webview_id: u32, zoom_level: f64) void {
|
|
2620
|
+
clearLastError();
|
|
2621
|
+
const WebviewSetPageZoomFn = *const fn (WebviewPtr, f64) callconv(.C) void;
|
|
2622
|
+
const webview = requireWebviewPtr(webview_id) orelse return;
|
|
2623
|
+
const webview_set_page_zoom = lookupNativeSymbol(WebviewSetPageZoomFn, "webviewSetPageZoom") orelse return;
|
|
2624
|
+
webview_set_page_zoom(webview, zoom_level);
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
export fn webviewGetPageZoom(webview_id: u32) f64 {
|
|
2628
|
+
clearLastError();
|
|
2629
|
+
const WebviewGetPageZoomFn = *const fn (WebviewPtr) callconv(.C) f64;
|
|
2630
|
+
const webview = lookupWebviewPtr(webview_id) orelse return 1.0;
|
|
2631
|
+
const webview_get_page_zoom = lookupNativeSymbol(WebviewGetPageZoomFn, "webviewGetPageZoom") orelse return 1.0;
|
|
2632
|
+
return webview_get_page_zoom(webview);
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
export fn createWGPUView(
|
|
2636
|
+
window_id: u32,
|
|
2637
|
+
x: f64,
|
|
2638
|
+
y: f64,
|
|
2639
|
+
width: f64,
|
|
2640
|
+
height: f64,
|
|
2641
|
+
auto_resize: bool,
|
|
2642
|
+
start_transparent: bool,
|
|
2643
|
+
start_passthrough: bool,
|
|
2644
|
+
) u32 {
|
|
2645
|
+
clearLastError();
|
|
2646
|
+
|
|
2647
|
+
const InitWGPUViewFn = *const fn (
|
|
2648
|
+
u32,
|
|
2649
|
+
WindowPtr,
|
|
2650
|
+
f64,
|
|
2651
|
+
f64,
|
|
2652
|
+
f64,
|
|
2653
|
+
f64,
|
|
2654
|
+
bool,
|
|
2655
|
+
bool,
|
|
2656
|
+
bool,
|
|
2657
|
+
) callconv(.C) WgpuViewPtr;
|
|
2658
|
+
|
|
2659
|
+
const window = requireWindowPtr(window_id) orelse return 0;
|
|
2660
|
+
const init_wgpu_view = lookupNativeSymbol(InitWGPUViewFn, "initWGPUView") orelse return 0;
|
|
2661
|
+
|
|
2662
|
+
wgpu_view_registry_mutex.lock();
|
|
2663
|
+
const start_id = next_wgpu_view_id;
|
|
2664
|
+
var wgpu_view_id = next_wgpu_view_id;
|
|
2665
|
+
|
|
2666
|
+
while (wgpu_view_id == 0 or wgpu_view_registry.contains(wgpu_view_id)) {
|
|
2667
|
+
wgpu_view_id +%= 1;
|
|
2668
|
+
if (wgpu_view_id == 0) {
|
|
2669
|
+
wgpu_view_id = 1;
|
|
2670
|
+
}
|
|
2671
|
+
if (wgpu_view_id == start_id) {
|
|
2672
|
+
wgpu_view_registry_mutex.unlock();
|
|
2673
|
+
setLastError("Failed to allocate WGPUView id", .{});
|
|
2674
|
+
return 0;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
next_wgpu_view_id = wgpu_view_id +% 1;
|
|
2679
|
+
if (next_wgpu_view_id == 0) {
|
|
2680
|
+
next_wgpu_view_id = 1;
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
wgpu_view_registry.put(wgpu_view_id, .{
|
|
2684
|
+
.ptr = null,
|
|
2685
|
+
.window_id = window_id,
|
|
2686
|
+
}) catch |err| {
|
|
2687
|
+
wgpu_view_registry_mutex.unlock();
|
|
2688
|
+
setLastError("Failed to store WGPUView state: {s}", .{@errorName(err)});
|
|
2689
|
+
return 0;
|
|
2690
|
+
};
|
|
2691
|
+
wgpu_view_registry_mutex.unlock();
|
|
2692
|
+
|
|
2693
|
+
const wgpu_view_ptr = init_wgpu_view(
|
|
2694
|
+
wgpu_view_id,
|
|
2695
|
+
window,
|
|
2696
|
+
x,
|
|
2697
|
+
y,
|
|
2698
|
+
width,
|
|
2699
|
+
height,
|
|
2700
|
+
auto_resize,
|
|
2701
|
+
start_transparent,
|
|
2702
|
+
start_passthrough,
|
|
2703
|
+
);
|
|
2704
|
+
|
|
2705
|
+
if (wgpu_view_ptr == null) {
|
|
2706
|
+
wgpu_view_registry_mutex.lock();
|
|
2707
|
+
_ = wgpu_view_registry.remove(wgpu_view_id);
|
|
2708
|
+
wgpu_view_registry_mutex.unlock();
|
|
2709
|
+
setLastError("Failed to create WGPUView", .{});
|
|
2710
|
+
return 0;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
wgpu_view_registry_mutex.lock();
|
|
2714
|
+
const state = wgpu_view_registry.getPtr(wgpu_view_id) orelse {
|
|
2715
|
+
wgpu_view_registry_mutex.unlock();
|
|
2716
|
+
setLastError("WGPUView {d} disappeared during creation", .{wgpu_view_id});
|
|
2717
|
+
return 0;
|
|
2718
|
+
};
|
|
2719
|
+
state.ptr = wgpu_view_ptr;
|
|
2720
|
+
wgpu_view_registry_mutex.unlock();
|
|
2721
|
+
|
|
2722
|
+
return wgpu_view_id;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
export fn getWGPUViewPointer(wgpu_view_id: u32) WgpuViewPtr {
|
|
2726
|
+
clearLastError();
|
|
2727
|
+
return lookupWgpuViewPtr(wgpu_view_id);
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
export fn setWGPUViewFrame(
|
|
2731
|
+
wgpu_view_id: u32,
|
|
2732
|
+
x: f64,
|
|
2733
|
+
y: f64,
|
|
2734
|
+
width: f64,
|
|
2735
|
+
height: f64,
|
|
2736
|
+
) void {
|
|
2737
|
+
clearLastError();
|
|
2738
|
+
const SetWGPUViewFrameFn = *const fn (WgpuViewPtr, f64, f64, f64, f64) callconv(.C) void;
|
|
2739
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2740
|
+
const set_wgpu_view_frame = lookupNativeSymbol(SetWGPUViewFrameFn, "wgpuViewSetFrame") orelse return;
|
|
2741
|
+
set_wgpu_view_frame(wgpu_view, x, y, width, height);
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
export fn resizeWGPUView(
|
|
2745
|
+
wgpu_view_id: u32,
|
|
2746
|
+
x: f64,
|
|
2747
|
+
y: f64,
|
|
2748
|
+
width: f64,
|
|
2749
|
+
height: f64,
|
|
2750
|
+
masks_json: [*:0]const u8,
|
|
2751
|
+
) void {
|
|
2752
|
+
clearLastError();
|
|
2753
|
+
const ResizeWGPUViewFn = *const fn (WgpuViewPtr, f64, f64, f64, f64, [*:0]const u8) callconv(.C) void;
|
|
2754
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2755
|
+
const resize_wgpu_view = lookupNativeSymbol(ResizeWGPUViewFn, "resizeWebview") orelse return;
|
|
2756
|
+
resize_wgpu_view(wgpu_view, x, y, width, height, masks_json);
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
export fn setWGPUViewTransparent(wgpu_view_id: u32, transparent: bool) void {
|
|
2760
|
+
clearLastError();
|
|
2761
|
+
const SetWGPUViewTransparentFn = *const fn (WgpuViewPtr, bool) callconv(.C) void;
|
|
2762
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2763
|
+
const set_wgpu_view_transparent = lookupNativeSymbol(
|
|
2764
|
+
SetWGPUViewTransparentFn,
|
|
2765
|
+
"wgpuViewSetTransparent",
|
|
2766
|
+
) orelse return;
|
|
2767
|
+
set_wgpu_view_transparent(wgpu_view, transparent);
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
export fn setWGPUViewPassthrough(wgpu_view_id: u32, passthrough: bool) void {
|
|
2771
|
+
clearLastError();
|
|
2772
|
+
const SetWGPUViewPassthroughFn = *const fn (WgpuViewPtr, bool) callconv(.C) void;
|
|
2773
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2774
|
+
const set_wgpu_view_passthrough = lookupNativeSymbol(
|
|
2775
|
+
SetWGPUViewPassthroughFn,
|
|
2776
|
+
"wgpuViewSetPassthrough",
|
|
2777
|
+
) orelse return;
|
|
2778
|
+
set_wgpu_view_passthrough(wgpu_view, passthrough);
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
export fn setWGPUViewHidden(wgpu_view_id: u32, hidden: bool) void {
|
|
2782
|
+
clearLastError();
|
|
2783
|
+
const SetWGPUViewHiddenFn = *const fn (WgpuViewPtr, bool) callconv(.C) void;
|
|
2784
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2785
|
+
const set_wgpu_view_hidden = lookupNativeSymbol(SetWGPUViewHiddenFn, "wgpuViewSetHidden") orelse return;
|
|
2786
|
+
set_wgpu_view_hidden(wgpu_view, hidden);
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
export fn removeWGPUView(wgpu_view_id: u32) void {
|
|
2790
|
+
clearLastError();
|
|
2791
|
+
const RemoveWGPUViewFn = *const fn (WgpuViewPtr) callconv(.C) void;
|
|
2792
|
+
|
|
2793
|
+
wgpu_view_registry_mutex.lock();
|
|
2794
|
+
const removed = wgpu_view_registry.fetchRemove(wgpu_view_id);
|
|
2795
|
+
wgpu_view_registry_mutex.unlock();
|
|
2796
|
+
|
|
2797
|
+
const wgpu_view = if (removed) |entry| entry.value.ptr else null;
|
|
2798
|
+
if (wgpu_view == null) {
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
const remove_wgpu_view = lookupNativeSymbol(RemoveWGPUViewFn, "wgpuViewRemove") orelse return;
|
|
2803
|
+
remove_wgpu_view(wgpu_view);
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
export fn getWGPUViewNativeHandle(wgpu_view_id: u32) WgpuViewPtr {
|
|
2807
|
+
clearLastError();
|
|
2808
|
+
const GetWGPUViewNativeHandleFn = *const fn (WgpuViewPtr) callconv(.C) WgpuViewPtr;
|
|
2809
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return null;
|
|
2810
|
+
const get_wgpu_view_native_handle = lookupNativeSymbol(
|
|
2811
|
+
GetWGPUViewNativeHandleFn,
|
|
2812
|
+
"wgpuViewGetNativeHandle",
|
|
2813
|
+
) orelse return null;
|
|
2814
|
+
return get_wgpu_view_native_handle(wgpu_view);
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
export fn runWGPUViewTest(wgpu_view_id: u32) void {
|
|
2818
|
+
clearLastError();
|
|
2819
|
+
const RunWGPUViewTestFn = *const fn (WgpuViewPtr) callconv(.C) void;
|
|
2820
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2821
|
+
const run_wgpu_view_test = lookupNativeSymbol(RunWGPUViewTestFn, "wgpuRunGPUTest") orelse return;
|
|
2822
|
+
run_wgpu_view_test(wgpu_view);
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
export fn toggleWGPUViewTestShader(wgpu_view_id: u32) void {
|
|
2826
|
+
clearLastError();
|
|
2827
|
+
const ToggleWGPUViewTestShaderFn = *const fn (WgpuViewPtr) callconv(.C) void;
|
|
2828
|
+
const wgpu_view = requireWgpuViewPtr(wgpu_view_id) orelse return;
|
|
2829
|
+
const toggle_wgpu_view_test_shader = lookupNativeSymbol(
|
|
2830
|
+
ToggleWGPUViewTestShaderFn,
|
|
2831
|
+
"wgpuToggleGPUTestShader",
|
|
2832
|
+
) orelse return;
|
|
2833
|
+
toggle_wgpu_view_test_shader(wgpu_view);
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
export fn createTray(
|
|
2837
|
+
title: [*:0]const u8,
|
|
2838
|
+
image: [*:0]const u8,
|
|
2839
|
+
is_template: bool,
|
|
2840
|
+
width: u32,
|
|
2841
|
+
height: u32,
|
|
2842
|
+
tray_item_handler: ?StatusItemHandler,
|
|
2843
|
+
) u32 {
|
|
2844
|
+
clearLastError();
|
|
2845
|
+
|
|
2846
|
+
const owned_title = dupeZ(title) catch |err| {
|
|
2847
|
+
setLastError("Failed to allocate tray title: {s}", .{@errorName(err)});
|
|
2848
|
+
return 0;
|
|
2849
|
+
};
|
|
2850
|
+
errdefer allocator.free(owned_title);
|
|
2851
|
+
|
|
2852
|
+
const owned_image = dupeZ(image) catch |err| {
|
|
2853
|
+
setLastError("Failed to allocate tray image: {s}", .{@errorName(err)});
|
|
2854
|
+
return 0;
|
|
2855
|
+
};
|
|
2856
|
+
errdefer allocator.free(owned_image);
|
|
2857
|
+
|
|
2858
|
+
const tray_id = next_tray_id;
|
|
2859
|
+
next_tray_id += 1;
|
|
2860
|
+
|
|
2861
|
+
tray_registry.put(tray_id, .{
|
|
2862
|
+
.title = owned_title,
|
|
2863
|
+
.image = owned_image,
|
|
2864
|
+
.menu_config = null,
|
|
2865
|
+
.is_template = is_template,
|
|
2866
|
+
.width = width,
|
|
2867
|
+
.height = height,
|
|
2868
|
+
.handler = tray_item_handler,
|
|
2869
|
+
.ptr = null,
|
|
2870
|
+
.visible = false,
|
|
2871
|
+
}) catch |err| {
|
|
2872
|
+
setLastError("Failed to store tray state: {s}", .{@errorName(err)});
|
|
2873
|
+
return 0;
|
|
2874
|
+
};
|
|
2875
|
+
|
|
2876
|
+
const state = tray_registry.getPtr(tray_id).?;
|
|
2877
|
+
if (!createNativeTrayForState(tray_id, state)) {
|
|
2878
|
+
var removed = tray_registry.fetchRemove(tray_id).?;
|
|
2879
|
+
freeTrayState(&removed.value);
|
|
2880
|
+
return 0;
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
return tray_id;
|
|
2884
|
+
}
|
|
2885
|
+
|
|
2886
|
+
export fn showTray(tray_id: u32) bool {
|
|
2887
|
+
clearLastError();
|
|
2888
|
+
|
|
2889
|
+
const state = tray_registry.getPtr(tray_id) orelse {
|
|
2890
|
+
setLastError("Tray {d} not found", .{tray_id});
|
|
2891
|
+
return false;
|
|
2892
|
+
};
|
|
2893
|
+
|
|
2894
|
+
if (state.visible and state.ptr != null) {
|
|
2895
|
+
return true;
|
|
2896
|
+
}
|
|
2897
|
+
|
|
2898
|
+
return createNativeTrayForState(tray_id, state);
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
export fn hideTray(tray_id: u32) void {
|
|
2902
|
+
clearLastError();
|
|
2903
|
+
|
|
2904
|
+
const state = tray_registry.getPtr(tray_id) orelse return;
|
|
2905
|
+
hideNativeTray(state);
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
export fn setTrayTitle(tray_id: u32, title: [*:0]const u8) void {
|
|
2909
|
+
clearLastError();
|
|
2910
|
+
|
|
2911
|
+
const SetNativeTrayTitleFn = *const fn (TrayPtr, [*:0]const u8) callconv(.C) void;
|
|
2912
|
+
const state = tray_registry.getPtr(tray_id) orelse return;
|
|
2913
|
+
if (!replaceOwnedZ(&state.title, title)) {
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
if (state.ptr) |tray_ptr| {
|
|
2918
|
+
const set_native_tray_title = lookupNativeSymbol(SetNativeTrayTitleFn, "setTrayTitle") orelse return;
|
|
2919
|
+
set_native_tray_title(tray_ptr, state.title.ptr);
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
export fn setTrayImage(tray_id: u32, image: [*:0]const u8) void {
|
|
2924
|
+
clearLastError();
|
|
2925
|
+
|
|
2926
|
+
const SetNativeTrayImageFn = *const fn (TrayPtr, [*:0]const u8) callconv(.C) void;
|
|
2927
|
+
const state = tray_registry.getPtr(tray_id) orelse return;
|
|
2928
|
+
if (!replaceOwnedZ(&state.image, image)) {
|
|
2929
|
+
return;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
if (state.ptr) |tray_ptr| {
|
|
2933
|
+
const set_native_tray_image = lookupNativeSymbol(SetNativeTrayImageFn, "setTrayImage") orelse return;
|
|
2934
|
+
set_native_tray_image(tray_ptr, state.image.ptr);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
export fn setTrayMenu(tray_id: u32, menu_config: [*:0]const u8) void {
|
|
2939
|
+
clearLastError();
|
|
2940
|
+
|
|
2941
|
+
const SetNativeTrayMenuFn = *const fn (TrayPtr, [*:0]const u8) callconv(.C) void;
|
|
2942
|
+
const state = tray_registry.getPtr(tray_id) orelse return;
|
|
2943
|
+
if (!replaceOptionalOwnedZ(&state.menu_config, menu_config)) {
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
if (state.ptr) |tray_ptr| {
|
|
2948
|
+
const set_native_tray_menu = lookupNativeSymbol(SetNativeTrayMenuFn, "setTrayMenu") orelse return;
|
|
2949
|
+
set_native_tray_menu(tray_ptr, state.menu_config.?.ptr);
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
export fn removeTray(tray_id: u32) void {
|
|
2954
|
+
clearLastError();
|
|
2955
|
+
|
|
2956
|
+
var removed = tray_registry.fetchRemove(tray_id) orelse return;
|
|
2957
|
+
hideNativeTray(&removed.value);
|
|
2958
|
+
freeTrayState(&removed.value);
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
export fn getTrayBounds(tray_id: u32) [*:0]const u8 {
|
|
2962
|
+
clearLastError();
|
|
2963
|
+
|
|
2964
|
+
const GetNativeTrayBoundsFn = *const fn (TrayPtr) callconv(.C) ?[*:0]const u8;
|
|
2965
|
+
const state = tray_registry.getPtr(tray_id) orelse return empty_rect_json;
|
|
2966
|
+
const tray_ptr = state.ptr orelse return empty_rect_json;
|
|
2967
|
+
|
|
2968
|
+
const get_native_tray_bounds = lookupNativeSymbol(GetNativeTrayBoundsFn, "getTrayBounds") orelse {
|
|
2969
|
+
return empty_rect_json;
|
|
2970
|
+
};
|
|
2971
|
+
return get_native_tray_bounds(tray_ptr) orelse empty_rect_json;
|
|
2972
|
+
}
|
|
2973
|
+
|
|
2974
|
+
export fn setApplicationMenu(menu_config: [*:0]const u8, application_menu_handler: ?StatusItemHandler) void {
|
|
2975
|
+
const SetApplicationMenuFn = *const fn ([*:0]const u8, ?StatusItemHandler) callconv(.C) void;
|
|
2976
|
+
const set_application_menu = lookupNativeSymbol(
|
|
2977
|
+
SetApplicationMenuFn,
|
|
2978
|
+
"setApplicationMenu",
|
|
2979
|
+
) orelse return;
|
|
2980
|
+
set_application_menu(menu_config, application_menu_handler);
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
export fn showContextMenu(menu_config: [*:0]const u8, context_menu_handler: ?StatusItemHandler) void {
|
|
2984
|
+
const ShowContextMenuFn = *const fn ([*:0]const u8, ?StatusItemHandler) callconv(.C) void;
|
|
2985
|
+
const show_context_menu = lookupNativeSymbol(
|
|
2986
|
+
ShowContextMenuFn,
|
|
2987
|
+
"showContextMenu",
|
|
2988
|
+
) orelse return;
|
|
2989
|
+
show_context_menu(menu_config, context_menu_handler);
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
export fn moveToTrash(path: [*:0]const u8) bool {
|
|
2993
|
+
const MoveToTrashFn = *const fn ([*:0]const u8) callconv(.C) bool;
|
|
2994
|
+
const move_to_trash = lookupNativeSymbol(MoveToTrashFn, "moveToTrash") orelse return false;
|
|
2995
|
+
return move_to_trash(path);
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
export fn showItemInFolder(path: [*:0]const u8) void {
|
|
2999
|
+
const ShowItemInFolderFn = *const fn ([*:0]const u8) callconv(.C) void;
|
|
3000
|
+
const show_item_in_folder = lookupNativeSymbol(ShowItemInFolderFn, "showItemInFolder") orelse return;
|
|
3001
|
+
show_item_in_folder(path);
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
export fn openExternal(url: [*:0]const u8) bool {
|
|
3005
|
+
const OpenExternalFn = *const fn ([*:0]const u8) callconv(.C) bool;
|
|
3006
|
+
const open_external = lookupNativeSymbol(OpenExternalFn, "openExternal") orelse return false;
|
|
3007
|
+
return open_external(url);
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
export fn openPath(path: [*:0]const u8) bool {
|
|
3011
|
+
const OpenPathFn = *const fn ([*:0]const u8) callconv(.C) bool;
|
|
3012
|
+
const open_path = lookupNativeSymbol(OpenPathFn, "openPath") orelse return false;
|
|
3013
|
+
return open_path(path);
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
export fn showNotification(
|
|
3017
|
+
title: [*:0]const u8,
|
|
3018
|
+
body: [*:0]const u8,
|
|
3019
|
+
subtitle: [*:0]const u8,
|
|
3020
|
+
silent: bool,
|
|
3021
|
+
) void {
|
|
3022
|
+
const ShowNotificationFn = *const fn ([*:0]const u8, [*:0]const u8, [*:0]const u8, bool) callconv(.C) void;
|
|
3023
|
+
const show_notification = lookupNativeSymbol(ShowNotificationFn, "showNotification") orelse return;
|
|
3024
|
+
show_notification(title, body, subtitle, silent);
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
export fn setDockIconVisible(visible: bool) void {
|
|
3028
|
+
const SetDockIconVisibleFn = *const fn (bool) callconv(.C) void;
|
|
3029
|
+
const set_dock_icon_visible = lookupNativeSymbol(SetDockIconVisibleFn, "setDockIconVisible") orelse return;
|
|
3030
|
+
set_dock_icon_visible(visible);
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
export fn isDockIconVisible() bool {
|
|
3034
|
+
const IsDockIconVisibleFn = *const fn () callconv(.C) bool;
|
|
3035
|
+
const is_dock_icon_visible = lookupNativeSymbol(IsDockIconVisibleFn, "isDockIconVisible") orelse return false;
|
|
3036
|
+
return is_dock_icon_visible();
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
export fn openFileDialog(
|
|
3040
|
+
starting_folder: [*:0]const u8,
|
|
3041
|
+
allowed_file_types: [*:0]const u8,
|
|
3042
|
+
can_choose_files: c_int,
|
|
3043
|
+
can_choose_directories: c_int,
|
|
3044
|
+
allows_multiple_selection: c_int,
|
|
3045
|
+
) ?[*:0]const u8 {
|
|
3046
|
+
const OpenFileDialogFn = *const fn ([*:0]const u8, [*:0]const u8, c_int, c_int, c_int) callconv(.C) ?[*:0]const u8;
|
|
3047
|
+
const open_file_dialog = lookupNativeSymbol(OpenFileDialogFn, "openFileDialog") orelse return null;
|
|
3048
|
+
return open_file_dialog(
|
|
3049
|
+
starting_folder,
|
|
3050
|
+
allowed_file_types,
|
|
3051
|
+
can_choose_files,
|
|
3052
|
+
can_choose_directories,
|
|
3053
|
+
allows_multiple_selection,
|
|
3054
|
+
);
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
export fn showMessageBox(
|
|
3058
|
+
box_type: [*:0]const u8,
|
|
3059
|
+
title: [*:0]const u8,
|
|
3060
|
+
message: [*:0]const u8,
|
|
3061
|
+
detail: [*:0]const u8,
|
|
3062
|
+
buttons: [*:0]const u8,
|
|
3063
|
+
default_id: c_int,
|
|
3064
|
+
cancel_id: c_int,
|
|
3065
|
+
) c_int {
|
|
3066
|
+
const ShowMessageBoxFn = *const fn (
|
|
3067
|
+
[*:0]const u8,
|
|
3068
|
+
[*:0]const u8,
|
|
3069
|
+
[*:0]const u8,
|
|
3070
|
+
[*:0]const u8,
|
|
3071
|
+
[*:0]const u8,
|
|
3072
|
+
c_int,
|
|
3073
|
+
c_int,
|
|
3074
|
+
) callconv(.C) c_int;
|
|
3075
|
+
const show_message_box = lookupNativeSymbol(ShowMessageBoxFn, "showMessageBox") orelse return -1;
|
|
3076
|
+
return show_message_box(box_type, title, message, detail, buttons, default_id, cancel_id);
|
|
3077
|
+
}
|
|
3078
|
+
|
|
3079
|
+
export fn clipboardReadText() ?[*:0]const u8 {
|
|
3080
|
+
const ClipboardReadTextFn = *const fn () callconv(.C) ?[*:0]const u8;
|
|
3081
|
+
const clipboard_read_text = lookupNativeSymbol(ClipboardReadTextFn, "clipboardReadText") orelse return null;
|
|
3082
|
+
return clipboard_read_text();
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
export fn clipboardWriteText(text: [*:0]const u8) void {
|
|
3086
|
+
const ClipboardWriteTextFn = *const fn ([*:0]const u8) callconv(.C) void;
|
|
3087
|
+
const clipboard_write_text = lookupNativeSymbol(ClipboardWriteTextFn, "clipboardWriteText") orelse return;
|
|
3088
|
+
clipboard_write_text(text);
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
export fn clipboardReadImage(out_size: *u64) ?*const anyopaque {
|
|
3092
|
+
const ClipboardReadImageFn = *const fn (*u64) callconv(.C) ?*const anyopaque;
|
|
3093
|
+
const clipboard_read_image = lookupNativeSymbol(ClipboardReadImageFn, "clipboardReadImage") orelse return null;
|
|
3094
|
+
return clipboard_read_image(out_size);
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
export fn clipboardWriteImage(data: ?*const anyopaque, size: u64) void {
|
|
3098
|
+
const ClipboardWriteImageFn = *const fn (?*const anyopaque, u64) callconv(.C) void;
|
|
3099
|
+
const clipboard_write_image = lookupNativeSymbol(ClipboardWriteImageFn, "clipboardWriteImage") orelse return;
|
|
3100
|
+
clipboard_write_image(data, size);
|
|
3101
|
+
}
|
|
3102
|
+
|
|
3103
|
+
export fn clipboardClear() void {
|
|
3104
|
+
const ClipboardClearFn = *const fn () callconv(.C) void;
|
|
3105
|
+
const clipboard_clear = lookupNativeSymbol(ClipboardClearFn, "clipboardClear") orelse return;
|
|
3106
|
+
clipboard_clear();
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
export fn clipboardAvailableFormats() ?[*:0]const u8 {
|
|
3110
|
+
const ClipboardAvailableFormatsFn = *const fn () callconv(.C) ?[*:0]const u8;
|
|
3111
|
+
const clipboard_available_formats = lookupNativeSymbol(
|
|
3112
|
+
ClipboardAvailableFormatsFn,
|
|
3113
|
+
"clipboardAvailableFormats",
|
|
3114
|
+
) orelse return null;
|
|
3115
|
+
return clipboard_available_formats();
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
export fn getPrimaryDisplay() ?[*:0]const u8 {
|
|
3119
|
+
const GetPrimaryDisplayFn = *const fn () callconv(.C) ?[*:0]const u8;
|
|
3120
|
+
const get_primary_display = lookupNativeSymbol(GetPrimaryDisplayFn, "getPrimaryDisplay") orelse return null;
|
|
3121
|
+
return get_primary_display();
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
export fn getAllDisplays() ?[*:0]const u8 {
|
|
3125
|
+
const GetAllDisplaysFn = *const fn () callconv(.C) ?[*:0]const u8;
|
|
3126
|
+
const get_all_displays = lookupNativeSymbol(GetAllDisplaysFn, "getAllDisplays") orelse return null;
|
|
3127
|
+
return get_all_displays();
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
export fn getCursorScreenPoint() ?[*:0]const u8 {
|
|
3131
|
+
const GetCursorScreenPointFn = *const fn () callconv(.C) ?[*:0]const u8;
|
|
3132
|
+
const get_cursor_screen_point = lookupNativeSymbol(
|
|
3133
|
+
GetCursorScreenPointFn,
|
|
3134
|
+
"getCursorScreenPoint",
|
|
3135
|
+
) orelse return null;
|
|
3136
|
+
return get_cursor_screen_point();
|
|
3137
|
+
}
|
|
3138
|
+
|
|
3139
|
+
export fn getMouseButtons() u64 {
|
|
3140
|
+
const GetMouseButtonsFn = *const fn () callconv(.C) u64;
|
|
3141
|
+
const get_mouse_buttons = lookupNativeSymbol(GetMouseButtonsFn, "getMouseButtons") orelse return 0;
|
|
3142
|
+
return get_mouse_buttons();
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
export fn setGlobalShortcutCallback(callback: ?GlobalShortcutHandler) void {
|
|
3146
|
+
clearLastError();
|
|
3147
|
+
const SetGlobalShortcutCallbackFn = *const fn (?GlobalShortcutHandler) callconv(.C) void;
|
|
3148
|
+
const set_global_shortcut_callback = lookupNativeSymbol(
|
|
3149
|
+
SetGlobalShortcutCallbackFn,
|
|
3150
|
+
"setGlobalShortcutCallback",
|
|
3151
|
+
) orelse return;
|
|
3152
|
+
set_global_shortcut_callback(callback);
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
export fn registerGlobalShortcut(accelerator: [*:0]const u8) bool {
|
|
3156
|
+
clearLastError();
|
|
3157
|
+
const RegisterGlobalShortcutFn = *const fn ([*:0]const u8) callconv(.C) bool;
|
|
3158
|
+
const register_global_shortcut = lookupNativeSymbol(
|
|
3159
|
+
RegisterGlobalShortcutFn,
|
|
3160
|
+
"registerGlobalShortcut",
|
|
3161
|
+
) orelse return false;
|
|
3162
|
+
return register_global_shortcut(accelerator);
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
export fn unregisterGlobalShortcut(accelerator: [*:0]const u8) bool {
|
|
3166
|
+
clearLastError();
|
|
3167
|
+
const UnregisterGlobalShortcutFn = *const fn ([*:0]const u8) callconv(.C) bool;
|
|
3168
|
+
const unregister_global_shortcut = lookupNativeSymbol(
|
|
3169
|
+
UnregisterGlobalShortcutFn,
|
|
3170
|
+
"unregisterGlobalShortcut",
|
|
3171
|
+
) orelse return false;
|
|
3172
|
+
return unregister_global_shortcut(accelerator);
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
export fn unregisterAllGlobalShortcuts() void {
|
|
3176
|
+
clearLastError();
|
|
3177
|
+
const UnregisterAllGlobalShortcutsFn = *const fn () callconv(.C) void;
|
|
3178
|
+
const unregister_all_global_shortcuts = lookupNativeSymbol(
|
|
3179
|
+
UnregisterAllGlobalShortcutsFn,
|
|
3180
|
+
"unregisterAllGlobalShortcuts",
|
|
3181
|
+
) orelse return;
|
|
3182
|
+
unregister_all_global_shortcuts();
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
export fn isGlobalShortcutRegistered(accelerator: [*:0]const u8) bool {
|
|
3186
|
+
clearLastError();
|
|
3187
|
+
const IsGlobalShortcutRegisteredFn = *const fn ([*:0]const u8) callconv(.C) bool;
|
|
3188
|
+
const is_global_shortcut_registered = lookupNativeSymbol(
|
|
3189
|
+
IsGlobalShortcutRegisteredFn,
|
|
3190
|
+
"isGlobalShortcutRegistered",
|
|
3191
|
+
) orelse return false;
|
|
3192
|
+
return is_global_shortcut_registered(accelerator);
|
|
3193
|
+
}
|
|
3194
|
+
|
|
3195
|
+
export fn sessionGetCookies(
|
|
3196
|
+
partition_identifier: [*:0]const u8,
|
|
3197
|
+
filter_json: [*:0]const u8,
|
|
3198
|
+
) ?[*:0]const u8 {
|
|
3199
|
+
clearLastError();
|
|
3200
|
+
const SessionGetCookiesFn = *const fn ([*:0]const u8, [*:0]const u8) callconv(.C) ?[*:0]const u8;
|
|
3201
|
+
const session_get_cookies = lookupNativeSymbol(SessionGetCookiesFn, "sessionGetCookies") orelse return null;
|
|
3202
|
+
return session_get_cookies(partition_identifier, filter_json);
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
export fn sessionSetCookie(
|
|
3206
|
+
partition_identifier: [*:0]const u8,
|
|
3207
|
+
cookie_json: [*:0]const u8,
|
|
3208
|
+
) bool {
|
|
3209
|
+
clearLastError();
|
|
3210
|
+
const SessionSetCookieFn = *const fn ([*:0]const u8, [*:0]const u8) callconv(.C) bool;
|
|
3211
|
+
const session_set_cookie = lookupNativeSymbol(SessionSetCookieFn, "sessionSetCookie") orelse return false;
|
|
3212
|
+
return session_set_cookie(partition_identifier, cookie_json);
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
export fn sessionRemoveCookie(
|
|
3216
|
+
partition_identifier: [*:0]const u8,
|
|
3217
|
+
url: [*:0]const u8,
|
|
3218
|
+
cookie_name: [*:0]const u8,
|
|
3219
|
+
) bool {
|
|
3220
|
+
clearLastError();
|
|
3221
|
+
const SessionRemoveCookieFn = *const fn ([*:0]const u8, [*:0]const u8, [*:0]const u8) callconv(.C) bool;
|
|
3222
|
+
const session_remove_cookie = lookupNativeSymbol(SessionRemoveCookieFn, "sessionRemoveCookie") orelse return false;
|
|
3223
|
+
return session_remove_cookie(partition_identifier, url, cookie_name);
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
export fn sessionClearCookies(partition_identifier: [*:0]const u8) void {
|
|
3227
|
+
clearLastError();
|
|
3228
|
+
const SessionClearCookiesFn = *const fn ([*:0]const u8) callconv(.C) void;
|
|
3229
|
+
const session_clear_cookies = lookupNativeSymbol(
|
|
3230
|
+
SessionClearCookiesFn,
|
|
3231
|
+
"sessionClearCookies",
|
|
3232
|
+
) orelse return;
|
|
3233
|
+
session_clear_cookies(partition_identifier);
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
export fn sessionClearStorageData(
|
|
3237
|
+
partition_identifier: [*:0]const u8,
|
|
3238
|
+
storage_types_json: [*:0]const u8,
|
|
3239
|
+
) void {
|
|
3240
|
+
clearLastError();
|
|
3241
|
+
const SessionClearStorageDataFn = *const fn ([*:0]const u8, [*:0]const u8) callconv(.C) void;
|
|
3242
|
+
const session_clear_storage_data = lookupNativeSymbol(
|
|
3243
|
+
SessionClearStorageDataFn,
|
|
3244
|
+
"sessionClearStorageData",
|
|
3245
|
+
) orelse return;
|
|
3246
|
+
session_clear_storage_data(partition_identifier, storage_types_json);
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3249
|
+
export fn setURLOpenHandler(handler: ?URLOpenHandler) void {
|
|
3250
|
+
clearLastError();
|
|
3251
|
+
const SetURLOpenHandlerFn = *const fn (?URLOpenHandler) callconv(.C) void;
|
|
3252
|
+
const set_url_open_handler = lookupNativeSymbol(SetURLOpenHandlerFn, "setURLOpenHandler") orelse return;
|
|
3253
|
+
set_url_open_handler(handler);
|
|
3254
|
+
}
|
|
3255
|
+
|
|
3256
|
+
export fn setAppReopenHandler(handler: ?AppReopenHandler) void {
|
|
3257
|
+
clearLastError();
|
|
3258
|
+
const SetAppReopenHandlerFn = *const fn (?AppReopenHandler) callconv(.C) void;
|
|
3259
|
+
const set_app_reopen_handler = lookupNativeSymbol(
|
|
3260
|
+
SetAppReopenHandlerFn,
|
|
3261
|
+
"setAppReopenHandler",
|
|
3262
|
+
) orelse return;
|
|
3263
|
+
set_app_reopen_handler(handler);
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
export fn setQuitRequestedHandler(handler: ?QuitRequestedHandler) void {
|
|
3267
|
+
clearLastError();
|
|
3268
|
+
managed_quit_requested_handler = handler;
|
|
3269
|
+
const SetQuitRequestedHandlerFn = *const fn (?QuitRequestedHandler) callconv(.C) void;
|
|
3270
|
+
const set_quit_requested_handler = lookupNativeSymbol(
|
|
3271
|
+
SetQuitRequestedHandlerFn,
|
|
3272
|
+
"setQuitRequestedHandler",
|
|
3273
|
+
) orelse return;
|
|
3274
|
+
set_quit_requested_handler(if (handler != null) managedQuitRequestedTrampoline else null);
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
export fn setExitOnLastWindowClosed(enabled: bool) void {
|
|
3278
|
+
clearLastError();
|
|
3279
|
+
exit_on_last_window_closed = enabled;
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
export fn quitGracefully(code: c_int, timeout_ms: c_int) void {
|
|
3283
|
+
clearLastError();
|
|
3284
|
+
|
|
3285
|
+
const StopEventLoopFn = *const fn () callconv(.C) void;
|
|
3286
|
+
const WaitForShutdownCompleteFn = *const fn (c_int) callconv(.C) void;
|
|
3287
|
+
|
|
3288
|
+
if (lookupNativeSymbol(StopEventLoopFn, "stopEventLoop")) |stop_event_loop| {
|
|
3289
|
+
stop_event_loop();
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
if (lookupNativeSymbol(WaitForShutdownCompleteFn, "waitForShutdownComplete")) |wait_for_shutdown_complete| {
|
|
3293
|
+
wait_for_shutdown_complete(timeout_ms);
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
if (ensureNativeWrapperLoaded()) {
|
|
3297
|
+
native_wrapper_state.force_exit(code);
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
export fn stopEventLoop() void {
|
|
3302
|
+
clearLastError();
|
|
3303
|
+
const StopEventLoopFn = *const fn () callconv(.C) void;
|
|
3304
|
+
const stop_event_loop = lookupNativeSymbol(StopEventLoopFn, "stopEventLoop") orelse return;
|
|
3305
|
+
stop_event_loop();
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
export fn waitForShutdownComplete(timeout_ms: c_int) void {
|
|
3309
|
+
clearLastError();
|
|
3310
|
+
const WaitForShutdownCompleteFn = *const fn (c_int) callconv(.C) void;
|
|
3311
|
+
const wait_for_shutdown_complete = lookupNativeSymbol(
|
|
3312
|
+
WaitForShutdownCompleteFn,
|
|
3313
|
+
"waitForShutdownComplete",
|
|
3314
|
+
) orelse return;
|
|
3315
|
+
wait_for_shutdown_complete(timeout_ms);
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
export fn forceExit(code: c_int) void {
|
|
3319
|
+
clearLastError();
|
|
3320
|
+
if (!ensureNativeWrapperLoaded()) {
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
native_wrapper_state.force_exit(code);
|
|
3324
|
+
}
|
|
3325
|
+
|
|
3326
|
+
export fn wgpuCreateSurfaceForView(instance: ?*anyopaque, view_ptr: ?*anyopaque) ?*anyopaque {
|
|
3327
|
+
clearLastError();
|
|
3328
|
+
const WgpuCreateSurfaceForViewFn = *const fn (?*anyopaque, ?*anyopaque) callconv(.C) ?*anyopaque;
|
|
3329
|
+
const wgpu_create_surface_for_view = lookupNativeSymbol(
|
|
3330
|
+
WgpuCreateSurfaceForViewFn,
|
|
3331
|
+
"wgpuCreateSurfaceForView",
|
|
3332
|
+
) orelse return null;
|
|
3333
|
+
return wgpu_create_surface_for_view(instance, view_ptr);
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
export fn wgpuCreateAdapterDeviceMainThread(
|
|
3337
|
+
instance_ptr: ?*anyopaque,
|
|
3338
|
+
surface_ptr: ?*anyopaque,
|
|
3339
|
+
out_adapter_device: ?*anyopaque,
|
|
3340
|
+
) void {
|
|
3341
|
+
clearLastError();
|
|
3342
|
+
const WgpuCreateAdapterDeviceMainThreadFn = *const fn (?*anyopaque, ?*anyopaque, ?*anyopaque) callconv(.C) void;
|
|
3343
|
+
const wgpu_create_adapter_device_main_thread = lookupNativeSymbol(
|
|
3344
|
+
WgpuCreateAdapterDeviceMainThreadFn,
|
|
3345
|
+
"wgpuCreateAdapterDeviceMainThread",
|
|
3346
|
+
) orelse return;
|
|
3347
|
+
wgpu_create_adapter_device_main_thread(instance_ptr, surface_ptr, out_adapter_device);
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
export fn wgpuSurfaceConfigureMainThread(surface_ptr: ?*anyopaque, config_ptr: ?*anyopaque) void {
|
|
3351
|
+
clearLastError();
|
|
3352
|
+
const WgpuSurfaceConfigureMainThreadFn = *const fn (?*anyopaque, ?*anyopaque) callconv(.C) void;
|
|
3353
|
+
const wgpu_surface_configure_main_thread = lookupNativeSymbol(
|
|
3354
|
+
WgpuSurfaceConfigureMainThreadFn,
|
|
3355
|
+
"wgpuSurfaceConfigureMainThread",
|
|
3356
|
+
) orelse return;
|
|
3357
|
+
wgpu_surface_configure_main_thread(surface_ptr, config_ptr);
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
export fn wgpuSurfaceGetCurrentTextureMainThread(surface_ptr: ?*anyopaque, surface_texture_ptr: ?*anyopaque) void {
|
|
3361
|
+
clearLastError();
|
|
3362
|
+
const WgpuSurfaceGetCurrentTextureMainThreadFn = *const fn (?*anyopaque, ?*anyopaque) callconv(.C) void;
|
|
3363
|
+
const wgpu_surface_get_current_texture_main_thread = lookupNativeSymbol(
|
|
3364
|
+
WgpuSurfaceGetCurrentTextureMainThreadFn,
|
|
3365
|
+
"wgpuSurfaceGetCurrentTextureMainThread",
|
|
3366
|
+
) orelse return;
|
|
3367
|
+
wgpu_surface_get_current_texture_main_thread(surface_ptr, surface_texture_ptr);
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
export fn wgpuSurfacePresentMainThread(surface_ptr: ?*anyopaque) i32 {
|
|
3371
|
+
clearLastError();
|
|
3372
|
+
const WgpuSurfacePresentMainThreadFn = *const fn (?*anyopaque) callconv(.C) i32;
|
|
3373
|
+
const wgpu_surface_present_main_thread = lookupNativeSymbol(
|
|
3374
|
+
WgpuSurfacePresentMainThreadFn,
|
|
3375
|
+
"wgpuSurfacePresentMainThread",
|
|
3376
|
+
) orelse return -1;
|
|
3377
|
+
return wgpu_surface_present_main_thread(surface_ptr);
|
|
3378
|
+
}
|