mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 18:34:21 -10:00
The tree by what each part is, behind the top-level Makefile: - shims/ -> hosts/ (the browser, kernel, native and Wayland hosts) - web/ -> page/ (the page's source, build and serve), ports/mesa, ports/xwayland and ports/tint (the ports the page loads), site/ (static sites and the eject page) - tools/gexcc -> toolchain/gexcc, with the sysroot and busybox builds from tools/wine-wasm in toolchain/sysroot and toolchain/busybox, and one toolchain/pins (musl, the LP64 musl, LLVM, busybox) - tools/wine-wasm -> wine/: dist.sh (was wine64-dist.sh), wine.pins (64-bit Wine, and the 32-bit tree its wineserver builds from), libs.pins (the Unix libraries), the 32-bit-only pieces in wine/legacy32 (its own pins and legacy.mk, which wine/Makefile includes) - tools/*.py and tools/*.sh -> scripts/ (the GL generators in scripts/gl) - wine32/src.sh is scripts/fetch-pinned.sh, which takes the trees the old script marked as its own References follow the moves: paths from the tree's root by the map, paths from a script's own directory by where their target went. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_e0aa53aa95eb4b86910519b4d14812a5
103 lines
4.9 KiB
JavaScript
103 lines
4.9 KiB
JavaScript
// PointerLock: a click on a window whose program asks for a locked pointer
|
|
// takes the browser's lock, the mouse's movement reaches it as relative
|
|
// motion, and Escape gives it back. Clicks are real (run.mjs sends them).
|
|
// pointerlock-compositor.test.js: the same through the scene compositor.
|
|
import { System } from '../../page/runtime/src/index.js';
|
|
import { MemoryFilesystem } from '../../page/runtime/src/fs.js';
|
|
import { HostWindows } from '../../page/runtime/src/windows/host.js';
|
|
import { CompositorWindows } from '../../page/runtime/src/windows/compositor.js';
|
|
import { PointerLock } from '../../page/runtime/src/input.js';
|
|
|
|
export const meta = { hosts: ['browser'], images: ['busybox'], timeout: 120, html: `<style>html, body { margin: 0; } #desk { position: relative; width: 800px; height: 600px; }</style>
|
|
<div id="desk"></div>` };
|
|
|
|
|
|
export default (t) => pointerLock(t, false);
|
|
|
|
/** The test, through HostWindows, or through CompositorWindows when `scene`
|
|
* (pointerlock-compositor.test.js). */
|
|
export async function pointerLock(t, scene) {
|
|
const { check, eq, timeout, until, lines, input, click } = t;
|
|
const move = (x, y) => input({ method: 'Input.dispatchMouseEvent', type: 'mouseMoved', x, y, button: 'none', buttons: 0 });
|
|
const key = (type, k, code, vk) => ({ method: 'Input.dispatchKeyEvent', type, key: k, code, windowsVirtualKeyCode: vk });
|
|
const sys = new System({ baseUrl: t.baseUrl, debug: t.debug() });
|
|
const fs = new MemoryFilesystem(sys);
|
|
fs.addTar(t.work('bb.tar.zst'));
|
|
if (scene && !(await navigator.gpu?.requestAdapter().catch(() => null))) {
|
|
return t.skip('no WebGPU adapter here');
|
|
}
|
|
const desk = document.getElementById('desk');
|
|
const wins = [];
|
|
let hw;
|
|
// Where to click a window: its canvas's corner, or where the scene
|
|
// compositor put it.
|
|
let at;
|
|
if (scene) {
|
|
hw = new CompositorWindows(sys, { canvas: document.getElementById('out'), scale: 1 });
|
|
at = async (title) => {
|
|
const d = await hw.describe();
|
|
const line = d?.list?.find((l) => l.endsWith(`"${title}"`));
|
|
const m = line && /@(-?\d+),(-?\d+)/.exec(line);
|
|
return m ? [Number(m[1]) + 20, Number(m[2]) + 20] : null;
|
|
};
|
|
} else {
|
|
hw = new HostWindows(sys, { width: 800, height: 600, scale: 1 });
|
|
hw.addEventListener('window', (e) => {
|
|
const w = e.window;
|
|
wins.push(w);
|
|
Object.assign(w.canvas.style, { position: 'absolute', left: '0px', top: '0px' });
|
|
desk.append(w.canvas);
|
|
w.place(0, 0);
|
|
});
|
|
at = async (title) => (wins.find((w) => w.title === title) ? [50, 50] : null);
|
|
}
|
|
const lock = new PointerLock(sys);
|
|
const said = [];
|
|
lock.addEventListener('lock', () => said.push('lock'));
|
|
lock.addEventListener('unlock', () => said.push('unlock'));
|
|
await timeout(sys.boot(), 60000, 'boot');
|
|
const env = { PATH: '/bin', ...hw.env };
|
|
|
|
// A window that asks for no lock: a click is a click.
|
|
const plain = sys.spawn(['rt-wl', 'plain'], { env });
|
|
const plainOut = lines(plain);
|
|
const [px, py] = await until(async () => plainOut.includes('up') && at('plain'), 'the plain window');
|
|
await click(px, py);
|
|
await until(() => plainOut.includes('button'), 'its click');
|
|
check('a window that asks for none does not lock', !document.pointerLockElement && !lock.locked);
|
|
plain.kill();
|
|
await plain.exited;
|
|
|
|
const p = sys.spawn(['rt-wl', 'game', 'lock'], { env });
|
|
const out = lines(p);
|
|
const err = lines(p, p.stderr);
|
|
const [gx, gy] = await until(async () => out.includes('up') && at('game'), 'the game window')
|
|
.catch((e) => { throw new Error(`${e.message}: ${JSON.stringify(out)} ${JSON.stringify(err)}`); });
|
|
check('not locked before a click', !lock.locked && !out.includes('locked'));
|
|
await click(gx, gy);
|
|
await until(() => lock.locked, 'the lock', 5000);
|
|
await until(() => out.includes('locked'), 'the program told', 5000);
|
|
eq('the page locked the window\'s canvas', document.pointerLockElement === (scene ? document.getElementById('out') : wins.find((w) => w.title === 'game').canvas), true);
|
|
await move(gx + 30, gy + 20);
|
|
await move(gx + 70, gy + 10);
|
|
// The movement so far, said after each event: both moves in, or the
|
|
// last said at the time out.
|
|
await until(() => out.includes('rel 70 10'), 'relative motion', 5000).catch(() => {});
|
|
const last = out.filter((l) => /^rel /.test(l)).pop();
|
|
eq('the movement, relative', last, 'rel 70 10');
|
|
await input(key('rawKeyDown', 'Escape', 'Escape', 27), key('keyUp', 'Escape', 'Escape', 27));
|
|
// Headless Chromium need not take Escape as the user's: release() is the
|
|
// page's way out either way.
|
|
if (lock.locked) lock.release();
|
|
await until(() => !lock.locked && out.includes('unlocked'), 'the unlock', 5000);
|
|
eq('events', said, ['lock', 'unlock']);
|
|
// A persistent lock: the next click takes it again.
|
|
await click(gx, gy);
|
|
await until(() => lock.locked, 'locked again', 5000);
|
|
lock.release();
|
|
await until(() => !lock.locked, 'released', 5000);
|
|
p.kill();
|
|
await p.exited;
|
|
await sys.shutdown();
|
|
}
|