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.
- package/README.en.md +1 -2
- package/README.md +1 -2
- package/constants.ts +22 -0
- package/enum_map.d.ts +4 -0
- package/examples/PdfCanvas.js +1 -1
- package/examples/PdfCanvas.tsx +2 -2
- package/examples/execd/main.js +491 -217
- 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/http-server.js +50 -25
- package/lib/http-server.ts +51 -23
- 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/main.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { CreateWindow as CreateWindowWithoutScale, DefWindowProc, RegisterClass, SendMessage, SetWindowProc, } from "gui";
|
|
2
|
+
// Win32 直通:CallWindowProc / GetWindowLongPtr / GetWindowText 无引擎状态,
|
|
3
|
+
// 已从 gui 模块摘除,走 lib/windows 的 FFI 绑定
|
|
4
|
+
import { CallWindowProc, GetWindowLongPtr, GetWindowText, GetWindowTextLength, ShowWindow } from "./lib/windows/user32.js";
|
|
5
|
+
import { WCHAR } from "./lib/ffi/bind.js";
|
|
6
|
+
import { createSystemDpiFont, getScaleFactor } from "./lib/dpi.js";
|
|
7
|
+
const scaleFactor = getScaleFactor();
|
|
8
|
+
const dpiFont = createSystemDpiFont();
|
|
4
9
|
if (!dpiFont)
|
|
5
10
|
throw new Error('Failed to create DPI font');
|
|
6
11
|
const CreateWindow = (className, title, style, x, y, cx, cy, parent, id) => {
|
|
@@ -26,7 +31,10 @@ function wndProc(hwnd, msg, wParam, lParam) {
|
|
|
26
31
|
return 0;
|
|
27
32
|
if (!list)
|
|
28
33
|
return 0;
|
|
29
|
-
const
|
|
34
|
+
const cap = GetWindowTextLength(edit) + 1;
|
|
35
|
+
const buf = WCHAR.alloc(cap);
|
|
36
|
+
GetWindowText(edit, buf, cap);
|
|
37
|
+
const text = WCHAR.decode(buf);
|
|
30
38
|
if (text.trim())
|
|
31
39
|
SendMessage(list, 384 /* LbMsg.ADDSTRING */, 0, text);
|
|
32
40
|
return 0;
|
|
@@ -43,4 +51,4 @@ function wndProc(hwnd, msg, wParam, lParam) {
|
|
|
43
51
|
RegisterClass("MainWindow", wndProc);
|
|
44
52
|
var hwnd = CreateWindow("MainWindow", "Todo List", 13565952 /* WindowStyle.OVERLAPPEDWINDOW */, 0, 0, 600, 400, null, null);
|
|
45
53
|
if (hwnd)
|
|
46
|
-
ShowWindow(hwnd);
|
|
54
|
+
ShowWindow(hwnd, 1 /* ShowWindowCmd.SHOWNORMAL */);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "quickwin",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
5
|
"description": "QuickJS Win32 runtime — GUI, networking, WASM, FFI, and more",
|
|
6
6
|
"bin": {
|
|
7
7
|
"quickwin": "qwin.exe",
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"lib/",
|
|
23
23
|
"test/",
|
|
24
24
|
"examples/",
|
|
25
|
-
"vendor/",
|
|
26
25
|
"quickwin.d.ts",
|
|
27
26
|
"constants.ts",
|
|
28
27
|
"enum_map.d.ts",
|
|
@@ -36,6 +35,7 @@
|
|
|
36
35
|
"url": "git+https://github.com/quickwinlabs/quickwin.git"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
38
|
+
"@quickwinlabs/mupdf-wasm": "^1.28.0",
|
|
39
39
|
"@types/node": "^24.13.2",
|
|
40
40
|
"@types/react": "^19.2.17",
|
|
41
41
|
"@types/react-reconciler": "^0.33.0",
|
package/quickwin.d.ts
CHANGED
|
@@ -421,96 +421,23 @@ declare module "wolfssl" {
|
|
|
421
421
|
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
-
declare module "win" {
|
|
425
|
-
/** 与 ffi 的 Ptr<"HMODULE"> 同型(ptrBrand string 键)——FFI 侧 <HMODULE>ptr 位(HINSTANCE 语义)直收 */
|
|
426
|
-
type HMODULE = number & { readonly ptrBrand: "HMODULE" };
|
|
427
|
-
|
|
428
|
-
function LoadLibrary(libName: string): HMODULE | null;
|
|
429
|
-
function GetProcAddress(hModule: HMODULE, procName: string): number | null;
|
|
430
|
-
function FreeLibrary(hModule: HMODULE): boolean;
|
|
431
|
-
/** GetModuleFileName(hModule?) — 传 HMODULE 或不传(当前进程),返回模块完整路径;失败返回 undefined */
|
|
432
|
-
function GetModuleFileName(hModule?: HMODULE): string | undefined;
|
|
433
|
-
/** GetModuleHandle(moduleName?) — 不传返回当前 exe 的模块句柄;失败返回 null */
|
|
434
|
-
function GetModuleHandle(moduleName?: string): HMODULE | null;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
424
|
declare module "gui" {
|
|
438
425
|
/** 与 ffi 的 Ptr<"N"> 结构同型(ptrBrand string 键,见 lib/ffi/ctype.ts)——各句柄可在
|
|
439
426
|
* gui 模块与 FFI 绑定两体系间互认直传(原 unique symbol 键只有 HWND 打通) */
|
|
440
427
|
type HWND = number & { readonly ptrBrand: "HWND" };
|
|
441
428
|
type HMENU = number & { readonly ptrBrand: "HMENU" };
|
|
442
|
-
type HFONT = number & { readonly ptrBrand: "HFONT" };
|
|
443
429
|
type HICON = number & { readonly ptrBrand: "HICON" };
|
|
444
|
-
|
|
445
|
-
//
|
|
446
|
-
// 使用方请直接 import { WindowStyle } from 'quickwin/constants'。
|
|
447
|
-
// Gwlp/ImageType 的成员访问(如 Gwlp.WNDPROC)需写 import('./constants').Gwlp.WNDPROC
|
|
448
|
-
// ——类型别名不建命名空间,无法穿透取成员。
|
|
449
|
-
type GetWindowCmd = import('./constants').GetWindowCmd;
|
|
450
|
-
type ShowWindowCmd = import('./constants').ShowWindowCmd;
|
|
451
|
-
type SetWindowPosHwnd = import('./constants').SetWindowPosHwnd;
|
|
452
|
-
type Gwlp = import('./constants').Gwlp;
|
|
453
|
-
type NotifyIconCmd = import('./constants').NotifyIconCmd;
|
|
454
|
-
type ImageType = import('./constants').ImageType;
|
|
455
|
-
type IDI = import('./constants').IDI;
|
|
456
|
-
type ScrollBar = import('./constants').ScrollBar;
|
|
430
|
+
// 枚举唯一定义在 constants.ts(真实 .ts,tsc/esbuild 均内联);本模块不再转出枚举
|
|
431
|
+
// 别名与 RECT 等结构类型——Win32 直通请走 lib/windows(含常量与结构定义)
|
|
457
432
|
|
|
458
433
|
function RegisterClass(className: string, wndProc?: (hwnd: HWND, msg: number, wParam: number, lParam: number) => number): number;
|
|
459
434
|
function CreateWindow(className: string, title: string, style: number, x: number, y: number, width: number, height: number, parent: HWND | null, menu: HMENU | null): HWND | null;
|
|
460
435
|
// 销毁窗口及其所有子窗口,自动清理 WNDPROC 和 JS 引用
|
|
461
436
|
function DestroyWindow(hwnd: HWND): boolean;
|
|
462
|
-
function GetWindow(hwnd: HWND, cmd: GetWindowCmd): HWND;
|
|
463
|
-
|
|
464
|
-
function ShowWindow(hwnd: HWND, nCmdShow?: ShowWindowCmd): void;
|
|
465
437
|
function SetWindowProc(hwnd: HWND, wndProc: (hwnd: HWND, msg: number, wParam: number, lParam: number) => number): void;
|
|
466
438
|
function DefWindowProc(hwnd: HWND, msg: number, wParam: number, lParam: number): number;
|
|
467
|
-
function PostQuitMessage(exitCode: number): void;
|
|
468
439
|
function SendMessage(hwnd: HWND, msg: number, wParam: number, lParam: number | string): number;
|
|
469
|
-
function MessageBox(message: string): void;
|
|
470
|
-
function SetWindowText(hwnd: HWND, text: string): void;
|
|
471
|
-
function GetWindowText(hwnd: HWND): string;
|
|
472
|
-
function GetScaleFactor(): number;
|
|
473
|
-
function CreateSystemDpiFont(): HFONT | null;
|
|
474
|
-
type GwlpReturnType<T extends Gwlp> = T extends import('./constants').Gwlp.WNDPROC ? WNDPROC : number;
|
|
475
|
-
function GetWindowLongPtr<T extends Gwlp>(hwnd: HWND, nIndex: T): GwlpReturnType<T>;
|
|
476
|
-
function SetWindowLongPtr(hwnd: HWND, nIndex: Gwlp, newLong: number): number;
|
|
477
440
|
function UnsetWindowProc(hwnd: HWND): boolean;
|
|
478
|
-
function CallWindowProc(wndProc: WNDPROC, hwnd: HWND, msg: number, wParam: number, lParam: number): number;
|
|
479
|
-
function SetParent(hwnd: HWND, parent: HWND | null): void;
|
|
480
|
-
function EnableWindow(hwnd: HWND, enable: boolean): void;
|
|
481
|
-
function SetWindowPos(hwnd: HWND, insertAfter: SetWindowPosHwnd, x: number, y: number, width: number, height: number, flags: number): void;
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
interface NotifyIconData {
|
|
486
|
-
hwnd: HWND
|
|
487
|
-
uID?: number
|
|
488
|
-
flags?: number
|
|
489
|
-
callbackMessage?: number
|
|
490
|
-
hIcon?: HICON
|
|
491
|
-
tip?: string
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
function ShellNotifyIcon(cmd: NotifyIconCmd, nid: NotifyIconData): boolean;
|
|
495
|
-
/**
|
|
496
|
-
* Thin wrapper around LoadImageW. Parameter order matches Win32 LoadImageW: (hinst, name, uType?, cx?, cy?, fuLoad?).
|
|
497
|
-
* hinst: null for system resources/file path, number for module handle;
|
|
498
|
-
* name: when hinst=null, a system icon IDI or .ico file path; when hinst=module handle, a resource ID (number) or resource name (string);
|
|
499
|
-
* uType: ImageType constant, default IMAGE_ICON (returns HICON), pass BITMAP/CURSOR for number return;
|
|
500
|
-
* cx/cy: target size (0 for default, use LoadImageFlag.DEFAULTSIZE for system default size);
|
|
501
|
-
* fuLoad: LoadImageFlag constants. Note: loading system icons (IDI_*, hinst=null) requires LoadImageFlag.SHARED.
|
|
502
|
-
* Example: import { IDI, ImageType, LoadImageFlag } from 'quickwin/constants'
|
|
503
|
-
* gui.LoadImage(null, "app.ico", ImageType.ICON, 32, 32, LoadImageFlag.LOADFROMFILE)
|
|
504
|
-
* gui.LoadImage(null, IDI.APPLICATION, ImageType.ICON, 0, 0, LoadImageFlag.SHARED)
|
|
505
|
-
*/
|
|
506
|
-
function LoadImage<H extends (import("win").HMODULE) | null, T extends ImageType = import('./constants').ImageType.ICON>(
|
|
507
|
-
hinst: H,
|
|
508
|
-
name: H extends null ? string | IDI : string | number,
|
|
509
|
-
uType?: T,
|
|
510
|
-
cx?: number,
|
|
511
|
-
cy?: number,
|
|
512
|
-
fuLoad?: number
|
|
513
|
-
): T extends import('./constants').ImageType.ICON ? HICON | null : number | null;
|
|
514
441
|
/**
|
|
515
442
|
* Loads the first icon group (RT_GROUP_ICON) from this exe, i.e. the custom icon
|
|
516
443
|
* embedded by rcedit. Works with any resource ID including 0. Returns null if the
|
|
@@ -519,46 +446,6 @@ declare module "gui" {
|
|
|
519
446
|
* Example: gui.LoadAppIcon() → HICON of the exe's icon.
|
|
520
447
|
*/
|
|
521
448
|
function LoadAppIcon(cx?: number, cy?: number): HICON | null;
|
|
522
|
-
/** 从 BGRA 像素数据创建 32bpp top-down DIB,data 为 length >= width*height*4 的 ArrayBuffer,返回 HBITMAP */
|
|
523
|
-
function CreateBitmapFromPixels(width: number, height: number, data: ArrayBuffer): number | null;
|
|
524
|
-
function DeleteObject(hObject: number): boolean;
|
|
525
|
-
function ImageListCreate(cx: number, cy: number, flags?: number, initial?: number, grow?: number): number | null;
|
|
526
|
-
function ImageListAdd(himl: number, hBitmap: number): number;
|
|
527
|
-
function ImageListDestroy(himl: number): boolean;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
function CreatePopupMenu(): HMENU | null;
|
|
531
|
-
function AppendMenu(menu: HMENU, flags: number, id: number, text: string): boolean;
|
|
532
|
-
function TrackPopupMenu(menu: HMENU, x: number, y: number, flags?: number, hwnd?: HWND): number;
|
|
533
|
-
function DestroyMenu(menu: HMENU): boolean;
|
|
534
|
-
function SetForegroundWindow(hwnd: HWND): boolean;
|
|
535
|
-
/** Returns [x, y] or null */
|
|
536
|
-
function GetCursorPos(): [number, number] | null;
|
|
537
|
-
/** Returns [width, height] of the primary monitor */
|
|
538
|
-
function GetScreenSize(): [number, number];
|
|
539
|
-
/** Client-area/mouse rect: { left, top, right, bottom } */
|
|
540
|
-
type RECT = { left: number; top: number; right: number; bottom: number };
|
|
541
|
-
/** Returns { left, top, right, bottom } of client area, or null if invalid */
|
|
542
|
-
function GetClientRect(hwnd: HWND): RECT | null;
|
|
543
|
-
|
|
544
|
-
function GetWindowRect(hwnd: HWND): RECT | null;
|
|
545
|
-
/** Invalidates client area (rect can be null/undefined for full window) */
|
|
546
|
-
function InvalidateRect(hwnd: HWND, rect?: RECT | null, erase?: boolean): void;
|
|
547
|
-
/** Forces immediate repaint of invalidated area */
|
|
548
|
-
function UpdateWindow(hwnd: HWND): void;
|
|
549
|
-
/** Checks if the window handle is valid */
|
|
550
|
-
function IsWindow(hwnd: HWND): boolean;
|
|
551
|
-
/** Sets scroll info; returns current scroll box position. Signature matches Win32 SetScrollInfo. */
|
|
552
|
-
function SetScrollInfo(hwnd: HWND, bar: ScrollBar, info: { pos?: number; page?: number; min?: number; max?: number }, redraw?: boolean): number;
|
|
553
|
-
|
|
554
|
-
/** Gets scroll info. Signature matches Win32 GetScrollInfo. */
|
|
555
|
-
function GetScrollInfo(hwnd: HWND, bar: ScrollBar): { pos: number; page: number; min: number; max: number; trackPos: number };
|
|
556
|
-
|
|
557
|
-
/** Shows or hides a scroll bar. Signature matches Win32 ShowScrollBar. */
|
|
558
|
-
function ShowScrollBar(hwnd: HWND, bar: ScrollBar, show: boolean): boolean;
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
449
|
|
|
563
450
|
|
|
564
451
|
|
|
@@ -585,6 +472,12 @@ declare module "gui" {
|
|
|
585
472
|
}
|
|
586
473
|
|
|
587
474
|
declare module "ffi" {
|
|
475
|
+
/** 指针品牌唯一定义在 lib/ffi/ctype.ts(此处只转发,不手抄结构)——本模块返回的
|
|
476
|
+
* 句柄 / 地址即对应 C 侧指针位,空值形态同 FFI 层:NULL(0),不是 JS null。 */
|
|
477
|
+
type Ptr<N extends string> = import('./lib/ffi/ctype').Ptr<N>;
|
|
478
|
+
type MaybePtr<N extends string> = import('./lib/ffi/ctype').MaybePtr<N>;
|
|
479
|
+
type HMODULE = Ptr<"HMODULE">;
|
|
480
|
+
|
|
588
481
|
function ffiCall(func: number, argFrame: ArrayBuffer, retBuf: ArrayBuffer, retIsFp: 0 | 1): void;
|
|
589
482
|
function bufferPtr(buf: ArrayBuffer): number;
|
|
590
483
|
function readByte(ptr: number): number;
|
|
@@ -592,6 +485,11 @@ declare module "ffi" {
|
|
|
592
485
|
function writeByte(ptr: number, value: number): void;
|
|
593
486
|
function closureNew(argBytes: number, retKind: number, wrapper: (frame: ArrayBuffer, retBuf: ArrayBuffer) => void): number;
|
|
594
487
|
function closureFree(ptr: number): void;
|
|
488
|
+
/** LoadLibraryW 直映:FFI 自举入口(ffi 自身没有 dlopen,取符号必先装载),
|
|
489
|
+
* 成功为模块句柄 <HMODULE>ptr(HINSTANCE 语义),失败为空指针(0);按真值收窄。 */
|
|
490
|
+
function LoadLibrary(libName: string): MaybePtr<"HMODULE">;
|
|
491
|
+
/** GetProcAddress 直映:成功为函数地址 <FARPROC>ptr,失败为空指针(0)。 */
|
|
492
|
+
function GetProcAddress(hModule: HMODULE, procName: string): MaybePtr<"FARPROC">;
|
|
595
493
|
}
|
|
596
494
|
|
|
597
495
|
|
package/qwin-nowasm-x86.exe
CHANGED
|
Binary file
|
package/qwin-nowasm.exe
CHANGED
|
Binary file
|
package/qwin-x86.exe
CHANGED
|
Binary file
|
package/qwin.exe
CHANGED
|
Binary file
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
// 结束必须 DestroyWindow + std.exit。
|
|
14
14
|
import * as std from 'std';
|
|
15
15
|
import * as gui from 'gui';
|
|
16
|
-
import * as win from 'win';
|
|
17
16
|
import * as ffi from 'ffi';
|
|
18
17
|
import { bind } from '../lib/ffi/bind.js';
|
|
19
18
|
const WM_NULL = 0x0000;
|
|
@@ -37,11 +36,11 @@ function step(msg) {
|
|
|
37
36
|
step('stage: imports ok');
|
|
38
37
|
// ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
|
|
39
38
|
function procOf(dll, name) {
|
|
40
|
-
const h =
|
|
39
|
+
const h = ffi.LoadLibrary(dll);
|
|
41
40
|
if (!h)
|
|
42
41
|
throw new Error('LoadLibrary(' + dll + ') failed');
|
|
43
|
-
const p =
|
|
44
|
-
if (p
|
|
42
|
+
const p = ffi.GetProcAddress(h, name);
|
|
43
|
+
if (!p)
|
|
45
44
|
throw new Error('GetProcAddress(' + dll + '!' + name + ') failed');
|
|
46
45
|
return p;
|
|
47
46
|
}
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
// 结束必须 DestroyWindow + std.exit。
|
|
14
14
|
import * as std from 'std'
|
|
15
15
|
import * as gui from 'gui'
|
|
16
|
-
import * as win from 'win'
|
|
17
16
|
import * as ffi from 'ffi'
|
|
18
17
|
import { bind } from '../lib/ffi/bind.js'
|
|
19
18
|
|
|
@@ -39,10 +38,10 @@ step('stage: imports ok')
|
|
|
39
38
|
|
|
40
39
|
// ---- proc 地址(B/C 臂绕过 bind 直驱 ffiCall)----
|
|
41
40
|
function procOf(dll: string, name: string): number {
|
|
42
|
-
const h =
|
|
41
|
+
const h = ffi.LoadLibrary(dll)
|
|
43
42
|
if (!h) throw new Error('LoadLibrary(' + dll + ') failed')
|
|
44
|
-
const p =
|
|
45
|
-
if (p
|
|
43
|
+
const p = ffi.GetProcAddress(h, name)
|
|
44
|
+
if (!p) throw new Error('GetProcAddress(' + dll + '!' + name + ') failed')
|
|
46
45
|
return p
|
|
47
46
|
}
|
|
48
47
|
const pNop0 = procOf('test/nop.dll', 'nop0')
|
|
@@ -1,25 +1,7 @@
|
|
|
1
1
|
import * as std from 'std';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
throw new Error('Cannot open: ' + path);
|
|
6
|
-
fp.seek(0, 2);
|
|
7
|
-
const size = fp.tell();
|
|
8
|
-
fp.seek(0, 0);
|
|
9
|
-
const buffer = new ArrayBuffer(size);
|
|
10
|
-
fp.read(buffer, 0, size);
|
|
11
|
-
fp.close();
|
|
12
|
-
return buffer;
|
|
13
|
-
}
|
|
14
|
-
function setupMupdfModule(wasmPath) {
|
|
15
|
-
const wasmBinary = loadWasmBytes(wasmPath);
|
|
16
|
-
globalThis["$libmupdf_wasm_Module"] = {
|
|
17
|
-
wasmBinary,
|
|
18
|
-
locateFile: (p) => p
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
setupMupdfModule('./vendor/mupdf-wasm/mupdf-wasm.wasm');
|
|
22
|
-
await import('../../vendor/mupdf-wasm/mupdf.js');
|
|
2
|
+
import { MUPDF_JS_URL, preloadMupdfWasm } from '../mupdf-cdn.js';
|
|
3
|
+
await preloadMupdfWasm();
|
|
4
|
+
await import(MUPDF_JS_URL);
|
|
23
5
|
// Debug the instance exports
|
|
24
6
|
const inst = globalThis["$wasm_inst"];
|
|
25
7
|
const mod = globalThis["$wasm_mod"];
|
|
@@ -39,7 +21,7 @@ console.log('Sf index:', sf_idx, 'Tf index:', tf_idx);
|
|
|
39
21
|
console.log('instance exports Sf:', typeof inst.exports['Sf']);
|
|
40
22
|
console.log('instance exports Tf:', typeof inst.exports['Tf']);
|
|
41
23
|
// Load a simple WASM to verify bridge works
|
|
42
|
-
const fp = std.open('./example.pdf', 'rb');
|
|
24
|
+
const fp = std.open('./test/example.pdf', 'rb');
|
|
43
25
|
if (!fp) {
|
|
44
26
|
console.log('cannot open example.pdf');
|
|
45
27
|
std.exit(1);
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
const
|
|
3
|
-
if (!fp) {
|
|
4
|
-
console.log('cannot open vendor/mupdf-wasm/mupdf-wasm.wasm');
|
|
5
|
-
std.exit(1);
|
|
6
|
-
}
|
|
7
|
-
fp.seek(0, 2);
|
|
8
|
-
const size = fp.tell();
|
|
9
|
-
fp.seek(0, 0);
|
|
10
|
-
const buf = new ArrayBuffer(size);
|
|
11
|
-
fp.read(buf, 0, size);
|
|
12
|
-
fp.close();
|
|
1
|
+
import { fetchMupdfWasm } from '../mupdf-cdn.js';
|
|
2
|
+
const buf = await fetchMupdfWasm();
|
|
13
3
|
const module = new WebAssembly.Module(buf);
|
|
14
4
|
const exports = WebAssembly.Module.exports(module);
|
|
15
5
|
const imports = WebAssembly.Module.imports(module);
|
package/test/debug/wasm_names.js
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
//
|
|
3
|
-
const
|
|
4
|
-
if (!fp) {
|
|
5
|
-
console.log('cannot open vendor/mupdf-wasm/mupdf-wasm.wasm');
|
|
6
|
-
std.exit(1);
|
|
7
|
-
}
|
|
8
|
-
fp.seek(0, 2);
|
|
9
|
-
const size = fp.tell();
|
|
10
|
-
fp.seek(0, 0);
|
|
11
|
-
const buf = new ArrayBuffer(size);
|
|
12
|
-
fp.read(buf, 0, size);
|
|
13
|
-
fp.close();
|
|
1
|
+
import { fetchMupdfWasm } from '../mupdf-cdn.js';
|
|
2
|
+
// Fetch the mupdf-wasm binary over HTTPS (process-wide HTTP cache applies)
|
|
3
|
+
const buf = await fetchMupdfWasm();
|
|
14
4
|
// Check exports for Tf
|
|
15
5
|
const module = new WebAssembly.Module(buf);
|
|
16
6
|
const exports = WebAssembly.Module.exports(module);
|
package/test/example.pdf
ADDED
|
Binary file
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// ── mupdf-wasm CDN loading ──
|
|
2
|
+
//
|
|
3
|
+
// The runtime loads mupdf from unpkg; the npm package itself only ships
|
|
4
|
+
// types (devDependency @quickwinlabs/mupdf-wasm resolved through the
|
|
5
|
+
// package's exports types mapping — no local vendor copy involved).
|
|
6
|
+
// MUPDF_VERSION must stay in sync with the devDependency in package.json
|
|
7
|
+
// when bumping.
|
|
8
|
+
//
|
|
9
|
+
// Importing this module wires up the HTTPS stack (certs + fetch): the
|
|
10
|
+
// wasm bytes travel through the process-wide HTTP cache next to the
|
|
11
|
+
// executable, so the first fetch is the only network hit.
|
|
12
|
+
import '../lib/certs.js';
|
|
13
|
+
import '../lib/fetch.js';
|
|
14
|
+
export const MUPDF_VERSION = '1.28.0';
|
|
15
|
+
const BASE = `https://unpkg.com/@quickwinlabs/mupdf-wasm@${MUPDF_VERSION}`;
|
|
16
|
+
export const MUPDF_JS_URL = `${BASE}/mupdf.js`;
|
|
17
|
+
export const MUPDF_WASM_URL = `${BASE}/mupdf-wasm.wasm`;
|
|
18
|
+
/** Fetch the wasm binary over HTTPS (HTTP-cached, no filesystem involved). */
|
|
19
|
+
export async function fetchMupdfWasm() {
|
|
20
|
+
const resp = await fetch(MUPDF_WASM_URL);
|
|
21
|
+
if (resp.status !== 200)
|
|
22
|
+
throw new Error(`mupdf wasm fetch failed: HTTP ${resp.status}`);
|
|
23
|
+
const wasmBinary = await resp.arrayBuffer();
|
|
24
|
+
if (wasmBinary.byteLength < 1_000_000)
|
|
25
|
+
throw new Error(`mupdf wasm truncated: ${wasmBinary.byteLength} bytes`);
|
|
26
|
+
return wasmBinary;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Fetch the wasm binary and pre-wire it as $libmupdf_wasm_Module so the
|
|
30
|
+
* mupdf.js module factory instantiates from memory (same injection the
|
|
31
|
+
* local-vendor loading used).
|
|
32
|
+
*/
|
|
33
|
+
export async function preloadMupdfWasm() {
|
|
34
|
+
const wasmBinary = await fetchMupdfWasm();
|
|
35
|
+
globalThis.$libmupdf_wasm_Module = {
|
|
36
|
+
wasmBinary,
|
|
37
|
+
locateFile: (p) => p,
|
|
38
|
+
};
|
|
39
|
+
return wasmBinary;
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ── mupdf-wasm CDN loading ──
|
|
2
|
+
//
|
|
3
|
+
// The runtime loads mupdf from unpkg; the npm package itself only ships
|
|
4
|
+
// types (devDependency @quickwinlabs/mupdf-wasm resolved through the
|
|
5
|
+
// package's exports types mapping — no local vendor copy involved).
|
|
6
|
+
// MUPDF_VERSION must stay in sync with the devDependency in package.json
|
|
7
|
+
// when bumping.
|
|
8
|
+
//
|
|
9
|
+
// Importing this module wires up the HTTPS stack (certs + fetch): the
|
|
10
|
+
// wasm bytes travel through the process-wide HTTP cache next to the
|
|
11
|
+
// executable, so the first fetch is the only network hit.
|
|
12
|
+
|
|
13
|
+
import '../lib/certs.js'
|
|
14
|
+
import '../lib/fetch.js'
|
|
15
|
+
|
|
16
|
+
export const MUPDF_VERSION = '1.28.0'
|
|
17
|
+
const BASE = `https://unpkg.com/@quickwinlabs/mupdf-wasm@${MUPDF_VERSION}`
|
|
18
|
+
|
|
19
|
+
export const MUPDF_JS_URL = `${BASE}/mupdf.js`
|
|
20
|
+
export const MUPDF_WASM_URL = `${BASE}/mupdf-wasm.wasm`
|
|
21
|
+
|
|
22
|
+
/** Fetch the wasm binary over HTTPS (HTTP-cached, no filesystem involved). */
|
|
23
|
+
export async function fetchMupdfWasm(): Promise<ArrayBuffer> {
|
|
24
|
+
const resp = await fetch(MUPDF_WASM_URL)
|
|
25
|
+
if (resp.status !== 200) throw new Error(`mupdf wasm fetch failed: HTTP ${resp.status}`)
|
|
26
|
+
const wasmBinary = await resp.arrayBuffer()
|
|
27
|
+
if (wasmBinary.byteLength < 1_000_000) throw new Error(`mupdf wasm truncated: ${wasmBinary.byteLength} bytes`)
|
|
28
|
+
return wasmBinary
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fetch the wasm binary and pre-wire it as $libmupdf_wasm_Module so the
|
|
33
|
+
* mupdf.js module factory instantiates from memory (same injection the
|
|
34
|
+
* local-vendor loading used).
|
|
35
|
+
*/
|
|
36
|
+
export async function preloadMupdfWasm(): Promise<ArrayBuffer> {
|
|
37
|
+
const wasmBinary = await fetchMupdfWasm()
|
|
38
|
+
;(globalThis as any).$libmupdf_wasm_Module = {
|
|
39
|
+
wasmBinary,
|
|
40
|
+
locateFile: (p: string) => p,
|
|
41
|
+
}
|
|
42
|
+
return wasmBinary
|
|
43
|
+
}
|
package/test/test_ffi.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as std from 'std';
|
|
2
|
-
import * as win from 'win';
|
|
3
2
|
import * as ffi from 'ffi';
|
|
4
3
|
import * as os from 'os';
|
|
5
4
|
import { bind, bindLib } from '../lib/ffi/bind.js';
|
|
@@ -72,12 +71,12 @@ export const suite = {
|
|
|
72
71
|
name: 'ffi',
|
|
73
72
|
run: (t) => {
|
|
74
73
|
t.section('EnumPrinters');
|
|
75
|
-
const hWinspool =
|
|
76
|
-
t.checkTrue('LoadLibrary("winspool.drv") succeeds', hWinspool
|
|
74
|
+
const hWinspool = ffi.LoadLibrary('winspool.drv');
|
|
75
|
+
t.checkTrue('LoadLibrary("winspool.drv") succeeds', !!hWinspool);
|
|
77
76
|
if (!hWinspool)
|
|
78
77
|
return;
|
|
79
|
-
const enumPrinters =
|
|
80
|
-
t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', enumPrinters
|
|
78
|
+
const enumPrinters = ffi.GetProcAddress(hWinspool, 'EnumPrintersW');
|
|
79
|
+
t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', !!enumPrinters);
|
|
81
80
|
if (!enumPrinters)
|
|
82
81
|
return;
|
|
83
82
|
const { EnumPrintersW: EnumPrinters } = bindLib('winspool.drv', {
|
|
@@ -141,11 +140,11 @@ export const suite = {
|
|
|
141
140
|
std.printf(' no printers installed (skip)\n');
|
|
142
141
|
}
|
|
143
142
|
t.section('PTR accepts raw number handles (XP 32-bit fix)');
|
|
144
|
-
const hUser32 =
|
|
145
|
-
t.checkTrue('LoadLibrary("user32.dll") succeeds', hUser32
|
|
143
|
+
const hUser32 = ffi.LoadLibrary('user32.dll');
|
|
144
|
+
t.checkTrue('LoadLibrary("user32.dll") succeeds', !!hUser32);
|
|
146
145
|
if (hUser32) {
|
|
147
|
-
const getDC =
|
|
148
|
-
t.checkTrue('GetProcAddress("GetDC") succeeds', getDC
|
|
146
|
+
const getDC = ffi.GetProcAddress(hUser32, 'GetDC');
|
|
147
|
+
t.checkTrue('GetProcAddress("GetDC") succeeds', !!getDC);
|
|
149
148
|
if (getDC) {
|
|
150
149
|
const GetDC = bind('user32.dll', 'GetDC', '<>ptr -> <>ptr');
|
|
151
150
|
const screenDc = GetDC(0);
|
package/test/test_ffi.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as std from 'std'
|
|
2
|
-
import * as win from 'win'
|
|
3
2
|
import * as ffi from 'ffi'
|
|
4
3
|
import * as os from 'os'
|
|
5
4
|
import { bind, bindLib } from '../lib/ffi/bind.js'
|
|
@@ -75,12 +74,12 @@ export const suite = {
|
|
|
75
74
|
name: 'ffi',
|
|
76
75
|
run: (t: Tester) => {
|
|
77
76
|
t.section('EnumPrinters')
|
|
78
|
-
const hWinspool =
|
|
79
|
-
t.checkTrue('LoadLibrary("winspool.drv") succeeds', hWinspool
|
|
77
|
+
const hWinspool = ffi.LoadLibrary('winspool.drv')
|
|
78
|
+
t.checkTrue('LoadLibrary("winspool.drv") succeeds', !!hWinspool)
|
|
80
79
|
if (!hWinspool) return
|
|
81
80
|
|
|
82
|
-
const enumPrinters =
|
|
83
|
-
t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', enumPrinters
|
|
81
|
+
const enumPrinters = ffi.GetProcAddress(hWinspool, 'EnumPrintersW')
|
|
82
|
+
t.checkTrue('GetProcAddress("EnumPrintersW") succeeds', !!enumPrinters)
|
|
84
83
|
if (!enumPrinters) return
|
|
85
84
|
|
|
86
85
|
const { EnumPrintersW: EnumPrinters } = bindLib('winspool.drv', {
|
|
@@ -154,11 +153,11 @@ export const suite = {
|
|
|
154
153
|
}
|
|
155
154
|
|
|
156
155
|
t.section('PTR accepts raw number handles (XP 32-bit fix)')
|
|
157
|
-
const hUser32 =
|
|
158
|
-
t.checkTrue('LoadLibrary("user32.dll") succeeds', hUser32
|
|
156
|
+
const hUser32 = ffi.LoadLibrary('user32.dll')
|
|
157
|
+
t.checkTrue('LoadLibrary("user32.dll") succeeds', !!hUser32)
|
|
159
158
|
if (hUser32) {
|
|
160
|
-
const getDC =
|
|
161
|
-
t.checkTrue('GetProcAddress("GetDC") succeeds', getDC
|
|
159
|
+
const getDC = ffi.GetProcAddress(hUser32, 'GetDC')
|
|
160
|
+
t.checkTrue('GetProcAddress("GetDC") succeeds', !!getDC)
|
|
162
161
|
if (getDC) {
|
|
163
162
|
const GetDC = bind('user32.dll', 'GetDC', '<>ptr -> <>ptr')
|
|
164
163
|
const screenDc = GetDC(0)
|
package/test/test_ffi_abi.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as std from 'std';
|
|
2
|
-
import * as
|
|
2
|
+
import * as ffi from 'ffi';
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import { bind } from '../lib/ffi/bind.js';
|
|
5
5
|
import { struct } from '../lib/ffi/struct.js';
|
|
@@ -29,15 +29,15 @@ function readAscii(buf, len) {
|
|
|
29
29
|
return s;
|
|
30
30
|
}
|
|
31
31
|
function has(dll, name) {
|
|
32
|
-
return !!dll && !!
|
|
32
|
+
return !!dll && !!ffi.GetProcAddress(dll, name);
|
|
33
33
|
}
|
|
34
34
|
export const suite = {
|
|
35
35
|
name: 'ffi-abi',
|
|
36
36
|
run: (t) => {
|
|
37
|
-
const k32 =
|
|
38
|
-
const usr =
|
|
39
|
-
const gdi =
|
|
40
|
-
const crt =
|
|
37
|
+
const k32 = ffi.LoadLibrary('kernel32.dll');
|
|
38
|
+
const usr = ffi.LoadLibrary('user32.dll');
|
|
39
|
+
const gdi = ffi.LoadLibrary('gdi32.dll');
|
|
40
|
+
const crt = ffi.LoadLibrary('msvcrt.dll');
|
|
41
41
|
t.section('param count 0 ( -> u32)');
|
|
42
42
|
if (has(k32, 'GetTickCount')) {
|
|
43
43
|
const getTickCount = bind('kernel32.dll', 'GetTickCount', ' -> u32');
|
package/test/test_ffi_abi.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as std from 'std'
|
|
2
|
-
import * as
|
|
2
|
+
import * as ffi from 'ffi'
|
|
3
3
|
import * as os from 'os'
|
|
4
4
|
import { bind } from '../lib/ffi/bind.js'
|
|
5
5
|
import { struct } from '../lib/ffi/struct.js'
|
|
6
|
-
import { NULL, PtrArrayBuffer } from '../lib/ffi/ctype.js'
|
|
6
|
+
import { NULL, PtrArrayBuffer, type MaybePtr } from '../lib/ffi/ctype.js'
|
|
7
7
|
import { POINT, RECT } from '../lib/windows/structs.js'
|
|
8
8
|
import { Tester } from './test_helper.js'
|
|
9
9
|
|
|
@@ -31,17 +31,17 @@ function readAscii(buf: ArrayBuffer, len: number): string {
|
|
|
31
31
|
return s
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function has(dll:
|
|
35
|
-
return !!dll && !!
|
|
34
|
+
function has(dll: MaybePtr<'HMODULE'>, name: string): boolean {
|
|
35
|
+
return !!dll && !!ffi.GetProcAddress(dll, name)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export const suite = {
|
|
39
39
|
name: 'ffi-abi',
|
|
40
40
|
run: (t: Tester) => {
|
|
41
|
-
const k32 =
|
|
42
|
-
const usr =
|
|
43
|
-
const gdi =
|
|
44
|
-
const crt =
|
|
41
|
+
const k32 = ffi.LoadLibrary('kernel32.dll')
|
|
42
|
+
const usr = ffi.LoadLibrary('user32.dll')
|
|
43
|
+
const gdi = ffi.LoadLibrary('gdi32.dll')
|
|
44
|
+
const crt = ffi.LoadLibrary('msvcrt.dll')
|
|
45
45
|
|
|
46
46
|
t.section('param count 0 ( -> u32)')
|
|
47
47
|
if (has(k32, 'GetTickCount')) {
|