mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
example/wasm-vt: use ghostty_type_json for struct layouts
Replace hardcoded byte offsets and struct sizes with dynamic lookups from the ghostty_type_json API. On WASM load, the type layout JSON is fetched once and parsed into a lookup table. Two helpers, fieldInfo and setField, use this metadata to write struct fields at the correct offsets with the correct types. This removes the need to manually maintain wasm32 struct layout comments and magic numbers for GhosttyTerminalOptions and GhosttyFormatterTerminalOptions, so the example stays correct if the struct layouts change.
This commit is contained in:
@@ -117,6 +117,7 @@
|
||||
<script>
|
||||
let wasmInstance = null;
|
||||
let wasmMemory = null;
|
||||
let typeLayout = null;
|
||||
|
||||
async function loadWasm() {
|
||||
try {
|
||||
@@ -136,6 +137,14 @@
|
||||
wasmInstance = wasmModule.instance;
|
||||
wasmMemory = wasmInstance.exports.memory;
|
||||
|
||||
// Load the type layout JSON from the library
|
||||
const lenPtr = wasmInstance.exports.ghostty_wasm_alloc_usize();
|
||||
const jsonPtr = wasmInstance.exports.ghostty_type_json(lenPtr);
|
||||
const jsonLen = new DataView(wasmMemory.buffer).getUint32(lenPtr, true);
|
||||
wasmInstance.exports.ghostty_wasm_free_usize(lenPtr);
|
||||
const jsonBytes = new Uint8Array(wasmMemory.buffer, jsonPtr, jsonLen);
|
||||
typeLayout = JSON.parse(new TextDecoder().decode(jsonBytes));
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error('Failed to load WASM:', e);
|
||||
@@ -146,6 +155,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Look up a field's offset and DataView setter from the type layout JSON.
|
||||
function fieldInfo(structName, fieldName) {
|
||||
const field = typeLayout[structName].fields[fieldName];
|
||||
return field;
|
||||
}
|
||||
|
||||
// Set a field in a DataView using the type layout JSON metadata.
|
||||
function setField(view, structName, fieldName, value) {
|
||||
const field = fieldInfo(structName, fieldName);
|
||||
switch (field.type) {
|
||||
case 'u8': case 'bool': view.setUint8(field.offset, value); break;
|
||||
case 'u16': view.setUint16(field.offset, value, true); break;
|
||||
case 'u32': case 'enum': view.setUint32(field.offset, value, true); break;
|
||||
case 'u64': view.setBigUint64(field.offset, BigInt(value), true); break;
|
||||
default: throw new Error(`Unsupported field type: ${field.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getBuffer() {
|
||||
return wasmMemory.buffer;
|
||||
}
|
||||
@@ -173,14 +200,13 @@
|
||||
const rows = parseInt(document.getElementById('rows').value, 10);
|
||||
const vtText = parseEscapes(document.getElementById('vtInput').value);
|
||||
|
||||
// GhosttyTerminalOptions: { u16 cols, u16 rows, u32(wasm) max_scrollback }
|
||||
// On wasm32, size_t is u32. Struct layout: cols(u16) + rows(u16) + max_scrollback(u32) = 8 bytes.
|
||||
const TERM_OPTS_SIZE = 8;
|
||||
const TERM_OPTS_SIZE = typeLayout['GhosttyTerminalOptions'].size;
|
||||
const optsPtr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(TERM_OPTS_SIZE);
|
||||
new Uint8Array(getBuffer(), optsPtr, TERM_OPTS_SIZE).fill(0);
|
||||
const optsView = new DataView(getBuffer(), optsPtr, TERM_OPTS_SIZE);
|
||||
optsView.setUint16(0, cols, true);
|
||||
optsView.setUint16(2, rows, true);
|
||||
optsView.setUint32(4, 0, true); // max_scrollback = 0
|
||||
setField(optsView, 'GhosttyTerminalOptions', 'cols', cols);
|
||||
setField(optsView, 'GhosttyTerminalOptions', 'rows', rows);
|
||||
setField(optsView, 'GhosttyTerminalOptions', 'max_scrollback', 0);
|
||||
|
||||
// Allocate pointer to receive the terminal handle
|
||||
const termPtrPtr = wasmInstance.exports.ghostty_wasm_alloc_opaque();
|
||||
@@ -204,26 +230,25 @@
|
||||
wasmInstance.exports.ghostty_wasm_free_u8_array(dataPtr, vtBytes.length);
|
||||
|
||||
// Create a plain-text formatter
|
||||
// GhosttyFormatterTerminalOptions layout on wasm32 (extern struct):
|
||||
// size: u32 @ 0 (= sizeof struct = 36)
|
||||
// emit: u32 @ 4 (GhosttyFormatterFormat enum)
|
||||
// unwrap: u8 @ 8
|
||||
// trim: u8 @ 9
|
||||
// pad: 2 bytes
|
||||
// extra: GhosttyFormatterTerminalExtra @ 12 (24 bytes, all zeros)
|
||||
// Total: 36 bytes
|
||||
const FMT_OPTS_SIZE = 36;
|
||||
const FMT_OPTS_SIZE = typeLayout['GhosttyFormatterTerminalOptions'].size;
|
||||
const fmtOptsPtr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(FMT_OPTS_SIZE);
|
||||
new Uint8Array(getBuffer(), fmtOptsPtr, FMT_OPTS_SIZE).fill(0);
|
||||
const fmtOptsView = new DataView(getBuffer(), fmtOptsPtr, FMT_OPTS_SIZE);
|
||||
fmtOptsView.setUint32(0, FMT_OPTS_SIZE, true); // size
|
||||
fmtOptsView.setUint32(4, GHOSTTY_FORMATTER_FORMAT_PLAIN, true); // emit
|
||||
fmtOptsView.setUint8(8, 0); // unwrap = false
|
||||
fmtOptsView.setUint8(9, 1); // trim = true
|
||||
// extra.size @ offset 12 (GhosttyFormatterTerminalExtra = 24 bytes)
|
||||
fmtOptsView.setUint32(12, 24, true);
|
||||
// extra.screen.size @ offset 24 (GhosttyFormatterScreenExtra = 12 bytes)
|
||||
fmtOptsView.setUint32(24, 12, true);
|
||||
setField(fmtOptsView, 'GhosttyFormatterTerminalOptions', 'size', FMT_OPTS_SIZE);
|
||||
setField(fmtOptsView, 'GhosttyFormatterTerminalOptions', 'emit', GHOSTTY_FORMATTER_FORMAT_PLAIN);
|
||||
setField(fmtOptsView, 'GhosttyFormatterTerminalOptions', 'unwrap', 0);
|
||||
setField(fmtOptsView, 'GhosttyFormatterTerminalOptions', 'trim', 1);
|
||||
|
||||
// Set the nested sized-struct `size` fields for extra and extra.screen
|
||||
const extraOffset = fieldInfo('GhosttyFormatterTerminalOptions', 'extra').offset;
|
||||
const extraSize = typeLayout['GhosttyFormatterTerminalExtra'].size;
|
||||
const extraSizeField = fieldInfo('GhosttyFormatterTerminalExtra', 'size');
|
||||
fmtOptsView.setUint32(extraOffset + extraSizeField.offset, extraSize, true);
|
||||
|
||||
const screenOffset = fieldInfo('GhosttyFormatterTerminalExtra', 'screen').offset;
|
||||
const screenSize = typeLayout['GhosttyFormatterScreenExtra'].size;
|
||||
const screenSizeField = fieldInfo('GhosttyFormatterScreenExtra', 'size');
|
||||
fmtOptsView.setUint32(extraOffset + screenOffset + screenSizeField.offset, screenSize, true);
|
||||
|
||||
const fmtPtrPtr = wasmInstance.exports.ghostty_wasm_alloc_opaque();
|
||||
const fmtResult = wasmInstance.exports.ghostty_formatter_terminal_new(
|
||||
|
||||
Reference in New Issue
Block a user