quickwin 1.1.4 → 1.2.1
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 +7 -3
- package/README.md +7 -3
- package/examples/exec_server.js +3535 -34
- package/examples/exec_server.ts +71 -26
- package/examples/exec_server_worker.js +45 -0
- package/examples/exec_server_worker.ts +39 -0
- package/examples/pdf_viewer.js +11 -10
- package/examples/setres.js +104 -0
- package/examples/setres.ts +120 -0
- package/examples/test_gallery.js +11 -10
- package/examples/test_react_lazy.js +11 -10
- package/examples/test_react_listbox.js +11 -10
- package/examples/test_root_window.js +11 -10
- package/examples/test_tab.js +11 -10
- package/lib/fetch.js +35 -7
- package/lib/fetch.ts +24 -4
- package/lib/text-measure.js +8 -6
- package/lib/text-measure.ts +8 -6
- package/main.js +46 -0
- package/package.json +2 -1
- package/quickwin.d.ts +2 -1
- 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/run.js +4 -1
- package/test/run.ts +4 -1
- package/test/test_net_fetch.js +62 -0
- package/test/test_net_fetch.ts +72 -0
- package/test/test_react_complex.js +14 -13
- package/test/test_react_counter.js +11 -10
- package/test/test_react_flex.js +11 -10
- package/test/test_react_hidden.js +13 -12
- package/test/test_react_listview_link.js +11 -10
- package/test/test_react_render.js +11 -10
- package/test/test_wolfssl_read.js +201 -0
- package/test/test_wolfssl_read.ts +181 -0
- package/test/test_worker_concurrent.js +45 -0
- package/test/test_worker_concurrent.ts +56 -0
- package/test/test_worker_data_url.js +46 -0
- package/test/test_worker_data_url.ts +51 -0
package/examples/exec_server.ts
CHANGED
|
@@ -1,35 +1,80 @@
|
|
|
1
1
|
import { createServer } from '../lib/http-server.js'
|
|
2
|
-
import * as
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
|
|
4
|
+
// WORKER_DATA_URL 由 esbuild --define 在 build.ts 中注入(base64 data: URL)
|
|
5
|
+
declare const WORKER_DATA_URL: string
|
|
6
|
+
|
|
7
|
+
const EXEC_TIMEOUT_MS = 60_000
|
|
8
|
+
|
|
9
|
+
interface ExecResult {
|
|
10
|
+
out: Uint8Array
|
|
11
|
+
code: number
|
|
12
|
+
error: string | null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function runInWorker(cmd: string): Promise<ExecResult> {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const worker = new os.Worker(WORKER_DATA_URL)
|
|
18
|
+
const id = 1
|
|
19
|
+
let settled = false
|
|
20
|
+
let timer: ReturnType<typeof os.setTimeout>
|
|
21
|
+
|
|
22
|
+
const finish = (fn: () => void) => {
|
|
23
|
+
if (settled) return
|
|
24
|
+
settled = true
|
|
25
|
+
os.clearTimeout(timer)
|
|
26
|
+
worker.onmessage = null
|
|
27
|
+
fn()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
timer = os.setTimeout(() => {
|
|
31
|
+
finish(() => reject(new Error('exec timeout')))
|
|
32
|
+
}, EXEC_TIMEOUT_MS)
|
|
33
|
+
|
|
34
|
+
worker.onmessage = (e) => {
|
|
35
|
+
const msg = e.data as { type: string; id: number; out?: Uint8Array; code?: number; error?: string | null }
|
|
36
|
+
if (msg.type !== 'result' || msg.id !== id) return
|
|
37
|
+
finish(() => resolve({ out: msg.out ?? new Uint8Array(0), code: msg.code ?? -1, error: msg.error ?? null }))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
worker.postMessage({ type: 'run', id, cmd })
|
|
42
|
+
} catch (ex) {
|
|
43
|
+
finish(() => reject(ex instanceof Error ? ex : new Error(String(ex))))
|
|
44
|
+
}
|
|
45
|
+
})
|
|
18
46
|
}
|
|
19
47
|
|
|
20
48
|
const server = createServer(async (req) => {
|
|
21
|
-
|
|
49
|
+
const path = new URL(req.url).pathname
|
|
50
|
+
if (req.method === 'GET' && (path === '/' || path === '/health')) {
|
|
51
|
+
return new Response('ok', { headers: { 'Content-Type': 'text/plain' } })
|
|
52
|
+
}
|
|
53
|
+
if (req.method === 'POST' && path === '/exec') {
|
|
22
54
|
const body = await req.json() as { cmd: string }
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
55
|
+
try {
|
|
56
|
+
const { out, code, error } = await runInWorker(body.cmd)
|
|
57
|
+
if (error) {
|
|
58
|
+
return new Response(JSON.stringify({ error }), {
|
|
59
|
+
status: 500,
|
|
60
|
+
headers: { 'Content-Type': 'application/json' }
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
return new Response(out, {
|
|
64
|
+
headers: {
|
|
65
|
+
'Content-Type': 'application/octet-stream',
|
|
66
|
+
'X-Exit-Code': String(code),
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
} catch (ex) {
|
|
70
|
+
return new Response(JSON.stringify({ error: String(ex) }), {
|
|
71
|
+
status: 504,
|
|
72
|
+
headers: { 'Content-Type': 'application/json' }
|
|
73
|
+
})
|
|
74
|
+
}
|
|
30
75
|
}
|
|
31
76
|
return new Response('Not Found', { status: 404 })
|
|
32
77
|
})
|
|
33
78
|
|
|
34
|
-
server.listen(8080)
|
|
35
|
-
console.log('
|
|
79
|
+
const rc = server.listen(8080, '0.0.0.0')
|
|
80
|
+
console.log('listen rc=' + rc + ' addr=' + JSON.stringify(server.address()))
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as os from 'os';
|
|
2
|
+
import * as std from 'std';
|
|
3
|
+
function readBytes(file) {
|
|
4
|
+
const chunks = [];
|
|
5
|
+
const buf = new Uint8Array(4096);
|
|
6
|
+
while (true) {
|
|
7
|
+
const n = file.read(buf.buffer, 0, 4096);
|
|
8
|
+
if (n <= 0)
|
|
9
|
+
break;
|
|
10
|
+
chunks.push(buf.slice(0, n));
|
|
11
|
+
}
|
|
12
|
+
let total = 0;
|
|
13
|
+
for (const c of chunks)
|
|
14
|
+
total += c.length;
|
|
15
|
+
const out = new Uint8Array(total);
|
|
16
|
+
let offset = 0;
|
|
17
|
+
for (const c of chunks) {
|
|
18
|
+
out.set(c, offset);
|
|
19
|
+
offset += c.length;
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
const parent = os.Worker.parent;
|
|
24
|
+
parent.onmessage = (e) => {
|
|
25
|
+
const msg = e.data;
|
|
26
|
+
if (msg.type !== 'run' || typeof msg.cmd !== 'string')
|
|
27
|
+
return;
|
|
28
|
+
let out = new Uint8Array(0);
|
|
29
|
+
let code = -1;
|
|
30
|
+
let error = null;
|
|
31
|
+
try {
|
|
32
|
+
const file = std.popen(msg.cmd, 'r');
|
|
33
|
+
if (!file) {
|
|
34
|
+
error = 'popen failed';
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
out = readBytes(file);
|
|
38
|
+
code = file.close();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (ex) {
|
|
42
|
+
error = String(ex);
|
|
43
|
+
}
|
|
44
|
+
parent.postMessage({ type: 'result', id: msg.id, out, code, error });
|
|
45
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as os from 'os'
|
|
2
|
+
import * as std from 'std'
|
|
3
|
+
|
|
4
|
+
function readBytes(file: std.FILE): Uint8Array {
|
|
5
|
+
const chunks: Uint8Array[] = []
|
|
6
|
+
const buf = new Uint8Array(4096)
|
|
7
|
+
while (true) {
|
|
8
|
+
const n = file.read(buf.buffer, 0, 4096)
|
|
9
|
+
if (n <= 0) break
|
|
10
|
+
chunks.push(buf.slice(0, n))
|
|
11
|
+
}
|
|
12
|
+
let total = 0
|
|
13
|
+
for (const c of chunks) total += c.length
|
|
14
|
+
const out = new Uint8Array(total)
|
|
15
|
+
let offset = 0
|
|
16
|
+
for (const c of chunks) { out.set(c, offset); offset += c.length }
|
|
17
|
+
return out
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const parent = os.Worker.parent
|
|
21
|
+
parent.onmessage = (e) => {
|
|
22
|
+
const msg = e.data as { type: string; id: number; cmd?: string }
|
|
23
|
+
if (msg.type !== 'run' || typeof msg.cmd !== 'string') return
|
|
24
|
+
let out: Uint8Array = new Uint8Array(0)
|
|
25
|
+
let code = -1
|
|
26
|
+
let error: string | null = null
|
|
27
|
+
try {
|
|
28
|
+
const file = std.popen(msg.cmd, 'r')
|
|
29
|
+
if (!file) {
|
|
30
|
+
error = 'popen failed'
|
|
31
|
+
} else {
|
|
32
|
+
out = readBytes(file)
|
|
33
|
+
code = file.close()
|
|
34
|
+
}
|
|
35
|
+
} catch (ex) {
|
|
36
|
+
error = String(ex)
|
|
37
|
+
}
|
|
38
|
+
parent.postMessage({ type: 'result', id: msg.id, out, code, error })
|
|
39
|
+
}
|
package/examples/pdf_viewer.js
CHANGED
|
@@ -15370,7 +15370,7 @@ var import_react_reconciler = __toESM(require_react_reconciler(), 1);
|
|
|
15370
15370
|
var import_react = __toESM(require_react(), 1);
|
|
15371
15371
|
var import_constants = __toESM(require_constants(), 1);
|
|
15372
15372
|
import * as gui3 from "gui";
|
|
15373
|
-
import * as
|
|
15373
|
+
import * as os2 from "os";
|
|
15374
15374
|
|
|
15375
15375
|
// _build/lib/react-qw/props.js
|
|
15376
15376
|
import * as gui from "gui";
|
|
@@ -15491,6 +15491,7 @@ function calculateFlexLayout(flex, parentW, parentH, children) {
|
|
|
15491
15491
|
|
|
15492
15492
|
// _build/lib/text-measure.js
|
|
15493
15493
|
import * as ffi from "ffi";
|
|
15494
|
+
import * as os from "os";
|
|
15494
15495
|
import * as win from "win";
|
|
15495
15496
|
import * as gui2 from "gui";
|
|
15496
15497
|
function loadProc(lib, name) {
|
|
@@ -15507,7 +15508,7 @@ var DrawTextW = loadProc(user32, "DrawTextW");
|
|
|
15507
15508
|
var GetDC = loadProc(user32, "GetDC");
|
|
15508
15509
|
var ReleaseDC = loadProc(user32, "ReleaseDC");
|
|
15509
15510
|
var SelectObject = loadProc(gdi32, "SelectObject");
|
|
15510
|
-
var
|
|
15511
|
+
var FFI_HND = os.arch === "ia32" ? ffi.FFI_TYPE_UINT32 : ffi.FFI_TYPE_UINT64;
|
|
15511
15512
|
var FFI_S32 = ffi.FFI_TYPE_SINT32;
|
|
15512
15513
|
var FFI_PTR = ffi.FFI_TYPE_POINTER;
|
|
15513
15514
|
function measureText(hdc, text, maxWidth) {
|
|
@@ -15515,14 +15516,14 @@ function measureText(hdc, text, maxWidth) {
|
|
|
15515
15516
|
const rect = new ArrayBuffer(16);
|
|
15516
15517
|
const dv = new DataView(rect);
|
|
15517
15518
|
dv.setInt32(8, maxWidth, true);
|
|
15518
|
-
ffi.ffiCall(DrawTextW, [
|
|
15519
|
+
ffi.ffiCall(DrawTextW, [FFI_HND, FFI_PTR, FFI_S32, FFI_PTR, FFI_S32], [
|
|
15519
15520
|
hdc,
|
|
15520
15521
|
textBuf,
|
|
15521
15522
|
-1,
|
|
15522
15523
|
rect,
|
|
15523
15524
|
1024
|
|
15524
15525
|
/* gui.DrawTextFlag.CALCRECT */
|
|
15525
|
-
],
|
|
15526
|
+
], FFI_S32);
|
|
15526
15527
|
return { width: dv.getInt32(8, true), height: dv.getInt32(12, true) };
|
|
15527
15528
|
}
|
|
15528
15529
|
function getButtonIdealSize(hwnd) {
|
|
@@ -15532,15 +15533,15 @@ function getButtonIdealSize(hwnd) {
|
|
|
15532
15533
|
return { width: dv.getInt32(0, true), height: dv.getInt32(4, true) };
|
|
15533
15534
|
}
|
|
15534
15535
|
function measureTextForHwnd(hwnd, text) {
|
|
15535
|
-
const hdc = ffi.ffiCall(GetDC, [
|
|
15536
|
+
const hdc = ffi.ffiCall(GetDC, [FFI_HND], [hwnd], FFI_HND);
|
|
15536
15537
|
if (!hdc)
|
|
15537
15538
|
return { width: 0, height: 0 };
|
|
15538
15539
|
const hFont = gui2.SendMessage(hwnd, 49, 0, 0);
|
|
15539
|
-
const oldFont = hFont ? ffi.ffiCall(SelectObject, [
|
|
15540
|
+
const oldFont = hFont ? ffi.ffiCall(SelectObject, [FFI_HND, FFI_HND], [hdc, hFont], FFI_HND) : 0;
|
|
15540
15541
|
const result = measureText(hdc, text, 0);
|
|
15541
15542
|
if (hFont)
|
|
15542
|
-
ffi.ffiCall(SelectObject, [
|
|
15543
|
-
ffi.ffiCall(ReleaseDC, [
|
|
15543
|
+
ffi.ffiCall(SelectObject, [FFI_HND, FFI_HND], [hdc, oldFont], FFI_HND);
|
|
15544
|
+
ffi.ffiCall(ReleaseDC, [FFI_HND, FFI_HND], [hwnd, hdc], FFI_S32);
|
|
15544
15545
|
const wr = gui2.GetWindowRect(hwnd);
|
|
15545
15546
|
const cr = gui2.GetClientRect(hwnd);
|
|
15546
15547
|
if (wr && cr) {
|
|
@@ -15853,10 +15854,10 @@ var hostConfig = {
|
|
|
15853
15854
|
clearContainer(_container) {
|
|
15854
15855
|
},
|
|
15855
15856
|
scheduleTimeout(fn, delay) {
|
|
15856
|
-
return
|
|
15857
|
+
return os2.setTimeout(fn, delay);
|
|
15857
15858
|
},
|
|
15858
15859
|
cancelTimeout(id) {
|
|
15859
|
-
|
|
15860
|
+
os2.clearTimeout?.(id);
|
|
15860
15861
|
},
|
|
15861
15862
|
noTimeout: -1,
|
|
15862
15863
|
supportsMutation: true,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as std from 'std';
|
|
2
|
+
import * as gui from 'gui';
|
|
3
|
+
import * as win from 'win';
|
|
4
|
+
import * as ffi from 'ffi';
|
|
5
|
+
const user32 = win.LoadLibrary('user32.dll');
|
|
6
|
+
if (!user32) {
|
|
7
|
+
print('LoadLibrary user32 failed');
|
|
8
|
+
std.exit(1);
|
|
9
|
+
}
|
|
10
|
+
function loadProc(lib, name) {
|
|
11
|
+
const ptr = win.GetProcAddress(lib, name);
|
|
12
|
+
if (!ptr) {
|
|
13
|
+
print('GetProcAddress ' + name + ' failed');
|
|
14
|
+
std.exit(1);
|
|
15
|
+
}
|
|
16
|
+
return ptr;
|
|
17
|
+
}
|
|
18
|
+
const EnumDisplaySettingsA = loadProc(user32, 'EnumDisplaySettingsA');
|
|
19
|
+
const ChangeDisplaySettingsA = loadProc(user32, 'ChangeDisplaySettingsA');
|
|
20
|
+
/** DEVMODEA used by EnumDisplaySettings; driver reports actual dmSize (often 124). */
|
|
21
|
+
const DEVMODE_SIZE = 220;
|
|
22
|
+
const DM_PELSWIDTH = 0x00800000;
|
|
23
|
+
const DM_PELSHEIGHT = 0x01000000;
|
|
24
|
+
const ENUM_CURRENT_SETTINGS = -1;
|
|
25
|
+
const CDS_UPDATEREGISTRY = 0x00000001;
|
|
26
|
+
const CDS_TEST = 0x00000002;
|
|
27
|
+
const DISP_CHANGE = {
|
|
28
|
+
0: 'SUCCESSFUL',
|
|
29
|
+
1: 'RESTART',
|
|
30
|
+
[-1]: 'FAILED',
|
|
31
|
+
[-2]: 'BADMODE',
|
|
32
|
+
[-3]: 'NOTUPDATED',
|
|
33
|
+
[-4]: 'BADFLAGS',
|
|
34
|
+
[-5]: 'BADPARAM',
|
|
35
|
+
[-6]: 'BADDUALVIEW',
|
|
36
|
+
};
|
|
37
|
+
function newDevMode() {
|
|
38
|
+
const buf = new ArrayBuffer(DEVMODE_SIZE);
|
|
39
|
+
new DataView(buf).setUint16(36, DEVMODE_SIZE, true);
|
|
40
|
+
return buf;
|
|
41
|
+
}
|
|
42
|
+
function enumMode(modeNum, buf) {
|
|
43
|
+
return ffi.ffiCall(EnumDisplaySettingsA, [ffi.FFI_TYPE_POINTER, ffi.FFI_TYPE_SINT32, ffi.FFI_TYPE_POINTER], [null, modeNum, buf], ffi.FFI_TYPE_SINT32);
|
|
44
|
+
}
|
|
45
|
+
function getPels(dv) {
|
|
46
|
+
return [dv.getUint32(108, true), dv.getUint32(112, true), dv.getUint32(120, true)];
|
|
47
|
+
}
|
|
48
|
+
function applyFromCurrent(w, h, flags, label) {
|
|
49
|
+
const dm = newDevMode();
|
|
50
|
+
if (!enumMode(ENUM_CURRENT_SETTINGS, dm)) {
|
|
51
|
+
print(label + ': EnumDisplaySettings CURRENT failed');
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const dv = new DataView(dm);
|
|
55
|
+
const before = getPels(dv);
|
|
56
|
+
const size = dv.getUint16(36, true);
|
|
57
|
+
const extra = dv.getUint16(38, true);
|
|
58
|
+
const fields = dv.getUint32(40, true);
|
|
59
|
+
const bpp = dv.getUint32(104, true);
|
|
60
|
+
const freq = dv.getUint32(120, true);
|
|
61
|
+
print(label + ': base size=' + size + ' extra=' + extra + ' fields=0x' + fields.toString(16) +
|
|
62
|
+
' ' + before[0] + 'x' + before[1] + '@' + freq + ' bpp=' + bpp);
|
|
63
|
+
dv.setUint32(40, fields | DM_PELSWIDTH | DM_PELSHEIGHT, true);
|
|
64
|
+
dv.setUint32(108, w, true);
|
|
65
|
+
dv.setUint32(112, h, true);
|
|
66
|
+
const ret = ffi.ffiCall(ChangeDisplaySettingsA, [ffi.FFI_TYPE_POINTER, ffi.FFI_TYPE_UINT32], [dm, flags], ffi.FFI_TYPE_SINT32);
|
|
67
|
+
print(label + ': ChangeDisplaySettings ' + w + 'x' + h +
|
|
68
|
+
' flags=0x' + flags.toString(16) + ' ret=' + ret + ' (' + (DISP_CHANGE[ret] ?? '?') + ')');
|
|
69
|
+
return ret;
|
|
70
|
+
}
|
|
71
|
+
const targetW = Number(scriptArgs[1] ?? '1024');
|
|
72
|
+
const targetH = Number(scriptArgs[2] ?? '768');
|
|
73
|
+
if (!Number.isFinite(targetW) || !Number.isFinite(targetH) || targetW <= 0 || targetH <= 0) {
|
|
74
|
+
print('usage: qwin.exe setres.js [width] [height]');
|
|
75
|
+
std.exit(1);
|
|
76
|
+
}
|
|
77
|
+
print('before GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()));
|
|
78
|
+
const probe = newDevMode();
|
|
79
|
+
const modes32 = [];
|
|
80
|
+
for (let i = 0; i < 200; i++) {
|
|
81
|
+
if (!enumMode(i, probe))
|
|
82
|
+
break;
|
|
83
|
+
const dv = new DataView(probe);
|
|
84
|
+
if (dv.getUint32(104, true) !== 32)
|
|
85
|
+
continue;
|
|
86
|
+
const p = getPels(dv);
|
|
87
|
+
modes32.push(p[0] + 'x' + p[1] + '@' + p[2]);
|
|
88
|
+
}
|
|
89
|
+
print('32bpp modes sample: ' + modes32.slice(0, 25).join(', ') + (modes32.length > 25 ? ' ...' : ''));
|
|
90
|
+
const testRet = applyFromCurrent(targetW, targetH, CDS_TEST, 'TEST');
|
|
91
|
+
if (testRet === 0) {
|
|
92
|
+
applyFromCurrent(targetW, targetH, 0, 'DYNAMIC');
|
|
93
|
+
print('after dynamic GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()));
|
|
94
|
+
applyFromCurrent(targetW, targetH, CDS_UPDATEREGISTRY, 'REGISTRY');
|
|
95
|
+
print('after registry GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()));
|
|
96
|
+
}
|
|
97
|
+
else if (testRet !== null) {
|
|
98
|
+
print('target rejected; try 800x600');
|
|
99
|
+
const t2 = applyFromCurrent(800, 600, CDS_TEST, 'TEST800');
|
|
100
|
+
if (t2 === 0) {
|
|
101
|
+
applyFromCurrent(800, 600, 0, 'DYNAMIC800');
|
|
102
|
+
print('after 800x600 GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as std from 'std'
|
|
2
|
+
import * as gui from 'gui'
|
|
3
|
+
import * as win from 'win'
|
|
4
|
+
import * as ffi from 'ffi'
|
|
5
|
+
|
|
6
|
+
const user32 = win.LoadLibrary('user32.dll')
|
|
7
|
+
if (!user32) { print('LoadLibrary user32 failed'); std.exit(1) }
|
|
8
|
+
|
|
9
|
+
function loadProc(lib: win.HMODULE, name: string): number {
|
|
10
|
+
const ptr = win.GetProcAddress(lib, name)
|
|
11
|
+
if (!ptr) { print('GetProcAddress ' + name + ' failed'); std.exit(1) }
|
|
12
|
+
return ptr
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const EnumDisplaySettingsA = loadProc(user32, 'EnumDisplaySettingsA')
|
|
16
|
+
const ChangeDisplaySettingsA = loadProc(user32, 'ChangeDisplaySettingsA')
|
|
17
|
+
|
|
18
|
+
/** DEVMODEA used by EnumDisplaySettings; driver reports actual dmSize (often 124). */
|
|
19
|
+
const DEVMODE_SIZE = 220
|
|
20
|
+
const DM_PELSWIDTH = 0x00800000
|
|
21
|
+
const DM_PELSHEIGHT = 0x01000000
|
|
22
|
+
const ENUM_CURRENT_SETTINGS = -1
|
|
23
|
+
const CDS_UPDATEREGISTRY = 0x00000001
|
|
24
|
+
const CDS_TEST = 0x00000002
|
|
25
|
+
|
|
26
|
+
const DISP_CHANGE: Record<number, string> = {
|
|
27
|
+
0: 'SUCCESSFUL',
|
|
28
|
+
1: 'RESTART',
|
|
29
|
+
[-1]: 'FAILED',
|
|
30
|
+
[-2]: 'BADMODE',
|
|
31
|
+
[-3]: 'NOTUPDATED',
|
|
32
|
+
[-4]: 'BADFLAGS',
|
|
33
|
+
[-5]: 'BADPARAM',
|
|
34
|
+
[-6]: 'BADDUALVIEW',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function newDevMode(): ArrayBuffer {
|
|
38
|
+
const buf = new ArrayBuffer(DEVMODE_SIZE)
|
|
39
|
+
new DataView(buf).setUint16(36, DEVMODE_SIZE, true)
|
|
40
|
+
return buf
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function enumMode(modeNum: number, buf: ArrayBuffer): number {
|
|
44
|
+
return ffi.ffiCall(
|
|
45
|
+
EnumDisplaySettingsA,
|
|
46
|
+
[ffi.FFI_TYPE_POINTER, ffi.FFI_TYPE_SINT32, ffi.FFI_TYPE_POINTER],
|
|
47
|
+
[null, modeNum, buf],
|
|
48
|
+
ffi.FFI_TYPE_SINT32,
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getPels(dv: DataView): [number, number, number] {
|
|
53
|
+
return [dv.getUint32(108, true), dv.getUint32(112, true), dv.getUint32(120, true)]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function applyFromCurrent(w: number, h: number, flags: number, label: string): number | null {
|
|
57
|
+
const dm = newDevMode()
|
|
58
|
+
if (!enumMode(ENUM_CURRENT_SETTINGS, dm)) {
|
|
59
|
+
print(label + ': EnumDisplaySettings CURRENT failed')
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
const dv = new DataView(dm)
|
|
63
|
+
const before = getPels(dv)
|
|
64
|
+
const size = dv.getUint16(36, true)
|
|
65
|
+
const extra = dv.getUint16(38, true)
|
|
66
|
+
const fields = dv.getUint32(40, true)
|
|
67
|
+
const bpp = dv.getUint32(104, true)
|
|
68
|
+
const freq = dv.getUint32(120, true)
|
|
69
|
+
print(label + ': base size=' + size + ' extra=' + extra + ' fields=0x' + fields.toString(16) +
|
|
70
|
+
' ' + before[0] + 'x' + before[1] + '@' + freq + ' bpp=' + bpp)
|
|
71
|
+
|
|
72
|
+
dv.setUint32(40, fields | DM_PELSWIDTH | DM_PELSHEIGHT, true)
|
|
73
|
+
dv.setUint32(108, w, true)
|
|
74
|
+
dv.setUint32(112, h, true)
|
|
75
|
+
|
|
76
|
+
const ret = ffi.ffiCall(
|
|
77
|
+
ChangeDisplaySettingsA,
|
|
78
|
+
[ffi.FFI_TYPE_POINTER, ffi.FFI_TYPE_UINT32],
|
|
79
|
+
[dm, flags],
|
|
80
|
+
ffi.FFI_TYPE_SINT32,
|
|
81
|
+
)
|
|
82
|
+
print(label + ': ChangeDisplaySettings ' + w + 'x' + h +
|
|
83
|
+
' flags=0x' + flags.toString(16) + ' ret=' + ret + ' (' + (DISP_CHANGE[ret] ?? '?') + ')')
|
|
84
|
+
return ret
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const targetW = Number(scriptArgs[1] ?? '1024')
|
|
88
|
+
const targetH = Number(scriptArgs[2] ?? '768')
|
|
89
|
+
if (!Number.isFinite(targetW) || !Number.isFinite(targetH) || targetW <= 0 || targetH <= 0) {
|
|
90
|
+
print('usage: qwin.exe setres.js [width] [height]')
|
|
91
|
+
std.exit(1)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
print('before GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()))
|
|
95
|
+
|
|
96
|
+
const probe = newDevMode()
|
|
97
|
+
const modes32: string[] = []
|
|
98
|
+
for (let i = 0; i < 200; i++) {
|
|
99
|
+
if (!enumMode(i, probe)) break
|
|
100
|
+
const dv = new DataView(probe)
|
|
101
|
+
if (dv.getUint32(104, true) !== 32) continue
|
|
102
|
+
const p = getPels(dv)
|
|
103
|
+
modes32.push(p[0] + 'x' + p[1] + '@' + p[2])
|
|
104
|
+
}
|
|
105
|
+
print('32bpp modes sample: ' + modes32.slice(0, 25).join(', ') + (modes32.length > 25 ? ' ...' : ''))
|
|
106
|
+
|
|
107
|
+
const testRet = applyFromCurrent(targetW, targetH, CDS_TEST, 'TEST')
|
|
108
|
+
if (testRet === 0) {
|
|
109
|
+
applyFromCurrent(targetW, targetH, 0, 'DYNAMIC')
|
|
110
|
+
print('after dynamic GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()))
|
|
111
|
+
applyFromCurrent(targetW, targetH, CDS_UPDATEREGISTRY, 'REGISTRY')
|
|
112
|
+
print('after registry GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()))
|
|
113
|
+
} else if (testRet !== null) {
|
|
114
|
+
print('target rejected; try 800x600')
|
|
115
|
+
const t2 = applyFromCurrent(800, 600, CDS_TEST, 'TEST800')
|
|
116
|
+
if (t2 === 0) {
|
|
117
|
+
applyFromCurrent(800, 600, 0, 'DYNAMIC800')
|
|
118
|
+
print('after 800x600 GetScreenSize: ' + JSON.stringify(gui.GetScreenSize()))
|
|
119
|
+
}
|
|
120
|
+
}
|
package/examples/test_gallery.js
CHANGED
|
@@ -15367,7 +15367,7 @@ var import_react_reconciler = __toESM(require_react_reconciler(), 1);
|
|
|
15367
15367
|
var import_react = __toESM(require_react(), 1);
|
|
15368
15368
|
var import_constants = __toESM(require_constants(), 1);
|
|
15369
15369
|
import * as gui3 from "gui";
|
|
15370
|
-
import * as
|
|
15370
|
+
import * as os2 from "os";
|
|
15371
15371
|
|
|
15372
15372
|
// _build/lib/react-qw/props.js
|
|
15373
15373
|
import * as gui from "gui";
|
|
@@ -15488,6 +15488,7 @@ function calculateFlexLayout(flex, parentW, parentH, children) {
|
|
|
15488
15488
|
|
|
15489
15489
|
// _build/lib/text-measure.js
|
|
15490
15490
|
import * as ffi from "ffi";
|
|
15491
|
+
import * as os from "os";
|
|
15491
15492
|
import * as win from "win";
|
|
15492
15493
|
import * as gui2 from "gui";
|
|
15493
15494
|
function loadProc(lib, name) {
|
|
@@ -15504,7 +15505,7 @@ var DrawTextW = loadProc(user32, "DrawTextW");
|
|
|
15504
15505
|
var GetDC = loadProc(user32, "GetDC");
|
|
15505
15506
|
var ReleaseDC = loadProc(user32, "ReleaseDC");
|
|
15506
15507
|
var SelectObject = loadProc(gdi32, "SelectObject");
|
|
15507
|
-
var
|
|
15508
|
+
var FFI_HND = os.arch === "ia32" ? ffi.FFI_TYPE_UINT32 : ffi.FFI_TYPE_UINT64;
|
|
15508
15509
|
var FFI_S32 = ffi.FFI_TYPE_SINT32;
|
|
15509
15510
|
var FFI_PTR = ffi.FFI_TYPE_POINTER;
|
|
15510
15511
|
function measureText(hdc, text, maxWidth) {
|
|
@@ -15512,14 +15513,14 @@ function measureText(hdc, text, maxWidth) {
|
|
|
15512
15513
|
const rect = new ArrayBuffer(16);
|
|
15513
15514
|
const dv = new DataView(rect);
|
|
15514
15515
|
dv.setInt32(8, maxWidth, true);
|
|
15515
|
-
ffi.ffiCall(DrawTextW, [
|
|
15516
|
+
ffi.ffiCall(DrawTextW, [FFI_HND, FFI_PTR, FFI_S32, FFI_PTR, FFI_S32], [
|
|
15516
15517
|
hdc,
|
|
15517
15518
|
textBuf,
|
|
15518
15519
|
-1,
|
|
15519
15520
|
rect,
|
|
15520
15521
|
1024
|
|
15521
15522
|
/* gui.DrawTextFlag.CALCRECT */
|
|
15522
|
-
],
|
|
15523
|
+
], FFI_S32);
|
|
15523
15524
|
return { width: dv.getInt32(8, true), height: dv.getInt32(12, true) };
|
|
15524
15525
|
}
|
|
15525
15526
|
function getButtonIdealSize(hwnd2) {
|
|
@@ -15529,15 +15530,15 @@ function getButtonIdealSize(hwnd2) {
|
|
|
15529
15530
|
return { width: dv.getInt32(0, true), height: dv.getInt32(4, true) };
|
|
15530
15531
|
}
|
|
15531
15532
|
function measureTextForHwnd(hwnd2, text) {
|
|
15532
|
-
const hdc = ffi.ffiCall(GetDC, [
|
|
15533
|
+
const hdc = ffi.ffiCall(GetDC, [FFI_HND], [hwnd2], FFI_HND);
|
|
15533
15534
|
if (!hdc)
|
|
15534
15535
|
return { width: 0, height: 0 };
|
|
15535
15536
|
const hFont = gui2.SendMessage(hwnd2, 49, 0, 0);
|
|
15536
|
-
const oldFont = hFont ? ffi.ffiCall(SelectObject, [
|
|
15537
|
+
const oldFont = hFont ? ffi.ffiCall(SelectObject, [FFI_HND, FFI_HND], [hdc, hFont], FFI_HND) : 0;
|
|
15537
15538
|
const result = measureText(hdc, text, 0);
|
|
15538
15539
|
if (hFont)
|
|
15539
|
-
ffi.ffiCall(SelectObject, [
|
|
15540
|
-
ffi.ffiCall(ReleaseDC, [
|
|
15540
|
+
ffi.ffiCall(SelectObject, [FFI_HND, FFI_HND], [hdc, oldFont], FFI_HND);
|
|
15541
|
+
ffi.ffiCall(ReleaseDC, [FFI_HND, FFI_HND], [hwnd2, hdc], FFI_S32);
|
|
15541
15542
|
const wr = gui2.GetWindowRect(hwnd2);
|
|
15542
15543
|
const cr = gui2.GetClientRect(hwnd2);
|
|
15543
15544
|
if (wr && cr) {
|
|
@@ -15850,10 +15851,10 @@ var hostConfig = {
|
|
|
15850
15851
|
clearContainer(_container) {
|
|
15851
15852
|
},
|
|
15852
15853
|
scheduleTimeout(fn, delay) {
|
|
15853
|
-
return
|
|
15854
|
+
return os2.setTimeout(fn, delay);
|
|
15854
15855
|
},
|
|
15855
15856
|
cancelTimeout(id) {
|
|
15856
|
-
|
|
15857
|
+
os2.clearTimeout?.(id);
|
|
15857
15858
|
},
|
|
15858
15859
|
noTimeout: -1,
|
|
15859
15860
|
supportsMutation: true,
|
|
@@ -15367,7 +15367,7 @@ var import_react_reconciler = __toESM(require_react_reconciler(), 1);
|
|
|
15367
15367
|
var import_react = __toESM(require_react(), 1);
|
|
15368
15368
|
var import_constants = __toESM(require_constants(), 1);
|
|
15369
15369
|
import * as gui3 from "gui";
|
|
15370
|
-
import * as
|
|
15370
|
+
import * as os2 from "os";
|
|
15371
15371
|
|
|
15372
15372
|
// _build/lib/react-qw/props.js
|
|
15373
15373
|
import * as gui from "gui";
|
|
@@ -15488,6 +15488,7 @@ function calculateFlexLayout(flex, parentW, parentH, children) {
|
|
|
15488
15488
|
|
|
15489
15489
|
// _build/lib/text-measure.js
|
|
15490
15490
|
import * as ffi from "ffi";
|
|
15491
|
+
import * as os from "os";
|
|
15491
15492
|
import * as win from "win";
|
|
15492
15493
|
import * as gui2 from "gui";
|
|
15493
15494
|
function loadProc(lib, name) {
|
|
@@ -15504,7 +15505,7 @@ var DrawTextW = loadProc(user32, "DrawTextW");
|
|
|
15504
15505
|
var GetDC = loadProc(user32, "GetDC");
|
|
15505
15506
|
var ReleaseDC = loadProc(user32, "ReleaseDC");
|
|
15506
15507
|
var SelectObject = loadProc(gdi32, "SelectObject");
|
|
15507
|
-
var
|
|
15508
|
+
var FFI_HND = os.arch === "ia32" ? ffi.FFI_TYPE_UINT32 : ffi.FFI_TYPE_UINT64;
|
|
15508
15509
|
var FFI_S32 = ffi.FFI_TYPE_SINT32;
|
|
15509
15510
|
var FFI_PTR = ffi.FFI_TYPE_POINTER;
|
|
15510
15511
|
function measureText(hdc, text, maxWidth) {
|
|
@@ -15512,14 +15513,14 @@ function measureText(hdc, text, maxWidth) {
|
|
|
15512
15513
|
const rect = new ArrayBuffer(16);
|
|
15513
15514
|
const dv = new DataView(rect);
|
|
15514
15515
|
dv.setInt32(8, maxWidth, true);
|
|
15515
|
-
ffi.ffiCall(DrawTextW, [
|
|
15516
|
+
ffi.ffiCall(DrawTextW, [FFI_HND, FFI_PTR, FFI_S32, FFI_PTR, FFI_S32], [
|
|
15516
15517
|
hdc,
|
|
15517
15518
|
textBuf,
|
|
15518
15519
|
-1,
|
|
15519
15520
|
rect,
|
|
15520
15521
|
1024
|
|
15521
15522
|
/* gui.DrawTextFlag.CALCRECT */
|
|
15522
|
-
],
|
|
15523
|
+
], FFI_S32);
|
|
15523
15524
|
return { width: dv.getInt32(8, true), height: dv.getInt32(12, true) };
|
|
15524
15525
|
}
|
|
15525
15526
|
function getButtonIdealSize(hwnd2) {
|
|
@@ -15529,15 +15530,15 @@ function getButtonIdealSize(hwnd2) {
|
|
|
15529
15530
|
return { width: dv.getInt32(0, true), height: dv.getInt32(4, true) };
|
|
15530
15531
|
}
|
|
15531
15532
|
function measureTextForHwnd(hwnd2, text) {
|
|
15532
|
-
const hdc = ffi.ffiCall(GetDC, [
|
|
15533
|
+
const hdc = ffi.ffiCall(GetDC, [FFI_HND], [hwnd2], FFI_HND);
|
|
15533
15534
|
if (!hdc)
|
|
15534
15535
|
return { width: 0, height: 0 };
|
|
15535
15536
|
const hFont = gui2.SendMessage(hwnd2, 49, 0, 0);
|
|
15536
|
-
const oldFont = hFont ? ffi.ffiCall(SelectObject, [
|
|
15537
|
+
const oldFont = hFont ? ffi.ffiCall(SelectObject, [FFI_HND, FFI_HND], [hdc, hFont], FFI_HND) : 0;
|
|
15537
15538
|
const result = measureText(hdc, text, 0);
|
|
15538
15539
|
if (hFont)
|
|
15539
|
-
ffi.ffiCall(SelectObject, [
|
|
15540
|
-
ffi.ffiCall(ReleaseDC, [
|
|
15540
|
+
ffi.ffiCall(SelectObject, [FFI_HND, FFI_HND], [hdc, oldFont], FFI_HND);
|
|
15541
|
+
ffi.ffiCall(ReleaseDC, [FFI_HND, FFI_HND], [hwnd2, hdc], FFI_S32);
|
|
15541
15542
|
const wr = gui2.GetWindowRect(hwnd2);
|
|
15542
15543
|
const cr = gui2.GetClientRect(hwnd2);
|
|
15543
15544
|
if (wr && cr) {
|
|
@@ -15850,10 +15851,10 @@ var hostConfig = {
|
|
|
15850
15851
|
clearContainer(_container) {
|
|
15851
15852
|
},
|
|
15852
15853
|
scheduleTimeout(fn, delay) {
|
|
15853
|
-
return
|
|
15854
|
+
return os2.setTimeout(fn, delay);
|
|
15854
15855
|
},
|
|
15855
15856
|
cancelTimeout(id) {
|
|
15856
|
-
|
|
15857
|
+
os2.clearTimeout?.(id);
|
|
15857
15858
|
},
|
|
15858
15859
|
noTimeout: -1,
|
|
15859
15860
|
supportsMutation: true,
|