lib-vt: Wasm SGR helpers and example (#9362)
This adds some convenience functions for parsing SGR sequences WebAssembly and adds an example demonstrating SGR parsing in the browser.pull/9364/head
parent
19d1377659
commit
7d7c0bf5cd
|
|
@ -458,7 +458,7 @@
|
||||||
// Set UTF-8 text from the key event (the actual character produced)
|
// Set UTF-8 text from the key event (the actual character produced)
|
||||||
if (event.key.length === 1) {
|
if (event.key.length === 1) {
|
||||||
const utf8Bytes = new TextEncoder().encode(event.key);
|
const utf8Bytes = new TextEncoder().encode(event.key);
|
||||||
const utf8Ptr = wasmInstance.exports.ghostty_wasm_alloc_buffer(utf8Bytes.length);
|
const utf8Ptr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(utf8Bytes.length);
|
||||||
new Uint8Array(getBuffer()).set(utf8Bytes, utf8Ptr);
|
new Uint8Array(getBuffer()).set(utf8Bytes, utf8Ptr);
|
||||||
wasmInstance.exports.ghostty_key_event_set_utf8(eventPtr, utf8Ptr, utf8Bytes.length);
|
wasmInstance.exports.ghostty_key_event_set_utf8(eventPtr, utf8Ptr, utf8Bytes.length);
|
||||||
}
|
}
|
||||||
|
|
@ -477,7 +477,7 @@
|
||||||
|
|
||||||
const required = new DataView(getBuffer()).getUint32(requiredPtr, true);
|
const required = new DataView(getBuffer()).getUint32(requiredPtr, true);
|
||||||
|
|
||||||
const bufPtr = wasmInstance.exports.ghostty_wasm_alloc_buffer(required);
|
const bufPtr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(required);
|
||||||
const writtenPtr = wasmInstance.exports.ghostty_wasm_alloc_usize();
|
const writtenPtr = wasmInstance.exports.ghostty_wasm_alloc_usize();
|
||||||
const encodeResult = wasmInstance.exports.ghostty_key_encoder_encode(
|
const encodeResult = wasmInstance.exports.ghostty_key_encoder_encode(
|
||||||
encoderPtr, eventPtr, bufPtr, required, writtenPtr
|
encoderPtr, eventPtr, bufPtr, required, writtenPtr
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
# WebAssembly SGR Parser Example
|
||||||
|
|
||||||
|
This example demonstrates how to use the Ghostty VT library from WebAssembly
|
||||||
|
to parse terminal SGR (Select Graphic Rendition) sequences and extract text
|
||||||
|
styling attributes.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
First, build the WebAssembly module:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zig build lib-vt -Dtarget=wasm32-freestanding -Doptimize=ReleaseSmall
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create `zig-out/bin/ghostty-vt.wasm`.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
**Important:** You must serve this via HTTP, not open it as a file directly.
|
||||||
|
Browsers block loading WASM files from `file://` URLs.
|
||||||
|
|
||||||
|
From the **root of the ghostty repository**, serve with a local HTTP server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using Python (recommended)
|
||||||
|
python3 -m http.server 8000
|
||||||
|
|
||||||
|
# Or using Node.js
|
||||||
|
npx serve .
|
||||||
|
|
||||||
|
# Or using PHP
|
||||||
|
php -S localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open your browser to:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8000/example/wasm-sgr/
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,457 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Ghostty VT SGR Parser - WebAssembly Example</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 40px auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.input-section {
|
||||||
|
background: #f9f9f9;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
.input-section h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background: #0066cc;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background: #0052a3;
|
||||||
|
}
|
||||||
|
button:disabled {
|
||||||
|
background: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.output {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
background: #fee;
|
||||||
|
border-color: #faa;
|
||||||
|
color: #c00;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.attribute {
|
||||||
|
padding: 8px;
|
||||||
|
margin: 5px 0;
|
||||||
|
background: white;
|
||||||
|
border-left: 3px solid #0066cc;
|
||||||
|
}
|
||||||
|
.attribute-name {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #0066cc;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Ghostty VT SGR Parser - WebAssembly Example</h1>
|
||||||
|
<p>This example demonstrates parsing terminal SGR (Select Graphic Rendition) sequences using the Ghostty VT WebAssembly module.</p>
|
||||||
|
|
||||||
|
<div class="status" id="status">Loading WebAssembly module...</div>
|
||||||
|
|
||||||
|
<div class="input-section">
|
||||||
|
<h3>SGR Sequence</h3>
|
||||||
|
<label for="sequence">Enter SGR sequence (numbers separated by ':' or ';'):</label>
|
||||||
|
<textarea id="sequence" rows="2" disabled>4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136</textarea>
|
||||||
|
<p style="font-size: 13px; color: #666; margin-top: 5px;">The parser runs live as you type.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="output" class="output">Waiting for input...</div>
|
||||||
|
|
||||||
|
<p><strong>Note:</strong> This example must be served via HTTP (not opened directly as a file). See the README for instructions.</p>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let wasmInstance = null;
|
||||||
|
let wasmMemory = null;
|
||||||
|
|
||||||
|
async function loadWasm() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('../../zig-out/bin/ghostty-vt.wasm');
|
||||||
|
const wasmBytes = await response.arrayBuffer();
|
||||||
|
|
||||||
|
const wasmModule = await WebAssembly.instantiate(wasmBytes, {
|
||||||
|
env: {
|
||||||
|
log: (ptr, len) => {
|
||||||
|
const bytes = new Uint8Array(wasmModule.instance.exports.memory.buffer, ptr, len);
|
||||||
|
const text = new TextDecoder().decode(bytes);
|
||||||
|
console.log('[wasm]', text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wasmInstance = wasmModule.instance;
|
||||||
|
wasmMemory = wasmInstance.exports.memory;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to load WASM:', e);
|
||||||
|
if (window.location.protocol === 'file:') {
|
||||||
|
throw new Error('Cannot load WASM from file:// protocol. Please serve via HTTP (see README)');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBuffer() {
|
||||||
|
return wasmMemory.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SGR attribute tag values from include/ghostty/vt/sgr.h
|
||||||
|
const SGR_ATTR_TAGS = {
|
||||||
|
UNSET: 0,
|
||||||
|
UNKNOWN: 1,
|
||||||
|
BOLD: 2,
|
||||||
|
RESET_BOLD: 3,
|
||||||
|
ITALIC: 4,
|
||||||
|
RESET_ITALIC: 5,
|
||||||
|
FAINT: 6,
|
||||||
|
UNDERLINE: 7,
|
||||||
|
RESET_UNDERLINE: 8,
|
||||||
|
UNDERLINE_COLOR: 9,
|
||||||
|
UNDERLINE_COLOR_256: 10,
|
||||||
|
RESET_UNDERLINE_COLOR: 11,
|
||||||
|
OVERLINE: 12,
|
||||||
|
RESET_OVERLINE: 13,
|
||||||
|
BLINK: 14,
|
||||||
|
RESET_BLINK: 15,
|
||||||
|
INVERSE: 16,
|
||||||
|
RESET_INVERSE: 17,
|
||||||
|
INVISIBLE: 18,
|
||||||
|
RESET_INVISIBLE: 19,
|
||||||
|
STRIKETHROUGH: 20,
|
||||||
|
RESET_STRIKETHROUGH: 21,
|
||||||
|
DIRECT_COLOR_FG: 22,
|
||||||
|
DIRECT_COLOR_BG: 23,
|
||||||
|
BG_8: 24,
|
||||||
|
FG_8: 25,
|
||||||
|
RESET_FG: 26,
|
||||||
|
RESET_BG: 27,
|
||||||
|
BRIGHT_BG_8: 28,
|
||||||
|
BRIGHT_FG_8: 29,
|
||||||
|
BG_256: 30,
|
||||||
|
FG_256: 31
|
||||||
|
};
|
||||||
|
|
||||||
|
// Underline style values
|
||||||
|
const UNDERLINE_STYLES = {
|
||||||
|
0: 'none',
|
||||||
|
1: 'single',
|
||||||
|
2: 'double',
|
||||||
|
3: 'curly',
|
||||||
|
4: 'dotted',
|
||||||
|
5: 'dashed'
|
||||||
|
};
|
||||||
|
|
||||||
|
function getTagName(tag) {
|
||||||
|
for (const [name, value] of Object.entries(SGR_ATTR_TAGS)) {
|
||||||
|
if (value === tag) return name;
|
||||||
|
}
|
||||||
|
return `UNKNOWN(${tag})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSGR() {
|
||||||
|
const outputDiv = document.getElementById('output');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sequenceText = document.getElementById('sequence').value.trim();
|
||||||
|
|
||||||
|
if (!sequenceText) {
|
||||||
|
outputDiv.className = 'output';
|
||||||
|
outputDiv.textContent = 'Enter an SGR sequence to parse...';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the raw sequence into parameters and separators
|
||||||
|
const params = [];
|
||||||
|
const separators = [];
|
||||||
|
let currentNum = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < sequenceText.length; i++) {
|
||||||
|
const char = sequenceText[i];
|
||||||
|
|
||||||
|
if (char === ':' || char === ';') {
|
||||||
|
if (currentNum) {
|
||||||
|
const num = parseInt(currentNum, 10);
|
||||||
|
if (isNaN(num) || num < 0 || num > 65535) {
|
||||||
|
throw new Error(`Invalid parameter: ${currentNum}`);
|
||||||
|
}
|
||||||
|
params.push(num);
|
||||||
|
separators.push(char);
|
||||||
|
currentNum = '';
|
||||||
|
}
|
||||||
|
} else if (char >= '0' && char <= '9') {
|
||||||
|
currentNum += char;
|
||||||
|
} else if (char !== ' ' && char !== '\t' && char !== '\n') {
|
||||||
|
throw new Error(`Invalid character in sequence: '${char}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't forget the last number
|
||||||
|
if (currentNum) {
|
||||||
|
const num = parseInt(currentNum, 10);
|
||||||
|
if (isNaN(num) || num < 0 || num > 65535) {
|
||||||
|
throw new Error(`Invalid parameter: ${currentNum}`);
|
||||||
|
}
|
||||||
|
params.push(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.length === 0) {
|
||||||
|
outputDiv.className = 'output error';
|
||||||
|
outputDiv.textContent = 'Error: No parameters found in sequence';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create SGR parser
|
||||||
|
const parserPtrPtr = wasmInstance.exports.ghostty_wasm_alloc_opaque();
|
||||||
|
const result = wasmInstance.exports.ghostty_sgr_new(0, parserPtrPtr);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
throw new Error(`ghostty_sgr_new failed with result ${result}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parserPtr = new DataView(getBuffer()).getUint32(parserPtrPtr, true);
|
||||||
|
|
||||||
|
// Allocate and set parameters
|
||||||
|
const paramsPtr = wasmInstance.exports.ghostty_wasm_alloc_u16_array(params.length);
|
||||||
|
const paramsView = new Uint16Array(getBuffer(), paramsPtr, params.length);
|
||||||
|
params.forEach((p, i) => paramsView[i] = p);
|
||||||
|
|
||||||
|
// Allocate and set separators (or use null if empty)
|
||||||
|
let sepsPtr = 0;
|
||||||
|
if (separators.length > 0) {
|
||||||
|
sepsPtr = wasmInstance.exports.ghostty_wasm_alloc_u8_array(separators.length);
|
||||||
|
const sepsView = new Uint8Array(getBuffer(), sepsPtr, separators.length);
|
||||||
|
separators.forEach((s, i) => sepsView[i] = s.charCodeAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set parameters in parser
|
||||||
|
const setResult = wasmInstance.exports.ghostty_sgr_set_params(
|
||||||
|
parserPtr,
|
||||||
|
paramsPtr,
|
||||||
|
sepsPtr,
|
||||||
|
params.length
|
||||||
|
);
|
||||||
|
|
||||||
|
if (setResult !== 0) {
|
||||||
|
throw new Error(`ghostty_sgr_set_params failed with result ${setResult}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build output
|
||||||
|
let output = 'Parsing SGR sequence:\n';
|
||||||
|
output += 'ESC[';
|
||||||
|
params.forEach((p, i) => {
|
||||||
|
if (i > 0) output += separators[i - 1];
|
||||||
|
output += p;
|
||||||
|
});
|
||||||
|
output += 'm\n\n';
|
||||||
|
|
||||||
|
// Iterate through attributes
|
||||||
|
const attrPtr = wasmInstance.exports.ghostty_wasm_alloc_sgr_attribute();
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
while (wasmInstance.exports.ghostty_sgr_next(parserPtr, attrPtr)) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
// Use the new ghostty_sgr_attribute_tag getter function
|
||||||
|
const tag = wasmInstance.exports.ghostty_sgr_attribute_tag(attrPtr);
|
||||||
|
|
||||||
|
// Use ghostty_sgr_attribute_value to get a pointer to the value union
|
||||||
|
const valuePtr = wasmInstance.exports.ghostty_sgr_attribute_value(attrPtr);
|
||||||
|
|
||||||
|
output += `Attribute ${count}: `;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case SGR_ATTR_TAGS.UNDERLINE: {
|
||||||
|
const view = new DataView(getBuffer(), valuePtr, 4);
|
||||||
|
const style = view.getUint32(0, true);
|
||||||
|
output += `Underline style = ${UNDERLINE_STYLES[style] || `unknown(${style})`}\n`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.DIRECT_COLOR_FG: {
|
||||||
|
// Use ghostty_color_rgb_get to extract RGB components
|
||||||
|
const rPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
const gPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
const bPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
|
||||||
|
wasmInstance.exports.ghostty_color_rgb_get(valuePtr, rPtr, gPtr, bPtr);
|
||||||
|
|
||||||
|
const r = new Uint8Array(getBuffer(), rPtr, 1)[0];
|
||||||
|
const g = new Uint8Array(getBuffer(), gPtr, 1)[0];
|
||||||
|
const b = new Uint8Array(getBuffer(), bPtr, 1)[0];
|
||||||
|
|
||||||
|
output += `Foreground RGB = (${r}, ${g}, ${b})\n`;
|
||||||
|
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(rPtr);
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(gPtr);
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(bPtr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.DIRECT_COLOR_BG: {
|
||||||
|
// Use ghostty_color_rgb_get to extract RGB components
|
||||||
|
const rPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
const gPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
const bPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
|
||||||
|
wasmInstance.exports.ghostty_color_rgb_get(valuePtr, rPtr, gPtr, bPtr);
|
||||||
|
|
||||||
|
const r = new Uint8Array(getBuffer(), rPtr, 1)[0];
|
||||||
|
const g = new Uint8Array(getBuffer(), gPtr, 1)[0];
|
||||||
|
const b = new Uint8Array(getBuffer(), bPtr, 1)[0];
|
||||||
|
|
||||||
|
output += `Background RGB = (${r}, ${g}, ${b})\n`;
|
||||||
|
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(rPtr);
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(gPtr);
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(bPtr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.UNDERLINE_COLOR: {
|
||||||
|
// Use ghostty_color_rgb_get to extract RGB components
|
||||||
|
const rPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
const gPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
const bPtr = wasmInstance.exports.ghostty_wasm_alloc_u8();
|
||||||
|
|
||||||
|
wasmInstance.exports.ghostty_color_rgb_get(valuePtr, rPtr, gPtr, bPtr);
|
||||||
|
|
||||||
|
const r = new Uint8Array(getBuffer(), rPtr, 1)[0];
|
||||||
|
const g = new Uint8Array(getBuffer(), gPtr, 1)[0];
|
||||||
|
const b = new Uint8Array(getBuffer(), bPtr, 1)[0];
|
||||||
|
|
||||||
|
output += `Underline color RGB = (${r}, ${g}, ${b})\n`;
|
||||||
|
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(rPtr);
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(gPtr);
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_u8(bPtr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.FG_8:
|
||||||
|
case SGR_ATTR_TAGS.BG_8:
|
||||||
|
case SGR_ATTR_TAGS.FG_256:
|
||||||
|
case SGR_ATTR_TAGS.BG_256:
|
||||||
|
case SGR_ATTR_TAGS.UNDERLINE_COLOR_256: {
|
||||||
|
const view = new DataView(getBuffer(), valuePtr, 1);
|
||||||
|
const color = view.getUint8(0);
|
||||||
|
const colorType = tag === SGR_ATTR_TAGS.FG_8 ? 'Foreground 8-color' :
|
||||||
|
tag === SGR_ATTR_TAGS.BG_8 ? 'Background 8-color' :
|
||||||
|
tag === SGR_ATTR_TAGS.FG_256 ? 'Foreground 256-color' :
|
||||||
|
tag === SGR_ATTR_TAGS.BG_256 ? 'Background 256-color' :
|
||||||
|
'Underline 256-color';
|
||||||
|
output += `${colorType} = ${color}\n`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.BOLD:
|
||||||
|
output += 'Bold\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.ITALIC:
|
||||||
|
output += 'Italic\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.UNSET:
|
||||||
|
output += 'Reset all attributes\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SGR_ATTR_TAGS.UNKNOWN:
|
||||||
|
output += 'Unknown attribute\n';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
output += `Other attribute (tag=${getTagName(tag)})\n`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output += `\nTotal attributes parsed: ${count}`;
|
||||||
|
|
||||||
|
outputDiv.className = 'output';
|
||||||
|
outputDiv.textContent = output;
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
wasmInstance.exports.ghostty_wasm_free_sgr_attribute(attrPtr);
|
||||||
|
wasmInstance.exports.ghostty_sgr_free(parserPtr);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Parse error:', e);
|
||||||
|
outputDiv.className = 'output error';
|
||||||
|
outputDiv.textContent = `Error: ${e.message}\n\nStack trace:\n${e.stack}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
const statusDiv = document.getElementById('status');
|
||||||
|
const sequenceInput = document.getElementById('sequence');
|
||||||
|
|
||||||
|
try {
|
||||||
|
statusDiv.textContent = 'Loading WebAssembly module...';
|
||||||
|
|
||||||
|
const loaded = await loadWasm();
|
||||||
|
if (!loaded) {
|
||||||
|
throw new Error('Failed to load WebAssembly module');
|
||||||
|
}
|
||||||
|
|
||||||
|
statusDiv.textContent = '';
|
||||||
|
sequenceInput.disabled = false;
|
||||||
|
|
||||||
|
// Parse live as user types
|
||||||
|
sequenceInput.addEventListener('input', parseSGR);
|
||||||
|
|
||||||
|
// Parse the default example on load
|
||||||
|
parseSGR();
|
||||||
|
} catch (e) {
|
||||||
|
statusDiv.textContent = `Error: ${e.message}`;
|
||||||
|
statusDiv.style.color = '#c00';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('DOMContentLoaded', init);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -70,6 +70,25 @@ typedef uint8_t GhosttyColorPaletteIndex;
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the RGB color components.
|
||||||
|
*
|
||||||
|
* This function extracts the individual red, green, and blue components
|
||||||
|
* from a GhosttyColorRgb value. Primarily useful in WebAssembly environments
|
||||||
|
* where accessing struct fields directly is difficult.
|
||||||
|
*
|
||||||
|
* @param color The RGB color value
|
||||||
|
* @param r Pointer to store the red component (0-255)
|
||||||
|
* @param g Pointer to store the green component (0-255)
|
||||||
|
* @param b Pointer to store the blue component (0-255)
|
||||||
|
*
|
||||||
|
* @ingroup sgr
|
||||||
|
*/
|
||||||
|
void ghostty_color_rgb_get(GhosttyColorRgb color,
|
||||||
|
uint8_t* r,
|
||||||
|
uint8_t* g,
|
||||||
|
uint8_t* b);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@
|
||||||
*
|
*
|
||||||
* The parser processes SGR parameters from CSI sequences (e.g., `ESC[1;31m`)
|
* The parser processes SGR parameters from CSI sequences (e.g., `ESC[1;31m`)
|
||||||
* and returns individual text attributes like bold, italic, colors, etc.
|
* and returns individual text attributes like bold, italic, colors, etc.
|
||||||
* It supports both semicolon (`;`) and colon (`:`) separators, possibly mixed,
|
* It supports both semicolon (`;`) and colon (`:`) separators, possibly mixed,
|
||||||
* and handles various color formats including 8-color, 16-color, 256-color,
|
* and handles various color formats including 8-color, 16-color, 256-color,
|
||||||
* X11 named colors, and RGB in multiple formats.
|
* X11 named colors, and RGB in multiple formats.
|
||||||
*
|
*
|
||||||
* ## Basic Usage
|
* ## Basic Usage
|
||||||
*
|
*
|
||||||
|
|
@ -35,18 +35,18 @@
|
||||||
* #include <assert.h>
|
* #include <assert.h>
|
||||||
* #include <stdio.h>
|
* #include <stdio.h>
|
||||||
* #include <ghostty/vt.h>
|
* #include <ghostty/vt.h>
|
||||||
*
|
*
|
||||||
* int main() {
|
* int main() {
|
||||||
* // Create parser
|
* // Create parser
|
||||||
* GhosttySgrParser parser;
|
* GhosttySgrParser parser;
|
||||||
* GhosttyResult result = ghostty_sgr_new(NULL, &parser);
|
* GhosttyResult result = ghostty_sgr_new(NULL, &parser);
|
||||||
* assert(result == GHOSTTY_SUCCESS);
|
* assert(result == GHOSTTY_SUCCESS);
|
||||||
*
|
*
|
||||||
* // Parse "bold, red foreground" sequence: ESC[1;31m
|
* // Parse "bold, red foreground" sequence: ESC[1;31m
|
||||||
* uint16_t params[] = {1, 31};
|
* uint16_t params[] = {1, 31};
|
||||||
* result = ghostty_sgr_set_params(parser, params, NULL, 2);
|
* result = ghostty_sgr_set_params(parser, params, NULL, 2);
|
||||||
* assert(result == GHOSTTY_SUCCESS);
|
* assert(result == GHOSTTY_SUCCESS);
|
||||||
*
|
*
|
||||||
* // Iterate through attributes
|
* // Iterate through attributes
|
||||||
* GhosttySgrAttribute attr;
|
* GhosttySgrAttribute attr;
|
||||||
* while (ghostty_sgr_next(parser, &attr)) {
|
* while (ghostty_sgr_next(parser, &attr)) {
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
* break;
|
* break;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* // Cleanup
|
* // Cleanup
|
||||||
* ghostty_sgr_free(parser);
|
* ghostty_sgr_free(parser);
|
||||||
* return 0;
|
* return 0;
|
||||||
|
|
@ -71,12 +71,12 @@
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ghostty/vt/allocator.h>
|
||||||
|
#include <ghostty/vt/color.h>
|
||||||
|
#include <ghostty/vt/result.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <ghostty/vt/result.h>
|
|
||||||
#include <ghostty/vt/allocator.h>
|
|
||||||
#include <ghostty/vt/color.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -84,13 +84,13 @@ extern "C" {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opaque handle to an SGR parser instance.
|
* Opaque handle to an SGR parser instance.
|
||||||
*
|
*
|
||||||
* This handle represents an SGR (Select Graphic Rendition) parser that can
|
* This handle represents an SGR (Select Graphic Rendition) parser that can
|
||||||
* be used to parse SGR sequences and extract individual text attributes.
|
* be used to parse SGR sequences and extract individual text attributes.
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
typedef struct GhosttySgrParser *GhosttySgrParser;
|
typedef struct GhosttySgrParser* GhosttySgrParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SGR attribute tags.
|
* SGR attribute tags.
|
||||||
|
|
@ -158,9 +158,9 @@ typedef enum {
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const uint16_t *full_ptr;
|
const uint16_t* full_ptr;
|
||||||
size_t full_len;
|
size_t full_len;
|
||||||
const uint16_t *partial_ptr;
|
const uint16_t* partial_ptr;
|
||||||
size_t partial_len;
|
size_t partial_len;
|
||||||
} GhosttySgrUnknown;
|
} GhosttySgrUnknown;
|
||||||
|
|
||||||
|
|
@ -196,7 +196,7 @@ typedef union {
|
||||||
* Always check the tag field to determine which value union member is valid.
|
* Always check the tag field to determine which value union member is valid.
|
||||||
*
|
*
|
||||||
* Attributes without associated data (e.g., GHOSTTY_SGR_ATTR_BOLD) can be
|
* Attributes without associated data (e.g., GHOSTTY_SGR_ATTR_BOLD) can be
|
||||||
* identified by tag alone; the value union is not used for these and
|
* identified by tag alone; the value union is not used for these and
|
||||||
* the memory in the value field is undefined.
|
* the memory in the value field is undefined.
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
|
|
@ -208,94 +208,182 @@ typedef struct {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new SGR parser instance.
|
* Create a new SGR parser instance.
|
||||||
*
|
*
|
||||||
* Creates a new SGR (Select Graphic Rendition) parser using the provided
|
* Creates a new SGR (Select Graphic Rendition) parser using the provided
|
||||||
* allocator. The parser must be freed using ghostty_sgr_free() when
|
* allocator. The parser must be freed using ghostty_sgr_free() when
|
||||||
* no longer needed.
|
* no longer needed.
|
||||||
*
|
*
|
||||||
* @param allocator Pointer to the allocator to use for memory management, or NULL to use the default allocator
|
* @param allocator Pointer to the allocator to use for memory management, or
|
||||||
|
* NULL to use the default allocator
|
||||||
* @param parser Pointer to store the created parser handle
|
* @param parser Pointer to store the created parser handle
|
||||||
* @return GHOSTTY_SUCCESS on success, or an error code on failure
|
* @return GHOSTTY_SUCCESS on success, or an error code on failure
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
GhosttyResult ghostty_sgr_new(const GhosttyAllocator *allocator, GhosttySgrParser *parser);
|
GhosttyResult ghostty_sgr_new(const GhosttyAllocator* allocator,
|
||||||
|
GhosttySgrParser* parser);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free an SGR parser instance.
|
* Free an SGR parser instance.
|
||||||
*
|
*
|
||||||
* Releases all resources associated with the SGR parser. After this call,
|
* Releases all resources associated with the SGR parser. After this call,
|
||||||
* the parser handle becomes invalid and must not be used. This includes
|
* the parser handle becomes invalid and must not be used. This includes
|
||||||
* any attributes previously returned by ghostty_sgr_next().
|
* any attributes previously returned by ghostty_sgr_next().
|
||||||
*
|
*
|
||||||
* @param parser The parser handle to free (may be NULL)
|
* @param parser The parser handle to free (may be NULL)
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
void ghostty_sgr_free(GhosttySgrParser parser);
|
void ghostty_sgr_free(GhosttySgrParser parser);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset an SGR parser instance to the beginning of the parameter list.
|
* Reset an SGR parser instance to the beginning of the parameter list.
|
||||||
*
|
*
|
||||||
* Resets the parser's iteration state without clearing the parameters.
|
* Resets the parser's iteration state without clearing the parameters.
|
||||||
* After calling this, ghostty_sgr_next() will start from the beginning
|
* After calling this, ghostty_sgr_next() will start from the beginning
|
||||||
* of the parameter list again.
|
* of the parameter list again.
|
||||||
*
|
*
|
||||||
* @param parser The parser handle to reset, must not be NULL
|
* @param parser The parser handle to reset, must not be NULL
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
void ghostty_sgr_reset(GhosttySgrParser parser);
|
void ghostty_sgr_reset(GhosttySgrParser parser);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set SGR parameters for parsing.
|
* Set SGR parameters for parsing.
|
||||||
*
|
*
|
||||||
* Sets the SGR parameter list to parse. Parameters are the numeric values
|
* Sets the SGR parameter list to parse. Parameters are the numeric values
|
||||||
* from a CSI SGR sequence (e.g., for `ESC[1;31m`, params would be {1, 31}).
|
* from a CSI SGR sequence (e.g., for `ESC[1;31m`, params would be {1, 31}).
|
||||||
*
|
*
|
||||||
* The separators array optionally specifies the separator type for each
|
* The separators array optionally specifies the separator type for each
|
||||||
* parameter position. Each byte should be either ';' for semicolon or ':'
|
* parameter position. Each byte should be either ';' for semicolon or ':'
|
||||||
* for colon. This is needed for certain color formats that use colon
|
* for colon. This is needed for certain color formats that use colon
|
||||||
* separators (e.g., `ESC[4:3m` for curly underline). Any invalid separator
|
* separators (e.g., `ESC[4:3m` for curly underline). Any invalid separator
|
||||||
* values are treated as semicolons. The separators array must have the same
|
* values are treated as semicolons. The separators array must have the same
|
||||||
* length as the params array, if it is not NULL.
|
* length as the params array, if it is not NULL.
|
||||||
*
|
*
|
||||||
* If separators is NULL, all parameters are assumed to be semicolon-separated.
|
* If separators is NULL, all parameters are assumed to be semicolon-separated.
|
||||||
*
|
*
|
||||||
* This function makes an internal copy of the parameter and separator data,
|
* This function makes an internal copy of the parameter and separator data,
|
||||||
* so the caller can safely free or modify the input arrays after this call.
|
* so the caller can safely free or modify the input arrays after this call.
|
||||||
*
|
*
|
||||||
* After calling this function, the parser is automatically reset and ready
|
* After calling this function, the parser is automatically reset and ready
|
||||||
* to iterate from the beginning.
|
* to iterate from the beginning.
|
||||||
*
|
*
|
||||||
* @param parser The parser handle, must not be NULL
|
* @param parser The parser handle, must not be NULL
|
||||||
* @param params Array of SGR parameter values
|
* @param params Array of SGR parameter values
|
||||||
* @param separators Optional array of separator characters (';' or ':'), or NULL
|
* @param separators Optional array of separator characters (';' or ':'), or
|
||||||
|
* NULL
|
||||||
* @param len Number of parameters (and separators if provided)
|
* @param len Number of parameters (and separators if provided)
|
||||||
* @return GHOSTTY_SUCCESS on success, or an error code on failure
|
* @return GHOSTTY_SUCCESS on success, or an error code on failure
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
GhosttyResult ghostty_sgr_set_params(
|
GhosttyResult ghostty_sgr_set_params(GhosttySgrParser parser,
|
||||||
GhosttySgrParser parser,
|
const uint16_t* params,
|
||||||
const uint16_t *params,
|
const char* separators,
|
||||||
const char *separators,
|
size_t len);
|
||||||
size_t len);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next SGR attribute.
|
* Get the next SGR attribute.
|
||||||
*
|
*
|
||||||
* Parses and returns the next attribute from the parameter list.
|
* Parses and returns the next attribute from the parameter list.
|
||||||
* Call this function repeatedly until it returns false to process
|
* Call this function repeatedly until it returns false to process
|
||||||
* all attributes in the sequence.
|
* all attributes in the sequence.
|
||||||
*
|
*
|
||||||
* @param parser The parser handle, must not be NULL
|
* @param parser The parser handle, must not be NULL
|
||||||
* @param attr Pointer to store the next attribute
|
* @param attr Pointer to store the next attribute
|
||||||
* @return true if an attribute was returned, false if no more attributes
|
* @return true if an attribute was returned, false if no more attributes
|
||||||
*
|
*
|
||||||
* @ingroup sgr
|
* @ingroup sgr
|
||||||
*/
|
*/
|
||||||
bool ghostty_sgr_next(GhosttySgrParser parser, GhosttySgrAttribute *attr);
|
bool ghostty_sgr_next(GhosttySgrParser parser, GhosttySgrAttribute* attr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full parameter list from an unknown SGR attribute.
|
||||||
|
*
|
||||||
|
* This function retrieves the full parameter list that was provided to the
|
||||||
|
* parser when an unknown attribute was encountered. Primarily useful in
|
||||||
|
* WebAssembly environments where accessing struct fields directly is difficult.
|
||||||
|
*
|
||||||
|
* @param unknown The unknown attribute data
|
||||||
|
* @param ptr Pointer to store the pointer to the parameter array (may be NULL)
|
||||||
|
* @return The length of the full parameter array
|
||||||
|
*
|
||||||
|
* @ingroup sgr
|
||||||
|
*/
|
||||||
|
size_t ghostty_sgr_unknown_full(GhosttySgrUnknown unknown,
|
||||||
|
const uint16_t** ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the partial parameter list from an unknown SGR attribute.
|
||||||
|
*
|
||||||
|
* This function retrieves the partial parameter list where parsing stopped
|
||||||
|
* when an unknown attribute was encountered. Primarily useful in WebAssembly
|
||||||
|
* environments where accessing struct fields directly is difficult.
|
||||||
|
*
|
||||||
|
* @param unknown The unknown attribute data
|
||||||
|
* @param ptr Pointer to store the pointer to the parameter array (may be NULL)
|
||||||
|
* @return The length of the partial parameter array
|
||||||
|
*
|
||||||
|
* @ingroup sgr
|
||||||
|
*/
|
||||||
|
size_t ghostty_sgr_unknown_partial(GhosttySgrUnknown unknown,
|
||||||
|
const uint16_t** ptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the tag from an SGR attribute.
|
||||||
|
*
|
||||||
|
* This function extracts the tag that identifies which type of attribute
|
||||||
|
* this is. Primarily useful in WebAssembly environments where accessing
|
||||||
|
* struct fields directly is difficult.
|
||||||
|
*
|
||||||
|
* @param attr The SGR attribute
|
||||||
|
* @return The attribute tag
|
||||||
|
*
|
||||||
|
* @ingroup sgr
|
||||||
|
*/
|
||||||
|
GhosttySgrAttributeTag ghostty_sgr_attribute_tag(GhosttySgrAttribute attr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value from an SGR attribute.
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the value union from an SGR attribute. Use
|
||||||
|
* the tag to determine which field of the union is valid. Primarily useful in
|
||||||
|
* WebAssembly environments where accessing struct fields directly is difficult.
|
||||||
|
*
|
||||||
|
* @param attr Pointer to the SGR attribute
|
||||||
|
* @return Pointer to the attribute value union
|
||||||
|
*
|
||||||
|
* @ingroup sgr
|
||||||
|
*/
|
||||||
|
GhosttySgrAttributeValue* ghostty_sgr_attribute_value(
|
||||||
|
GhosttySgrAttribute* attr);
|
||||||
|
|
||||||
|
#ifdef __wasm__
|
||||||
|
/**
|
||||||
|
* Allocate memory for an SGR attribute (WebAssembly only).
|
||||||
|
*
|
||||||
|
* This is a convenience function for WebAssembly environments to allocate
|
||||||
|
* memory for an SGR attribute structure that can be passed to ghostty_sgr_next.
|
||||||
|
*
|
||||||
|
* @return Pointer to the allocated attribute structure
|
||||||
|
*
|
||||||
|
* @ingroup wasm
|
||||||
|
*/
|
||||||
|
GhosttySgrAttribute* ghostty_wasm_alloc_sgr_attribute(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free memory for an SGR attribute (WebAssembly only).
|
||||||
|
*
|
||||||
|
* Frees memory allocated by ghostty_wasm_alloc_sgr_attribute.
|
||||||
|
*
|
||||||
|
* @param attr Pointer to the attribute structure to free
|
||||||
|
*
|
||||||
|
* @ingroup wasm
|
||||||
|
*/
|
||||||
|
void ghostty_wasm_free_sgr_attribute(GhosttySgrAttribute* attr);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
*
|
*
|
||||||
* // Allocate output buffer and size pointer
|
* // Allocate output buffer and size pointer
|
||||||
* const bufferSize = 32;
|
* const bufferSize = 32;
|
||||||
* const bufPtr = exports.ghostty_wasm_alloc_buffer(bufferSize);
|
* const bufPtr = exports.ghostty_wasm_alloc_u8_array(bufferSize);
|
||||||
* const writtenPtr = exports.ghostty_wasm_alloc_usize();
|
* const writtenPtr = exports.ghostty_wasm_alloc_usize();
|
||||||
*
|
*
|
||||||
* // Encode the key event
|
* // Encode the key event
|
||||||
|
|
@ -85,22 +85,40 @@ void** ghostty_wasm_alloc_opaque(void);
|
||||||
void ghostty_wasm_free_opaque(void **ptr);
|
void ghostty_wasm_free_opaque(void **ptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a buffer of the specified length.
|
* Allocate an array of uint8_t values.
|
||||||
*
|
*
|
||||||
* @param len Number of bytes to allocate
|
* @param len Number of uint8_t elements to allocate
|
||||||
* @return Pointer to allocated buffer, or NULL if allocation failed
|
* @return Pointer to allocated array, or NULL if allocation failed
|
||||||
* @ingroup wasm
|
* @ingroup wasm
|
||||||
*/
|
*/
|
||||||
uint8_t* ghostty_wasm_alloc_buffer(size_t len);
|
uint8_t* ghostty_wasm_alloc_u8_array(size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a buffer allocated by ghostty_wasm_alloc_buffer().
|
* Free an array allocated by ghostty_wasm_alloc_u8_array().
|
||||||
*
|
*
|
||||||
* @param ptr Pointer to the buffer to free, or NULL (NULL is safely ignored)
|
* @param ptr Pointer to the array to free, or NULL (NULL is safely ignored)
|
||||||
* @param len Length of the buffer (must match the length passed to alloc)
|
* @param len Length of the array (must match the length passed to alloc)
|
||||||
* @ingroup wasm
|
* @ingroup wasm
|
||||||
*/
|
*/
|
||||||
void ghostty_wasm_free_buffer(uint8_t *ptr, size_t len);
|
void ghostty_wasm_free_u8_array(uint8_t *ptr, size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate an array of uint16_t values.
|
||||||
|
*
|
||||||
|
* @param len Number of uint16_t elements to allocate
|
||||||
|
* @return Pointer to allocated array, or NULL if allocation failed
|
||||||
|
* @ingroup wasm
|
||||||
|
*/
|
||||||
|
uint16_t* ghostty_wasm_alloc_u16_array(size_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free an array allocated by ghostty_wasm_alloc_u16_array().
|
||||||
|
*
|
||||||
|
* @param ptr Pointer to the array to free, or NULL (NULL is safely ignored)
|
||||||
|
* @param len Length of the array (must match the length passed to alloc)
|
||||||
|
* @ingroup wasm
|
||||||
|
*/
|
||||||
|
void ghostty_wasm_free_u16_array(uint16_t *ptr, size_t len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate a single uint8_t value.
|
* Allocate a single uint8_t value.
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,21 @@ pub fn freeOpaque(ptr: ?*Opaque) callconv(.c) void {
|
||||||
if (ptr) |p| alloc.destroy(p);
|
if (ptr) |p| alloc.destroy(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocBuffer(len: usize) callconv(.c) ?[*]u8 {
|
pub fn allocU8Array(len: usize) callconv(.c) ?[*]u8 {
|
||||||
const slice = alloc.alloc(u8, len) catch return null;
|
const slice = alloc.alloc(u8, len) catch return null;
|
||||||
return slice.ptr;
|
return slice.ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn freeBuffer(ptr: ?[*]u8, len: usize) callconv(.c) void {
|
pub fn freeU8Array(ptr: ?[*]u8, len: usize) callconv(.c) void {
|
||||||
|
if (ptr) |p| alloc.free(p[0..len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocU16Array(len: usize) callconv(.c) ?[*]u16 {
|
||||||
|
const slice = alloc.alloc(u16, len) catch return null;
|
||||||
|
return slice.ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn freeU16Array(ptr: ?[*]u16, len: usize) callconv(.c) void {
|
||||||
if (ptr) |p| alloc.free(p[0..len]);
|
if (ptr) |p| alloc.free(p[0..len]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,23 +126,32 @@ comptime {
|
||||||
@export(&c.osc_command_type, .{ .name = "ghostty_osc_command_type" });
|
@export(&c.osc_command_type, .{ .name = "ghostty_osc_command_type" });
|
||||||
@export(&c.osc_command_data, .{ .name = "ghostty_osc_command_data" });
|
@export(&c.osc_command_data, .{ .name = "ghostty_osc_command_data" });
|
||||||
@export(&c.paste_is_safe, .{ .name = "ghostty_paste_is_safe" });
|
@export(&c.paste_is_safe, .{ .name = "ghostty_paste_is_safe" });
|
||||||
|
@export(&c.color_rgb_get, .{ .name = "ghostty_color_rgb_get" });
|
||||||
@export(&c.sgr_new, .{ .name = "ghostty_sgr_new" });
|
@export(&c.sgr_new, .{ .name = "ghostty_sgr_new" });
|
||||||
@export(&c.sgr_free, .{ .name = "ghostty_sgr_free" });
|
@export(&c.sgr_free, .{ .name = "ghostty_sgr_free" });
|
||||||
@export(&c.sgr_reset, .{ .name = "ghostty_sgr_reset" });
|
@export(&c.sgr_reset, .{ .name = "ghostty_sgr_reset" });
|
||||||
@export(&c.sgr_set_params, .{ .name = "ghostty_sgr_set_params" });
|
@export(&c.sgr_set_params, .{ .name = "ghostty_sgr_set_params" });
|
||||||
@export(&c.sgr_next, .{ .name = "ghostty_sgr_next" });
|
@export(&c.sgr_next, .{ .name = "ghostty_sgr_next" });
|
||||||
|
@export(&c.sgr_unknown_full, .{ .name = "ghostty_sgr_unknown_full" });
|
||||||
|
@export(&c.sgr_unknown_partial, .{ .name = "ghostty_sgr_unknown_partial" });
|
||||||
|
@export(&c.sgr_attribute_tag, .{ .name = "ghostty_sgr_attribute_tag" });
|
||||||
|
@export(&c.sgr_attribute_value, .{ .name = "ghostty_sgr_attribute_value" });
|
||||||
|
|
||||||
// On Wasm we need to export our allocator convenience functions.
|
// On Wasm we need to export our allocator convenience functions.
|
||||||
if (builtin.target.cpu.arch.isWasm()) {
|
if (builtin.target.cpu.arch.isWasm()) {
|
||||||
const alloc = @import("lib/allocator/convenience.zig");
|
const alloc = @import("lib/allocator/convenience.zig");
|
||||||
@export(&alloc.allocOpaque, .{ .name = "ghostty_wasm_alloc_opaque" });
|
@export(&alloc.allocOpaque, .{ .name = "ghostty_wasm_alloc_opaque" });
|
||||||
@export(&alloc.freeOpaque, .{ .name = "ghostty_wasm_free_opaque" });
|
@export(&alloc.freeOpaque, .{ .name = "ghostty_wasm_free_opaque" });
|
||||||
@export(&alloc.allocBuffer, .{ .name = "ghostty_wasm_alloc_buffer" });
|
@export(&alloc.allocU8Array, .{ .name = "ghostty_wasm_alloc_u8_array" });
|
||||||
@export(&alloc.freeBuffer, .{ .name = "ghostty_wasm_free_buffer" });
|
@export(&alloc.freeU8Array, .{ .name = "ghostty_wasm_free_u8_array" });
|
||||||
|
@export(&alloc.allocU16Array, .{ .name = "ghostty_wasm_alloc_u16_array" });
|
||||||
|
@export(&alloc.freeU16Array, .{ .name = "ghostty_wasm_free_u16_array" });
|
||||||
@export(&alloc.allocU8, .{ .name = "ghostty_wasm_alloc_u8" });
|
@export(&alloc.allocU8, .{ .name = "ghostty_wasm_alloc_u8" });
|
||||||
@export(&alloc.freeU8, .{ .name = "ghostty_wasm_free_u8" });
|
@export(&alloc.freeU8, .{ .name = "ghostty_wasm_free_u8" });
|
||||||
@export(&alloc.allocUsize, .{ .name = "ghostty_wasm_alloc_usize" });
|
@export(&alloc.allocUsize, .{ .name = "ghostty_wasm_alloc_usize" });
|
||||||
@export(&alloc.freeUsize, .{ .name = "ghostty_wasm_free_usize" });
|
@export(&alloc.freeUsize, .{ .name = "ghostty_wasm_free_usize" });
|
||||||
|
@export(&c.wasm_alloc_sgr_attribute, .{ .name = "ghostty_wasm_alloc_sgr_attribute" });
|
||||||
|
@export(&c.wasm_free_sgr_attribute, .{ .name = "ghostty_wasm_free_sgr_attribute" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
const color = @import("../color.zig");
|
||||||
|
|
||||||
|
pub fn rgb_get(
|
||||||
|
c: color.RGB.C,
|
||||||
|
r: *u8,
|
||||||
|
g: *u8,
|
||||||
|
b: *u8,
|
||||||
|
) callconv(.c) void {
|
||||||
|
r.* = c.r;
|
||||||
|
g.* = c.g;
|
||||||
|
b.* = c.b;
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub const color = @import("color.zig");
|
||||||
pub const osc = @import("osc.zig");
|
pub const osc = @import("osc.zig");
|
||||||
pub const key_event = @import("key_event.zig");
|
pub const key_event = @import("key_event.zig");
|
||||||
pub const key_encode = @import("key_encode.zig");
|
pub const key_encode = @import("key_encode.zig");
|
||||||
|
|
@ -13,11 +14,19 @@ pub const osc_end = osc.end;
|
||||||
pub const osc_command_type = osc.commandType;
|
pub const osc_command_type = osc.commandType;
|
||||||
pub const osc_command_data = osc.commandData;
|
pub const osc_command_data = osc.commandData;
|
||||||
|
|
||||||
|
pub const color_rgb_get = color.rgb_get;
|
||||||
|
|
||||||
pub const sgr_new = sgr.new;
|
pub const sgr_new = sgr.new;
|
||||||
pub const sgr_free = sgr.free;
|
pub const sgr_free = sgr.free;
|
||||||
pub const sgr_reset = sgr.reset;
|
pub const sgr_reset = sgr.reset;
|
||||||
pub const sgr_set_params = sgr.setParams;
|
pub const sgr_set_params = sgr.setParams;
|
||||||
pub const sgr_next = sgr.next;
|
pub const sgr_next = sgr.next;
|
||||||
|
pub const sgr_unknown_full = sgr.unknown_full;
|
||||||
|
pub const sgr_unknown_partial = sgr.unknown_partial;
|
||||||
|
pub const sgr_attribute_tag = sgr.attribute_tag;
|
||||||
|
pub const sgr_attribute_value = sgr.attribute_value;
|
||||||
|
pub const wasm_alloc_sgr_attribute = sgr.wasm_alloc_attribute;
|
||||||
|
pub const wasm_free_sgr_attribute = sgr.wasm_free_attribute;
|
||||||
|
|
||||||
pub const key_event_new = key_event.new;
|
pub const key_event_new = key_event.new;
|
||||||
pub const key_event_free = key_event.free;
|
pub const key_event_free = key_event.free;
|
||||||
|
|
@ -44,6 +53,7 @@ pub const key_encoder_encode = key_encode.encode;
|
||||||
pub const paste_is_safe = paste.is_safe;
|
pub const paste_is_safe = paste.is_safe;
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
_ = color;
|
||||||
_ = osc;
|
_ = osc;
|
||||||
_ = key_event;
|
_ = key_event;
|
||||||
_ = key_encode;
|
_ = key_encode;
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,45 @@ pub fn next(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unknown_full(
|
||||||
|
unknown: sgr.Attribute.Unknown.C,
|
||||||
|
ptr: ?*[*]const u16,
|
||||||
|
) callconv(.c) usize {
|
||||||
|
if (ptr) |p| p.* = unknown.full_ptr;
|
||||||
|
return unknown.full_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unknown_partial(
|
||||||
|
unknown: sgr.Attribute.Unknown.C,
|
||||||
|
ptr: ?*[*]const u16,
|
||||||
|
) callconv(.c) usize {
|
||||||
|
if (ptr) |p| p.* = unknown.partial_ptr;
|
||||||
|
return unknown.partial_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attribute_tag(
|
||||||
|
attr: sgr.Attribute.C,
|
||||||
|
) callconv(.c) sgr.Attribute.Tag {
|
||||||
|
return attr.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attribute_value(
|
||||||
|
attr: *sgr.Attribute.C,
|
||||||
|
) callconv(.c) *sgr.Attribute.CValue {
|
||||||
|
return &attr.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wasm_alloc_attribute() callconv(.c) *sgr.Attribute.C {
|
||||||
|
const alloc = std.heap.wasm_allocator;
|
||||||
|
const ptr = alloc.create(sgr.Attribute.C) catch @panic("out of memory");
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wasm_free_attribute(attr: *sgr.Attribute.C) callconv(.c) void {
|
||||||
|
const alloc = std.heap.wasm_allocator;
|
||||||
|
alloc.destroy(attr);
|
||||||
|
}
|
||||||
|
|
||||||
test "alloc" {
|
test "alloc" {
|
||||||
var p: Parser = undefined;
|
var p: Parser = undefined;
|
||||||
try testing.expectEqual(Result.success, new(
|
try testing.expectEqual(Result.success, new(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue