quickwin 2.3.1 → 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.
- package/README.en.md +1 -2
- package/README.md +1 -2
- package/constants.ts +15 -0
- package/enum_map.d.ts +3 -0
- package/examples/PdfCanvas.js +1 -1
- package/examples/PdfCanvas.tsx +2 -2
- package/examples/execd/main.js +334 -188
- package/examples/pdf_preview2.js +76 -67
- package/examples/pdf_preview2.ts +84 -64
- package/examples/pdf_viewer.js +4932 -1029
- package/examples/pdf_viewer.tsx +11 -22
- package/examples/test_gallery.js +308 -143
- package/examples/test_gallery.tsx +12 -7
- package/examples/test_react_lazy.js +295 -133
- package/examples/test_react_lazy.tsx +4 -3
- package/examples/test_react_listbox.js +293 -132
- package/examples/test_react_listbox.tsx +3 -2
- package/examples/test_root_window.js +283 -126
- package/examples/test_tab.js +295 -133
- package/examples/test_tab.tsx +4 -3
- package/examples/tray_demo.js +37 -30
- package/examples/tray_demo.tsx +39 -32
- package/lib/bitmap.js +40 -0
- package/lib/bitmap.ts +39 -0
- package/lib/certs.js +31 -3
- package/lib/certs.ts +32 -3
- package/lib/dpi.js +30 -0
- package/lib/dpi.ts +31 -0
- package/lib/dumpRects.js +14 -7
- package/lib/dumpRects.ts +14 -6
- package/lib/ffi/bind.js +5 -4
- package/lib/ffi/bind.ts +9 -8
- package/lib/react-qw/components/DateTimePicker.js +6 -7
- package/lib/react-qw/components/Input.js +5 -1
- package/lib/react-qw/components/ListView.js +8 -6
- package/lib/react-qw/components/ScrollView.js +28 -22
- package/lib/react-qw/index.js +7 -5
- package/lib/react-qw/index.ts +5 -5
- package/lib/react-qw/props.js +3 -3
- package/lib/react-qw/props.ts +3 -3
- package/lib/react-qw/reconciler.js +12 -17
- package/lib/react-qw/reconciler.ts +12 -16
- package/lib/text-measure.js +9 -6
- package/lib/text-measure.ts +9 -6
- package/lib/windows/comctl32.js +3 -2
- package/lib/windows/comctl32.ts +3 -2
- package/lib/windows/gdi32.js +28 -6
- package/lib/windows/gdi32.ts +30 -6
- package/lib/windows/kernel32.js +9 -0
- package/lib/windows/kernel32.ts +9 -0
- package/lib/windows/shell32.js +11 -2
- package/lib/windows/shell32.ts +13 -2
- package/lib/windows/structs.js +51 -0
- package/lib/windows/structs.ts +55 -0
- package/lib/windows/user32.js +6 -5
- package/lib/windows/user32.ts +6 -5
- package/main.js +13 -5
- package/package.json +2 -2
- package/quickwin.d.ts +13 -115
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/test/bench_ffi_callraw.js +3 -4
- package/test/bench_ffi_callraw.ts +3 -4
- package/test/debug/mupdf_debug.js +4 -22
- package/test/debug/wasm_export_debug.js +2 -12
- package/test/debug/wasm_names.js +3 -13
- package/test/example.pdf +0 -0
- package/test/mupdf-cdn.js +40 -0
- package/test/mupdf-cdn.ts +43 -0
- package/test/test_ffi.js +8 -9
- package/test/test_ffi.ts +8 -9
- package/test/test_ffi_abi.js +6 -6
- package/test/test_ffi_abi.ts +8 -8
- package/test/test_ffi_windows.js +82 -11
- package/test/test_ffi_windows.ts +85 -11
- package/test/test_gdi_draw.js +3 -3
- package/test/test_gdi_draw.ts +4 -4
- package/test/test_mupdf_render.js +4 -22
- package/test/test_mupdf_render.ts +4 -23
- package/test/test_mupdf_twice.js +4 -19
- package/test/test_mupdf_twice.ts +4 -20
- package/test/test_mupdf_wasm.js +10 -4
- package/test/test_mupdf_wasm.ts +6 -4
- package/test/test_react_complex.js +316 -140
- package/test/test_react_counter.js +295 -133
- package/test/test_react_flex.js +297 -138
- package/test/test_react_hidden.js +296 -134
- package/test/test_react_listview_link.js +288 -131
- package/test/test_react_notify.js +290 -133
- package/test/test_react_render.js +295 -133
- package/vendor/mupdf-wasm/mupdf-wasm.d.ts +0 -571
- package/vendor/mupdf-wasm/mupdf-wasm.js +0 -2749
- package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
- package/vendor/mupdf-wasm/mupdf.d.ts +0 -939
- package/vendor/mupdf-wasm/mupdf.js +0 -3317
package/examples/test_tab.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
75
|
+
ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL)
|
|
75
76
|
render(<App />, hwnd)
|
|
76
77
|
}
|
package/examples/tray_demo.js
CHANGED
|
@@ -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 ===
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
if (evt === 513 /* WmMsg.LBUTTONDOWN */) {
|
|
13
|
+
ShowWindow(hwnd, 5 /* ShowWindowCmd.SHOW */);
|
|
14
|
+
SetForegroundWindow(hwnd);
|
|
10
15
|
}
|
|
11
|
-
else if (evt ===
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
30
|
+
ShowWindow(hwnd, 5 /* ShowWindowCmd.SHOW */);
|
|
25
31
|
}
|
|
26
32
|
else if (cmd === 2) {
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
54
|
+
ShowWindow(hwnd, 1 /* ShowWindowCmd.SHOWNORMAL */);
|
|
49
55
|
// 添加说明文字
|
|
50
|
-
const
|
|
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 =
|
|
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 =
|
|
71
|
+
hIcon = LoadImageOrdinal(NULL, 32512 /* IDI.APPLICATION */, 1 /* ImageType.ICON */, 0, 0, 32768 /* LoadImageFlag.SHARED */);
|
|
66
72
|
if (hIcon) {
|
|
67
|
-
const ok =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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');
|
package/examples/tray_demo.tsx
CHANGED
|
@@ -1,39 +1,45 @@
|
|
|
1
1
|
import * as gui from 'gui'
|
|
2
2
|
import * as std from 'std'
|
|
3
|
-
import {
|
|
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 ===
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} else if (evt ===
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
32
|
+
ShowWindow(hwnd, ShowWindowCmd.SHOW)
|
|
27
33
|
} else if (cmd === 2) {
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
55
|
+
ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL)
|
|
50
56
|
|
|
51
57
|
// 添加说明文字
|
|
52
|
-
const
|
|
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 =
|
|
67
|
+
const font = createSystemDpiFont()
|
|
64
68
|
if (font)
|
|
65
69
|
gui.SendMessage(text, WmMsg.SETFONT, font, 1)
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
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 =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 (
|
|
10
|
-
// bundle uses
|
|
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
|
-
|
|
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 (
|
|
10
|
-
// bundle uses
|
|
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 =
|
|
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
|
|
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 =
|
|
7
|
+
var ch = GetWindow(parent, 5 /* GetWindowCmd.CHILD */);
|
|
6
8
|
var n = 0;
|
|
7
9
|
while (ch) {
|
|
8
10
|
n++;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 =
|
|
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 =
|
|
10
|
+
var ch = GetWindow(parent, GetWindowCmd.CHILD)
|
|
8
11
|
var n = 0
|
|
9
12
|
while (ch) {
|
|
10
13
|
n++
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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);
|