quickwin 2.1.0 → 2.2.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 +8 -8
- package/README.md +6 -7
- package/examples/{exec_server.js → execd.js} +618 -228
- package/examples/execd.tsx +798 -0
- package/examples/execd_worker.js +122 -0
- package/examples/execd_worker.ts +127 -0
- package/examples/pdf_viewer.js +100 -111
- package/examples/test_gallery.js +100 -111
- package/examples/test_react_lazy.js +96 -109
- package/examples/test_react_listbox.js +96 -109
- package/examples/test_root_window.js +96 -109
- package/examples/test_tab.js +96 -109
- package/lib/ffi/ctype.ts +3 -1
- package/lib/ffi/struct.js +23 -7
- package/lib/ffi/struct.ts +50 -23
- package/lib/http-server.js +1 -1
- package/lib/http-server.ts +1 -1
- package/lib/windows/gdi32.js +1 -1
- package/lib/windows/gdi32.ts +1 -1
- package/lib/windows/structs.js +176 -92
- package/lib/windows/structs.ts +216 -92
- package/package.json +1 -1
- package/quickwin_const.d.ts +26 -0
- 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/test_ffi_bind.js +5 -3
- package/test/test_ffi_bind.ts +6 -4
- package/test/test_ffi_struct.js +58 -24
- package/test/test_ffi_struct.ts +55 -25
- package/test/test_gdi_draw.js +1 -1
- package/test/test_gdi_draw.ts +1 -1
- package/test/test_react_complex.js +96 -109
- package/test/test_react_counter.js +96 -109
- package/test/test_react_flex.js +96 -109
- package/test/test_react_hidden.js +96 -109
- package/test/test_react_listview_link.js +96 -109
- package/test/test_react_notify.js +96 -109
- package/test/test_react_render.js +96 -109
- package/examples/exec_server.tsx +0 -489
- package/examples/exec_server_worker.js +0 -395
- package/examples/exec_server_worker.ts +0 -417
- package/examples/setres.js +0 -79
- package/examples/setres.ts +0 -86
package/lib/ffi/ctype.ts
CHANGED
|
@@ -106,7 +106,9 @@ type First =
|
|
|
106
106
|
// 对象值的唯一判别键是 '#'(多字段/聚合元素无法用糖表达,故保留)
|
|
107
107
|
// '#' 必填(无「裸嵌套 map 默认 struct」):漏写 '#' 时 union 不会静默翻转成 struct,
|
|
108
108
|
// 类型层直接报错、运行时 throw。顶层 struct()/union() 例外 —— kind 由函数名表明,
|
|
109
|
-
//
|
|
109
|
+
// 参数是平铺字段表(本身不含 '#';DECL 常量直传时 '#' 须与函数名 kind 一致 —— 参数
|
|
110
|
+
// 类型交叉 `{ '#'?: 'struct' }` / `{ '#'?: 'union' }` 编译期锁),'#'/`#pack` 由构造器
|
|
111
|
+
// 写入声明(buildDecl)。
|
|
110
112
|
// ============================================================
|
|
111
113
|
|
|
112
114
|
// token 值面:规范 token(C_Number 档)∪ 指针形(含 '!' 非空尾缀)∪ '@枚举标注形'
|
package/lib/ffi/struct.js
CHANGED
|
@@ -350,7 +350,7 @@ function doEncode(dv, base, fields, v) {
|
|
|
350
350
|
function createStruct(t) {
|
|
351
351
|
const { fields, size, maxEffectiveAlign } = computeStructLayout(t);
|
|
352
352
|
return {
|
|
353
|
-
|
|
353
|
+
fields,
|
|
354
354
|
size,
|
|
355
355
|
structAlign: maxEffectiveAlign,
|
|
356
356
|
decode: (p) => {
|
|
@@ -389,10 +389,26 @@ function createStruct(t) {
|
|
|
389
389
|
},
|
|
390
390
|
};
|
|
391
391
|
}
|
|
392
|
-
/** 平铺字段表 → '#' 声明:'#'/`#pack` 由构造器写入(顶层 kind
|
|
393
|
-
*
|
|
394
|
-
*
|
|
392
|
+
/** 平铺字段表 → '#' 声明:'#'/`#pack` 由构造器写入(顶层 kind 由函数名表明)。
|
|
393
|
+
* member 里混进的其他 '#' 前缀键在此剔除 —— fresh literal 由 excess check 拒,这里
|
|
394
|
+
* 运行时兜底,避免用户键覆盖构造器写入的 '#'/`#pack`。
|
|
395
|
+
* 两处守卫(DECL 常量直传形态,`struct('X', X_DECL)`):
|
|
396
|
+
* - member 自带 '#' 时须与函数名 kind 一致,否则 throw。TS 侧 overload 参数收
|
|
397
|
+
* `M & { '#'?: 'struct' }`(union 镜像),'#' 与函数名 kind 不符在编译期就交叉成
|
|
398
|
+
* never 报错;此 throw 是 JS / `as any` / 动态构造 DECL 调用方的运行时兜底 ——
|
|
399
|
+
* union DECL 误传 struct() 会静默翻转成 struct 布局,是最难查的一类错;
|
|
400
|
+
* - '#pack' 透传(剥键即丢,pack 布局静默变自然对齐):opts.pack 未给时取 member 的,
|
|
401
|
+
* 两者都给且不等则 throw —— 跨参数的相等约束类型层表达不了,只能运行时校。 */
|
|
395
402
|
function buildDecl(kind, member, pack) {
|
|
403
|
+
const declKind = member['#'];
|
|
404
|
+
if (declKind !== undefined && declKind !== kind)
|
|
405
|
+
throw new Error(`ffi-struct: ${kind}() received a '${String(declKind)}' declaration ` +
|
|
406
|
+
`(kind mismatch — use ${declKind}() for it)`);
|
|
407
|
+
const declPack = member['#pack'];
|
|
408
|
+
if (pack === undefined && declPack !== undefined)
|
|
409
|
+
pack = declPack;
|
|
410
|
+
else if (pack !== undefined && declPack !== undefined && pack !== declPack)
|
|
411
|
+
throw new Error(`ffi-struct: #pack conflict: opts.pack=${pack} vs declaration ${String(declPack)}`);
|
|
396
412
|
const decl = { '#': kind };
|
|
397
413
|
if (pack !== undefined)
|
|
398
414
|
decl['#pack'] = pack;
|
|
@@ -418,10 +434,10 @@ function buildDecl(kind, member, pack) {
|
|
|
418
434
|
* - 裸地址形:count 必填(地址无长度信息),逐字节拷读(def.decode 的 number 形
|
|
419
435
|
* 同机制);长度不可校 → 信任 C 的 count。
|
|
420
436
|
* - count = 0 → [](读 0 个真空安全,与 encode 禁空的 PAB(0) 理由刻意不对称)。
|
|
421
|
-
* 实现:encode 走纯公开 API(alloc + encode offset 循环);decode
|
|
422
|
-
*
|
|
437
|
+
* 实现:encode 走纯公开 API(alloc + encode offset 循环);decode 直接读 def.fields
|
|
438
|
+
* (createStruct 暴露的布局 IR,与 def.decode 同一份,无需重算)。 */
|
|
423
439
|
export function structArray(def) {
|
|
424
|
-
const { fields, size } =
|
|
440
|
+
const { fields, size } = def;
|
|
425
441
|
const readN = (dv, n) => {
|
|
426
442
|
const out = [];
|
|
427
443
|
for (let i = 0; i < n; i++)
|
package/lib/ffi/struct.ts
CHANGED
|
@@ -204,7 +204,10 @@ export type Fields = Field[]
|
|
|
204
204
|
// N = 布局品牌(struct(name, …) 传入):decode 入参与 alloc().ptr 同品牌。默认 '' ——
|
|
205
205
|
// `Ptr<''>` 归一裸 number(与旧 never 守卫结果相同),品牌化 def 未来收窄时签名不用再动。
|
|
206
206
|
export type StructDef<T extends { '#': 'struct' | 'union' }, N extends string = ''> = {
|
|
207
|
-
|
|
207
|
+
// 布局 IR 直接暴露(原 __struct 成员读已删:成员读是可观察副作用,esbuild 无法把
|
|
208
|
+
// 「PURE 调用 + X.__struct 实参」与 X 的定义一起摇掉 —— DECL 常量(对象字面量)走
|
|
209
|
+
// 标识符引用则整链可摇。T 只活在 decode/encode 的类型签名里,运行时不挂成员)。
|
|
210
|
+
readonly fields: Fields
|
|
208
211
|
readonly size: number
|
|
209
212
|
readonly structAlign: number
|
|
210
213
|
decode(buf: ArrayBuffer | Ptr<N>): ShapeOfC<T>
|
|
@@ -593,7 +596,7 @@ function doEncode(dv: DataView, base: number, fields: Fields, v: Record<string,
|
|
|
593
596
|
function createStruct(t: C_Struct | C_Union): any {
|
|
594
597
|
const { fields, size, maxEffectiveAlign } = computeStructLayout(t)
|
|
595
598
|
return {
|
|
596
|
-
|
|
599
|
+
fields,
|
|
597
600
|
size,
|
|
598
601
|
structAlign: maxEffectiveAlign,
|
|
599
602
|
decode: (p: ArrayBuffer | number) => {
|
|
@@ -638,10 +641,25 @@ function createStruct(t: C_Struct | C_Union): any {
|
|
|
638
641
|
/** 聚合对齐上限(pack 压 maxAlign,语义同 MSVC #pragma pack)。 */
|
|
639
642
|
export type AggOpts = { pack?: number }
|
|
640
643
|
|
|
641
|
-
/** 平铺字段表 → '#' 声明:'#'/`#pack` 由构造器写入(顶层 kind
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
+
/** 平铺字段表 → '#' 声明:'#'/`#pack` 由构造器写入(顶层 kind 由函数名表明)。
|
|
645
|
+
* member 里混进的其他 '#' 前缀键在此剔除 —— fresh literal 由 excess check 拒,这里
|
|
646
|
+
* 运行时兜底,避免用户键覆盖构造器写入的 '#'/`#pack`。
|
|
647
|
+
* 两处守卫(DECL 常量直传形态,`struct('X', X_DECL)`):
|
|
648
|
+
* - member 自带 '#' 时须与函数名 kind 一致,否则 throw。TS 侧 overload 参数收
|
|
649
|
+
* `M & { '#'?: 'struct' }`(union 镜像),'#' 与函数名 kind 不符在编译期就交叉成
|
|
650
|
+
* never 报错;此 throw 是 JS / `as any` / 动态构造 DECL 调用方的运行时兜底 ——
|
|
651
|
+
* union DECL 误传 struct() 会静默翻转成 struct 布局,是最难查的一类错;
|
|
652
|
+
* - '#pack' 透传(剥键即丢,pack 布局静默变自然对齐):opts.pack 未给时取 member 的,
|
|
653
|
+
* 两者都给且不等则 throw —— 跨参数的相等约束类型层表达不了,只能运行时校。 */
|
|
644
654
|
function buildDecl(kind: 'struct' | 'union', member: Record<string, unknown>, pack?: number): C_Struct | C_Union {
|
|
655
|
+
const declKind = member['#']
|
|
656
|
+
if (declKind !== undefined && declKind !== kind)
|
|
657
|
+
throw new Error(`ffi-struct: ${kind}() received a '${String(declKind)}' declaration ` +
|
|
658
|
+
`(kind mismatch — use ${declKind}() for it)`)
|
|
659
|
+
const declPack = member['#pack']
|
|
660
|
+
if (pack === undefined && declPack !== undefined) pack = declPack as number
|
|
661
|
+
else if (pack !== undefined && declPack !== undefined && pack !== declPack)
|
|
662
|
+
throw new Error(`ffi-struct: #pack conflict: opts.pack=${pack} vs declaration ${String(declPack)}`)
|
|
645
663
|
const decl: Record<string, unknown> = { '#': kind }
|
|
646
664
|
if (pack !== undefined) decl['#pack'] = pack
|
|
647
665
|
for (const [k, v] of Object.entries(member))
|
|
@@ -649,11 +667,19 @@ function buildDecl(kind: 'struct' | 'union', member: Record<string, unknown>, pa
|
|
|
649
667
|
return decl as unknown as C_Struct | C_Union
|
|
650
668
|
}
|
|
651
669
|
|
|
670
|
+
/** def 的类型经方法签名索引提取(StructDef 不再挂 __struct 成员 —— T 只活在签名里):
|
|
671
|
+
* encode 重载末条首参 = EncodeIn<T>、decode 返回 = ShapeOfC<T>、alloc 返回 = PAB<N>、
|
|
672
|
+
* decode 首参剥 ArrayBuffer = Ptr<N>(裸地址形)。 */
|
|
673
|
+
type EncInOf<D extends StructDef<any, any>> = Parameters<D['encode']>[0]
|
|
674
|
+
type DecOf<D extends StructDef<any, any>> = ReturnType<D['decode']>
|
|
675
|
+
type PABOf<D extends StructDef<any, any>> = ReturnType<D['alloc']>
|
|
676
|
+
type AddrOf<D extends StructDef<any, any>> = Exclude<Parameters<D['decode']>[0], ArrayBuffer>
|
|
677
|
+
|
|
652
678
|
/** decode 双态重载:有 byteLength 的 buffer 形 count 可缺省(自算);裸地址无长度
|
|
653
679
|
* 信息,count 必填(类型层拦,缺省调用编译红)。 */
|
|
654
|
-
type StructArrayDecode<
|
|
655
|
-
(p: ArrayBuffer, count?: number):
|
|
656
|
-
(p:
|
|
680
|
+
type StructArrayDecode<D extends StructDef<any, any>> = {
|
|
681
|
+
(p: ArrayBuffer, count?: number): DecOf<D>[]
|
|
682
|
+
(p: AddrOf<D>, count: number): DecOf<D>[]
|
|
657
683
|
}
|
|
658
684
|
|
|
659
685
|
/** 结构/联合体的数组编解码对工厂:返回 `{ encode, decode }`。
|
|
@@ -673,25 +699,23 @@ type StructArrayDecode<T extends { '#': 'struct' | 'union' }, N extends string>
|
|
|
673
699
|
* - 裸地址形:count 必填(地址无长度信息),逐字节拷读(def.decode 的 number 形
|
|
674
700
|
* 同机制);长度不可校 → 信任 C 的 count。
|
|
675
701
|
* - count = 0 → [](读 0 个真空安全,与 encode 禁空的 PAB(0) 理由刻意不对称)。
|
|
676
|
-
* 实现:encode 走纯公开 API(alloc + encode offset 循环);decode
|
|
677
|
-
*
|
|
678
|
-
export function structArray<
|
|
679
|
-
|
|
680
|
-
) {
|
|
681
|
-
const { fields, size } = computeStructLayout(def.__struct)
|
|
702
|
+
* 实现:encode 走纯公开 API(alloc + encode offset 循环);decode 直接读 def.fields
|
|
703
|
+
* (createStruct 暴露的布局 IR,与 def.decode 同一份,无需重算)。 */
|
|
704
|
+
export function structArray<D extends StructDef<any, any>>(def: D) {
|
|
705
|
+
const { fields, size } = def
|
|
682
706
|
const readN = (dv: DataView, n: number): unknown[] => {
|
|
683
707
|
const out: unknown[] = []
|
|
684
708
|
for (let i = 0; i < n; i++) out.push(doDecode(dv, i * size, fields))
|
|
685
709
|
return out
|
|
686
710
|
}
|
|
687
711
|
return {
|
|
688
|
-
encode: (els: readonly
|
|
712
|
+
encode: (els: readonly EncInOf<D>[]): PABOf<D> => {
|
|
689
713
|
if (!Array.isArray(els) || els.length === 0)
|
|
690
714
|
throw new Error(`ffi-struct: structArray(def).encode(els) requires a ` +
|
|
691
715
|
`non-empty element array (pass NULL for an empty pointer position)`)
|
|
692
716
|
const out = def.alloc(els.length)
|
|
693
717
|
for (let i = 0; i < els.length; i++) def.encode(els[i], out, i * def.size)
|
|
694
|
-
return out
|
|
718
|
+
return out as PABOf<D>
|
|
695
719
|
},
|
|
696
720
|
decode: ((p: ArrayBuffer | number, count?: number): unknown[] => {
|
|
697
721
|
// fail-loud 校验矩阵(见 JSDoc):显式 count 信调用方但拦越界;缺省只对
|
|
@@ -725,16 +749,18 @@ export function structArray<T extends { '#': 'struct' | 'union' }, N extends str
|
|
|
725
749
|
}
|
|
726
750
|
if (n === 0) return []
|
|
727
751
|
return readN(new DataView(p), n)
|
|
728
|
-
}) as StructArrayDecode<
|
|
752
|
+
}) as StructArrayDecode<D>,
|
|
729
753
|
}
|
|
730
754
|
}
|
|
731
755
|
|
|
732
756
|
/** 平铺字段表定义结构体(token/`X[n]`/`unit:width`/`'#'` 声明均可,见 ctype.ts §4)。
|
|
733
|
-
* 给 name 则 alloc().ptr 与 decode 入参带 Ptr<name> 品牌。
|
|
757
|
+
* 给 name 则 alloc().ptr 与 decode 入参带 Ptr<name> 品牌。DECL 常量可直传(
|
|
758
|
+
* `struct('X', X_DECL)`)—— 参数交叉 `{ '#'?: 'struct' }` 锁 kind:传 union/array
|
|
759
|
+
* DECL 时 '#' 字面类型交叉成 never,编译期即报错。 */
|
|
734
760
|
type StructOf<M, N extends string = ''> = StructDef<{ '#': 'struct', '#pack'?: number } & M, N>
|
|
735
761
|
type UnionOf<M, N extends string = ''> = StructDef<{ '#': 'union', '#pack'?: number } & M, N>
|
|
736
|
-
export function struct<const M extends SimpleMember>(member: M, opts?: AggOpts): StructOf<M>
|
|
737
|
-
export function struct<const N extends string, const M extends SimpleMember>(name: N, member: M, opts?: AggOpts): StructOf<M, N>
|
|
762
|
+
export function struct<const M extends SimpleMember>(member: M & { '#'?: 'struct' }, opts?: AggOpts): StructOf<M>
|
|
763
|
+
export function struct<const N extends string, const M extends SimpleMember>(name: N, member: M & { '#'?: 'struct' }, opts?: AggOpts): StructOf<M, N>
|
|
738
764
|
export function struct(a: string | SimpleMember, b?: SimpleMember | AggOpts, c?: AggOpts): any {
|
|
739
765
|
const isNamed = typeof a === 'string'
|
|
740
766
|
const raw = (isNamed ? b : a) as Record<string, unknown>
|
|
@@ -742,9 +768,10 @@ export function struct(a: string | SimpleMember, b?: SimpleMember | AggOpts, c?:
|
|
|
742
768
|
return createStruct(buildDecl('struct', raw, opts?.pack))
|
|
743
769
|
}
|
|
744
770
|
|
|
745
|
-
/** 平铺字段表定义联合体;布局/读写与 struct 相同,仅成员偏移重叠。
|
|
746
|
-
|
|
747
|
-
export function union<const
|
|
771
|
+
/** 平铺字段表定义联合体;布局/读写与 struct 相同,仅成员偏移重叠。DECL 常量可直传,
|
|
772
|
+
* 参数交叉 `{ '#'?: 'union' }` 锁 kind(struct 侧镜像,理由见上)。 */
|
|
773
|
+
export function union<const M extends SimpleMember>(member: M & { '#'?: 'union' }, opts?: AggOpts): UnionOf<M>
|
|
774
|
+
export function union<const N extends string, const M extends SimpleMember>(name: N, member: M & { '#'?: 'union' }, opts?: AggOpts): UnionOf<M, N>
|
|
748
775
|
export function union(a: string | SimpleMember, b?: SimpleMember | AggOpts, c?: AggOpts): any {
|
|
749
776
|
const isNamed = typeof a === 'string'
|
|
750
777
|
const raw = (isNamed ? b : a) as Record<string, unknown>
|
package/lib/http-server.js
CHANGED
|
@@ -342,7 +342,7 @@ async function writeStreamingBody(c, body, chunked, trailer) {
|
|
|
342
342
|
}
|
|
343
343
|
if (c.closed) {
|
|
344
344
|
// 客户端断开(FD_CLOSE / send 失败):取消响应体,让 producer 感知
|
|
345
|
-
//(
|
|
345
|
+
//(execd 借此 kill 子进程),而不是干等它自然结束。
|
|
346
346
|
try {
|
|
347
347
|
await reader.cancel();
|
|
348
348
|
}
|
package/lib/http-server.ts
CHANGED
|
@@ -357,7 +357,7 @@ async function writeStreamingBody(c: Conn, body: ReadableStream<Uint8Array> | nu
|
|
|
357
357
|
}
|
|
358
358
|
if (c.closed) {
|
|
359
359
|
// 客户端断开(FD_CLOSE / send 失败):取消响应体,让 producer 感知
|
|
360
|
-
//(
|
|
360
|
+
//(execd 借此 kill 子进程),而不是干等它自然结束。
|
|
361
361
|
try { await reader.cancel() } catch (e) {
|
|
362
362
|
console.error('[http-server] body cancel error:', e)
|
|
363
363
|
}
|
package/lib/windows/gdi32.js
CHANGED
|
@@ -12,7 +12,7 @@ import { LOGFONTW, POINT, RECT } from './structs.js';
|
|
|
12
12
|
const b = (name, sig, encoders) => bind('gdi32.dll', name, sig, encoders);
|
|
13
13
|
// 点数组编解码对(Polyline/Polygon/PlgBlt 共享):注册后 <POINT>ptr 位收元素数组
|
|
14
14
|
// (值域由 encoder 推导),C 侧 NULL 位传 NULL(number 直通),[]/单对象 fail-loud
|
|
15
|
-
const POINTArr = structArray(POINT);
|
|
15
|
+
const POINTArr = /*@__PURE__*/ structArray(POINT);
|
|
16
16
|
/** 创建实心画刷;配对 DeleteObject
|
|
17
17
|
* @returns 失败 → 0
|
|
18
18
|
* @param args_0 crColor 画刷颜色 0x00BBGGRR */
|
package/lib/windows/gdi32.ts
CHANGED
|
@@ -15,7 +15,7 @@ const b = <const S extends string, const LE extends CodecMap = {}>(name: string,
|
|
|
15
15
|
|
|
16
16
|
// 点数组编解码对(Polyline/Polygon/PlgBlt 共享):注册后 <POINT>ptr 位收元素数组
|
|
17
17
|
// (值域由 encoder 推导),C 侧 NULL 位传 NULL(number 直通),[]/单对象 fail-loud
|
|
18
|
-
const POINTArr = structArray(POINT)
|
|
18
|
+
const POINTArr = /*@__PURE__*/ structArray(POINT)
|
|
19
19
|
|
|
20
20
|
/** 创建实心画刷;配对 DeleteObject
|
|
21
21
|
* @returns 失败 → 0
|