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/quickwin_const.d.ts
CHANGED
|
@@ -854,6 +854,32 @@ declare module "gui" {
|
|
|
854
854
|
BITSPIXEL = 12, // 0xC
|
|
855
855
|
}
|
|
856
856
|
|
|
857
|
+
// Display mode (ChangeDisplaySettings)
|
|
858
|
+
export const enum DevModeField {
|
|
859
|
+
PELSWIDTH = 524288, // 0x80000
|
|
860
|
+
PELSHEIGHT = 1048576, // 0x100000
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
export const enum ChangeDisplayFlag {
|
|
864
|
+
UPDATEREGISTRY = 1, // 0x1
|
|
865
|
+
TEST = 2, // 0x2
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export const enum EnumDisplaySetting {
|
|
869
|
+
CURRENT_SETTINGS = -1, // 0xFFFFFFFF
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
export const enum DisplayChangeResult {
|
|
873
|
+
SUCCESSFUL = 0, // 0x0
|
|
874
|
+
RESTART = 1, // 0x1
|
|
875
|
+
FAILED = -1, // 0xFFFFFFFF
|
|
876
|
+
BADMODE = -2, // 0xFFFFFFFE
|
|
877
|
+
NOTUPDATED = -3, // 0xFFFFFFFD
|
|
878
|
+
BADFLAGS = -4, // 0xFFFFFFFC
|
|
879
|
+
BADPARAM = -5, // 0xFFFFFFFB
|
|
880
|
+
BADDUALVIEW = -6, // 0xFFFFFFFA
|
|
881
|
+
}
|
|
882
|
+
|
|
857
883
|
// Static controls
|
|
858
884
|
export const enum StaticMsg {
|
|
859
885
|
SETICON = 368, // 0x170
|
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
|
package/test/test_ffi_bind.js
CHANGED
|
@@ -238,13 +238,15 @@ export const suite = {
|
|
|
238
238
|
if (brushL)
|
|
239
239
|
t.checkTrue('bindLib DeleteObject succeeds', gdi32Lib.DeleteObject(brushL) !== 0);
|
|
240
240
|
// 嵌套 struct(LOGPEN 内含 POINT)验证 ShapeOf 递归 + 子结构写入
|
|
241
|
-
const
|
|
241
|
+
const POINT_DECL = {
|
|
242
|
+
'#': 'struct',
|
|
242
243
|
x: 'i32',
|
|
243
244
|
y: 'i32',
|
|
244
|
-
}
|
|
245
|
+
};
|
|
246
|
+
const POINT = /* @__PURE__ */ struct('POINT', POINT_DECL);
|
|
245
247
|
const LOGPEN = struct('LOGPEN', {
|
|
246
248
|
lopnStyle: 'u32',
|
|
247
|
-
lopnWidth:
|
|
249
|
+
lopnWidth: POINT_DECL,
|
|
248
250
|
lopnColor: 'u32',
|
|
249
251
|
});
|
|
250
252
|
const createPenIndirect = bind('gdi32.dll', 'CreatePenIndirect', '<LOGPEN>ptr -> <>ptr', { LOGPEN });
|
package/test/test_ffi_bind.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as os from 'os'
|
|
|
5
5
|
import { Tester } from './test_helper.js'
|
|
6
6
|
import { bind, bindLib, closure, WCHAR, BYTE, type CodecMap } from '../lib/ffi/bind.js'
|
|
7
7
|
import { struct } from '../lib/ffi/struct.js'
|
|
8
|
-
import { NULL, type MaybePtr, Ptr, PtrArrayBuffer, readScalar, writeScalar, PTR_SIZE, normToken } from '../lib/ffi/ctype.js'
|
|
8
|
+
import { NULL, type C_Struct, type MaybePtr, Ptr, PtrArrayBuffer, readScalar, writeScalar, PTR_SIZE, normToken } from '../lib/ffi/ctype.js'
|
|
9
9
|
|
|
10
10
|
// 编译期断言工具(仅类型层,运行时无开销)
|
|
11
11
|
type Equal<A, B> = (<G>() => G extends A ? 1 : 2) extends (<G>() => G extends B ? 1 : 2) ? true : false
|
|
@@ -238,13 +238,15 @@ export const suite = {
|
|
|
238
238
|
if (brushL) t.checkTrue('bindLib DeleteObject succeeds', gdi32Lib.DeleteObject(brushL) !== 0)
|
|
239
239
|
|
|
240
240
|
// 嵌套 struct(LOGPEN 内含 POINT)验证 ShapeOf 递归 + 子结构写入
|
|
241
|
-
const
|
|
241
|
+
const POINT_DECL = {
|
|
242
|
+
'#': 'struct',
|
|
242
243
|
x: 'i32',
|
|
243
244
|
y: 'i32',
|
|
244
|
-
}
|
|
245
|
+
} satisfies C_Struct
|
|
246
|
+
const POINT = /* @__PURE__ */ struct('POINT', POINT_DECL)
|
|
245
247
|
const LOGPEN = struct('LOGPEN', {
|
|
246
248
|
lopnStyle: 'u32',
|
|
247
|
-
lopnWidth:
|
|
249
|
+
lopnWidth: POINT_DECL,
|
|
248
250
|
lopnColor: 'u32',
|
|
249
251
|
})
|
|
250
252
|
const createPenIndirect = bind('gdi32.dll', 'CreatePenIndirect', '<LOGPEN>ptr -> <>ptr', { LOGPEN })
|
package/test/test_ffi_struct.js
CHANGED
|
@@ -13,7 +13,8 @@ const RECT = /* @__PURE__ */ struct('RECT', {
|
|
|
13
13
|
right: 'i32',
|
|
14
14
|
bottom: 'i32',
|
|
15
15
|
});
|
|
16
|
-
const
|
|
16
|
+
const TVITEM_DECL = {
|
|
17
|
+
'#': 'struct',
|
|
17
18
|
mask: 'u32',
|
|
18
19
|
hItem: '<>ptr',
|
|
19
20
|
state: 'u32',
|
|
@@ -24,11 +25,12 @@ const TVITEM = /* @__PURE__ */ struct('TVITEM', {
|
|
|
24
25
|
iSelectedImage: 'i32',
|
|
25
26
|
cChildren: 'i32',
|
|
26
27
|
lParam: '<>ptr',
|
|
27
|
-
}
|
|
28
|
+
};
|
|
29
|
+
const TVITEM = /* @__PURE__ */ struct('TVITEM', TVITEM_DECL);
|
|
28
30
|
const TVINSERTSTRUCT = /* @__PURE__ */ struct('TVINSERTSTRUCT', {
|
|
29
31
|
hParent: '<>ptr',
|
|
30
32
|
hInsertAfter: '<>ptr',
|
|
31
|
-
item:
|
|
33
|
+
item: TVITEM_DECL,
|
|
32
34
|
});
|
|
33
35
|
export const suite = {
|
|
34
36
|
name: 'ffi-struct',
|
|
@@ -228,13 +230,15 @@ export const suite = {
|
|
|
228
230
|
t.check('length 1', 1, one.v.length);
|
|
229
231
|
t.check('[0]', 42, one.v[0]);
|
|
230
232
|
t.section('array of struct roundtrip (stride = child size)');
|
|
231
|
-
const
|
|
233
|
+
const PT_DECL = {
|
|
234
|
+
'#': 'struct',
|
|
232
235
|
x: 'i32',
|
|
233
236
|
y: 'i32',
|
|
234
|
-
}
|
|
237
|
+
};
|
|
238
|
+
const PT = /* @__PURE__ */ struct('PT', PT_DECL);
|
|
235
239
|
const POLY = struct('POLY', {
|
|
236
240
|
count: 'u32',
|
|
237
|
-
pts: { '#': 'array', element:
|
|
241
|
+
pts: { '#': 'array', element: PT_DECL, length: 3 },
|
|
238
242
|
});
|
|
239
243
|
t.check('POLY.size == 28', 28, POLY.size);
|
|
240
244
|
t.check('POLY.offsetOf pts', 4, POLY.offsetOf('pts'));
|
|
@@ -274,9 +278,28 @@ export const suite = {
|
|
|
274
278
|
rejectedMap = true;
|
|
275
279
|
}
|
|
276
280
|
t.check('bare nested map rejected(漏写 #)', true, rejectedMap);
|
|
277
|
-
// 顶层 '#'
|
|
278
|
-
|
|
279
|
-
|
|
281
|
+
// 顶层 '#' 与函数名 kind 不符 → throw(union DECL 误传 struct() 防线);
|
|
282
|
+
// 一致('#': 'struct' 的 DECL 直传形态)→ 按 kind 正常布局
|
|
283
|
+
let rejectedKind = false;
|
|
284
|
+
try {
|
|
285
|
+
struct({ '#': 'union', a: 'u8', b: 'u32' });
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
rejectedKind = true;
|
|
289
|
+
}
|
|
290
|
+
t.check('top-level # kind mismatch rejected', true, rejectedKind);
|
|
291
|
+
const H = struct('H', { '#': 'struct', a: 'u8', b: 'u32' });
|
|
292
|
+
t.check('top-level # matching kind accepted(DECL 直传)', 8, H.size);
|
|
293
|
+
// 类型层 kind 锁(参数交叉 `{ '#'?: 'struct' }` / `{ '#'?: 'union' }`):DECL 的
|
|
294
|
+
// '#' 字面与函数名 kind 不符 → 交叉 never,编译期报错。@ts-expect-error 断言该
|
|
295
|
+
// 报错存在(锁失效则指令未使用、tsc 报错);仅类型期检查,函数不调用
|
|
296
|
+
const _kindLock = () => {
|
|
297
|
+
// @ts-expect-error —— union DECL 传 struct()
|
|
298
|
+
struct('BAD_U', { '#': 'union', a: 'u8', b: 'u32' });
|
|
299
|
+
// @ts-expect-error —— struct DECL 传 union()
|
|
300
|
+
union('BAD_S', { '#': 'struct', a: 'u8', b: 'u32' });
|
|
301
|
+
};
|
|
302
|
+
void _kindLock;
|
|
280
303
|
t.section("pointer member '<>ptr' raw vs '<T>ptr' branded");
|
|
281
304
|
const PTR = struct('PTR', {
|
|
282
305
|
raw: '<>ptr',
|
|
@@ -376,16 +399,18 @@ export const suite = {
|
|
|
376
399
|
t.check('BROWSEINFOW.offsetOf lpszTitle', is64 ? 24 : 12, BROWSEINFOW.offsetOf('lpszTitle'));
|
|
377
400
|
t.check('BROWSEINFOW.offsetOf ulFlags', is64 ? 32 : 16, BROWSEINFOW.offsetOf('ulFlags'));
|
|
378
401
|
t.section('NMHDR(嵌套时 MSVC 尾 padding 传染)');
|
|
379
|
-
const
|
|
402
|
+
const NMHDR_DECL = {
|
|
403
|
+
'#': 'struct',
|
|
380
404
|
hwndFrom: '<>ptr',
|
|
381
405
|
idFrom: '<>ptr',
|
|
382
406
|
code: 'i32',
|
|
383
|
-
}
|
|
407
|
+
};
|
|
408
|
+
const NMHDR = /* @__PURE__ */ struct('NMHDR', NMHDR_DECL);
|
|
384
409
|
t.check('NMHDR.size', is64 ? 24 : 12, NMHDR.size);
|
|
385
410
|
t.check('NMHDR.offsetOf code', is64 ? 16 : 8, NMHDR.offsetOf('code'));
|
|
386
411
|
t.section('NMCUSTOMDRAW / NMLVCUSTOMDRAW (ListView custom draw)');
|
|
387
412
|
const NMCUSTOMDRAW = struct('NMCUSTOMDRAW', {
|
|
388
|
-
hdr:
|
|
413
|
+
hdr: NMHDR_DECL,
|
|
389
414
|
dwDrawStage: 'u32',
|
|
390
415
|
hdc: '<>ptr',
|
|
391
416
|
rc: 'i32[4]',
|
|
@@ -398,7 +423,7 @@ export const suite = {
|
|
|
398
423
|
t.check('NMCUSTOMDRAW.offsetOf hdc', is64 ? 32 : 16, NMCUSTOMDRAW.offsetOf('hdc'));
|
|
399
424
|
t.check('NMCUSTOMDRAW.offsetOf dwItemSpec', is64 ? 56 : 36, NMCUSTOMDRAW.offsetOf('dwItemSpec'));
|
|
400
425
|
const NMLVCUSTOMDRAW = struct('NMLVCUSTOMDRAW', {
|
|
401
|
-
hdr:
|
|
426
|
+
hdr: NMHDR_DECL,
|
|
402
427
|
dwDrawStage: 'u32',
|
|
403
428
|
hdc: '<>ptr',
|
|
404
429
|
rc: 'i32[4]',
|
|
@@ -416,7 +441,7 @@ export const suite = {
|
|
|
416
441
|
t.check('NMLVCUSTOMDRAW.offsetOf iSubItem', is64 ? 88 : 56, NMLVCUSTOMDRAW.offsetOf('iSubItem'));
|
|
417
442
|
t.section('NMLISTVIEW / NMITEMACTIVATE');
|
|
418
443
|
const NMLISTVIEW = struct('NMLISTVIEW', {
|
|
419
|
-
hdr:
|
|
444
|
+
hdr: NMHDR_DECL,
|
|
420
445
|
iItem: 'i32',
|
|
421
446
|
iSubItem: 'i32',
|
|
422
447
|
uNewState: 'u32',
|
|
@@ -471,7 +496,8 @@ export const suite = {
|
|
|
471
496
|
t.check('LVCOLUMNW.offsetOf iSubItem', is64 ? 28 : 20, LVCOLUMNW.offsetOf('iSubItem'));
|
|
472
497
|
t.check('LVCOLUMNW.offsetOf iOrder', is64 ? 36 : 28, LVCOLUMNW.offsetOf('iOrder'));
|
|
473
498
|
t.section('NMDATETIMECHANGE');
|
|
474
|
-
const
|
|
499
|
+
const SYSTEMTIME_DECL = {
|
|
500
|
+
'#': 'struct',
|
|
475
501
|
wYear: 'u16',
|
|
476
502
|
wMonth: 'u16',
|
|
477
503
|
wDayOfWeek: 'u16',
|
|
@@ -480,26 +506,28 @@ export const suite = {
|
|
|
480
506
|
wMinute: 'u16',
|
|
481
507
|
wSecond: 'u16',
|
|
482
508
|
wMilliseconds: 'u16',
|
|
483
|
-
}
|
|
509
|
+
};
|
|
484
510
|
const NMDATETIMECHANGE = struct('NMDATETIMECHANGE', {
|
|
485
|
-
hdr:
|
|
511
|
+
hdr: NMHDR_DECL,
|
|
486
512
|
dwFlags: 'u32',
|
|
487
|
-
st:
|
|
513
|
+
st: SYSTEMTIME_DECL,
|
|
488
514
|
});
|
|
489
515
|
t.check('NMDATETIMECHANGE.size', is64 ? 48 : 32, NMDATETIMECHANGE.size);
|
|
490
516
|
t.check('NMDATETIMECHANGE.offsetOf dwFlags', is64 ? 24 : 12, NMDATETIMECHANGE.offsetOf('dwFlags'));
|
|
491
517
|
t.check('NMDATETIMECHANGE.offsetOf st', is64 ? 28 : 16, NMDATETIMECHANGE.offsetOf('st'));
|
|
492
518
|
t.section('NMLINK szUrl offset');
|
|
493
|
-
const
|
|
519
|
+
const LITEM_DECL = {
|
|
520
|
+
'#': 'struct',
|
|
494
521
|
mask: 'u32',
|
|
495
522
|
iLink: 'i32',
|
|
496
523
|
state: 'u32',
|
|
497
524
|
stateMask: 'u32',
|
|
498
525
|
szID: 'u16[48]@utf-16le',
|
|
499
|
-
}
|
|
526
|
+
};
|
|
527
|
+
const LITEM = /* @__PURE__ */ struct('LITEM', LITEM_DECL);
|
|
500
528
|
const NMLINK = struct('NMLINK', {
|
|
501
|
-
hdr:
|
|
502
|
-
item:
|
|
529
|
+
hdr: NMHDR_DECL,
|
|
530
|
+
item: LITEM_DECL,
|
|
503
531
|
szUrl: 'u16[2084]@utf-16le',
|
|
504
532
|
});
|
|
505
533
|
t.check('LITEM.offsetOf szID', 16, LITEM.offsetOf('szID'));
|
|
@@ -715,8 +743,14 @@ export const suite = {
|
|
|
715
743
|
t.section('EncodeIn DFS 后序:子树含必填 ⇒ 键必填;值后序合成(非必填兄弟可省)');
|
|
716
744
|
{
|
|
717
745
|
// 嵌套子结构含 '!'(内层强制)+ 标量/字符串糖(内层可省:''/0 是合法零值)
|
|
718
|
-
const
|
|
719
|
-
|
|
746
|
+
const NSUB_DECL = {
|
|
747
|
+
'#': 'struct',
|
|
748
|
+
hp: '<X>ptr!',
|
|
749
|
+
rest: 'u32',
|
|
750
|
+
s: 'u16[4]@utf-8',
|
|
751
|
+
};
|
|
752
|
+
const NSUB = /* @__PURE__ */ struct('NSUB', NSUB_DECL);
|
|
753
|
+
const NOUT = struct('NOUT', { top: 'u32', sub: NSUB_DECL });
|
|
720
754
|
// decode 造全量对象(hp 槽预填非零,绕 '!' 的 decode 0 断言)
|
|
721
755
|
const nbuf = new PtrArrayBuffer(NOUT.size);
|
|
722
756
|
new DataView(nbuf).setUint32(NOUT.offsetOf('sub') + NSUB.offsetOf('hp'), 0x1234, true);
|
package/test/test_ffi_struct.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Tester } from './test_helper.js'
|
|
|
4
4
|
import { struct, union } from '../lib/ffi/struct.js'
|
|
5
5
|
import { bind, } from '../lib/ffi/bind.js'
|
|
6
6
|
import { type MaybePtr, Ptr, PtrArrayBuffer } from '../lib/ffi/ctype.js'
|
|
7
|
-
import type { C_Union } from '../lib/ffi/ctype.js'
|
|
7
|
+
import type { C_Struct, C_Union } from '../lib/ffi/ctype.js'
|
|
8
8
|
|
|
9
9
|
// 编译期断言工具(仅类型层,运行时无开销)
|
|
10
10
|
type Equal<A, B> = (<G>() => G extends A ? 1 : 2) extends (<G>() => G extends B ? 1 : 2) ? true : false
|
|
@@ -22,7 +22,8 @@ const RECT = /* @__PURE__ */ struct('RECT', {
|
|
|
22
22
|
bottom: 'i32',
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const TVITEM_DECL = {
|
|
26
|
+
'#': 'struct',
|
|
26
27
|
mask: 'u32',
|
|
27
28
|
hItem: '<>ptr',
|
|
28
29
|
state: 'u32',
|
|
@@ -33,11 +34,12 @@ const TVITEM = /* @__PURE__ */ struct('TVITEM', {
|
|
|
33
34
|
iSelectedImage: 'i32',
|
|
34
35
|
cChildren: 'i32',
|
|
35
36
|
lParam: '<>ptr',
|
|
36
|
-
}
|
|
37
|
+
} satisfies C_Struct
|
|
38
|
+
const TVITEM = /* @__PURE__ */ struct('TVITEM', TVITEM_DECL)
|
|
37
39
|
const TVINSERTSTRUCT = /* @__PURE__ */ struct('TVINSERTSTRUCT', {
|
|
38
40
|
hParent: '<>ptr',
|
|
39
41
|
hInsertAfter: '<>ptr',
|
|
40
|
-
item:
|
|
42
|
+
item: TVITEM_DECL,
|
|
41
43
|
})
|
|
42
44
|
|
|
43
45
|
export const suite = {
|
|
@@ -260,13 +262,15 @@ export const suite = {
|
|
|
260
262
|
t.check('[0]', 42, one.v[0])
|
|
261
263
|
|
|
262
264
|
t.section('array of struct roundtrip (stride = child size)')
|
|
263
|
-
const
|
|
265
|
+
const PT_DECL = {
|
|
266
|
+
'#': 'struct',
|
|
264
267
|
x: 'i32',
|
|
265
268
|
y: 'i32',
|
|
266
|
-
}
|
|
269
|
+
} satisfies C_Struct
|
|
270
|
+
const PT = /* @__PURE__ */ struct('PT', PT_DECL)
|
|
267
271
|
const POLY = struct('POLY', {
|
|
268
272
|
count: 'u32',
|
|
269
|
-
pts: { '#': 'array', element:
|
|
273
|
+
pts: { '#': 'array', element: PT_DECL, length: 3 },
|
|
270
274
|
})
|
|
271
275
|
t.check('POLY.size == 28', 28, POLY.size)
|
|
272
276
|
t.check('POLY.offsetOf pts', 4, POLY.offsetOf('pts'))
|
|
@@ -296,9 +300,24 @@ export const suite = {
|
|
|
296
300
|
try { (struct as any)({ outer: { inner: 'u32' } }) } catch { rejectedMap = true }
|
|
297
301
|
t.check('bare nested map rejected(漏写 #)', true, rejectedMap)
|
|
298
302
|
|
|
299
|
-
// 顶层 '#'
|
|
300
|
-
|
|
301
|
-
|
|
303
|
+
// 顶层 '#' 与函数名 kind 不符 → throw(union DECL 误传 struct() 防线);
|
|
304
|
+
// 一致('#': 'struct' 的 DECL 直传形态)→ 按 kind 正常布局
|
|
305
|
+
let rejectedKind = false
|
|
306
|
+
try { (struct as any)({ '#': 'union', a: 'u8', b: 'u32' }) } catch { rejectedKind = true }
|
|
307
|
+
t.check('top-level # kind mismatch rejected', true, rejectedKind)
|
|
308
|
+
const H = (struct as any)('H', { '#': 'struct', a: 'u8', b: 'u32' })
|
|
309
|
+
t.check('top-level # matching kind accepted(DECL 直传)', 8, H.size)
|
|
310
|
+
|
|
311
|
+
// 类型层 kind 锁(参数交叉 `{ '#'?: 'struct' }` / `{ '#'?: 'union' }`):DECL 的
|
|
312
|
+
// '#' 字面与函数名 kind 不符 → 交叉 never,编译期报错。@ts-expect-error 断言该
|
|
313
|
+
// 报错存在(锁失效则指令未使用、tsc 报错);仅类型期检查,函数不调用
|
|
314
|
+
const _kindLock = () => {
|
|
315
|
+
// @ts-expect-error —— union DECL 传 struct()
|
|
316
|
+
struct('BAD_U', { '#': 'union', a: 'u8', b: 'u32' })
|
|
317
|
+
// @ts-expect-error —— struct DECL 传 union()
|
|
318
|
+
union('BAD_S', { '#': 'struct', a: 'u8', b: 'u32' })
|
|
319
|
+
}
|
|
320
|
+
void _kindLock
|
|
302
321
|
|
|
303
322
|
t.section("pointer member '<>ptr' raw vs '<T>ptr' branded")
|
|
304
323
|
const PTR = struct('PTR', {
|
|
@@ -405,17 +424,19 @@ export const suite = {
|
|
|
405
424
|
t.check('BROWSEINFOW.offsetOf ulFlags', is64 ? 32 : 16, BROWSEINFOW.offsetOf('ulFlags'))
|
|
406
425
|
|
|
407
426
|
t.section('NMHDR(嵌套时 MSVC 尾 padding 传染)')
|
|
408
|
-
const
|
|
427
|
+
const NMHDR_DECL = {
|
|
428
|
+
'#': 'struct',
|
|
409
429
|
hwndFrom: '<>ptr',
|
|
410
430
|
idFrom: '<>ptr',
|
|
411
431
|
code: 'i32',
|
|
412
|
-
}
|
|
432
|
+
} satisfies C_Struct
|
|
433
|
+
const NMHDR = /* @__PURE__ */ struct('NMHDR', NMHDR_DECL)
|
|
413
434
|
t.check('NMHDR.size', is64 ? 24 : 12, NMHDR.size)
|
|
414
435
|
t.check('NMHDR.offsetOf code', is64 ? 16 : 8, NMHDR.offsetOf('code'))
|
|
415
436
|
|
|
416
437
|
t.section('NMCUSTOMDRAW / NMLVCUSTOMDRAW (ListView custom draw)')
|
|
417
438
|
const NMCUSTOMDRAW = struct('NMCUSTOMDRAW', {
|
|
418
|
-
hdr:
|
|
439
|
+
hdr: NMHDR_DECL,
|
|
419
440
|
dwDrawStage: 'u32',
|
|
420
441
|
hdc: '<>ptr',
|
|
421
442
|
rc: 'i32[4]',
|
|
@@ -428,7 +449,7 @@ export const suite = {
|
|
|
428
449
|
t.check('NMCUSTOMDRAW.offsetOf hdc', is64 ? 32 : 16, NMCUSTOMDRAW.offsetOf('hdc'))
|
|
429
450
|
t.check('NMCUSTOMDRAW.offsetOf dwItemSpec', is64 ? 56 : 36, NMCUSTOMDRAW.offsetOf('dwItemSpec'))
|
|
430
451
|
const NMLVCUSTOMDRAW = struct('NMLVCUSTOMDRAW', {
|
|
431
|
-
hdr:
|
|
452
|
+
hdr: NMHDR_DECL,
|
|
432
453
|
dwDrawStage: 'u32',
|
|
433
454
|
hdc: '<>ptr',
|
|
434
455
|
rc: 'i32[4]',
|
|
@@ -447,7 +468,7 @@ export const suite = {
|
|
|
447
468
|
|
|
448
469
|
t.section('NMLISTVIEW / NMITEMACTIVATE')
|
|
449
470
|
const NMLISTVIEW = struct('NMLISTVIEW', {
|
|
450
|
-
hdr:
|
|
471
|
+
hdr: NMHDR_DECL,
|
|
451
472
|
iItem: 'i32',
|
|
452
473
|
iSubItem: 'i32',
|
|
453
474
|
uNewState: 'u32',
|
|
@@ -504,7 +525,8 @@ export const suite = {
|
|
|
504
525
|
t.check('LVCOLUMNW.offsetOf iOrder', is64 ? 36 : 28, LVCOLUMNW.offsetOf('iOrder'))
|
|
505
526
|
|
|
506
527
|
t.section('NMDATETIMECHANGE')
|
|
507
|
-
const
|
|
528
|
+
const SYSTEMTIME_DECL = {
|
|
529
|
+
'#': 'struct',
|
|
508
530
|
wYear: 'u16',
|
|
509
531
|
wMonth: 'u16',
|
|
510
532
|
wDayOfWeek: 'u16',
|
|
@@ -513,27 +535,29 @@ export const suite = {
|
|
|
513
535
|
wMinute: 'u16',
|
|
514
536
|
wSecond: 'u16',
|
|
515
537
|
wMilliseconds: 'u16',
|
|
516
|
-
}
|
|
538
|
+
} satisfies C_Struct
|
|
517
539
|
const NMDATETIMECHANGE = struct('NMDATETIMECHANGE', {
|
|
518
|
-
hdr:
|
|
540
|
+
hdr: NMHDR_DECL,
|
|
519
541
|
dwFlags: 'u32',
|
|
520
|
-
st:
|
|
542
|
+
st: SYSTEMTIME_DECL,
|
|
521
543
|
})
|
|
522
544
|
t.check('NMDATETIMECHANGE.size', is64 ? 48 : 32, NMDATETIMECHANGE.size)
|
|
523
545
|
t.check('NMDATETIMECHANGE.offsetOf dwFlags', is64 ? 24 : 12, NMDATETIMECHANGE.offsetOf('dwFlags'))
|
|
524
546
|
t.check('NMDATETIMECHANGE.offsetOf st', is64 ? 28 : 16, NMDATETIMECHANGE.offsetOf('st'))
|
|
525
547
|
|
|
526
548
|
t.section('NMLINK szUrl offset')
|
|
527
|
-
const
|
|
549
|
+
const LITEM_DECL = {
|
|
550
|
+
'#': 'struct',
|
|
528
551
|
mask: 'u32',
|
|
529
552
|
iLink: 'i32',
|
|
530
553
|
state: 'u32',
|
|
531
554
|
stateMask: 'u32',
|
|
532
555
|
szID: 'u16[48]@utf-16le',
|
|
533
|
-
}
|
|
556
|
+
} satisfies C_Struct
|
|
557
|
+
const LITEM = /* @__PURE__ */ struct('LITEM', LITEM_DECL)
|
|
534
558
|
const NMLINK = struct('NMLINK', {
|
|
535
|
-
hdr:
|
|
536
|
-
item:
|
|
559
|
+
hdr: NMHDR_DECL,
|
|
560
|
+
item: LITEM_DECL,
|
|
537
561
|
szUrl: 'u16[2084]@utf-16le',
|
|
538
562
|
})
|
|
539
563
|
t.check('LITEM.offsetOf szID', 16, LITEM.offsetOf('szID'))
|
|
@@ -766,8 +790,14 @@ export const suite = {
|
|
|
766
790
|
t.section('EncodeIn DFS 后序:子树含必填 ⇒ 键必填;值后序合成(非必填兄弟可省)')
|
|
767
791
|
{
|
|
768
792
|
// 嵌套子结构含 '!'(内层强制)+ 标量/字符串糖(内层可省:''/0 是合法零值)
|
|
769
|
-
const
|
|
770
|
-
|
|
793
|
+
const NSUB_DECL = {
|
|
794
|
+
'#': 'struct',
|
|
795
|
+
hp: '<X>ptr!',
|
|
796
|
+
rest: 'u32',
|
|
797
|
+
s: 'u16[4]@utf-8',
|
|
798
|
+
} satisfies C_Struct
|
|
799
|
+
const NSUB = /* @__PURE__ */ struct('NSUB', NSUB_DECL)
|
|
800
|
+
const NOUT = struct('NOUT', { top: 'u32', sub: NSUB_DECL })
|
|
771
801
|
// decode 造全量对象(hp 槽预填非零,绕 '!' 的 decode 0 断言)
|
|
772
802
|
const nbuf = new PtrArrayBuffer(NOUT.size)
|
|
773
803
|
new DataView(nbuf).setUint32(NOUT.offsetOf('sub') + NSUB.offsetOf('hp'), 0x1234, true)
|
package/test/test_gdi_draw.js
CHANGED
|
@@ -14,7 +14,7 @@ const H = 240;
|
|
|
14
14
|
export const suite = {
|
|
15
15
|
name: 'gdi-draw',
|
|
16
16
|
run: (t) => {
|
|
17
|
-
// —— 基座:屏幕 DC → 内存 DC + 32bpp 位图(与
|
|
17
|
+
// —— 基座:屏幕 DC → 内存 DC + 32bpp 位图(与 execd_worker 截屏同型)——
|
|
18
18
|
t.section('setup: memory DC + 32bpp bitmap');
|
|
19
19
|
const screen = GetDC(NULL);
|
|
20
20
|
const mem = CreateCompatibleDC(screen);
|
package/test/test_gdi_draw.ts
CHANGED
|
@@ -27,7 +27,7 @@ const H = 240
|
|
|
27
27
|
export const suite = {
|
|
28
28
|
name: 'gdi-draw',
|
|
29
29
|
run: (t: Tester) => {
|
|
30
|
-
// —— 基座:屏幕 DC → 内存 DC + 32bpp 位图(与
|
|
30
|
+
// —— 基座:屏幕 DC → 内存 DC + 32bpp 位图(与 execd_worker 截屏同型)——
|
|
31
31
|
t.section('setup: memory DC + 32bpp bitmap')
|
|
32
32
|
const screen = GetDC(NULL)
|
|
33
33
|
const mem = CreateCompatibleDC(screen)
|