bunite-core 0.10.1 → 0.11.1
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/package.json +2 -2
- package/src/host/core/App.ts +18 -4
- package/src/host/index.ts +1 -1
- package/src/host/native.ts +37 -9
- package/src/host/paths.ts +39 -20
- package/src/native/win-webview2/bunite_webview2_ffi.cpp +294 -0
- package/src/native/win-webview2/spike/main.cpp +375 -0
- package/src/native/win-webview2/webview2_appres.cpp +210 -0
- package/src/native/win-webview2/webview2_internal.h +210 -0
- package/src/native/win-webview2/webview2_runtime.cpp +874 -0
- package/src/native/win-webview2/webview2_utils.cpp +246 -0
- package/src/preload/runtime.built.js +1 -1
- package/src/rpc/peer.ts +3 -2
- package/src/rpc/renderer.ts +20 -0
- package/src/rpc/schema.ts +0 -1
- package/src/rpc/wire.ts +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunite-core",
|
|
3
3
|
"description": "Uniting UI and Bun",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"setup:cef": "bun ../tools/bunite-dev/scripts/setup-cef.ts",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"msgpackr": "^1.11.9"
|
|
25
25
|
},
|
|
26
26
|
"optionalDependencies": {
|
|
27
|
-
"bunite-native-win-x64": "0.0.
|
|
27
|
+
"bunite-native-win-x64": "0.0.5",
|
|
28
28
|
"bunite-native-mac-arm64": "0.0.1",
|
|
29
29
|
"bunite-native-linux-x64": "0.0.1"
|
|
30
30
|
}
|
package/src/host/core/App.ts
CHANGED
|
@@ -88,9 +88,15 @@ export class AppRuntime {
|
|
|
88
88
|
process.env.BUNITE_USER_DATA_DIR = join(appDataDir, name);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
const envEngine = process.env.BUNITE_ENGINE;
|
|
92
|
+
const engineFromEnv = envEngine === "cef" || envEngine === "webview2"
|
|
93
|
+
? envEngine
|
|
94
|
+
: undefined;
|
|
95
|
+
|
|
91
96
|
await initNativeRuntime({
|
|
92
97
|
hideConsole: options.hideConsole,
|
|
93
98
|
popupBlocking: options.popupBlocking,
|
|
99
|
+
engine: options.engine ?? engineFromEnv,
|
|
94
100
|
engineFlags: options.engineFlags
|
|
95
101
|
});
|
|
96
102
|
|
|
@@ -126,11 +132,21 @@ export class AppRuntime {
|
|
|
126
132
|
}
|
|
127
133
|
|
|
128
134
|
run() {
|
|
135
|
+
if (!getNativeRuntimeState()) {
|
|
136
|
+
throw new Error("AppRuntime.run() called before await app.ready completed");
|
|
137
|
+
}
|
|
129
138
|
const lib = getNativeLibrary();
|
|
139
|
+
// CEF: bunite_run_loop blocks here. WebView2 / mac / linux: returns immediately
|
|
140
|
+
// (cooperative pump kicks in below).
|
|
130
141
|
lib?.symbols.bunite_run_loop();
|
|
131
142
|
|
|
132
|
-
|
|
133
|
-
|
|
143
|
+
const engine = getNativeEngineName();
|
|
144
|
+
const cooperative =
|
|
145
|
+
process.platform === "darwin" ||
|
|
146
|
+
process.platform === "linux" ||
|
|
147
|
+
(process.platform === "win32" && engine !== "cef");
|
|
148
|
+
|
|
149
|
+
if (cooperative) {
|
|
134
150
|
this.pumpActive = true;
|
|
135
151
|
const pump = () => {
|
|
136
152
|
if (!this.pumpActive) return;
|
|
@@ -139,8 +155,6 @@ export class AppRuntime {
|
|
|
139
155
|
};
|
|
140
156
|
pump();
|
|
141
157
|
}
|
|
142
|
-
// Windows: native UI thread is separate. Bun's loop stays alive via the RPC
|
|
143
|
-
// listening socket started by ensureRpcServer() in the constructor.
|
|
144
158
|
}
|
|
145
159
|
|
|
146
160
|
quit(code = 0) {
|
package/src/host/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { acquireSingleInstanceLock, type SingleInstanceLock } from "./core/singl
|
|
|
8
8
|
import { log, type LogLevel } from "./log";
|
|
9
9
|
|
|
10
10
|
export { serveWeb } from "./serveWeb";
|
|
11
|
-
export type { WebRpcMount } from "./serveWeb";
|
|
11
|
+
export type { WebRpcMount, WsData, ServeWebOptions } from "./serveWeb";
|
|
12
12
|
|
|
13
13
|
export {
|
|
14
14
|
acquireSingleInstanceLock,
|
package/src/host/native.ts
CHANGED
|
@@ -2,16 +2,20 @@ import { CString, dlopen, FFIType, JSCallback, ptr, type Pointer } from "bun:ffi
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { delimiter, join } from "node:path";
|
|
4
4
|
import { buniteEventEmitter } from "./events/eventEmitter";
|
|
5
|
-
import { resolveNativeArtifacts, type ResolvedNativeArtifacts } from "./paths";
|
|
5
|
+
import { resolveNativeArtifacts, type ResolvedNativeArtifacts, type WindowsEngine } from "./paths";
|
|
6
|
+
import { resolvePackageRoot } from "./paths";
|
|
6
7
|
import { log } from "./log";
|
|
7
8
|
|
|
8
9
|
export type NativeBootstrapOptions = {
|
|
9
10
|
hideConsole?: boolean;
|
|
10
11
|
popupBlocking?: boolean;
|
|
12
|
+
/** Windows-only engine selection. Default "webview2"; "cef" requires bunite-cef-win-x64. */
|
|
13
|
+
engine?: WindowsEngine;
|
|
11
14
|
/**
|
|
12
15
|
* Engine-specific opaque config. Each adapter parses its own keys.
|
|
13
16
|
* - CEF (Windows): Chromium command-line flags as `Record<flag, value | true>`.
|
|
14
|
-
* -
|
|
17
|
+
* - WebView2 (Windows): `{ userDataFolder?, additionalBrowserArguments?, language? }`.
|
|
18
|
+
* - WKWebView, WebKitGTK: defined per adapter.
|
|
15
19
|
*/
|
|
16
20
|
engineFlags?: Record<string, string | boolean>;
|
|
17
21
|
};
|
|
@@ -356,7 +360,8 @@ export function toCString(value: string): CStringPointer {
|
|
|
356
360
|
}
|
|
357
361
|
|
|
358
362
|
function applyEnvironment(artifacts: ResolvedNativeArtifacts) {
|
|
359
|
-
// CEF needs engine dir on PATH (libcef.dll) and ICU_DATA pointing at resources.
|
|
363
|
+
// CEF needs engine dir on PATH (libcef.dll) and ICU_DATA pointing at resources.
|
|
364
|
+
// WebView2 needs the directory containing WebView2Loader.dll on PATH.
|
|
360
365
|
const engineBinaryDir = artifacts.cefDir && existsSync(join(artifacts.cefDir, "Release", "libcef.dll"))
|
|
361
366
|
? join(artifacts.cefDir, "Release")
|
|
362
367
|
: artifacts.cefDir;
|
|
@@ -367,10 +372,18 @@ function applyEnvironment(artifacts: ResolvedNativeArtifacts) {
|
|
|
367
372
|
if (engineResourceDir && !process.env.ICU_DATA) {
|
|
368
373
|
process.env.ICU_DATA = engineResourceDir;
|
|
369
374
|
}
|
|
370
|
-
|
|
375
|
+
|
|
376
|
+
const pathDirs: string[] = [];
|
|
377
|
+
if (engineBinaryDir) pathDirs.push(engineBinaryDir);
|
|
378
|
+
if (artifacts.engine === "webview2" && artifacts.nativeLibPath) {
|
|
379
|
+
const dir = join(artifacts.nativeLibPath, "..");
|
|
380
|
+
if (existsSync(join(dir, "WebView2Loader.dll"))) pathDirs.push(dir);
|
|
381
|
+
}
|
|
382
|
+
if (pathDirs.length > 0) {
|
|
371
383
|
const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
372
|
-
|
|
373
|
-
|
|
384
|
+
const newDirs = pathDirs.filter((d) => !pathEntries.includes(d));
|
|
385
|
+
if (newDirs.length > 0) {
|
|
386
|
+
process.env.PATH = [...newDirs, ...pathEntries].join(delimiter);
|
|
374
387
|
}
|
|
375
388
|
}
|
|
376
389
|
}
|
|
@@ -575,18 +588,33 @@ export async function initNativeRuntime(
|
|
|
575
588
|
return state;
|
|
576
589
|
}
|
|
577
590
|
|
|
578
|
-
const artifacts = resolveNativeArtifacts();
|
|
591
|
+
const artifacts = resolveNativeArtifacts(options.engine);
|
|
579
592
|
const hasNativeArtifacts = Boolean(
|
|
580
593
|
artifacts.nativeLibPath && existsSync(artifacts.nativeLibPath)
|
|
581
594
|
);
|
|
582
595
|
|
|
583
596
|
applyEnvironment(artifacts);
|
|
584
597
|
|
|
598
|
+
// Migration nudge: pre-existing CEF deps + unset engine ⇒ default flipped to WebView2.
|
|
599
|
+
if (
|
|
600
|
+
process.platform === "win32" &&
|
|
601
|
+
options.engine === undefined &&
|
|
602
|
+
resolvePackageRoot("bunite-cef-win-x64") != null
|
|
603
|
+
) {
|
|
604
|
+
log.warn(
|
|
605
|
+
"[bunite] Detected bunite-cef-win-x64. Windows default engine is now \"webview2\" — " +
|
|
606
|
+
"this app is currently running on WebView2. " +
|
|
607
|
+
"To stay on CEF: pass engine: \"cef\" to AppRuntime. " +
|
|
608
|
+
"To accept the new default: drop bunite-cef-win-x64 from dependencies."
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
|
|
585
612
|
if (!hasNativeArtifacts) {
|
|
613
|
+
const engineSuffix = artifacts.engine === "cef" ? " (engine=cef requires bunite-cef-win-x64)" : "";
|
|
586
614
|
throw new Error(
|
|
587
615
|
"bunite: native runtime not found. Install the platform package " +
|
|
588
|
-
`(bunite-native-${process.platform === "win32" ? "win" : process.platform === "darwin" ? "mac" : "linux"}-<arch>)
|
|
589
|
-
|
|
616
|
+
`(bunite-native-${process.platform === "win32" ? "win" : process.platform === "darwin" ? "mac" : "linux"}-<arch>)` +
|
|
617
|
+
engineSuffix + "."
|
|
590
618
|
);
|
|
591
619
|
}
|
|
592
620
|
|
package/src/host/paths.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { CEF_VERSION } from "./cefVersion";
|
|
|
6
6
|
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
|
|
9
|
+
export type WindowsEngine = "webview2" | "cef";
|
|
10
|
+
|
|
9
11
|
export type ResolvedNativeArtifacts = {
|
|
10
12
|
packageRoot: string;
|
|
11
13
|
source: "optional-package" | "local-build" | "missing";
|
|
@@ -14,8 +16,17 @@ export type ResolvedNativeArtifacts = {
|
|
|
14
16
|
nativeLibPath: string | null;
|
|
15
17
|
/** CEF framework dir containing libcef.dll. Null on macOS/Linux (system framework). */
|
|
16
18
|
cefDir: string | null;
|
|
19
|
+
/** Selected Windows engine. Undefined on mac/linux. */
|
|
20
|
+
engine?: WindowsEngine;
|
|
17
21
|
};
|
|
18
22
|
|
|
23
|
+
function nativeLibBasename(engine: WindowsEngine | undefined): string {
|
|
24
|
+
if (PLATFORM_TAG === "win" && engine === "webview2") {
|
|
25
|
+
return `libBuniteNativeWebView2${NATIVE_LIB_EXT}`;
|
|
26
|
+
}
|
|
27
|
+
return `libBuniteNative${NATIVE_LIB_EXT}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
19
30
|
export function resolvePackageRoot(packageName: string): string | null {
|
|
20
31
|
try {
|
|
21
32
|
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
@@ -106,12 +117,14 @@ export function resolveDefaultAppResRoot(): string | null {
|
|
|
106
117
|
return existsSync(candidate) ? candidate : null;
|
|
107
118
|
}
|
|
108
119
|
|
|
109
|
-
export function resolveNativeArtifacts(): ResolvedNativeArtifacts {
|
|
120
|
+
export function resolveNativeArtifacts(engine?: WindowsEngine): ResolvedNativeArtifacts {
|
|
110
121
|
const exeDir = getBaseDir();
|
|
122
|
+
const resolvedEngine: WindowsEngine | undefined =
|
|
123
|
+
PLATFORM_TAG === "win" ? (engine ?? "webview2") : undefined;
|
|
124
|
+
const libName = nativeLibBasename(resolvedEngine);
|
|
111
125
|
|
|
112
|
-
// 1. Entry-script-dir / executable-relative
|
|
113
|
-
|
|
114
|
-
const exeNativeLib = join(exeDir, `libBuniteNative${NATIVE_LIB_EXT}`);
|
|
126
|
+
// 1. Entry-script-dir / executable-relative.
|
|
127
|
+
const exeNativeLib = join(exeDir, libName);
|
|
115
128
|
if (existsSync(exeNativeLib)) {
|
|
116
129
|
return {
|
|
117
130
|
packageRoot: exeDir,
|
|
@@ -119,21 +132,22 @@ export function resolveNativeArtifacts(): ResolvedNativeArtifacts {
|
|
|
119
132
|
nativePackageName: null,
|
|
120
133
|
enginePackageName: null,
|
|
121
134
|
nativeLibPath: exeNativeLib,
|
|
122
|
-
cefDir: resolveCefDir([exeDir])
|
|
135
|
+
cefDir: resolvedEngine === "cef" ? resolveCefDir([exeDir]) : null,
|
|
136
|
+
engine: resolvedEngine
|
|
123
137
|
};
|
|
124
138
|
}
|
|
125
139
|
|
|
126
140
|
const packageRoot = resolveBunitePackageRoot();
|
|
127
141
|
|
|
128
|
-
// 2. Optional npm packages
|
|
142
|
+
// 2. Optional npm packages.
|
|
129
143
|
const nativePackageName = `bunite-native-${PLATFORM_TAG}-${ARCH}`;
|
|
130
|
-
const enginePackageName = PLATFORM_TAG === "win"
|
|
144
|
+
const enginePackageName = PLATFORM_TAG === "win" && resolvedEngine === "cef"
|
|
145
|
+
? `bunite-cef-${PLATFORM_TAG}-${ARCH}`
|
|
146
|
+
: null;
|
|
131
147
|
const nativePackageRoot = resolvePackageRoot(nativePackageName);
|
|
132
148
|
const enginePackageRoot = enginePackageName ? resolvePackageRoot(enginePackageName) : null;
|
|
133
149
|
|
|
134
|
-
const packagedNativeLibPath = nativePackageRoot
|
|
135
|
-
? join(nativePackageRoot, `libBuniteNative${NATIVE_LIB_EXT}`)
|
|
136
|
-
: null;
|
|
150
|
+
const packagedNativeLibPath = nativePackageRoot ? join(nativePackageRoot, libName) : null;
|
|
137
151
|
const packagedEngineDir = enginePackageRoot ?? null;
|
|
138
152
|
|
|
139
153
|
if (packagedNativeLibPath && existsSync(packagedNativeLibPath)) {
|
|
@@ -143,16 +157,19 @@ export function resolveNativeArtifacts(): ResolvedNativeArtifacts {
|
|
|
143
157
|
nativePackageName,
|
|
144
158
|
enginePackageName: packagedEngineDir && existsSync(packagedEngineDir) ? enginePackageName : null,
|
|
145
159
|
nativeLibPath: packagedNativeLibPath,
|
|
146
|
-
cefDir:
|
|
147
|
-
? packagedEngineDir
|
|
148
|
-
|
|
160
|
+
cefDir: resolvedEngine === "cef"
|
|
161
|
+
? ((packagedEngineDir && existsSync(packagedEngineDir))
|
|
162
|
+
? packagedEngineDir
|
|
163
|
+
: resolveCefDir([nativePackageRoot, packageRoot].filter(Boolean) as string[]))
|
|
164
|
+
: null,
|
|
165
|
+
engine: resolvedEngine
|
|
149
166
|
};
|
|
150
167
|
}
|
|
151
168
|
|
|
152
|
-
// 3. Local build (development)
|
|
169
|
+
// 3. Local build (development).
|
|
153
170
|
if (packageRoot) {
|
|
154
171
|
const localBuildRoot = join(packageRoot, "native-build", `${PLATFORM_TAG}-${ARCH}`);
|
|
155
|
-
const directLib = join(localBuildRoot,
|
|
172
|
+
const directLib = join(localBuildRoot, libName);
|
|
156
173
|
|
|
157
174
|
if (existsSync(directLib)) {
|
|
158
175
|
return {
|
|
@@ -161,12 +178,12 @@ export function resolveNativeArtifacts(): ResolvedNativeArtifacts {
|
|
|
161
178
|
nativePackageName: null,
|
|
162
179
|
enginePackageName: null,
|
|
163
180
|
nativeLibPath: directLib,
|
|
164
|
-
cefDir: resolveCefDir([localBuildRoot])
|
|
181
|
+
cefDir: resolvedEngine === "cef" ? resolveCefDir([localBuildRoot]) : null,
|
|
182
|
+
engine: resolvedEngine
|
|
165
183
|
};
|
|
166
184
|
}
|
|
167
185
|
|
|
168
|
-
const releaseLib = join(localBuildRoot, "Release",
|
|
169
|
-
|
|
186
|
+
const releaseLib = join(localBuildRoot, "Release", libName);
|
|
170
187
|
if (existsSync(releaseLib)) {
|
|
171
188
|
return {
|
|
172
189
|
packageRoot,
|
|
@@ -174,7 +191,8 @@ export function resolveNativeArtifacts(): ResolvedNativeArtifacts {
|
|
|
174
191
|
nativePackageName: null,
|
|
175
192
|
enginePackageName: null,
|
|
176
193
|
nativeLibPath: releaseLib,
|
|
177
|
-
cefDir: resolveCefDir([localBuildRoot])
|
|
194
|
+
cefDir: resolvedEngine === "cef" ? resolveCefDir([localBuildRoot]) : null,
|
|
195
|
+
engine: resolvedEngine
|
|
178
196
|
};
|
|
179
197
|
}
|
|
180
198
|
}
|
|
@@ -185,6 +203,7 @@ export function resolveNativeArtifacts(): ResolvedNativeArtifacts {
|
|
|
185
203
|
nativePackageName: nativePackageRoot ? nativePackageName : null,
|
|
186
204
|
enginePackageName: enginePackageRoot ? enginePackageName : null,
|
|
187
205
|
nativeLibPath: null,
|
|
188
|
-
cefDir: null
|
|
206
|
+
cefDir: null,
|
|
207
|
+
engine: resolvedEngine
|
|
189
208
|
};
|
|
190
209
|
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#include "webview2_internal.h"
|
|
2
|
+
|
|
3
|
+
#include <cstring>
|
|
4
|
+
|
|
5
|
+
using namespace bunite_webview2;
|
|
6
|
+
|
|
7
|
+
// Forward declaration of helper defined in webview2_runtime.cpp.
|
|
8
|
+
namespace bunite_webview2 {
|
|
9
|
+
void setViewInputPassthrough(ViewHost* v, bool passthrough);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extern "C" {
|
|
13
|
+
|
|
14
|
+
BUNITE_EXPORT int32_t bunite_abi_version(void) { return 4; }
|
|
15
|
+
|
|
16
|
+
BUNITE_EXPORT void bunite_set_log_level(int32_t level) {
|
|
17
|
+
if (level < 0) level = 0;
|
|
18
|
+
if (level > 4) level = 4;
|
|
19
|
+
buniteSetLogLevel(static_cast<BuniteLogLevel>(level));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
BUNITE_EXPORT bool bunite_init(const char* engine_dir, bool hide_console,
|
|
23
|
+
bool popup_blocking, const char* engine_config_json) {
|
|
24
|
+
return initRuntime(engine_dir, hide_console, popup_blocking, engine_config_json);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
BUNITE_EXPORT const char* bunite_engine_name(void) { return "webview2"; }
|
|
28
|
+
|
|
29
|
+
BUNITE_EXPORT const char* bunite_engine_version(void) {
|
|
30
|
+
// Cache only on success — env is created lazily on first view, so early
|
|
31
|
+
// callers (e.g. window-title formatting) would otherwise pin "unknown".
|
|
32
|
+
static std::string cached;
|
|
33
|
+
if (!cached.empty()) return cached.c_str();
|
|
34
|
+
if (!g_runtime.env) return "unknown";
|
|
35
|
+
LPWSTR ver = nullptr;
|
|
36
|
+
if (FAILED(g_runtime.env->get_BrowserVersionString(&ver)) || !ver) {
|
|
37
|
+
if (ver) CoTaskMemFree(ver);
|
|
38
|
+
return "unknown";
|
|
39
|
+
}
|
|
40
|
+
cached = wideToUtf8(ver);
|
|
41
|
+
CoTaskMemFree(ver);
|
|
42
|
+
return cached.c_str();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
BUNITE_EXPORT void bunite_run_loop(void) {
|
|
46
|
+
// Cooperative engine — TS drives via bunite_pump_once.
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
BUNITE_EXPORT void bunite_pump_once(void) { pumpOnce(); }
|
|
50
|
+
|
|
51
|
+
BUNITE_EXPORT void bunite_quit(void) { shutdownRuntime(); }
|
|
52
|
+
|
|
53
|
+
BUNITE_EXPORT void bunite_free_cstring(const char* value) {
|
|
54
|
+
if (value) free(const_cast<char*>(value));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
BUNITE_EXPORT void bunite_set_webview_event_handler(BuniteWebviewEventHandler h) {
|
|
58
|
+
g_runtime.webview_event_handler = h;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
BUNITE_EXPORT void bunite_set_window_event_handler(BuniteWindowEventHandler h) {
|
|
62
|
+
g_runtime.window_event_handler = h;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---- windows ----------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
BUNITE_EXPORT bool bunite_window_create(
|
|
68
|
+
uint32_t window_id, double x, double y, double w, double h,
|
|
69
|
+
const char* title, const char* title_bar_style,
|
|
70
|
+
bool transparent, bool hidden, bool minimized, bool maximized) {
|
|
71
|
+
return createWindow(window_id, x, y, w, h, title, title_bar_style,
|
|
72
|
+
transparent, hidden, minimized, maximized);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
BUNITE_EXPORT void bunite_window_destroy(uint32_t window_id) { destroyWindow(window_id); }
|
|
76
|
+
|
|
77
|
+
BUNITE_EXPORT void bunite_window_reset_close_pending(uint32_t window_id) {
|
|
78
|
+
WindowHost* w = getWindow(window_id);
|
|
79
|
+
if (w) w->close_pending.store(false);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
BUNITE_EXPORT void bunite_window_show(uint32_t window_id) {
|
|
83
|
+
WindowHost* w = getWindow(window_id);
|
|
84
|
+
if (w && w->hwnd) ShowWindow(w->hwnd, SW_SHOW);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
BUNITE_EXPORT void bunite_window_close(uint32_t window_id) {
|
|
88
|
+
WindowHost* w = getWindow(window_id);
|
|
89
|
+
if (w && w->hwnd) DestroyWindow(w->hwnd);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
BUNITE_EXPORT void bunite_window_set_title(uint32_t window_id, const char* title) {
|
|
93
|
+
WindowHost* w = getWindow(window_id);
|
|
94
|
+
if (w && w->hwnd && title) SetWindowTextW(w->hwnd, utf8ToWide(title).c_str());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
BUNITE_EXPORT void bunite_window_minimize(uint32_t window_id) {
|
|
98
|
+
WindowHost* w = getWindow(window_id);
|
|
99
|
+
if (w && w->hwnd) ShowWindow(w->hwnd, SW_MINIMIZE);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
BUNITE_EXPORT void bunite_window_unminimize(uint32_t window_id) {
|
|
103
|
+
WindowHost* w = getWindow(window_id);
|
|
104
|
+
if (w && w->hwnd) ShowWindow(w->hwnd, SW_RESTORE);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
BUNITE_EXPORT bool bunite_window_is_minimized(uint32_t window_id) {
|
|
108
|
+
WindowHost* w = getWindow(window_id);
|
|
109
|
+
return w && w->hwnd && IsIconic(w->hwnd);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
BUNITE_EXPORT void bunite_window_maximize(uint32_t window_id) {
|
|
113
|
+
WindowHost* w = getWindow(window_id);
|
|
114
|
+
if (w && w->hwnd) ShowWindow(w->hwnd, SW_MAXIMIZE);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
BUNITE_EXPORT void bunite_window_unmaximize(uint32_t window_id) {
|
|
118
|
+
WindowHost* w = getWindow(window_id);
|
|
119
|
+
if (w && w->hwnd) ShowWindow(w->hwnd, SW_RESTORE);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
BUNITE_EXPORT bool bunite_window_is_maximized(uint32_t window_id) {
|
|
123
|
+
WindowHost* w = getWindow(window_id);
|
|
124
|
+
return w && w->hwnd && IsZoomed(w->hwnd);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
BUNITE_EXPORT void bunite_window_set_frame(uint32_t window_id,
|
|
128
|
+
double x, double y, double w, double h) {
|
|
129
|
+
WindowHost* win = getWindow(window_id);
|
|
130
|
+
if (win && win->hwnd) {
|
|
131
|
+
SetWindowPos(win->hwnd, nullptr,
|
|
132
|
+
static_cast<int>(x), static_cast<int>(y),
|
|
133
|
+
static_cast<int>(w), static_cast<int>(h),
|
|
134
|
+
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ---- views ------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
BUNITE_EXPORT bool bunite_view_create(
|
|
141
|
+
uint32_t view_id, uint32_t window_id,
|
|
142
|
+
const char* url, const char* html,
|
|
143
|
+
const char* preload, const char* appres_root,
|
|
144
|
+
const char* navigation_rules_json,
|
|
145
|
+
double x, double y, double w, double h,
|
|
146
|
+
bool auto_resize, bool sandbox, const char* preload_origins_json) {
|
|
147
|
+
return createView(view_id, window_id, url, html, preload, appres_root,
|
|
148
|
+
navigation_rules_json, x, y, w, h,
|
|
149
|
+
auto_resize, sandbox, preload_origins_json);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
BUNITE_EXPORT void bunite_view_execute_javascript(uint32_t view_id, const char* script) {
|
|
153
|
+
ViewHost* v = getView(view_id);
|
|
154
|
+
if (!v || !v->webview || !script) return;
|
|
155
|
+
v->webview->ExecuteScript(utf8ToWide(script).c_str(), nullptr);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
BUNITE_EXPORT void bunite_view_load_url(uint32_t view_id, const char* url) {
|
|
159
|
+
ViewHost* v = getView(view_id);
|
|
160
|
+
if (!v || !url) return;
|
|
161
|
+
if (v->webview) v->webview->Navigate(utf8ToWide(url).c_str());
|
|
162
|
+
else v->url = url;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
BUNITE_EXPORT void bunite_view_load_html(uint32_t view_id, const char* html) {
|
|
166
|
+
ViewHost* v = getView(view_id);
|
|
167
|
+
if (!v || !html) return;
|
|
168
|
+
if (v->webview) v->webview->NavigateToString(utf8ToWide(html).c_str());
|
|
169
|
+
else v->html = html;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
BUNITE_EXPORT void bunite_register_appres_route(const char* path) {
|
|
173
|
+
registerAppResRoute(path);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
BUNITE_EXPORT void bunite_unregister_appres_route(const char* path) {
|
|
177
|
+
unregisterAppResRoute(path);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
BUNITE_EXPORT void bunite_complete_route_request(uint32_t request_id, const char* html) {
|
|
181
|
+
completeRouteRequest(request_id, html);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
BUNITE_EXPORT void bunite_view_set_visible(uint32_t view_id, bool visible) {
|
|
185
|
+
ViewHost* v = getView(view_id);
|
|
186
|
+
if (!v) return;
|
|
187
|
+
v->pending_visible = visible;
|
|
188
|
+
if (v->container_hwnd) ShowWindow(v->container_hwnd, visible ? SW_SHOW : SW_HIDE);
|
|
189
|
+
if (v->controller) v->controller->put_IsVisible(visible);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
BUNITE_EXPORT void bunite_view_set_input_passthrough(uint32_t view_id, bool passthrough) {
|
|
193
|
+
ViewHost* v = getView(view_id);
|
|
194
|
+
if (v) setViewInputPassthrough(v, passthrough);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
BUNITE_EXPORT void bunite_view_set_mask_region(uint32_t /*view_id*/,
|
|
198
|
+
const double* /*rects*/, uint32_t /*count*/) {
|
|
199
|
+
static bool warned = false;
|
|
200
|
+
if (!warned) {
|
|
201
|
+
BUNITE_WARN("webview2: set_mask_region ignored — WebView2 D3D surface cannot be masked. "
|
|
202
|
+
"Use engine \"cef\" + bunite-cef-win-x64 if mask region is required.");
|
|
203
|
+
warned = true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
BUNITE_EXPORT void bunite_view_bring_to_front(uint32_t view_id) {
|
|
208
|
+
ViewHost* v = getView(view_id);
|
|
209
|
+
if (!v || !v->container_hwnd) return;
|
|
210
|
+
// Raise our own container HWND. WebView2's internal HWND tree under the
|
|
211
|
+
// container moves with it; we never touch the Edge-owned HWNDs directly.
|
|
212
|
+
SetWindowPos(v->container_hwnd, HWND_TOP, 0, 0, 0, 0,
|
|
213
|
+
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static void applyBounds(ViewHost* v, double x, double y, double w, double h) {
|
|
217
|
+
v->bounds = { static_cast<LONG>(x), static_cast<LONG>(y),
|
|
218
|
+
static_cast<LONG>(x + w), static_cast<LONG>(y + h) };
|
|
219
|
+
if (v->container_hwnd) {
|
|
220
|
+
SetWindowPos(v->container_hwnd, nullptr,
|
|
221
|
+
v->bounds.left, v->bounds.top,
|
|
222
|
+
v->bounds.right - v->bounds.left,
|
|
223
|
+
v->bounds.bottom - v->bounds.top,
|
|
224
|
+
SWP_NOZORDER | SWP_NOACTIVATE);
|
|
225
|
+
}
|
|
226
|
+
if (v->controller) {
|
|
227
|
+
RECT inner{ 0, 0,
|
|
228
|
+
v->bounds.right - v->bounds.left,
|
|
229
|
+
v->bounds.bottom - v->bounds.top };
|
|
230
|
+
v->controller->put_Bounds(inner);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
BUNITE_EXPORT void bunite_view_set_bounds(uint32_t view_id,
|
|
235
|
+
double x, double y, double w, double h) {
|
|
236
|
+
ViewHost* v = getView(view_id);
|
|
237
|
+
if (v) applyBounds(v, x, y, w, h);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
BUNITE_EXPORT void bunite_view_set_bounds_async(uint32_t view_id,
|
|
241
|
+
double x, double y, double w, double h) {
|
|
242
|
+
postUiTask([view_id, x, y, w, h]() {
|
|
243
|
+
ViewHost* v = getView(view_id);
|
|
244
|
+
if (v) applyBounds(v, x, y, w, h);
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
BUNITE_EXPORT void bunite_view_set_anchor(uint32_t view_id, int mode, double /*inset*/) {
|
|
249
|
+
ViewHost* v = getView(view_id);
|
|
250
|
+
if (!v) return;
|
|
251
|
+
v->auto_resize = (mode == 1); // ViewAnchorMode::Fill
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
BUNITE_EXPORT void bunite_view_go_back(uint32_t view_id) {
|
|
255
|
+
ViewHost* v = getView(view_id);
|
|
256
|
+
if (v && v->webview) v->webview->GoBack();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
BUNITE_EXPORT void bunite_view_reload(uint32_t view_id) {
|
|
260
|
+
ViewHost* v = getView(view_id);
|
|
261
|
+
if (v && v->webview) v->webview->Reload();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
BUNITE_EXPORT void bunite_view_remove(uint32_t view_id) { destroyView(view_id); }
|
|
265
|
+
|
|
266
|
+
BUNITE_EXPORT void bunite_view_open_devtools(uint32_t view_id) {
|
|
267
|
+
ViewHost* v = getView(view_id);
|
|
268
|
+
if (v && v->webview) v->webview->OpenDevToolsWindow();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
BUNITE_EXPORT void bunite_view_close_devtools(uint32_t /*view_id*/) {
|
|
272
|
+
// WebView2 doesn't expose a "close devtools" API; the user closes the panel.
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
BUNITE_EXPORT void bunite_view_toggle_devtools(uint32_t view_id) {
|
|
276
|
+
bunite_view_open_devtools(view_id);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ---- permissions ------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
BUNITE_EXPORT void bunite_complete_permission_request(uint32_t request_id, uint32_t state) {
|
|
282
|
+
PendingPermissionRequest p;
|
|
283
|
+
{
|
|
284
|
+
std::lock_guard<std::mutex> g(g_runtime.permission_mutex);
|
|
285
|
+
auto it = g_runtime.pending_permissions.find(request_id);
|
|
286
|
+
if (it == g_runtime.pending_permissions.end()) return;
|
|
287
|
+
p = std::move(it->second);
|
|
288
|
+
g_runtime.pending_permissions.erase(it);
|
|
289
|
+
}
|
|
290
|
+
if (p.args) p.args->put_State(buniteStateToWebView2(state));
|
|
291
|
+
if (p.deferral) p.deferral->Complete();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
} // extern "C"
|