quickwin 2.3.0 → 3.0.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 (99) hide show
  1. package/README.en.md +1 -2
  2. package/README.md +1 -2
  3. package/constants.ts +22 -0
  4. package/enum_map.d.ts +4 -0
  5. package/examples/PdfCanvas.js +1 -1
  6. package/examples/PdfCanvas.tsx +2 -2
  7. package/examples/execd/main.js +491 -217
  8. package/examples/pdf_preview2.js +76 -67
  9. package/examples/pdf_preview2.ts +84 -64
  10. package/examples/pdf_viewer.js +4932 -1029
  11. package/examples/pdf_viewer.tsx +11 -22
  12. package/examples/test_gallery.js +308 -143
  13. package/examples/test_gallery.tsx +12 -7
  14. package/examples/test_react_lazy.js +295 -133
  15. package/examples/test_react_lazy.tsx +4 -3
  16. package/examples/test_react_listbox.js +293 -132
  17. package/examples/test_react_listbox.tsx +3 -2
  18. package/examples/test_root_window.js +283 -126
  19. package/examples/test_tab.js +295 -133
  20. package/examples/test_tab.tsx +4 -3
  21. package/examples/tray_demo.js +37 -30
  22. package/examples/tray_demo.tsx +39 -32
  23. package/lib/bitmap.js +40 -0
  24. package/lib/bitmap.ts +39 -0
  25. package/lib/certs.js +31 -3
  26. package/lib/certs.ts +32 -3
  27. package/lib/dpi.js +30 -0
  28. package/lib/dpi.ts +31 -0
  29. package/lib/dumpRects.js +14 -7
  30. package/lib/dumpRects.ts +14 -6
  31. package/lib/ffi/bind.js +5 -4
  32. package/lib/ffi/bind.ts +9 -8
  33. package/lib/http-server.js +50 -25
  34. package/lib/http-server.ts +51 -23
  35. package/lib/react-qw/components/DateTimePicker.js +6 -7
  36. package/lib/react-qw/components/Input.js +5 -1
  37. package/lib/react-qw/components/ListView.js +8 -6
  38. package/lib/react-qw/components/ScrollView.js +28 -22
  39. package/lib/react-qw/index.js +7 -5
  40. package/lib/react-qw/index.ts +5 -5
  41. package/lib/react-qw/props.js +3 -3
  42. package/lib/react-qw/props.ts +3 -3
  43. package/lib/react-qw/reconciler.js +12 -17
  44. package/lib/react-qw/reconciler.ts +12 -16
  45. package/lib/text-measure.js +9 -6
  46. package/lib/text-measure.ts +9 -6
  47. package/lib/windows/comctl32.js +3 -2
  48. package/lib/windows/comctl32.ts +3 -2
  49. package/lib/windows/gdi32.js +28 -6
  50. package/lib/windows/gdi32.ts +30 -6
  51. package/lib/windows/kernel32.js +9 -0
  52. package/lib/windows/kernel32.ts +9 -0
  53. package/lib/windows/shell32.js +11 -2
  54. package/lib/windows/shell32.ts +13 -2
  55. package/lib/windows/structs.js +51 -0
  56. package/lib/windows/structs.ts +55 -0
  57. package/lib/windows/user32.js +6 -5
  58. package/lib/windows/user32.ts +6 -5
  59. package/main.js +13 -5
  60. package/package.json +2 -2
  61. package/quickwin.d.ts +13 -115
  62. package/qwin-nowasm-x86.exe +0 -0
  63. package/qwin-nowasm.exe +0 -0
  64. package/qwin-x86.exe +0 -0
  65. package/qwin.exe +0 -0
  66. package/test/bench_ffi_callraw.js +3 -4
  67. package/test/bench_ffi_callraw.ts +3 -4
  68. package/test/debug/mupdf_debug.js +4 -22
  69. package/test/debug/wasm_export_debug.js +2 -12
  70. package/test/debug/wasm_names.js +3 -13
  71. package/test/example.pdf +0 -0
  72. package/test/mupdf-cdn.js +40 -0
  73. package/test/mupdf-cdn.ts +43 -0
  74. package/test/test_ffi.js +8 -9
  75. package/test/test_ffi.ts +8 -9
  76. package/test/test_ffi_abi.js +6 -6
  77. package/test/test_ffi_abi.ts +8 -8
  78. package/test/test_ffi_windows.js +82 -11
  79. package/test/test_ffi_windows.ts +85 -11
  80. package/test/test_gdi_draw.js +3 -3
  81. package/test/test_gdi_draw.ts +4 -4
  82. package/test/test_mupdf_render.js +4 -22
  83. package/test/test_mupdf_render.ts +4 -23
  84. package/test/test_mupdf_twice.js +4 -19
  85. package/test/test_mupdf_twice.ts +4 -20
  86. package/test/test_mupdf_wasm.js +10 -4
  87. package/test/test_mupdf_wasm.ts +6 -4
  88. package/test/test_react_complex.js +316 -140
  89. package/test/test_react_counter.js +295 -133
  90. package/test/test_react_flex.js +297 -138
  91. package/test/test_react_hidden.js +296 -134
  92. package/test/test_react_listview_link.js +288 -131
  93. package/test/test_react_notify.js +290 -133
  94. package/test/test_react_render.js +295 -133
  95. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +0 -571
  96. package/vendor/mupdf-wasm/mupdf-wasm.js +0 -2749
  97. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  98. package/vendor/mupdf-wasm/mupdf.d.ts +0 -939
  99. package/vendor/mupdf-wasm/mupdf.js +0 -3317
@@ -2,12 +2,13 @@ import '../lib/polyfill.js'
2
2
  import * as gui from 'gui'
3
3
  import { useState } from 'react'
4
4
  import { render, Button, Tab } from '../lib/react-qw/index.js'
5
- import { WindowStyle, WmMsg } from '../constants'
5
+ import { ShowWindowCmd, WindowStyle, WmMsg } from '../constants'
6
+ import { PostQuitMessage, ShowWindow } from '../lib/windows/user32.js'
6
7
 
7
8
  gui.RegisterClass('TabDemo', (hwnd, msg, wParam, lParam) => {
8
9
  if (!hwnd) return gui.DefWindowProc(hwnd, msg, wParam, lParam)
9
10
  if (msg === WmMsg.DESTROY) {
10
- gui.PostQuitMessage(0)
11
+ PostQuitMessage(0)
11
12
  return 0
12
13
  }
13
14
  return gui.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -71,6 +72,6 @@ const hwnd = gui.CreateWindow(
71
72
  )
72
73
 
73
74
  if (hwnd) {
74
- gui.ShowWindow(hwnd)
75
+ ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL)
75
76
  render(<App />, hwnd)
76
77
  }
@@ -1,73 +1,80 @@
1
1
  import * as gui from 'gui';
2
2
  import * as std from 'std';
3
+ import { NULL } from '../lib/ffi/ctype.js';
4
+ import { GetCursorPos, CreatePopupMenu, AppendMenu, TrackPopupMenu, DestroyMenu, GetSystemMetrics, LoadImageOrdinal, PostQuitMessage, SetForegroundWindow, ShowWindow } from '../lib/windows/user32.js';
5
+ import { NOTIFYICONDATAW, POINT } from '../lib/windows/structs.js';
6
+ import { ShellNotifyIcon } from '../lib/windows/shell32.js';
7
+ import { createSystemDpiFont } from '../lib/dpi.js';
3
8
  const WM_TRAY = 0x8001;
4
9
  gui.RegisterClass('TrayApp', (hwnd, msg, wParam, lParam) => {
5
10
  if (msg === WM_TRAY) {
6
11
  const evt = lParam;
7
- if (evt === 0x0201) { // WM_LBUTTONDOWN
8
- gui.ShowWindow(hwnd, 5); // SW_SHOW
9
- gui.SetForegroundWindow(hwnd);
12
+ if (evt === 513 /* WmMsg.LBUTTONDOWN */) {
13
+ ShowWindow(hwnd, 5 /* ShowWindowCmd.SHOW */);
14
+ SetForegroundWindow(hwnd);
10
15
  }
11
- else if (evt === 0x0204 || evt === 0x007B) { // WM_RBUTTONDOWN or WM_CONTEXTMENU
12
- gui.SetForegroundWindow(hwnd);
13
- const pos = gui.GetCursorPos();
14
- const x = pos ? pos[0] : 0;
15
- const y = pos ? pos[1] : 0;
16
- const hMenu = gui.CreatePopupMenu();
17
- if (hMenu) {
18
- gui.AppendMenu(hMenu, 0, 1, '显示窗口');
19
- gui.AppendMenu(hMenu, 0x0800, 0, '');
20
- gui.AppendMenu(hMenu, 0, 2, '退出');
21
- const cmd = gui.TrackPopupMenu(hMenu, x, y, undefined, hwnd);
22
- gui.DestroyMenu(hMenu);
16
+ else if (evt === 516 /* WmMsg.RBUTTONDOWN */ || evt === 123 /* WmMsg.CONTEXTMENU */) {
17
+ SetForegroundWindow(hwnd);
18
+ const pt = POINT.alloc();
19
+ const pos = GetCursorPos(pt.ptr) ? POINT.decode(pt) : null;
20
+ const x = pos ? pos.x : 0;
21
+ const y = pos ? pos.y : 0;
22
+ const hMenu = CreatePopupMenu();
23
+ if (hMenu !== NULL) {
24
+ AppendMenu(hMenu, 0, 1, '显示窗口');
25
+ AppendMenu(hMenu, 2048 /* MenuFlag.SEPARATOR */, 0, NULL);
26
+ AppendMenu(hMenu, 0, 2, '退出');
27
+ const cmd = TrackPopupMenu(hMenu, 256 /* TrackPopupMenuFlag.RETURNCMD */ | 2 /* TrackPopupMenuFlag.RIGHTBUTTON */, x, y, 0, hwnd, NULL);
28
+ DestroyMenu(hMenu);
23
29
  if (cmd === 1) {
24
- gui.ShowWindow(hwnd, 5); // SW_SHOW
30
+ ShowWindow(hwnd, 5 /* ShowWindowCmd.SHOW */);
25
31
  }
26
32
  else if (cmd === 2) {
27
- gui.ShellNotifyIcon(2 /* NotifyIconCmd.DELETE */, { hwnd, uID: 1 });
28
- gui.PostQuitMessage(0);
33
+ ShellNotifyIcon(2 /* NotifyIconCmd.DELETE */, { cbSize: NOTIFYICONDATAW.size, hWnd: hwnd, uID: 1 });
34
+ PostQuitMessage(0);
29
35
  }
30
36
  }
31
37
  }
32
38
  return 0;
33
39
  }
34
40
  if (msg === 16 /* WmMsg.CLOSE */) {
35
- gui.ShowWindow(hwnd, 0); // SW_HIDE
41
+ ShowWindow(hwnd, 0 /* ShowWindowCmd.HIDE */);
36
42
  return 0;
37
43
  }
38
44
  return gui.DefWindowProc(hwnd, msg, wParam, lParam);
39
45
  });
40
46
  // 创建窗口
41
47
  const W = 500, H = 400;
42
- const scr = gui.GetScreenSize();
48
+ const scr = [GetSystemMetrics(0 /* SysMetrics.CXSCREEN */), GetSystemMetrics(1 /* SysMetrics.CYSCREEN */)];
43
49
  const x = (scr[0] - W) >> 1;
44
50
  const y = (scr[1] - H) >> 1;
45
51
  const hwnd = gui.CreateWindow('TrayApp', '系统托盘示例', 13565952 /* WindowStyle.OVERLAPPEDWINDOW */, x, y, W, H, null, null);
46
52
  if (!hwnd)
47
53
  std.exit(0);
48
- gui.ShowWindow(hwnd);
54
+ ShowWindow(hwnd, 1 /* ShowWindowCmd.SHOWNORMAL */);
49
55
  // 添加说明文字
50
- const ES_MULTILINE = 0x0004;
51
- const ES_READONLY = 0x0800;
52
- const style = 1073741824 /* WindowStyle.CHILD */ | 268435456 /* WindowStyle.VISIBLE */ | ES_MULTILINE | ES_READONLY;
56
+ const style = 1073741824 /* WindowStyle.CHILD */ | 268435456 /* WindowStyle.VISIBLE */ | 4 /* EditStyle.MULTILINE */ | 2048 /* EditStyle.READONLY */;
53
57
  const text = gui.CreateWindow('EDIT', 'QuickWin 系统托盘示例\r\n'
54
58
  + '\r\n'
55
59
  + '右键点击托盘图标打开上下文菜单。\r\n'
56
60
  + '关闭此窗口将隐藏到托盘。\r\n'
57
61
  + '从菜单中选择"显示窗口"恢复窗口,"退出"结束程序。', style, 15, 15, W - 30, H - 70, hwnd, null);
58
62
  if (text) {
59
- const font = gui.CreateSystemDpiFont();
63
+ const font = createSystemDpiFont();
60
64
  if (font)
61
65
  gui.SendMessage(text, 48 /* WmMsg.SETFONT */, font, 1);
62
66
  }
67
+ // LoadImageW 返回的句柄随 uType 变(HICON/HCURSOR/HBITMAP),绑定只能给裸地址档;
68
+ // LoadAppIcon 的 HICON 是同一档的品牌子型,故变量按裸地址声明
63
69
  let hIcon = gui.LoadAppIcon();
64
70
  if (!hIcon)
65
- hIcon = gui.LoadImage(null, 32512 /* IDI.APPLICATION */, 1 /* ImageType.ICON */, 0, 0, 32768 /* LoadImageFlag.SHARED */);
71
+ hIcon = LoadImageOrdinal(NULL, 32512 /* IDI.APPLICATION */, 1 /* ImageType.ICON */, 0, 0, 32768 /* LoadImageFlag.SHARED */);
66
72
  if (hIcon) {
67
- const ok = gui.ShellNotifyIcon(0 /* NotifyIconCmd.ADD */, {
68
- hwnd: hwnd, uID: 1,
69
- flags: 1 /* NotifyIconFlag.MESSAGE */ | 2 /* NotifyIconFlag.ICON */,
70
- callbackMessage: WM_TRAY,
73
+ const ok = ShellNotifyIcon(0 /* NotifyIconCmd.ADD */, {
74
+ cbSize: NOTIFYICONDATAW.size,
75
+ hWnd: hwnd, uID: 1,
76
+ uFlags: 1 /* NotifyIconFlag.MESSAGE */ | 2 /* NotifyIconFlag.ICON */,
77
+ uCallbackMessage: WM_TRAY,
71
78
  hIcon,
72
79
  });
73
80
  std.out.printf('tray icon %s\n', ok ? 'added' : 'FAILED');
@@ -1,39 +1,45 @@
1
1
  import * as gui from 'gui'
2
2
  import * as std from 'std'
3
- import { IDI, ImageType, LoadImageFlag, NotifyIconCmd, NotifyIconFlag, WindowStyle, WmMsg } from '../constants'
3
+ import { NULL, type MaybePtr } from '../lib/ffi/ctype.js'
4
+ import { GetCursorPos, CreatePopupMenu, AppendMenu, TrackPopupMenu, DestroyMenu, GetSystemMetrics, LoadImageOrdinal, PostQuitMessage, SetForegroundWindow, ShowWindow } from '../lib/windows/user32.js'
5
+ import { NOTIFYICONDATAW, POINT } from '../lib/windows/structs.js'
6
+ import { ShellNotifyIcon } from '../lib/windows/shell32.js'
7
+ import { createSystemDpiFont } from '../lib/dpi.js'
8
+ import { EditStyle, IDI, ImageType, LoadImageFlag, MenuFlag, NotifyIconCmd, NotifyIconFlag, ShowWindowCmd, SysMetrics, TrackPopupMenuFlag, WindowStyle, WmMsg } from '../constants'
4
9
 
5
10
  const WM_TRAY = 0x8001
6
11
 
7
12
  gui.RegisterClass('TrayApp', (hwnd, msg, wParam, lParam) => {
8
13
  if (msg === WM_TRAY) {
9
14
  const evt = lParam
10
- if (evt === 0x0201) { // WM_LBUTTONDOWN
11
- gui.ShowWindow(hwnd, 5) // SW_SHOW
12
- gui.SetForegroundWindow(hwnd)
13
- } else if (evt === 0x0204 || evt === 0x007B) { // WM_RBUTTONDOWN or WM_CONTEXTMENU
14
- gui.SetForegroundWindow(hwnd)
15
- const pos = gui.GetCursorPos()
16
- const x = pos ? pos[0] : 0
17
- const y = pos ? pos[1] : 0
18
- const hMenu = gui.CreatePopupMenu()
19
- if (hMenu) {
20
- gui.AppendMenu(hMenu, 0, 1, '显示窗口')
21
- gui.AppendMenu(hMenu, 0x0800, 0, '')
22
- gui.AppendMenu(hMenu, 0, 2, '退出')
23
- const cmd = gui.TrackPopupMenu(hMenu, x, y, undefined, hwnd)
24
- gui.DestroyMenu(hMenu)
15
+ if (evt === WmMsg.LBUTTONDOWN) {
16
+ ShowWindow(hwnd, ShowWindowCmd.SHOW)
17
+ SetForegroundWindow(hwnd)
18
+ } else if (evt === WmMsg.RBUTTONDOWN || evt === WmMsg.CONTEXTMENU) {
19
+ SetForegroundWindow(hwnd)
20
+ const pt = POINT.alloc()
21
+ const pos = GetCursorPos(pt.ptr) ? POINT.decode(pt) : null
22
+ const x = pos ? pos.x : 0
23
+ const y = pos ? pos.y : 0
24
+ const hMenu = CreatePopupMenu()
25
+ if (hMenu !== NULL) {
26
+ AppendMenu(hMenu, 0, 1, '显示窗口')
27
+ AppendMenu(hMenu, MenuFlag.SEPARATOR, 0, NULL)
28
+ AppendMenu(hMenu, 0, 2, '退出')
29
+ const cmd = TrackPopupMenu(hMenu, TrackPopupMenuFlag.RETURNCMD | TrackPopupMenuFlag.RIGHTBUTTON, x, y, 0, hwnd, NULL)
30
+ DestroyMenu(hMenu)
25
31
  if (cmd === 1) {
26
- gui.ShowWindow(hwnd, 5) // SW_SHOW
32
+ ShowWindow(hwnd, ShowWindowCmd.SHOW)
27
33
  } else if (cmd === 2) {
28
- gui.ShellNotifyIcon(NotifyIconCmd.DELETE, { hwnd, uID: 1 })
29
- gui.PostQuitMessage(0)
34
+ ShellNotifyIcon(NotifyIconCmd.DELETE, { cbSize: NOTIFYICONDATAW.size, hWnd: hwnd, uID: 1 })
35
+ PostQuitMessage(0)
30
36
  }
31
37
  }
32
38
  }
33
39
  return 0
34
40
  }
35
41
  if (msg === WmMsg.CLOSE) {
36
- gui.ShowWindow(hwnd, 0) // SW_HIDE
42
+ ShowWindow(hwnd, ShowWindowCmd.HIDE)
37
43
  return 0
38
44
  }
39
45
  return gui.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -41,17 +47,15 @@ gui.RegisterClass('TrayApp', (hwnd, msg, wParam, lParam) => {
41
47
 
42
48
  // 创建窗口
43
49
  const W = 500, H = 400
44
- const scr = gui.GetScreenSize()
50
+ const scr: [number, number] = [GetSystemMetrics(SysMetrics.CXSCREEN), GetSystemMetrics(SysMetrics.CYSCREEN)]
45
51
  const x = (scr[0] - W) >> 1
46
52
  const y = (scr[1] - H) >> 1
47
53
  const hwnd = gui.CreateWindow('TrayApp', '系统托盘示例', WindowStyle.OVERLAPPEDWINDOW, x, y, W, H, null, null)
48
54
  if (!hwnd) std.exit(0)
49
- gui.ShowWindow(hwnd)
55
+ ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL)
50
56
 
51
57
  // 添加说明文字
52
- const ES_MULTILINE = 0x0004
53
- const ES_READONLY = 0x0800
54
- const style = WindowStyle.CHILD | WindowStyle.VISIBLE | ES_MULTILINE | ES_READONLY
58
+ const style = WindowStyle.CHILD | WindowStyle.VISIBLE | EditStyle.MULTILINE | EditStyle.READONLY
55
59
  const text = gui.CreateWindow('EDIT',
56
60
  'QuickWin 系统托盘示例\r\n'
57
61
  + '\r\n'
@@ -60,18 +64,21 @@ const text = gui.CreateWindow('EDIT',
60
64
  + '从菜单中选择"显示窗口"恢复窗口,"退出"结束程序。',
61
65
  style, 15, 15, W - 30, H - 70, hwnd, null)
62
66
  if (text) {
63
- const font = gui.CreateSystemDpiFont()
67
+ const font = createSystemDpiFont()
64
68
  if (font)
65
69
  gui.SendMessage(text, WmMsg.SETFONT, font, 1)
66
70
  }
67
71
 
68
- let hIcon = gui.LoadAppIcon()
69
- if (!hIcon) hIcon = gui.LoadImage(null, IDI.APPLICATION, ImageType.ICON, 0, 0, LoadImageFlag.SHARED)
72
+ // LoadImageW 返回的句柄随 uType 变(HICON/HCURSOR/HBITMAP),绑定只能给裸地址档;
73
+ // LoadAppIcon 的 HICON 是同一档的品牌子型,故变量按裸地址声明
74
+ let hIcon: MaybePtr<''> | null = gui.LoadAppIcon()
75
+ if (!hIcon) hIcon = LoadImageOrdinal(NULL, IDI.APPLICATION, ImageType.ICON, 0, 0, LoadImageFlag.SHARED)
70
76
  if (hIcon) {
71
- const ok = gui.ShellNotifyIcon(NotifyIconCmd.ADD, {
72
- hwnd: hwnd, uID: 1,
73
- flags: NotifyIconFlag.MESSAGE | NotifyIconFlag.ICON,
74
- callbackMessage: WM_TRAY,
77
+ const ok = ShellNotifyIcon(NotifyIconCmd.ADD, {
78
+ cbSize: NOTIFYICONDATAW.size,
79
+ hWnd: hwnd, uID: 1,
80
+ uFlags: NotifyIconFlag.MESSAGE | NotifyIconFlag.ICON,
81
+ uCallbackMessage: WM_TRAY,
75
82
  hIcon,
76
83
  })
77
84
  std.out.printf('tray icon %s\n', ok ? 'added' : 'FAILED')
package/lib/bitmap.js ADDED
@@ -0,0 +1,40 @@
1
+ // 按像素建位图 —— gui 模块下沉到 JS 层的复合逻辑(原 js_createBitmapFromPixels)。
2
+ // 旧 C 体是 CreateDIBSection + memcpy 直写位数据;FFI 侧没有「JS 缓冲 → 原生地址」的
3
+ // 批量写原语(ffi 只导出 readBytes / writeByte,逐字节循环不值当),改用 GDI 自带的
4
+ // SetDIBits 把调用方缓冲装进 DIB 段:ColorUse.RGB_COLORS 下 hdc 被忽略(MSDN:
5
+ // "used only if the DIB_PAL_COLORS constant is set ... otherwise it is ignored"),
6
+ // 双机实测传 NULL 可用、GetDIBits 读回逐字节一致(含 alpha),与旧 C 的 memcpy 等价。
7
+ import { NULL, PtrArrayBuffer } from './ffi/ctype.js';
8
+ import { CreateDIBSection, DeleteObject, SetDIBits } from './windows/gdi32.js';
9
+ import { BITMAPINFOHEADER } from './windows/structs.js';
10
+ /** 建 32bpp 自顶向下 DIB 段位图(像素为 BGRA,[B,G,R,A] 字节序,alpha 保留 —— 可喂
11
+ * ImageListAdd(ILC_COLOR32) 等)。width/height ≤ 0 或像素缓冲不足 width*height*4 →
12
+ * NULL(同旧 gui.CreateBitmapFromPixels 语义:非法入参静默返空,由调用方按「无图」处理);
13
+ * GDI 失败同样返 NULL(已建的位图当场回收)。
14
+ * 位图归调用方所有:用完 DeleteObject。 */
15
+ export function createBitmapFromPixels(width, height, pixels) {
16
+ if (width <= 0 || height <= 0)
17
+ return NULL;
18
+ const len = width * height * 4;
19
+ if (pixels.byteLength < len)
20
+ return NULL;
21
+ // 32bpp BI_RGB:头部即全部(无颜色表);负 biHeight = 自顶向下,与 JS 缓冲行序一致
22
+ const bi = BITMAPINFOHEADER.encode({
23
+ biSize: BITMAPINFOHEADER.size,
24
+ biWidth: width,
25
+ biHeight: -height,
26
+ biPlanes: 1,
27
+ biBitCount: 32,
28
+ });
29
+ const hbm = CreateDIBSection(NULL, bi.ptr, 0 /* ColorUse.RGB_COLORS */, NULL, NULL, 0);
30
+ if (!hbm)
31
+ return NULL;
32
+ // 像素先落自有原生缓冲(BYTE.encode 需带 .ptr 的缓冲),再让 GDI 装进 DIB 段
33
+ const src = new PtrArrayBuffer(len);
34
+ new Uint8Array(src).set(new Uint8Array(pixels, 0, len));
35
+ if (SetDIBits(NULL, hbm, 0, height, src, bi.ptr, 0 /* ColorUse.RGB_COLORS */) !== height) {
36
+ DeleteObject(hbm);
37
+ return NULL;
38
+ }
39
+ return hbm;
40
+ }
package/lib/bitmap.ts ADDED
@@ -0,0 +1,39 @@
1
+ // 按像素建位图 —— gui 模块下沉到 JS 层的复合逻辑(原 js_createBitmapFromPixels)。
2
+ // 旧 C 体是 CreateDIBSection + memcpy 直写位数据;FFI 侧没有「JS 缓冲 → 原生地址」的
3
+ // 批量写原语(ffi 只导出 readBytes / writeByte,逐字节循环不值当),改用 GDI 自带的
4
+ // SetDIBits 把调用方缓冲装进 DIB 段:ColorUse.RGB_COLORS 下 hdc 被忽略(MSDN:
5
+ // "used only if the DIB_PAL_COLORS constant is set ... otherwise it is ignored"),
6
+ // 双机实测传 NULL 可用、GetDIBits 读回逐字节一致(含 alpha),与旧 C 的 memcpy 等价。
7
+ import { NULL, PtrArrayBuffer, type MaybePtr } from './ffi/ctype.js'
8
+ import { CreateDIBSection, DeleteObject, SetDIBits } from './windows/gdi32.js'
9
+ import { BITMAPINFOHEADER } from './windows/structs.js'
10
+ import { ColorUse } from '../constants'
11
+
12
+ /** 建 32bpp 自顶向下 DIB 段位图(像素为 BGRA,[B,G,R,A] 字节序,alpha 保留 —— 可喂
13
+ * ImageListAdd(ILC_COLOR32) 等)。width/height ≤ 0 或像素缓冲不足 width*height*4 →
14
+ * NULL(同旧 gui.CreateBitmapFromPixels 语义:非法入参静默返空,由调用方按「无图」处理);
15
+ * GDI 失败同样返 NULL(已建的位图当场回收)。
16
+ * 位图归调用方所有:用完 DeleteObject。 */
17
+ export function createBitmapFromPixels(width: number, height: number, pixels: ArrayBuffer): MaybePtr<'HGDIOBJ'> {
18
+ if (width <= 0 || height <= 0) return NULL
19
+ const len = width * height * 4
20
+ if (pixels.byteLength < len) return NULL
21
+ // 32bpp BI_RGB:头部即全部(无颜色表);负 biHeight = 自顶向下,与 JS 缓冲行序一致
22
+ const bi = BITMAPINFOHEADER.encode({
23
+ biSize: BITMAPINFOHEADER.size,
24
+ biWidth: width,
25
+ biHeight: -height,
26
+ biPlanes: 1,
27
+ biBitCount: 32,
28
+ })
29
+ const hbm = CreateDIBSection(NULL, bi.ptr, ColorUse.RGB_COLORS, NULL, NULL, 0)
30
+ if (!hbm) return NULL
31
+ // 像素先落自有原生缓冲(BYTE.encode 需带 .ptr 的缓冲),再让 GDI 装进 DIB 段
32
+ const src = new PtrArrayBuffer<'BYTE'>(len)
33
+ new Uint8Array(src).set(new Uint8Array(pixels, 0, len))
34
+ if (SetDIBits(NULL, hbm, 0, height, src, bi.ptr, ColorUse.RGB_COLORS) !== height) {
35
+ DeleteObject(hbm)
36
+ return NULL
37
+ }
38
+ return hbm
39
+ }
package/lib/certs.js CHANGED
@@ -6,8 +6,10 @@
6
6
  // bundle and are pushed into wolfSSL's process-wide trust store as soon
7
7
  // as this module is imported.
8
8
  //
9
- // The process-wide store has a fixed QW_CA_SLOTS (8) capacity; this
10
- // bundle uses 7, leaving headroom for run-time registrations.
9
+ // The process-wide store has a fixed QW_CA_SLOTS (16) capacity; this
10
+ // bundle uses 8, leaving headroom for future anchors. If the bundle
11
+ // ever outgrows it, raise QW_CA_SLOTS in src/quickjs-wolfssl.c first —
12
+ // a full store throws at import time.
11
13
  //
12
14
  // Each anchor covers a different root hierarchy, and none is
13
15
  // interchangeable: wolfSSL resolves a CA by AKID/SKID and does not walk
@@ -25,6 +27,10 @@
25
27
  // G2 (bing.com), RSA 2048.
26
28
  // * GLOBALSIGN_R46, ISRG_X1, ISRG_X2 — the newer GlobalSign R46 and
27
29
  // Let's Encrypt hierarchies (taobao.com, qq.com).
30
+ // * GTS_R4 — cross-signed GTS Root R4 (issued by GlobalSign Root CA,
31
+ // notAfter 2028-01-28, EC P-256). unpkg.com and other properties
32
+ // whose chains terminate at Google Trust Services (WE1/E5/E6
33
+ // intermediates).
28
34
  //
29
35
  // Known limitation (wolfSSL, not the bundle): GLOBALSIGN_R46 and ISRG_X1
30
36
  // are RSA 4096, and this wolfSSL build compiles the SP bignum with
@@ -202,6 +208,27 @@ zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
202
208
  tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
203
209
  /q4AaOeMSQ+2b1tbFfLn
204
210
  -----END CERTIFICATE-----`;
211
+ const GTS_R4 = `-----BEGIN CERTIFICATE-----
212
+ MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX
213
+ MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
214
+ CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx
215
+ NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT
216
+ GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0
217
+ MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube
218
+ Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e
219
+ WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd
220
+ BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
221
+ BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN
222
+ l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw
223
+ Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v
224
+ Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG
225
+ SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ
226
+ odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY
227
+ +SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs
228
+ kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep
229
+ 8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1
230
+ vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl
231
+ -----END CERTIFICATE-----`;
205
232
  // Register at import time. Importers of this module get the anchors
206
233
  // without writing any code themselves. addTrustedCA only returns the
207
234
  // slot count (-1 when full/empty); the fatal-vs-not decision is ours.
@@ -216,4 +243,5 @@ trust(DIGICERT_GLOBAL_G2);
216
243
  trust(GLOBALSIGN_R46);
217
244
  trust(ISRG_X1);
218
245
  trust(ISRG_X2);
219
- export const TRUSTED_CA_COUNT = 7;
246
+ trust(GTS_R4);
247
+ export const TRUSTED_CA_COUNT = 8;
package/lib/certs.ts CHANGED
@@ -6,8 +6,10 @@
6
6
  // bundle and are pushed into wolfSSL's process-wide trust store as soon
7
7
  // as this module is imported.
8
8
  //
9
- // The process-wide store has a fixed QW_CA_SLOTS (8) capacity; this
10
- // bundle uses 7, leaving headroom for run-time registrations.
9
+ // The process-wide store has a fixed QW_CA_SLOTS (16) capacity; this
10
+ // bundle uses 8, leaving headroom for future anchors. If the bundle
11
+ // ever outgrows it, raise QW_CA_SLOTS in src/quickjs-wolfssl.c first —
12
+ // a full store throws at import time.
11
13
  //
12
14
  // Each anchor covers a different root hierarchy, and none is
13
15
  // interchangeable: wolfSSL resolves a CA by AKID/SKID and does not walk
@@ -25,6 +27,10 @@
25
27
  // G2 (bing.com), RSA 2048.
26
28
  // * GLOBALSIGN_R46, ISRG_X1, ISRG_X2 — the newer GlobalSign R46 and
27
29
  // Let's Encrypt hierarchies (taobao.com, qq.com).
30
+ // * GTS_R4 — cross-signed GTS Root R4 (issued by GlobalSign Root CA,
31
+ // notAfter 2028-01-28, EC P-256). unpkg.com and other properties
32
+ // whose chains terminate at Google Trust Services (WE1/E5/E6
33
+ // intermediates).
28
34
  //
29
35
  // Known limitation (wolfSSL, not the bundle): GLOBALSIGN_R46 and ISRG_X1
30
36
  // are RSA 4096, and this wolfSSL build compiles the SP bignum with
@@ -211,6 +217,28 @@ tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
211
217
  /q4AaOeMSQ+2b1tbFfLn
212
218
  -----END CERTIFICATE-----`
213
219
 
220
+ const GTS_R4 = `-----BEGIN CERTIFICATE-----
221
+ MIIDejCCAmKgAwIBAgIQf+UwvzMTQ77dghYQST2KGzANBgkqhkiG9w0BAQsFADBX
222
+ MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEQMA4GA1UE
223
+ CxMHUm9vdCBDQTEbMBkGA1UEAxMSR2xvYmFsU2lnbiBSb290IENBMB4XDTIzMTEx
224
+ NTAzNDMyMVoXDTI4MDEyODAwMDA0MlowRzELMAkGA1UEBhMCVVMxIjAgBgNVBAoT
225
+ GUdvb2dsZSBUcnVzdCBTZXJ2aWNlcyBMTEMxFDASBgNVBAMTC0dUUyBSb290IFI0
226
+ MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE83Rzp2iLYK5DuDXFgTB7S0md+8Fhzube
227
+ Rr1r1WEYNa5A3XP3iZEwWus87oV8okB2O6nGuEfYKueSkWpz6bFyOZ8pn6KY019e
228
+ WIZlD6GEZQbR3IvJx3PIjGov5cSr0R2Ko4H/MIH8MA4GA1UdDwEB/wQEAwIBhjAd
229
+ BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
230
+ BgNVHQ4EFgQUgEzW63T/STaj1dj8tT7FavCUHYwwHwYDVR0jBBgwFoAUYHtmGkUN
231
+ l8qJUC99BM00qP/8/UswNgYIKwYBBQUHAQEEKjAoMCYGCCsGAQUFBzAChhpodHRw
232
+ Oi8vaS5wa2kuZ29vZy9nc3IxLmNydDAtBgNVHR8EJjAkMCKgIKAehhxodHRwOi8v
233
+ Yy5wa2kuZ29vZy9yL2dzcjEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqG
234
+ SIb3DQEBCwUAA4IBAQAYQrsPBtYDh5bjP2OBDwmkoWhIDDkic574y04tfzHpn+cJ
235
+ odI2D4SseesQ6bDrarZ7C30ddLibZatoKiws3UL9xnELz4ct92vID24FfVbiI1hY
236
+ +SW6FoVHkNeWIP0GCbaM4C6uVdF5dTUsMVs/ZbzNnIdCp5Gxmx5ejvEau8otR/Cs
237
+ kGN+hr/W5GvT1tMBjgWKZ1i4//emhA1JG1BbPzoLJQvyEotc03lXjTaCzv8mEbep
238
+ 8RqZ7a2CPsgRbuvTPBwcOMBBmuFeU88+FSBX6+7iP0il8b4Z0QFqIwwMHfs/L6K1
239
+ vepuoxtGzi4CZ68zJpiq1UvSqTbFJjtbD4seiMHl
240
+ -----END CERTIFICATE-----`
241
+
214
242
  // Register at import time. Importers of this module get the anchors
215
243
  // without writing any code themselves. addTrustedCA only returns the
216
244
  // slot count (-1 when full/empty); the fatal-vs-not decision is ours.
@@ -225,5 +253,6 @@ trust(DIGICERT_GLOBAL_G2)
225
253
  trust(GLOBALSIGN_R46)
226
254
  trust(ISRG_X1)
227
255
  trust(ISRG_X2)
256
+ trust(GTS_R4)
228
257
 
229
- export const TRUSTED_CA_COUNT = 7
258
+ export const TRUSTED_CA_COUNT = 8
package/lib/dpi.js ADDED
@@ -0,0 +1,30 @@
1
+ // DPI 与系统字体 —— gui 模块下沉到 JS 层的两处复合逻辑(原 js_getScaleFactor /
2
+ // js_createSystemDpiFont)。两者都只按「一次操作一次」调用,FFI 的固定溢价无感;
3
+ // 留在 C 只会与 lib/windows 的绑定重复一层,遂收在此处单点实现。
4
+ import { NULL } from './ffi/ctype.js';
5
+ import { GetDC, ReleaseDC, SystemParametersInfo } from './windows/user32.js';
6
+ import { GetDeviceCaps, CreateFontIndirect } from './windows/gdi32.js';
7
+ import { NONCLIENTMETRICSW } from './windows/structs.js';
8
+ /** 屏幕缩放系数(96 DPI = 1):GetDC(NULL) → GetDeviceCaps(LOGPIXELSX) → ReleaseDC 成对释放 */
9
+ export function getScaleFactor() {
10
+ const hdc = GetDC(NULL);
11
+ const dpi = hdc ? GetDeviceCaps(hdc, 88 /* DeviceCap.LOGPIXELSX */) : 96;
12
+ if (hdc)
13
+ ReleaseDC(NULL, hdc);
14
+ return dpi / 96;
15
+ }
16
+ /** 系统消息字体(CreateFontIndirectW(NONCLIENTMETRICS.lfMessageFont))——界面控件的默认字体;
17
+ * 出参位不注册 encoder:encode({cbSize}) 建缓冲带初值 → 传 .ptr → decode 读回。
18
+ * cbSize/uiParam 必须与系统认知的结构尺寸一致:Vista+ 为 504(含尾部 iPaddedBorderWidth)、
19
+ * XP/2003 为 500 —— 先按 504 试,失败退回 500(lfMessageFont 落在前 500 字节内,尾部留 0);
20
+ * 两次都失败 → NULL,调用方按「无字体」处理(控件回落系统默认字体)。 */
21
+ export function createSystemDpiFont() {
22
+ const ncm = NONCLIENTMETRICSW.encode({ cbSize: NONCLIENTMETRICSW.size });
23
+ if (!SystemParametersInfo(41 /* Spi.GETNONCLIENTMETRICS */, NONCLIENTMETRICSW.size, ncm, 0)) {
24
+ const legacySize = NONCLIENTMETRICSW.size - 4;
25
+ NONCLIENTMETRICSW.encode({ cbSize: legacySize }, ncm);
26
+ if (!SystemParametersInfo(41 /* Spi.GETNONCLIENTMETRICS */, legacySize, ncm, 0))
27
+ return NULL;
28
+ }
29
+ return CreateFontIndirect(NONCLIENTMETRICSW.decode(ncm).lfMessageFont);
30
+ }
package/lib/dpi.ts ADDED
@@ -0,0 +1,31 @@
1
+ // DPI 与系统字体 —— gui 模块下沉到 JS 层的两处复合逻辑(原 js_getScaleFactor /
2
+ // js_createSystemDpiFont)。两者都只按「一次操作一次」调用,FFI 的固定溢价无感;
3
+ // 留在 C 只会与 lib/windows 的绑定重复一层,遂收在此处单点实现。
4
+ import { NULL, type Ptr } from './ffi/ctype.js'
5
+ import { GetDC, ReleaseDC, SystemParametersInfo } from './windows/user32.js'
6
+ import { GetDeviceCaps, CreateFontIndirect } from './windows/gdi32.js'
7
+ import { NONCLIENTMETRICSW } from './windows/structs.js'
8
+ import { DeviceCap, Spi } from '../constants'
9
+
10
+ /** 屏幕缩放系数(96 DPI = 1):GetDC(NULL) → GetDeviceCaps(LOGPIXELSX) → ReleaseDC 成对释放 */
11
+ export function getScaleFactor(): number {
12
+ const hdc = GetDC(NULL)
13
+ const dpi = hdc ? GetDeviceCaps(hdc, DeviceCap.LOGPIXELSX) : 96
14
+ if (hdc) ReleaseDC(NULL, hdc)
15
+ return dpi / 96
16
+ }
17
+
18
+ /** 系统消息字体(CreateFontIndirectW(NONCLIENTMETRICS.lfMessageFont))——界面控件的默认字体;
19
+ * 出参位不注册 encoder:encode({cbSize}) 建缓冲带初值 → 传 .ptr → decode 读回。
20
+ * cbSize/uiParam 必须与系统认知的结构尺寸一致:Vista+ 为 504(含尾部 iPaddedBorderWidth)、
21
+ * XP/2003 为 500 —— 先按 504 试,失败退回 500(lfMessageFont 落在前 500 字节内,尾部留 0);
22
+ * 两次都失败 → NULL,调用方按「无字体」处理(控件回落系统默认字体)。 */
23
+ export function createSystemDpiFont(): Ptr<'HGDIOBJ'> | NULL {
24
+ const ncm = NONCLIENTMETRICSW.encode({ cbSize: NONCLIENTMETRICSW.size })
25
+ if (!SystemParametersInfo(Spi.GETNONCLIENTMETRICS, NONCLIENTMETRICSW.size, ncm, 0)) {
26
+ const legacySize = NONCLIENTMETRICSW.size - 4
27
+ NONCLIENTMETRICSW.encode({ cbSize: legacySize }, ncm)
28
+ if (!SystemParametersInfo(Spi.GETNONCLIENTMETRICS, legacySize, ncm, 0)) return NULL
29
+ }
30
+ return CreateFontIndirect(NONCLIENTMETRICSW.decode(ncm).lfMessageFont)
31
+ }
package/lib/dumpRects.js CHANGED
@@ -1,17 +1,24 @@
1
1
  import * as os from 'os';
2
- import * as gui from 'gui';
2
+ import { GetClientRect, GetWindow, GetWindowRect, GetWindowText, GetWindowTextLength } from '../lib/windows/user32.js';
3
+ import { WCHAR } from '../lib/ffi/bind.js';
4
+ import { RECT } from '../lib/windows/structs.js';
3
5
  export function startDumpRects(rootHwnd, intervalMs = 3000) {
4
6
  function dumpChild(parent, indent) {
5
- var ch = gui.GetWindow(parent, 5 /* GetWindowCmd.CHILD */);
7
+ var ch = GetWindow(parent, 5 /* GetWindowCmd.CHILD */);
6
8
  var n = 0;
7
9
  while (ch) {
8
10
  n++;
9
- var wr = gui.GetWindowRect(ch);
10
- var cr = gui.GetClientRect(ch);
11
- var txt = String(gui.GetWindowText(ch) || '').slice(0, 16);
12
- console.log(indent + '[' + n + '] hwnd=' + String(ch) + ' txt="' + txt + '" wr=' + JSON.stringify(wr) + ' cr=' + JSON.stringify(cr));
11
+ const wr = RECT.alloc();
12
+ const cr = RECT.alloc();
13
+ GetWindowRect(ch, wr.ptr);
14
+ GetClientRect(ch, cr.ptr);
15
+ const cap = GetWindowTextLength(ch) + 1;
16
+ const textBuf = WCHAR.alloc(cap);
17
+ GetWindowText(ch, textBuf, cap);
18
+ var txt = WCHAR.decode(textBuf).slice(0, 16);
19
+ console.log(indent + '[' + n + '] hwnd=' + String(ch) + ' txt="' + txt + '" wr=' + JSON.stringify(RECT.decode(wr)) + ' cr=' + JSON.stringify(RECT.decode(cr)));
13
20
  dumpChild(ch, indent + ' ');
14
- ch = gui.GetWindow(ch, 2 /* GetWindowCmd.NEXT */);
21
+ ch = GetWindow(ch, 2 /* GetWindowCmd.NEXT */);
15
22
  }
16
23
  if (n === 0)
17
24
  console.log(indent + '(no children)');
package/lib/dumpRects.ts CHANGED
@@ -1,19 +1,27 @@
1
1
  import * as os from 'os'
2
2
  import * as gui from 'gui'
3
3
  import { GetWindowCmd } from '../constants'
4
+ import { GetClientRect, GetWindow, GetWindowRect, GetWindowText, GetWindowTextLength } from '../lib/windows/user32.js'
5
+ import { WCHAR } from '../lib/ffi/bind.js'
6
+ import { RECT } from '../lib/windows/structs.js'
4
7
 
5
8
  export function startDumpRects(rootHwnd: gui.HWND, intervalMs: number = 3000): void {
6
9
  function dumpChild(parent: gui.HWND, indent: string): void {
7
- var ch = gui.GetWindow(parent, GetWindowCmd.CHILD)
10
+ var ch = GetWindow(parent, GetWindowCmd.CHILD)
8
11
  var n = 0
9
12
  while (ch) {
10
13
  n++
11
- var wr = gui.GetWindowRect(ch)
12
- var cr = gui.GetClientRect(ch)
13
- var txt = String(gui.GetWindowText(ch) || '').slice(0, 16)
14
- console.log(indent + '[' + n + '] hwnd=' + String(ch) + ' txt="' + txt + '" wr=' + JSON.stringify(wr) + ' cr=' + JSON.stringify(cr))
14
+ const wr = RECT.alloc()
15
+ const cr = RECT.alloc()
16
+ GetWindowRect(ch, wr.ptr)
17
+ GetClientRect(ch, cr.ptr)
18
+ const cap = GetWindowTextLength(ch) + 1
19
+ const textBuf = WCHAR.alloc(cap)
20
+ GetWindowText(ch, textBuf, cap)
21
+ var txt = WCHAR.decode(textBuf).slice(0, 16)
22
+ console.log(indent + '[' + n + '] hwnd=' + String(ch) + ' txt="' + txt + '" wr=' + JSON.stringify(RECT.decode(wr)) + ' cr=' + JSON.stringify(RECT.decode(cr)))
15
23
  dumpChild(ch, indent + ' ')
16
- ch = gui.GetWindow(ch, GetWindowCmd.NEXT)
24
+ ch = GetWindow(ch, GetWindowCmd.NEXT)
17
25
  }
18
26
  if (n === 0) console.log(indent + '(no children)')
19
27
  }
package/lib/ffi/bind.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as ffi from 'ffi';
2
2
  import * as os from 'os';
3
3
  import * as std from 'std';
4
- import * as win from 'win';
5
4
  import '../text-codec.js';
6
5
  import { cTokenToType, isCPtrToken, isCValToken, valName, normToken, PTR_SIZE, ptrName, readScalar, writeSlot, PtrArrayBuffer } from './ctype.js';
7
6
  // CodecMap 的值是结构形(size 只在 StructDef 上、不属 Codec 契约),统一走 unknown
@@ -14,7 +13,7 @@ function loadDll(dll) {
14
13
  const cached = _dllCache.get(dll);
15
14
  if (cached !== undefined)
16
15
  return cached;
17
- const loaded = win.LoadLibrary(dll);
16
+ const loaded = ffi.LoadLibrary(dll);
18
17
  if (!loaded)
19
18
  throw new Error(`ffi-bind: LoadLibrary("${dll}") failed`);
20
19
  _dllCache.set(dll, loaded);
@@ -55,6 +54,8 @@ function parseSig(sig) {
55
54
  sig,
56
55
  };
57
56
  }
57
+ /** proc 是 GetProcAddress 拿到的函数地址(<FARPROC>ptr):调用方已校验非空,故收窄后的
58
+ * Ptr 而非 MaybePtr;品牌只在绑定层内部走,落到 ffiCall 的 uintptr 槽(number)时脱落。 */
58
59
  function makeFn(proc, sig, encoders, decoders) {
59
60
  const ps = parseSig(sig);
60
61
  // 按值位的能力校验前移到绑定期(模块加载即炸,不留到首个调用):布局缺位 / 非结构
@@ -296,7 +297,7 @@ const builtinCodecs = { WCHAR, BYTE };
296
297
  * 只需传一次(与运行时 `decoders ?? encoders` 同构:都只在实参缺省时生效,
297
298
  * 显式传空对象不 fallback)。内建 WCHAR/BYTE 无条件参与两张表。 */
298
299
  export function bind(dll, name, sig, encoders, decoders) {
299
- const proc = win.GetProcAddress(loadDll(dll), name);
300
+ const proc = ffi.GetProcAddress(loadDll(dll), name);
300
301
  if (!proc)
301
302
  throw new Error(`ffi-bind: proc "${name}" not found in ${dll}`);
302
303
  const effDec = decoders ?? encoders;
@@ -310,7 +311,7 @@ export function bindLib(dll, map, encoders, decoders) {
310
311
  const encTable = { ...builtinCodecs, ...encoders };
311
312
  const decTable = { ...builtinCodecs, ...(effDec ?? {}) };
312
313
  for (const [name, sig] of Object.entries(map)) {
313
- const proc = win.GetProcAddress(h, name);
314
+ const proc = ffi.GetProcAddress(h, name);
314
315
  if (!proc)
315
316
  throw new Error(`ffi-bind: proc "${name}" not found in ${dll}`);
316
317
  out[name] = makeFn(proc, sig, encTable, decTable);