quickwin 1.0.2 → 1.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.
Files changed (78) hide show
  1. package/README.en.md +204 -204
  2. package/README.md +203 -203
  3. package/examples/PdfCanvas.tsx +83 -83
  4. package/examples/exec_server.ts +35 -35
  5. package/examples/pdf_preview2.ts +375 -375
  6. package/examples/pdf_viewer.tsx +279 -279
  7. package/examples/test_gallery.tsx +285 -285
  8. package/examples/test_react_lazy.tsx +63 -63
  9. package/examples/test_react_listbox.tsx +71 -71
  10. package/examples/test_root_window.tsx +41 -41
  11. package/examples/test_tab.tsx +75 -75
  12. package/examples/tray_demo.tsx +81 -81
  13. package/lib/assert.ts +6 -6
  14. package/lib/cache-utils.ts +40 -40
  15. package/lib/dumpRects.ts +25 -25
  16. package/lib/fetch.ts +502 -502
  17. package/lib/http-common.ts +399 -399
  18. package/lib/http-server.ts +424 -424
  19. package/lib/polyfill.ts +171 -171
  20. package/lib/react-qw/components/index.ts +17 -17
  21. package/lib/react-qw/index.ts +90 -90
  22. package/lib/react-qw/jsx.d.ts +34 -34
  23. package/lib/react-qw/layout.ts +111 -111
  24. package/lib/react-qw/props.ts +36 -36
  25. package/lib/react-qw/reconciler.ts +373 -373
  26. package/lib/stream.ts +21 -21
  27. package/lib/text-codec.ts +171 -171
  28. package/lib/text-measure.ts +54 -54
  29. package/lib/url.ts +201 -201
  30. package/lib/vendor/web-streams/ponyfill.d.mts +829 -829
  31. package/lib/vendor/web-streams/ponyfill.mjs +8 -8
  32. package/lib/websocket.js +8 -0
  33. package/lib/websocket.ts +563 -555
  34. package/package.json +50 -44
  35. package/qwin-nowasm-x86.exe +0 -0
  36. package/qwin-nowasm.exe +0 -0
  37. package/qwin-x86.exe +0 -0
  38. package/qwin.exe +0 -0
  39. package/test/run.ts +96 -96
  40. package/test/test_basic.ts +9 -9
  41. package/test/test_basic_fetch.ts +33 -33
  42. package/test/test_brotli.ts +52 -52
  43. package/test/test_fetch_cache.ts +187 -187
  44. package/test/test_ffi.ts +174 -174
  45. package/test/test_frame_encoding.ts +132 -132
  46. package/test/test_helper.ts +80 -80
  47. package/test/test_http_import.ts +74 -74
  48. package/test/test_http_server.ts +136 -136
  49. package/test/test_import_dir.ts +5 -5
  50. package/test/test_ipv6.ts +129 -129
  51. package/test/test_meta_url.ts +4 -4
  52. package/test/test_mupdf_render.ts +74 -74
  53. package/test/test_mupdf_twice.ts +81 -81
  54. package/test/test_mupdf_wasm.ts +30 -30
  55. package/test/test_net_event.ts +55 -55
  56. package/test/test_net_fetch.ts +238 -238
  57. package/test/test_net_websocket.ts +164 -164
  58. package/test/test_polyfill.ts +159 -159
  59. package/test/test_url.ts +183 -183
  60. package/test/test_wasm_basic.ts +73 -73
  61. package/test/test_wasm_import_global.ts +45 -45
  62. package/test/test_wasm_sjlj.ts +125 -125
  63. package/test/test_wasm_types.ts +124 -124
  64. package/test/test_worker.ts +24 -24
  65. package/test/test_worker_fetch.ts +27 -27
  66. package/test/test_worker_net.ts +54 -54
  67. package/test/test_worker_wasm_concurrent.ts +45 -45
  68. package/test/worker_fetch.ts +13 -13
  69. package/test/worker_helper.ts +1 -1
  70. package/test/worker_http.ts +20 -20
  71. package/test/worker_import_lib.ts +13 -13
  72. package/test/worker_module.ts +17 -17
  73. package/test/worker_wasm_concurrent.ts +22 -22
  74. package/tsconfig.json +27 -27
  75. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -571
  76. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -2749
  77. package/vendor/mupdf-wasm/mupdf.d.ts +939 -939
  78. package/vendor/mupdf-wasm/mupdf.js +3317 -3317
