quickwin 1.3.0 → 2.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 (167) hide show
  1. package/README.en.md +18 -24
  2. package/README.md +17 -23
  3. package/examples/PdfCanvas.js +8 -37
  4. package/examples/PdfCanvas.tsx +11 -41
  5. package/examples/exec_server.js +20155 -1420
  6. package/examples/exec_server.tsx +489 -0
  7. package/examples/exec_server_worker.js +383 -33
  8. package/examples/exec_server_worker.ts +408 -30
  9. package/examples/pdf_preview2.js +25 -82
  10. package/examples/pdf_preview2.ts +27 -84
  11. package/examples/pdf_viewer.js +1424 -1180
  12. package/examples/pdf_viewer.tsx +16 -67
  13. package/examples/setres.js +22 -47
  14. package/examples/setres.ts +23 -57
  15. package/examples/test_gallery.js +1418 -1075
  16. package/examples/test_react_lazy.js +1391 -1059
  17. package/examples/test_react_listbox.js +1390 -1058
  18. package/examples/test_root_window.js +1384 -1052
  19. package/examples/test_tab.js +1391 -1059
  20. package/lib/abort.js +70 -0
  21. package/lib/abort.ts +69 -0
  22. package/lib/certs.js +219 -0
  23. package/lib/certs.ts +229 -0
  24. package/lib/exec-log.js +15 -0
  25. package/lib/exec-log.ts +30 -0
  26. package/lib/fetch.js +88 -7
  27. package/lib/fetch.ts +73 -7
  28. package/lib/ffi/bind.js +441 -0
  29. package/lib/ffi/bind.ts +539 -0
  30. package/lib/ffi/ctype.js +194 -0
  31. package/lib/ffi/ctype.ts +369 -0
  32. package/lib/ffi/index.js +6 -0
  33. package/lib/ffi/index.ts +6 -0
  34. package/lib/ffi/struct.js +490 -0
  35. package/lib/ffi/struct.ts +753 -0
  36. package/lib/http-common.js +43 -5
  37. package/lib/http-common.ts +48 -6
  38. package/lib/http-server.js +187 -29
  39. package/lib/http-server.ts +177 -26
  40. package/lib/react-qw/components/DateTimePicker.js +18 -37
  41. package/lib/react-qw/components/Input.js +3 -10
  42. package/lib/react-qw/components/Link.js +6 -19
  43. package/lib/react-qw/components/ListView.js +50 -154
  44. package/lib/react-qw/components/PathPicker.js +28 -88
  45. package/lib/react-qw/components/ScrollView.js +15 -8
  46. package/lib/react-qw/components/Slider.js +2 -1
  47. package/lib/react-qw/components/Tab.js +6 -22
  48. package/lib/react-qw/components/Tooltip.js +10 -21
  49. package/lib/react-qw/components/TreeView.js +8 -31
  50. package/lib/react-qw/index.js +5 -3
  51. package/lib/react-qw/index.ts +8 -3
  52. package/lib/react-qw/nmhdr.js +3 -24
  53. package/lib/react-qw/nmhdr.ts +5 -30
  54. package/lib/react-qw/props.js +7 -2
  55. package/lib/react-qw/props.ts +7 -3
  56. package/lib/react-qw/reconciler.js +23 -13
  57. package/lib/react-qw/reconciler.ts +22 -13
  58. package/lib/text-measure.js +20 -23
  59. package/lib/text-measure.ts +22 -26
  60. package/lib/websocket.js +44 -4
  61. package/lib/websocket.ts +40 -4
  62. package/lib/windows/advapi32.js +53 -0
  63. package/lib/windows/advapi32.ts +64 -0
  64. package/lib/windows/comctl32.js +18 -0
  65. package/lib/windows/comctl32.ts +19 -0
  66. package/lib/windows/comdlg32.js +8 -0
  67. package/lib/windows/comdlg32.ts +10 -0
  68. package/lib/windows/gdi32.js +392 -0
  69. package/lib/windows/gdi32.ts +420 -0
  70. package/lib/windows/kernel32.js +262 -0
  71. package/lib/windows/kernel32.ts +291 -0
  72. package/lib/windows/msimg32.js +38 -0
  73. package/lib/windows/msimg32.ts +45 -0
  74. package/lib/windows/ntdll.js +9 -0
  75. package/lib/windows/ntdll.ts +11 -0
  76. package/lib/windows/ole32.js +7 -0
  77. package/lib/windows/ole32.ts +9 -0
  78. package/lib/windows/psapi.js +32 -0
  79. package/lib/windows/psapi.ts +37 -0
  80. package/lib/windows/shell32.js +13 -0
  81. package/lib/windows/shell32.ts +16 -0
  82. package/lib/windows/structs.js +519 -0
  83. package/lib/windows/structs.ts +566 -0
  84. package/lib/windows/user32.js +572 -0
  85. package/lib/windows/user32.ts +607 -0
  86. package/lib/windows/version.js +29 -0
  87. package/lib/windows/version.ts +35 -0
  88. package/lib/windows/winspool.js +71 -0
  89. package/lib/windows/winspool.ts +80 -0
  90. package/package.json +1 -1
  91. package/quickwin.d.ts +31 -40
  92. package/quickwin_const.d.ts +137 -0
  93. package/qwin-nowasm-x86.exe +0 -0
  94. package/qwin-nowasm.exe +0 -0
  95. package/qwin-x86.exe +0 -0
  96. package/qwin.exe +0 -0
  97. package/test/bench_ffi_callraw.js +236 -0
  98. package/test/bench_ffi_callraw.ts +236 -0
  99. package/test/bench_ffi_overhead.js +139 -0
  100. package/test/bench_ffi_overhead.ts +141 -0
  101. package/test/bench_sendmessage.js +104 -0
  102. package/test/bench_sendmessage.ts +100 -0
  103. package/test/bench_sendmessage_nonnull.js +187 -0
  104. package/test/bench_sendmessage_nonnull.ts +188 -0
  105. package/test/run.js +19 -4
  106. package/test/run.ts +20 -5
  107. package/test/test_basic_fetch.js +29 -0
  108. package/test/test_basic_fetch.ts +26 -0
  109. package/test/test_ffi.js +27 -45
  110. package/test/test_ffi.ts +35 -54
  111. package/test/test_ffi_abi.js +290 -0
  112. package/test/test_ffi_abi.ts +274 -0
  113. package/test/test_ffi_array.js +116 -0
  114. package/test/test_ffi_array.ts +141 -0
  115. package/test/test_ffi_bind.js +620 -8
  116. package/test/test_ffi_bind.ts +630 -8
  117. package/test/test_ffi_struct.js +632 -88
  118. package/test/test_ffi_struct.ts +666 -91
  119. package/test/test_ffi_struct_layout.js +495 -0
  120. package/test/test_ffi_struct_layout.ts +543 -0
  121. package/test/test_ffi_user32.js +103 -0
  122. package/test/test_ffi_user32.ts +113 -0
  123. package/test/test_ffi_windows.js +101 -0
  124. package/test/test_ffi_windows.ts +113 -0
  125. package/test/test_files.js +102 -0
  126. package/test/test_files.ts +121 -0
  127. package/test/test_gdi_draw.js +681 -0
  128. package/test/test_gdi_draw.ts +725 -0
  129. package/test/test_helper.js +8 -1
  130. package/test/test_helper.ts +9 -1
  131. package/test/test_http_import.js +18 -0
  132. package/test/test_http_import.ts +19 -0
  133. package/test/test_http_server.js +73 -1
  134. package/test/test_http_server.ts +75 -1
  135. package/test/test_ipv6.js +2 -2
  136. package/test/test_ipv6.ts +2 -2
  137. package/test/test_net_fetch.js +74 -11
  138. package/test/test_net_fetch.ts +73 -10
  139. package/test/test_net_websocket.js +1 -1
  140. package/test/test_net_websocket.ts +1 -1
  141. package/test/test_printer.js +137 -0
  142. package/test/test_printer.ts +148 -0
  143. package/test/test_proc.js +141 -0
  144. package/test/test_proc.ts +163 -0
  145. package/test/test_react_complex.js +1401 -1069
  146. package/test/test_react_counter.js +1391 -1059
  147. package/test/test_react_flex.js +1411 -1072
  148. package/test/test_react_hidden.js +1394 -1062
  149. package/test/test_react_listview_link.js +1391 -1059
  150. package/test/test_react_notify.js +18638 -0
  151. package/test/test_react_render.js +1391 -1059
  152. package/test/test_registry.js +74 -0
  153. package/test/test_registry.ts +95 -0
  154. package/test/test_sysinfo.js +122 -0
  155. package/test/test_sysinfo.ts +141 -0
  156. package/test/test_wolfssl_read.js +45 -43
  157. package/test/test_wolfssl_read.ts +42 -40
  158. package/test/test_worker_net.js +35 -17
  159. package/test/test_worker_net.ts +27 -15
  160. package/tsconfig.json +1 -1
  161. package/examples/exec_server.ts +0 -80
  162. package/lib/ffi-bind.js +0 -85
  163. package/lib/ffi-bind.ts +0 -170
  164. package/lib/ffi-struct.js +0 -224
  165. package/lib/ffi-struct.ts +0 -239
  166. package/lib/win-common-structs.js +0 -39
  167. package/lib/win-common-structs.ts +0 -41
package/README.en.md CHANGED
@@ -30,7 +30,7 @@ The npm package ships 4 executables: `qwin.exe` (64-bit) / `qwin-x86.exe` (32-bi
30
30
  - **HTTP/HTTPS** — `fetch()` API, Brotli decompression, chunked transfer, conditional caching
31
31
  - **WebSocket** — full RFC 6455 implementation, ws:// + wss://
32
32
  - **WebAssembly** — WAMR-based, supports `WebAssembly.*` standard API
33
- - **FFI** — call any DLL function via libffi
33
+ - **FFI** — call any DLL function via a built-in asm stub and declarative `bind()` signatures (no third-party dependency)
34
34
  - **Polyfills** — `TextEncoder`, `URL`, `URLSearchParams`, `btoa`/`atob`, `crypto.subtle`, `setTimeout`
35
35
  - **Dynamic import** — `import('https://esm.sh/...')`, no dependency installation needed
36
36
 
@@ -105,7 +105,6 @@ When run without a script file argument, `qwin.exe` checks for embedded JS at th
105
105
  | `fetch` | `import './lib/fetch.js'` | adds `fetch()`, `Response`, `Headers` to globalThis |
106
106
  | `websocket` | `import './lib/websocket.js'` | adds `WebSocket` to globalThis |
107
107
  | `polyfill` | `import './lib/polyfill.js'` | adds `TextEncoder`, `URL`, `btoa`/`atob`, `setTimeout` to globalThis |
108
- | `preact` | `lib/preact/...` | JSX → Win32 renderer (`render`, `useState`, `useEffect`) |
109
108
  | `react-qw` | `lib/react-qw/` | React Custom Renderer for Win32 GUI ([docs](lib/react-qw/)) |
110
109
 
111
110
  ## Worker
@@ -169,8 +168,7 @@ Both sides must set `onmessage = null` when communication is complete. This rele
169
168
 
170
169
  ### Prerequisites
171
170
 
172
- - MSYS2 UCRT64 or MINGW64
173
- - Node.js (for TypeScript compilation via tsc)
171
+ - **podman** (dev container; build/test/QEMU verification all run inside the container, consistent with CI)
174
172
  - Git (for submodules)
175
173
 
176
174
  ### Build
@@ -179,38 +177,34 @@ Both sides must set `onmessage = null` when communication is complete. This rele
179
177
  git clone --recursive https://github.com/xna00/quickwin.git
180
178
  cd quickwin
181
179
 
182
- .\run.ps1 "make wamr" # build WAMR library (first time only)
183
- .\run.ps1 "make small" # build qwin.exe (-Os + LTO, ~1.5MB)
184
- .\run.ps1 "make js" # compile TypeScript
185
- .\run.ps1 "make test" # run all tests
180
+ ./docker/dev-podman.sh up # start dev container (reuses ghcr.io/xna00/quickwin-dev)
181
+ podman exec quickwin-dev bash -lc 'cd /workspace && make js && make cc64 && make test TEST=-net'
186
182
  ```
187
183
 
188
- `make nowasm` produces a smaller `_build/qwin-nowasm.exe` without WASM/WAMR
189
- (no `WebAssembly` global); run `make test TEST=-wasm` to skip WASM tests on it.
184
+ `make cc64` produces `_build/qwin.exe`. `cc64-nowasm` produces a smaller
185
+ `_build/qwin-nowasm.exe` without WASM/WAMR (no `WebAssembly` global); for
186
+ that build run `make test TEST=-wasm` to skip WASM tests.
190
187
 
191
188
  ### Build Targets
192
189
 
193
190
  | Target | Description |
194
191
  |--------|-------------|
195
- | `make` / `make nodebug` | fast build |
196
- | `make small` | `-Os` + LTO + strip, ~1.5MB (recommended) |
197
- | `make minimal` | `small` + UPX compression |
198
- | `make release` | `-O2` + LTO + strip (not `-Os` optimized) |
199
- | `make debug` | debug build with bridge logs |
200
- | `make nowasm` | no WASM/WAMR build → `_build/qwin-nowasm.exe` (~1.2MB, no `WebAssembly` global) |
201
- | `make cc64-small` | cross-compile x86_64 `_build/qwin.exe` (`-Os`, used by CI) |
202
- | `make cc32-small` | cross-compile i686 `_build/qwin-x86.exe` (`-Os`, XP-compatible, used by CI) |
192
+ | `make cc64` | cross-compile x86_64 → `_build/qwin.exe` (default target, `BUILD=fast`) |
193
+ | `make cc32` | cross-compile i686/XP → `_build/qwin-x86.exe` |
194
+ | `make cc64-nowasm` / `cc32-nowasm` | no WASM/WAMR builds |
195
+ | `BUILD=fast\|small\|debug` | build flavor: `make cc64 BUILD=small` = `-Os`+LTO (~1.5MB, used by CI) |
203
196
  | `make js` | compile TypeScript via tsc |
204
- | `make wasm` | compile WAT → WASM fixtures |
197
+ | `make wasm` | compile `test/*.wat` → WASM fixtures |
205
198
  | `make test` | run all tests |
206
199
  | `make test TEST=-net` | skip network tests (fast) |
207
200
  | `make test TEST=wasm` | run WASM tests only |
208
- | `make wamr` | rebuild WAMR library |
209
- | `make embed-js` | embed `embed.js` into exe (use `JS_EMBED=file.js`) |
210
- | `make embed-js-br` | embed brotli-compressed JS into exe |
211
- | `make exec_server` | Bundle `examples/exec_server.ts` and brotli-embed into `_build/exec_server.exe` |
201
+ | `make exec_server` | bundle `examples/exec_server.ts` and brotli-embed into `_build/exec_server.exe` |
202
+ | `make embed-js` / `embed-js-br` | embed JS (brotli-compressed) into exe |
212
203
  | `make npm-pkg` | package into `dist/quickwin/` |
213
- | `make clean` | clean build artifacts |
204
+ | `make gen-const` | regenerate `quickwin_const.d.ts` |
205
+ | `make clean` / `distclean` | clean (wipes all of `_build/`; afterwards run `make js wasm`) |
206
+
207
+ Build and troubleshooting details: `.agents/DEVELOPMENT_WORKFLOW.md`.
214
208
 
215
209
  ## License
216
210
 
package/README.md CHANGED
@@ -30,7 +30,7 @@ npm 包自带 4 个可执行文件:`qwin.exe`(64 位)/ `qwin-x86.exe`(32
30
30
  - **HTTP/HTTPS** —— `fetch()` API、Brotli 解压、chunked 传输、条件缓存
31
31
  - **WebSocket** —— 完整的 RFC 6455 实现,支持 ws:// 与 wss://
32
32
  - **WebAssembly** —— 基于 WAMR,支持标准 `WebAssembly.*` API
33
- - **FFI** —— 通过 libffi 调用任意 DLL 函数
33
+ - **FFI** —— 内置汇编调用桩,用 `bind()` 声明式签名调用任意 DLL 函数(无第三方依赖)
34
34
  - **Polyfills** —— `TextEncoder`、`URL`、`URLSearchParams`、`btoa`/`atob`、`crypto.subtle`、`setTimeout`
35
35
  - **动态导入** —— `import('https://esm.sh/...')`,无需安装任何依赖
36
36
 
@@ -104,7 +104,6 @@ _build/exec_server.exe
104
104
  | `fetch` | `import './lib/fetch.js'` | 向 globalThis 添加 `fetch()`、`Response`、`Headers` |
105
105
  | `websocket` | `import './lib/websocket.js'` | 向 globalThis 添加 `WebSocket` |
106
106
  | `polyfill` | `import './lib/polyfill.js'` | 向 globalThis 添加 `TextEncoder`、`URL`、`btoa`/`atob`、`setTimeout` |
107
- | `preact` | `lib/preact/...` | JSX → Win32 渲染器(`render`、`useState`、`useEffect`) |
108
107
  | `react-qw` | `lib/react-qw/` | 面向 Win32 GUI 的 React 自定义渲染器([文档](lib/react-qw/)) |
109
108
 
110
109
  ## Worker
@@ -168,8 +167,7 @@ parent.onmessage = (e) => {
168
167
 
169
168
  ### 前置依赖
170
169
 
171
- - MSYS2 UCRT64 或 MINGW64
172
- - Node.js(用于通过 tsc 编译 TypeScript)
170
+ - **podman**(开发容器;构建/测试/QEMU 验证都在容器内完成,与 CI 一致)
173
171
  - Git(用于子模块)
174
172
 
175
173
  ### 构建
@@ -178,38 +176,34 @@ parent.onmessage = (e) => {
178
176
  git clone --recursive https://github.com/xna00/quickwin.git
179
177
  cd quickwin
180
178
 
181
- .\run.ps1 "make wamr" # 构建 WAMR 库(仅首次)
182
- .\run.ps1 "make small" # 构建 qwin.exe(-Os + LTO,约 1.5MB)
183
- .\run.ps1 "make js" # 编译 TypeScript
184
- .\run.ps1 "make test" # 运行全部测试
179
+ ./docker/dev-podman.sh up # 启动开发容器(复用 ghcr.io/xna00/quickwin-dev)
180
+ podman exec quickwin-dev bash -lc 'cd /workspace && make js && make cc64 && make test TEST=-net'
185
181
  ```
186
182
 
187
- `make nowasm` 会产出更小的 `_build/qwin-nowasm.exe`,不含 WASM/WAMR
188
- (没有 `WebAssembly` 全局对象);对这种版本运行 `make test TEST=-wasm` 跳过 WASM 测试。
183
+ `make cc64` 产出 `_build/qwin.exe`。`cc64-nowasm` 会产出更小的
184
+ `_build/qwin-nowasm.exe`,不含 WASM/WAMR(没有 `WebAssembly` 全局对象);
185
+ 对这种版本运行 `make test TEST=-wasm` 跳过 WASM 测试。
189
186
 
190
187
  ### 构建目标
191
188
 
192
189
  | Target | Description |
193
190
  |--------|-------------|
194
- | `make` / `make nodebug` | 快速构建 |
195
- | `make small` | `-Os` + LTO + strip,约 1.5MB(推荐) |
196
- | `make minimal` | `small` + UPX 压缩 |
197
- | `make release` | `-O2` + LTO + strip(未做 `-Os` 优化) |
198
- | `make debug` | 带 bridge 日志的调试构建 |
199
- | `make nowasm` | 无 WASM/WAMR 构建 → `_build/qwin-nowasm.exe`(约 1.2MB,无 `WebAssembly` 全局) |
200
- | `make cc64-small` | 交叉编译 x86_64 `_build/qwin.exe`(`-Os`,CI 用) |
201
- | `make cc32-small` | 交叉编译 i686 `_build/qwin-x86.exe`(`-Os`,兼容 XP,CI 用) |
191
+ | `make cc64` | 交叉编译 x86_64 → `_build/qwin.exe`(默认目标,`BUILD=fast`) |
192
+ | `make cc32` | 交叉编译 i686/XP → `_build/qwin-x86.exe` |
193
+ | `make cc64-nowasm` / `cc32-nowasm` | 无 WASM/WAMR 版 |
194
+ | `BUILD=fast\|small\|debug` | 构建 flavor:`make cc64 BUILD=small` = `-Os`+LTO(约 1.5MB,CI 用) |
202
195
  | `make js` | 通过 tsc 编译 TypeScript |
203
- | `make wasm` | 编译 WAT → WASM fixtures |
196
+ | `make wasm` | 编译 `test/*.wat` → WASM fixtures |
204
197
  | `make test` | 运行全部测试 |
205
198
  | `make test TEST=-net` | 跳过网络测试(快速) |
206
199
  | `make test TEST=wasm` | 仅运行 WASM 测试 |
207
- | `make wamr` | 重建 WAMR 库 |
208
- | `make embed-js` | 把 `embed.js` 内嵌进 exe(用 `JS_EMBED=file.js`) |
209
- | `make embed-js-br` | 把 Brotli 压缩的 JS 内嵌进 exe |
210
200
  | `make exec_server` | 打包 `examples/exec_server.ts` 并 Brotli 内嵌进 `_build/exec_server.exe` |
201
+ | `make embed-js` / `embed-js-br` | 把 JS(Brotli 压缩)内嵌进 exe |
211
202
  | `make npm-pkg` | 打包到 `dist/quickwin/` |
212
- | `make clean` | 清理构建产物 |
203
+ | `make gen-const` | 重新生成 `quickwin_const.d.ts` |
204
+ | `make clean` / `distclean` | 清理(会清掉整个 `_build/`,之后需 `make js wasm`) |
205
+
206
+ 构建与排错细节见 `.agents/DEVELOPMENT_WORKFLOW.md`。
213
207
 
214
208
  ## 许可证
215
209
 
@@ -1,32 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useRef } from 'react';
3
- import * as win from 'win';
4
- import * as ffi from 'ffi';
5
- const _user32 = win.LoadLibrary('user32.dll');
6
- const _gdi32 = win.LoadLibrary('gdi32.dll');
7
- if (!_user32 || !_gdi32)
8
- throw new Error('Failed to load user32/gdi32');
9
- function loadProc(lib, name) {
10
- const ptr = win.GetProcAddress(lib, name);
11
- if (!ptr)
12
- throw new Error('Failed to load ' + name);
13
- return ptr;
14
- }
15
- const GetDC_ = loadProc(_user32, 'GetDC');
16
- const ReleaseDC_ = loadProc(_user32, 'ReleaseDC');
17
- const SetDIBitsToDevice_ = loadProc(_gdi32, 'SetDIBitsToDevice');
18
- const FFI_PTR = ffi.FFI_TYPE_POINTER;
19
- const FFI_U32 = ffi.FFI_TYPE_UINT32;
20
- const FFI_S32 = ffi.FFI_TYPE_SINT32;
3
+ import { GetDC, ReleaseDC } from '../lib/windows/user32.js';
4
+ import { SetDIBitsToDevice } from '../lib/windows/gdi32.js';
5
+ import { BITMAPINFOHEADER } from '../lib/windows/structs.js';
21
6
  function makeBitmapInfo(w, h) {
22
- const bmi = new ArrayBuffer(40);
23
- const bv = new DataView(bmi);
24
- bv.setUint32(0, 40, true);
25
- bv.setInt32(4, w, true);
26
- bv.setInt32(8, -h, true);
27
- bv.setUint16(12, 1, true);
28
- bv.setUint16(14, 24, true);
29
- return bmi;
7
+ // biHeight 负 = 自顶向下(DIB 原点左上);biCompression 缺省 0 = BI_RGB 无压缩
8
+ return BITMAPINFOHEADER.encode({ biSize: BITMAPINFOHEADER.size, biWidth: w, biHeight: -h, biPlanes: 1, biBitCount: 24 });
30
9
  }
31
10
  export const PdfCanvas = forwardRef(({ pixmap, style }, ref) => {
32
11
  const canvasRef = useRef(null);
@@ -46,19 +25,11 @@ export const PdfCanvas = forwardRef(({ pixmap, style }, ref) => {
46
25
  const pm = pixmapRef.current;
47
26
  if (!pm)
48
27
  return 0;
49
- const hdc = ffi.ffiCall(GetDC_, [ffi.FFI_TYPE_POINTER], [hwnd], ffi.FFI_TYPE_POINTER);
28
+ const hdc = GetDC(hwnd);
50
29
  if (hdc) {
51
30
  const bmi = makeBitmapInfo(pm.w, pm.h);
52
- ffi.ffiCall(SetDIBitsToDevice_, [
53
- ffi.FFI_TYPE_POINTER, FFI_S32, FFI_S32, FFI_U32, FFI_U32,
54
- FFI_S32, FFI_S32, FFI_U32, FFI_U32,
55
- FFI_PTR, FFI_PTR, FFI_U32
56
- ], [
57
- hdc, 0, 0, pm.w, pm.h,
58
- 0, 0, 0, pm.h,
59
- pm.data, bmi, 0
60
- ], FFI_S32);
61
- ffi.ffiCall(ReleaseDC_, [ffi.FFI_TYPE_POINTER, ffi.FFI_TYPE_POINTER], [hwnd, hdc], FFI_S32);
31
+ SetDIBitsToDevice(hdc, 0, 0, pm.w, pm.h, 0, 0, 0, pm.h, pm.data, bmi, 0);
32
+ ReleaseDC(hwnd, hdc);
62
33
  }
63
34
  return 0;
64
35
  }
@@ -1,40 +1,18 @@
1
+ import { PtrArrayBuffer } from '../lib/ffi/ctype.js'
1
2
  import { forwardRef, useRef } from 'react'
2
3
  import * as gui from 'gui'
3
- import * as win from 'win'
4
- import * as ffi from 'ffi'
4
+ import { GetDC, ReleaseDC } from '../lib/windows/user32.js'
5
+ import { SetDIBitsToDevice } from '../lib/windows/gdi32.js'
6
+ import { BITMAPINFOHEADER } from '../lib/windows/structs.js'
5
7
  import type { WStyle } from '../lib/react-qw/jsx.d.ts'
6
8
 
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
9
+ function makeBitmapInfo(w: number, h: number) {
10
+ // biHeight 负 = 自顶向下(DIB 原点左上);biCompression 缺省 0 = BI_RGB 无压缩
11
+ return BITMAPINFOHEADER.encode({ biSize: BITMAPINFOHEADER.size, biWidth: w, biHeight: -h, biPlanes: 1, biBitCount: 24 })
34
12
  }
35
13
 
36
14
  export interface PdfCanvasProps {
37
- pixmap?: { data: ArrayBuffer; w: number; h: number }
15
+ pixmap?: { data: PtrArrayBuffer<string>; w: number; h: number }
38
16
  style?: WStyle
39
17
  }
40
18
 
@@ -59,19 +37,11 @@ export const PdfCanvas = forwardRef<gui.HWND, PdfCanvasProps>(
59
37
  if (e.msg === gui.WmMsg.PAINT) {
60
38
  const pm = pixmapRef.current
61
39
  if (!pm) return 0
62
- const hdc = ffi.ffiCall(GetDC_, [ffi.FFI_TYPE_POINTER], [hwnd], ffi.FFI_TYPE_POINTER)
40
+ const hdc = GetDC(hwnd)
63
41
  if (hdc) {
64
42
  const bmi = makeBitmapInfo(pm.w, pm.h)
65
- ffi.ffiCall(SetDIBitsToDevice_, [
66
- ffi.FFI_TYPE_POINTER, 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_POINTER, ffi.FFI_TYPE_POINTER], [hwnd, hdc], FFI_S32)
43
+ SetDIBitsToDevice(hdc, 0, 0, pm.w, pm.h, 0, 0, 0, pm.h, pm.data, bmi, 0)
44
+ ReleaseDC(hwnd, hdc)
75
45
  }
76
46
  return 0
77
47
  }