package/README.md CHANGED
@@ -1,204 +1,204 @@
1
- > [English](README.en.md) · **中文**
2
-
3
- # QuickWin
4
-
5
- Windows 平台上的 QuickJS 运行时,支持原生 GUI、网络、WASM、FFI 等能力。
6
-
7
- 目标:兼容 Windows XP+(32/64 位),保持最小二进制体积。
8
-
9
- 现状:当前支持 Windows 7+(64 位),体积约 1.5MB。
10
-
11
- 项目处于早期,功能待完善,包含已知 bug。
12
-
13
- 直接从 GitHub Release 下载运行:
14
-
15
- ```powershell
16
- iwr https://github.com/xna00/quickwin/releases/latest/download/qwin.exe -OutFile qwin.exe
17
- iwr https://github.com/xna00/quickwin/releases/latest/download/main.js -OutFile main.js
18
- .\qwin.exe main.js
19
- ```
20
-
21
- ## 特性
22
-
23
- - **Win32 原生 GUI** —— 原生窗口、按钮、编辑框、列表框、托盘图标、弹出菜单
24
- - **React 渲染器** —— 用 JSX 声明式 GUI,支持 `useState`/`useEffect`,diff 增量更新([react-qw](lib/react-qw/))
25
- - **HTTP/HTTPS** —— `fetch()` API、Brotli 解压、chunked 传输、条件缓存
26
- - **WebSocket** —— 完整的 RFC 6455 实现,支持 ws:// 与 wss://
27
- - **WebAssembly** —— 基于 WAMR,支持标准 `WebAssembly.*` API
28
- - **FFI** —— 通过 libffi 调用任意 DLL 函数
29
- - **Polyfills** —— `TextEncoder`、`URL`、`URLSearchParams`、`btoa`/`atob`、`crypto.subtle`、`setTimeout`
30
- - **动态导入** —— `import('https://esm.sh/...')`,无需安装任何依赖
31
-
32
- ## CLI
33
-
34
- ```bash
35
- quickwin script.js # 运行脚本
36
- quickwin -o CON script.js # 带控制台运行(AllocConsole)
37
- quickwin -o LOG script.js # 自动生成日志文件运行
38
- quickwin -e "console.log('hi')" # 执行表达式
39
- quickwin -- script.js --flag # -- 停止选项解析
40
- ```
41
-
42
- ### 选项
43
-
44
- | Flag | Description |
45
- |------|-------------|
46
- | `-e <expr>` | 执行表达式而不是文件 |
47
- | `-o CON` | 分配控制台(`AllocConsole`);仅 GUI 子系统构建所需 |
48
- | `-o LOG` | 将 stdout+stderr 重定向到 `log_YYYY_MM_DD_HH_MM_SS.txt`(exe 所在目录) |
49
- | `-o <file>` | 将 stdout+stderr 重定向到指定文件 |
50
- | `-d` | 开启 HTTP 调试日志 |
51
- | `--` | 停止选项解析,其余参数传给脚本 |
52
-
53
- 所有未知参数都会透明转发给 `scriptArgs` —— 不报错、不消费。选项解析后的第一个非参数即脚本文件。
54
-
55
- ## 内嵌脚本
56
-
57
- 可以把一个 JS 脚本直接内嵌进 `qwin.exe` 二进制 —— 无需重新编译。
58
-
59
- ### 格式(追加到 exe 末尾)
60
-
61
- ```
62
- [JS bytes (N)] [N: uint32 LE] [magic "QWJS"]
63
- ```
64
-
65
- ### 用法
66
-
67
- ```bash
68
- # 内嵌一个脚本
69
- powershell -ExecutionPolicy Bypass -File scripts/embed-js.ps1 -ExePath _build/qwin.exe -JsFile script.js
70
-
71
- # 或通过 make
72
- make embed-js JS_EMBED=script.js
73
-
74
- # 内嵌 Brotli 压缩的脚本(exe 更小,运行时解压)
75
- make embed-js-br JS_EMBED=script.js
76
-
77
- # 运行它(不需要文件参数)
78
- qwin.exe
79
- ```
80
-
81
- 不带脚本文件参数运行时,`qwin.exe` 会检查自身末尾是否有内嵌 JS:找到则执行内嵌代码,否则回退到 `main.js`。同时支持原始(`QWJS`)与 Brotli 压缩(`QWBR`)两种内嵌格式;压缩格式在启动时解压。
82
-
83
- ## 模块
84
-
85
- | Module | Import | Description |
86
- |--------|--------|-------------|
87
- | `std` | built-in | 文件 I/O、环境变量、URL 下载 |
88
- | `os` | built-in | 文件系统、进程、Worker、定时器 |
89
- | `gui` | built-in | Win32 窗口/控件/消息/托盘 API |
90
- | `sock` | built-in | Socket 网络(`AddrFamily`、`FdEvent` 等) |
91
- | `wolfssl` | built-in | TLS/SSL(`VerifyMode`、`ReturnCode` 等) |
92
- | `ffi` | built-in | 外部函数接口 |
93
- | `win` | built-in | DLL 加载(`LoadLibrary`、`GetProcAddress`) |
94
- | `brotli` | built-in | Brotli 解压 |
95
- | `wamr` | built-in | 底层 WAMR API |
96
- | `fetch` | `import './lib/fetch.js'` | 向 globalThis 添加 `fetch()`、`Response`、`Headers` |
97
- | `websocket` | `import './lib/websocket.js'` | 向 globalThis 添加 `WebSocket` |
98
- | `polyfill` | `import './lib/polyfill.js'` | 向 globalThis 添加 `TextEncoder`、`URL`、`btoa`/`atob`、`setTimeout` |
99
- | `preact` | `lib/preact/...` | JSX → Win32 渲染器(`render`、`useState`、`useEffect`) |
100
- | `react-qw` | `lib/react-qw/` | 面向 Win32 GUI 的 React 自定义渲染器([文档](lib/react-qw/)) |
101
-
102
- ## Worker
103
-
104
- 面向 CPU 密集或 I/O 密集工作创建后台线程。Worker 运行在独立的 QuickJS runtime 中,各自拥有自己的事件循环。
105
-
106
- ### 主线程
107
-
108
- ```js
109
- import * as os from 'os'
110
-
111
- const worker = new os.Worker('./worker.js')
112
-
113
- worker.onmessage = (e) => {
114
- console.log('received:', e.data)
115
- worker.onmessage = null // 完成后清理
116
- }
117
-
118
- worker.postMessage({ type: 'start', value: 42 })
119
- ```
120
-
121
- ### Worker 线程
122
-
123
- ```js
124
- // worker.js
125
- import * as os from 'os'
126
-
127
- const parent = os.Worker.parent
128
-
129
- parent.onmessage = (e) => {
130
- if (e.data.type === 'start') {
131
- parent.postMessage({ type: 'result', value: e.data.value * 2 })
132
- } else if (e.data.type === 'done') {
133
- parent.onmessage = null // 清理以允许事件循环退出
134
- }
135
- }
136
- ```
137
-
138
- ### API
139
-
140
- | API | Description |
141
- |-----|-------------|
142
- | `new os.Worker(specifier)` | 创建 worker。`specifier` 是文件路径或 URL(支持 `https://` ESM 导入) |
143
- | `worker.postMessage(data)` | 向 worker 发送消息(JSON 可序列化) |
144
- | `worker.onmessage = fn` | 设置来自 worker 的消息回调。完成后设为 `null` 以释放消息端口 |
145
- | `os.Worker.parent` | (Worker 侧)父线程的引用 |
146
-
147
- ### 清理
148
-
149
- 通信完成后,双方都必须把 `onmessage` 设为 `null`。这会释放消息端口,使事件循环能够干净退出。忘记设置会导致进程挂起。
150
-
151
- ## 示例
152
-
153
- ```bash
154
- .\qwin.exe examples/preact_demo.js # JSX + hooks 的计数器 GUI
155
- .\qwin.exe examples/tray_demo.js # 系统托盘应用
156
- .\qwin.exe examples/pdf_preview.js # 基于 mupdf 的 PDF 阅读器
157
- ```
158
-
159
- ## 从源码构建
160
-
161
- ### 前置依赖
162
-
163
- - MSYS2 UCRT64 或 MINGW64
164
- - Node.js(用于通过 tsc 编译 TypeScript)
165
- - Git(用于子模块)
166
-
167
- ### 构建
168
-
169
- ```bash
170
- git clone --recursive https://github.com/anomalyco/quickwin.git
171
- cd quickwin
172
-
173
- .\run.ps1 "make wamr" # 构建 WAMR 库(仅首次)
174
- .\run.ps1 "make minimal" # 构建 qwin.exe(-Os + LTO + UPX)
175
- .\run.ps1 "make js" # 编译 TypeScript
176
- .\run.ps1 "make test" # 运行全部测试
177
- ```
178
-
179
- `make nowasm` 会产出更小的 `_build/qwin-nowasm.exe`,不含 WASM/WAMR
180
- (没有 `WebAssembly` 全局对象);对这种版本运行 `make test TEST=-wasm` 跳过 WASM 测试。
181
-
182
- ### 构建目标
183
-
184
- | Target | Description |
185
- |--------|-------------|
186
- | `make` / `make nodebug` | 快速构建 |
187
- | `make minimal` | `-Os` + LTO + `-mwindows` + UPX,无控制台,需要控制台时加 `-o CON` |
188
- | `make nowasm` | 无 WASM/WAMR 构建 → `_build/qwin-nowasm.exe`(约 1.2MB,无 `WebAssembly` 全局) |
189
- | `make release` | `-O2` + LTO + strip,约 1.5MB |
190
- | `make debug` | 带 bridge 日志的调试构建 |
191
- | `make js` | 通过 tsc 编译 TypeScript |
192
- | `make wasm` | 编译 WAT → WASM fixtures |
193
- | `make test` | 运行全部测试 |
194
- | `make test TEST=-net` | 跳过网络测试(快速) |
195
- | `make test TEST=wasm` | 仅运行 WASM 测试 |
196
- | `make wamr` | 重建 WAMR 库 |
197
- | `make embed-js` | 把 `embed.js` 内嵌进 exe(用 `JS_EMBED=file.js`) |
198
- | `make embed-js-br` | 把 Brotli 压缩的 JS 内嵌进 exe |
199
- | `make npm-pkg` | 打包到 `dist/quickwin/` |
200
- | `make clean` | 清理构建产物 |
201
-
202
- ## 许可证
203
-
1
+ > [English](README.en.md) · **中文**
2
+
3
+ # QuickWin
4
+
5
+ Windows 平台上的 QuickJS 运行时,支持原生 GUI、网络、WASM、FFI 等能力。
6
+
7
+ 目标:兼容 Windows XP+(32/64 位),保持最小二进制体积。
8
+
9
+ 现状:当前支持 Windows 7+(64 位),体积约 1.5MB。
10
+
11
+ 项目处于早期,功能待完善,包含已知 bug。
12
+
13
+ 直接从 GitHub Release 下载运行:
14
+
15
+ ```powershell
16
+ iwr https://github.com/xna00/quickwin/releases/latest/download/qwin.exe -OutFile qwin.exe
17
+ iwr https://github.com/xna00/quickwin/releases/latest/download/main.js -OutFile main.js
18
+ .\qwin.exe main.js
19
+ ```
20
+
21
+ ## 特性
22
+
23
+ - **Win32 原生 GUI** —— 原生窗口、按钮、编辑框、列表框、托盘图标、弹出菜单
24
+ - **React 渲染器** —— 用 JSX 声明式 GUI,支持 `useState`/`useEffect`,diff 增量更新([react-qw](lib/react-qw/))
25
+ - **HTTP/HTTPS** —— `fetch()` API、Brotli 解压、chunked 传输、条件缓存
26
+ - **WebSocket** —— 完整的 RFC 6455 实现,支持 ws:// 与 wss://
27
+ - **WebAssembly** —— 基于 WAMR,支持标准 `WebAssembly.*` API
28
+ - **FFI** —— 通过 libffi 调用任意 DLL 函数
29
+ - **Polyfills** —— `TextEncoder`、`URL`、`URLSearchParams`、`btoa`/`atob`、`crypto.subtle`、`setTimeout`
30
+ - **动态导入** —— `import('https://esm.sh/...')`,无需安装任何依赖
31
+
32
+ ## CLI
33
+
34
+ ```bash
35
+ quickwin script.js # 运行脚本
36
+ quickwin -o CON script.js # 带控制台运行(AllocConsole)
37
+ quickwin -o LOG script.js # 自动生成日志文件运行
38
+ quickwin -e "console.log('hi')" # 执行表达式
39
+ quickwin -- script.js --flag # -- 停止选项解析
40
+ ```
41
+
42
+ ### 选项
43
+
44
+ | Flag | Description |
45
+ |------|-------------|
46
+ | `-e <expr>` | 执行表达式而不是文件 |
47
+ | `-o CON` | 分配控制台(`AllocConsole`);仅 GUI 子系统构建所需 |
48
+ | `-o LOG` | 将 stdout+stderr 重定向到 `log_YYYY_MM_DD_HH_MM_SS.txt`(exe 所在目录) |
49
+ | `-o <file>` | 将 stdout+stderr 重定向到指定文件 |
50
+ | `-d` | 开启 HTTP 调试日志 |
51
+ | `--` | 停止选项解析,其余参数传给脚本 |
52
+
53
+ 所有未知参数都会透明转发给 `scriptArgs` —— 不报错、不消费。选项解析后的第一个非参数即脚本文件。
54
+
55
+ ## 内嵌脚本
56
+
57
+ 可以把一个 JS 脚本直接内嵌进 `qwin.exe` 二进制 —— 无需重新编译。
58
+
59
+ ### 格式(追加到 exe 末尾)
60
+
61
+ ```
62
+ [JS bytes (N)] [N: uint32 LE] [magic "QWJS"]
63
+ ```
64
+
65
+ ### 用法
66
+
67
+ ```bash
68
+ # 内嵌一个脚本
69
+ powershell -ExecutionPolicy Bypass -File scripts/embed-js.ps1 -ExePath _build/qwin.exe -JsFile script.js
70
+
71
+ # 或通过 make
72
+ make embed-js JS_EMBED=script.js
73
+
74
+ # 内嵌 Brotli 压缩的脚本(exe 更小,运行时解压)
75
+ make embed-js-br JS_EMBED=script.js
76
+
77
+ # 运行它(不需要文件参数)
78
+ qwin.exe
79
+ ```
80
+
81
+ 不带脚本文件参数运行时,`qwin.exe` 会检查自身末尾是否有内嵌 JS:找到则执行内嵌代码,否则回退到 `main.js`。同时支持原始(`QWJS`)与 Brotli 压缩(`QWBR`)两种内嵌格式;压缩格式在启动时解压。
82
+
83
+ ## 模块
84
+
85
+ | Module | Import | Description |
86
+ |--------|--------|-------------|
87
+ | `std` | built-in | 文件 I/O、环境变量、URL 下载 |
88
+ | `os` | built-in | 文件系统、进程、Worker、定时器 |
89
+ | `gui` | built-in | Win32 窗口/控件/消息/托盘 API |
90
+ | `sock` | built-in | Socket 网络(`AddrFamily`、`FdEvent` 等) |
91
+ | `wolfssl` | built-in | TLS/SSL(`VerifyMode`、`ReturnCode` 等) |
92
+ | `ffi` | built-in | 外部函数接口 |
93
+ | `win` | built-in | DLL 加载(`LoadLibrary`、`GetProcAddress`) |
94
+ | `brotli` | built-in | Brotli 解压 |
95
+ | `wamr` | built-in | 底层 WAMR API |
96
+ | `fetch` | `import './lib/fetch.js'` | 向 globalThis 添加 `fetch()`、`Response`、`Headers` |
97
+ | `websocket` | `import './lib/websocket.js'` | 向 globalThis 添加 `WebSocket` |
98
+ | `polyfill` | `import './lib/polyfill.js'` | 向 globalThis 添加 `TextEncoder`、`URL`、`btoa`/`atob`、`setTimeout` |
99
+ | `preact` | `lib/preact/...` | JSX → Win32 渲染器(`render`、`useState`、`useEffect`) |
100
+ | `react-qw` | `lib/react-qw/` | 面向 Win32 GUI 的 React 自定义渲染器([文档](lib/react-qw/)) |
101
+
102
+ ## Worker
103
+
104
+ 面向 CPU 密集或 I/O 密集工作创建后台线程。Worker 运行在独立的 QuickJS runtime 中,各自拥有自己的事件循环。
105
+
106
+ ### 主线程
107
+
108
+ ```js
109
+ import * as os from 'os'
110
+
111
+ const worker = new os.Worker('./worker.js')
112
+
113
+ worker.onmessage = (e) => {
114
+ console.log('received:', e.data)
115
+ worker.onmessage = null // 完成后清理
116
+ }
117
+
118
+ worker.postMessage({ type: 'start', value: 42 })
119
+ ```
120
+
121
+ ### Worker 线程
122
+
123
+ ```js
124
+ // worker.js
125
+ import * as os from 'os'
126
+
127
+ const parent = os.Worker.parent
128
+
129
+ parent.onmessage = (e) => {
130
+ if (e.data.type === 'start') {
131
+ parent.postMessage({ type: 'result', value: e.data.value * 2 })
132
+ } else if (e.data.type === 'done') {
133
+ parent.onmessage = null // 清理以允许事件循环退出
134
+ }
135
+ }
136
+ ```
137
+
138
+ ### API
139
+
140
+ | API | Description |
141
+ |-----|-------------|
142
+ | `new os.Worker(specifier)` | 创建 worker。`specifier` 是文件路径或 URL(支持 `https://` ESM 导入) |
143
+ | `worker.postMessage(data)` | 向 worker 发送消息(JSON 可序列化) |
144
+ | `worker.onmessage = fn` | 设置来自 worker 的消息回调。完成后设为 `null` 以释放消息端口 |
145
+ | `os.Worker.parent` | (Worker 侧)父线程的引用 |
146
+
147
+ ### 清理
148
+
149
+ 通信完成后,双方都必须把 `onmessage` 设为 `null`。这会释放消息端口,使事件循环能够干净退出。忘记设置会导致进程挂起。
150
+
151
+ ## 示例
152
+
153
+ ```bash
154
+ .\qwin.exe examples/preact_demo.js # JSX + hooks 的计数器 GUI
155
+ .\qwin.exe examples/tray_demo.js # 系统托盘应用
156
+ .\qwin.exe examples/pdf_preview.js # 基于 mupdf 的 PDF 阅读器
157
+ ```
158
+
159
+ ## 从源码构建
160
+
161
+ ### 前置依赖
162
+
163
+ - MSYS2 UCRT64 或 MINGW64
164
+ - Node.js(用于通过 tsc 编译 TypeScript)
165
+ - Git(用于子模块)
166
+
167
+ ### 构建
168
+
169
+ ```bash
170
+ git clone --recursive https://github.com/anomalyco/quickwin.git
171
+ cd quickwin
172
+
173
+ .\run.ps1 "make wamr" # 构建 WAMR 库(仅首次)
174
+ .\run.ps1 "make minimal" # 构建 qwin.exe(-Os + LTO + UPX)
175
+ .\run.ps1 "make js" # 编译 TypeScript
176
+ .\run.ps1 "make test" # 运行全部测试
177
+ ```
178
+
179
+ `make nowasm` 会产出更小的 `_build/qwin-nowasm.exe`,不含 WASM/WAMR
180
+ (没有 `WebAssembly` 全局对象);对这种版本运行 `make test TEST=-wasm` 跳过 WASM 测试。
181
+
182
+ ### 构建目标
183
+
184
+ | Target | Description |
185
+ |--------|-------------|
186
+ | `make` / `make nodebug` | 快速构建 |
187
+ | `make minimal` | `-Os` + LTO + `-mwindows` + UPX,无控制台,需要控制台时加 `-o CON` |
188
+ | `make nowasm` | 无 WASM/WAMR 构建 → `_build/qwin-nowasm.exe`(约 1.2MB,无 `WebAssembly` 全局) |
189
+ | `make release` | `-O2` + LTO + strip,约 1.5MB |
190
+ | `make debug` | 带 bridge 日志的调试构建 |
191
+ | `make js` | 通过 tsc 编译 TypeScript |
192
+ | `make wasm` | 编译 WAT → WASM fixtures |
193
+ | `make test` | 运行全部测试 |
194
+ | `make test TEST=-net` | 跳过网络测试(快速) |
195
+ | `make test TEST=wasm` | 仅运行 WASM 测试 |
196
+ | `make wamr` | 重建 WAMR 库 |
197
+ | `make embed-js` | 把 `embed.js` 内嵌进 exe(用 `JS_EMBED=file.js`) |
198
+ | `make embed-js-br` | 把 Brotli 压缩的 JS 内嵌进 exe |
199
+ | `make npm-pkg` | 打包到 `dist/quickwin/` |
200
+ | `make clean` | 清理构建产物 |
201
+
202
+ ## 许可证
203
+
204
204
  MIT
@@ -1,83 +1,83 @@
1
- import { forwardRef, useRef } from 'react'
2
- import * as gui from 'gui'
3
- import * as win from 'win'
4
- import * as ffi from 'ffi'
5
- import type { WStyle } from '../lib/react-qw/jsx.d.ts'
6
-
7
- const _user32 = win.LoadLibrary('user32.dll')
8
- const _gdi32 = win.LoadLibrary('gdi32.dll')
9
- if (!_user32 || !_gdi32) throw new Error('Failed to load user32/gdi32')
10
-
11
- function loadProc(lib: win.HMODULE, name: string): number {
12
- const ptr = win.GetProcAddress(lib, name)
13
- if (!ptr) throw new Error('Failed to load ' + name)
14
- return ptr
15
- }
16
-
17
- const GetDC_ = loadProc(_user32, 'GetDC')
18
- const ReleaseDC_ = loadProc(_user32, 'ReleaseDC')
19
- const SetDIBitsToDevice_ = loadProc(_gdi32, 'SetDIBitsToDevice')
20
-
21
- const FFI_PTR = ffi.FFI_TYPE_POINTER
22
- const FFI_U32 = ffi.FFI_TYPE_UINT32
23
- const FFI_S32 = ffi.FFI_TYPE_SINT32
24
-
25
- function makeBitmapInfo(w: number, h: number): ArrayBuffer {
26
- const bmi = new ArrayBuffer(40)
27
- const bv = new DataView(bmi)
28
- bv.setUint32(0, 40, true)
29
- bv.setInt32(4, w, true)
30
- bv.setInt32(8, -h, true)
31
- bv.setUint16(12, 1, true)
32
- bv.setUint16(14, 24, true)
33
- return bmi
34
- }
35
-
36
- export interface PdfCanvasProps {
37
- pixmap?: { data: ArrayBuffer; w: number; h: number }
38
- style?: WStyle
39
- }
40
-
41
- export const PdfCanvas = forwardRef<gui.HWND, PdfCanvasProps>(
42
- ({ pixmap, style }, ref) => {
43
- const canvasRef = useRef<gui.HWND>(null)
44
- const pixmapRef = useRef(pixmap)
45
- pixmapRef.current = pixmap
46
-
47
- return (
48
- <w type="STATIC"
49
- ws={gui.WindowStyle.VISIBLE | gui.WindowStyle.CLIPCHILDREN}
50
- style={style}
51
- ref={(h: gui.HWND) => {
52
- canvasRef.current = h
53
- if (typeof ref === 'function') ref(h)
54
- else if (ref) (ref as React.RefObject<gui.HWND | null>).current = h
55
- }}
56
- onEvent={(e) => {
57
- const hwnd = e.hwnd
58
- if (e.msg === 0x14) return 1
59
- if (e.msg === gui.WmMsg.PAINT) {
60
- const pm = pixmapRef.current
61
- if (!pm) return 0
62
- const hdc = ffi.ffiCall(GetDC_, [ffi.FFI_TYPE_UINT64], [hwnd], ffi.FFI_TYPE_UINT64)
63
- if (hdc) {
64
- const bmi = makeBitmapInfo(pm.w, pm.h)
65
- ffi.ffiCall(SetDIBitsToDevice_, [
66
- ffi.FFI_TYPE_UINT64, FFI_S32, FFI_S32, FFI_U32, FFI_U32,
67
- FFI_S32, FFI_S32, FFI_U32, FFI_U32,
68
- FFI_PTR, FFI_PTR, FFI_U32
69
- ], [
70
- hdc, 0, 0, pm.w, pm.h,
71
- 0, 0, 0, pm.h,
72
- pm.data, bmi, 0
73
- ], FFI_S32)
74
- ffi.ffiCall(ReleaseDC_, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32)
75
- }
76
- return 0
77
- }
78
- return
79
- }}
80
- />
81
- )
82
- }
83
- )
1
+ import { forwardRef, useRef } from 'react'
2
+ import * as gui from 'gui'
3
+ import * as win from 'win'
4
+ import * as ffi from 'ffi'
5
+ import type { WStyle } from '../lib/react-qw/jsx.d.ts'
6
+
7
+ const _user32 = win.LoadLibrary('user32.dll')
8
+ const _gdi32 = win.LoadLibrary('gdi32.dll')
9
+ if (!_user32 || !_gdi32) throw new Error('Failed to load user32/gdi32')
10
+
11
+ function loadProc(lib: win.HMODULE, name: string): number {
12
+ const ptr = win.GetProcAddress(lib, name)
13
+ if (!ptr) throw new Error('Failed to load ' + name)
14
+ return ptr
15
+ }
16
+
17
+ const GetDC_ = loadProc(_user32, 'GetDC')
18
+ const ReleaseDC_ = loadProc(_user32, 'ReleaseDC')
19
+ const SetDIBitsToDevice_ = loadProc(_gdi32, 'SetDIBitsToDevice')
20
+
21
+ const FFI_PTR = ffi.FFI_TYPE_POINTER
22
+ const FFI_U32 = ffi.FFI_TYPE_UINT32
23
+ const FFI_S32 = ffi.FFI_TYPE_SINT32
24
+
25
+ function makeBitmapInfo(w: number, h: number): ArrayBuffer {
26
+ const bmi = new ArrayBuffer(40)
27
+ const bv = new DataView(bmi)
28
+ bv.setUint32(0, 40, true)
29
+ bv.setInt32(4, w, true)
30
+ bv.setInt32(8, -h, true)
31
+ bv.setUint16(12, 1, true)
32
+ bv.setUint16(14, 24, true)
33
+ return bmi
34
+ }
35
+
36
+ export interface PdfCanvasProps {
37
+ pixmap?: { data: ArrayBuffer; w: number; h: number }
38
+ style?: WStyle
39
+ }
40
+
41
+ export const PdfCanvas = forwardRef<gui.HWND, PdfCanvasProps>(
42
+ ({ pixmap, style }, ref) => {
43
+ const canvasRef = useRef<gui.HWND>(null)
44
+ const pixmapRef = useRef(pixmap)
45
+ pixmapRef.current = pixmap
46
+
47
+ return (
48
+ <w type="STATIC"
49
+ ws={gui.WindowStyle.VISIBLE | gui.WindowStyle.CLIPCHILDREN}
50
+ style={style}
51
+ ref={(h: gui.HWND) => {
52
+ canvasRef.current = h
53
+ if (typeof ref === 'function') ref(h)
54
+ else if (ref) (ref as React.RefObject<gui.HWND | null>).current = h
55
+ }}
56
+ onEvent={(e) => {
57
+ const hwnd = e.hwnd
58
+ if (e.msg === 0x14) return 1
59
+ if (e.msg === gui.WmMsg.PAINT) {
60
+ const pm = pixmapRef.current
61
+ if (!pm) return 0
62
+ const hdc = ffi.ffiCall(GetDC_, [ffi.FFI_TYPE_UINT64], [hwnd], ffi.FFI_TYPE_UINT64)
63
+ if (hdc) {
64
+ const bmi = makeBitmapInfo(pm.w, pm.h)
65
+ ffi.ffiCall(SetDIBitsToDevice_, [
66
+ ffi.FFI_TYPE_UINT64, FFI_S32, FFI_S32, FFI_U32, FFI_U32,
67
+ FFI_S32, FFI_S32, FFI_U32, FFI_U32,
68
+ FFI_PTR, FFI_PTR, FFI_U32
69
+ ], [
70
+ hdc, 0, 0, pm.w, pm.h,
71
+ 0, 0, 0, pm.h,
72
+ pm.data, bmi, 0
73
+ ], FFI_S32)
74
+ ffi.ffiCall(ReleaseDC_, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32)
75
+ }
76
+ return 0
77
+ }
78
+ return
79
+ }}
80
+ />
81
+ )
82
+ }
83
+ )
@@ -1,35 +1,35 @@
1
- import { createServer } from '../lib/http-server.js'
2
- import * as std from 'std'
3
-
4
- function readAll(file: std.FILE): string {
5
- const chunks: Uint8Array[] = []
6
- const buf = new Uint8Array(4096)
7
- while (true) {
8
- const n = file.read(buf.buffer, 0, 4096)
9
- if (n <= 0) break
10
- chunks.push(buf.slice(0, n))
11
- }
12
- let total = 0
13
- for (const c of chunks) total += c.length
14
- const out = new Uint8Array(total)
15
- let offset = 0
16
- for (const c of chunks) { out.set(c, offset); offset += c.length }
17
- return new TextDecoder().decode(out)
18
- }
19
-
20
- const server = createServer(async (req) => {
21
- if (req.method === 'POST' && new URL(req.url).pathname === '/exec') {
22
- const body = await req.json() as { cmd: string }
23
- const file = std.popen(body.cmd, 'r')
24
- if (!file) return new Response('popen failed', { status: 500 })
25
- const output = readAll(file)
26
- file.close()
27
- return new Response(output, {
28
- headers: { 'Content-Type': 'application/octet-stream' }
29
- })
30
- }
31
- return new Response('Not Found', { status: 404 })
32
- })
33
-
34
- server.listen(8080)
35
- console.log('listening on http://localhost:8080')
1
+ import { createServer } from '../lib/http-server.js'
2
+ import * as std from 'std'
3
+
4
+ function readAll(file: std.FILE): string {
5
+ const chunks: Uint8Array[] = []
6
+ const buf = new Uint8Array(4096)
7
+ while (true) {
8
+ const n = file.read(buf.buffer, 0, 4096)
9
+ if (n <= 0) break
10
+ chunks.push(buf.slice(0, n))
11
+ }
12
+ let total = 0
13
+ for (const c of chunks) total += c.length
14
+ const out = new Uint8Array(total)
15
+ let offset = 0
16
+ for (const c of chunks) { out.set(c, offset); offset += c.length }
17
+ return new TextDecoder().decode(out)
18
+ }
19
+
20
+ const server = createServer(async (req) => {
21
+ if (req.method === 'POST' && new URL(req.url).pathname === '/exec') {
22
+ const body = await req.json() as { cmd: string }
23
+ const file = std.popen(body.cmd, 'r')
24
+ if (!file) return new Response('popen failed', { status: 500 })
25
+ const output = readAll(file)
26
+ file.close()
27
+ return new Response(output, {
28
+ headers: { 'Content-Type': 'application/octet-stream' }
29
+ })
30
+ }
31
+ return new Response('Not Found', { status: 404 })
32
+ })
33
+
34
+ server.listen(8080)
35
+ console.log('listening on http://localhost:8080')