From 81168ea2781fd03fb090921bc13af149a7697547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 16 Apr 2023 22:18:49 +0200 Subject: [PATCH 01/14] initial transfer --- addons/xterm-addon-serialize2/.gitignore | 7 + addons/xterm-addon-serialize2/.npmignore | 29 + addons/xterm-addon-serialize2/README.md | 42 ++ .../benchmark/SerializeAddon.benchmark.ts_ | 65 ++ .../benchmark/benchmark.json | 19 + .../benchmark/tsconfig.json | 23 + addons/xterm-addon-serialize2/package.json | 32 + .../src/Serialize2Addon.test.ts | 88 +++ .../src/Serialize2Addon.ts | 39 ++ .../src/serializer.wasm.ts | 496 ++++++++++++++ .../xterm-addon-serialize2/src/tsconfig.json | 39 ++ .../test/SerializeAddon.api.ts | 612 ++++++++++++++++++ .../xterm-addon-serialize2/test/tsconfig.json | 34 + addons/xterm-addon-serialize2/tsconfig.json | 9 + .../typings/xterm-addon-serialize.d.ts | 88 +++ .../xterm-addon-serialize2/webpack.config.js | 32 + addons/xterm-addon-serialize2/yarn.lock | 202 ++++++ tsconfig.all.json | 1 + 18 files changed, 1857 insertions(+) create mode 100644 addons/xterm-addon-serialize2/.gitignore create mode 100644 addons/xterm-addon-serialize2/.npmignore create mode 100644 addons/xterm-addon-serialize2/README.md create mode 100644 addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ create mode 100644 addons/xterm-addon-serialize2/benchmark/benchmark.json create mode 100644 addons/xterm-addon-serialize2/benchmark/tsconfig.json create mode 100644 addons/xterm-addon-serialize2/package.json create mode 100644 addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts create mode 100644 addons/xterm-addon-serialize2/src/Serialize2Addon.ts create mode 100644 addons/xterm-addon-serialize2/src/serializer.wasm.ts create mode 100644 addons/xterm-addon-serialize2/src/tsconfig.json create mode 100644 addons/xterm-addon-serialize2/test/SerializeAddon.api.ts create mode 100644 addons/xterm-addon-serialize2/test/tsconfig.json create mode 100644 addons/xterm-addon-serialize2/tsconfig.json create mode 100644 addons/xterm-addon-serialize2/typings/xterm-addon-serialize.d.ts create mode 100644 addons/xterm-addon-serialize2/webpack.config.js create mode 100644 addons/xterm-addon-serialize2/yarn.lock diff --git a/addons/xterm-addon-serialize2/.gitignore b/addons/xterm-addon-serialize2/.gitignore new file mode 100644 index 0000000000..fece6c7fe0 --- /dev/null +++ b/addons/xterm-addon-serialize2/.gitignore @@ -0,0 +1,7 @@ +lib +node_modules +out-benchmark + +# skip inwasm folders +inwasm-sdks/ +inwasm-builds/ diff --git a/addons/xterm-addon-serialize2/.npmignore b/addons/xterm-addon-serialize2/.npmignore new file mode 100644 index 0000000000..b203232aff --- /dev/null +++ b/addons/xterm-addon-serialize2/.npmignore @@ -0,0 +1,29 @@ +# Blacklist - exclude everything except npm defaults such as LICENSE, etc +* +!*/ + +# Whitelist - lib/ +!lib/**/*.d.ts + +!lib/**/*.js +!lib/**/*.js.map + +!lib/**/*.css + +# Whitelist - src/ +!src/**/*.ts +!src/**/*.d.ts + +!src/**/*.js +!src/**/*.js.map + +!src/**/*.css + +# Blacklist - src/ test files +src/**/*.test.ts +src/**/*.test.d.ts +src/**/*.test.js +src/**/*.test.js.map + +# Whitelist - typings/ +!typings/*.d.ts diff --git a/addons/xterm-addon-serialize2/README.md b/addons/xterm-addon-serialize2/README.md new file mode 100644 index 0000000000..12e0ac8761 --- /dev/null +++ b/addons/xterm-addon-serialize2/README.md @@ -0,0 +1,42 @@ +## xterm-addon-serialize + +An addon for [xterm.js](https://github.com/xtermjs/xterm.js) that enables xterm.js to serialize a terminal framebuffer into string or html. This addon requires xterm.js v4+. + +⚠️ This is an experimental addon that is still under construction ⚠️ + +### Install + +```bash +npm install --save xterm-addon-serialize +``` + +### Usage + +```ts +import { Terminal } from "xterm"; +import { SerializeAddon } from "xterm-addon-serialize"; + +const terminal = new Terminal(); +const serializeAddon = new SerializeAddon(); +terminal.loadAddon(serializeAddon); + +terminal.write("something...", () => { + console.log(serializeAddon.serialize()); +}); +``` + +See the full [API](https://github.com/xtermjs/xterm.js/blob/master/addons/xterm-addon-serialize/typings/xterm-addon-serialize.d.ts) for more advanced usage. + +### Benchmark + +⚠️ Ensure you have `lolcat`, `hexdump` programs installed in your computer + +```shell +$ git clone https://github.com/xtermjs/xterm.js.git +$ cd xterm.js +$ yarn +$ cd addons/xterm-addon-serialize +$ yarn benchmark && yarn benchmark-baseline +$ # change some code in `xterm-addon-serialize` +$ yarn benchmark-eval +``` diff --git a/addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ b/addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ new file mode 100644 index 0000000000..87741b6367 --- /dev/null +++ b/addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; + +import { spawn } from 'node-pty'; +import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; +import { Terminal } from 'browser/public/Terminal'; +import { SerializeAddon } from 'SerializeAddon'; + +class TestTerminal extends Terminal { + public writeSync(data: string): void { + (this as any)._core.writeSync(data); + } +} + +perfContext('Terminal: sh -c "dd if=/dev/urandom count=40 bs=1k | hexdump | lolcat -f"', () => { + let content = ''; + let contentUtf8: Uint8Array; + + before(async () => { + const p = spawn('sh', ['-c', 'dd if=/dev/urandom count=40 bs=1k | hexdump | lolcat -f'], { + name: 'xterm-256color', + cols: 80, + rows: 25, + cwd: process.env.HOME, + env: process.env, + encoding: (null as unknown as string) // needs to be fixed in node-pty + }); + const chunks: Buffer[] = []; + let length = 0; + p.on('data', data => { + chunks.push(data as unknown as Buffer); + length += data.length; + }); + await new Promise(resolve => p.on('exit', () => resolve())); + contentUtf8 = Buffer.concat(chunks, length); + // translate to content string + const buffer = new Uint32Array(contentUtf8.length); + const decoder = new Utf8ToUtf32(); + const codepoints = decoder.decode(contentUtf8, buffer); + for (let i = 0; i < codepoints; ++i) { + content += stringFromCodePoint(buffer[i]); + // peek into content to force flat repr in v8 + if (!(i % 10000000)) { + content[i]; + } + } + }); + + perfContext('serialize', () => { + let terminal: TestTerminal; + const serializeAddon = new SerializeAddon(); + before(() => { + terminal = new TestTerminal({ cols: 80, rows: 25, scrollback: 5000 }); + serializeAddon.activate(terminal); + terminal.writeSync(content); + }); + new ThroughputRuntimeCase('', () => { + return { payloadSize: serializeAddon.serialize().length }; + }, { fork: false }).showAverageThroughput(); + }); +}); diff --git a/addons/xterm-addon-serialize2/benchmark/benchmark.json b/addons/xterm-addon-serialize2/benchmark/benchmark.json new file mode 100644 index 0000000000..f8b99b5565 --- /dev/null +++ b/addons/xterm-addon-serialize2/benchmark/benchmark.json @@ -0,0 +1,19 @@ +{ + "APP_PATH": ".benchmark", + "evalConfig": { + "tolerance": { + "*": [0.75, 1.5], + "*.dev": [0.01, 1.5], + "*.cv": [0.01, 1.5], + "EscapeSequenceParser.benchmark.js.*.averageThroughput.mean": [0.9, 5] + }, + "skip": [ + "*.median", + "*.runs", + "*.dev", + "*.cv", + "EscapeSequenceParser.benchmark.js.*.averageRuntime", + "Terminal.benchmark.js.*.averageRuntime" + ] + } +} diff --git a/addons/xterm-addon-serialize2/benchmark/tsconfig.json b/addons/xterm-addon-serialize2/benchmark/tsconfig.json new file mode 100644 index 0000000000..bf5e335c2b --- /dev/null +++ b/addons/xterm-addon-serialize2/benchmark/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "lib": ["dom", "es6"], + "outDir": "../out-benchmark", + "types": ["../../../node_modules/@types/node"], + "moduleResolution": "node", + "strict": false, + "target": "es2015", + "module": "commonjs", + "baseUrl": ".", + "paths": { + "common/*": ["../../../src/common/*"], + "browser/*": ["../../../src/browser/*"], + "SerializeAddon": ["../src/SerializeAddon"] + } + }, + "include": ["../**/*", "../../../typings/xterm.d.ts"], + "exclude": ["../../../**/*test.ts", "../../**/*api.ts"], + "references": [ + { "path": "../../../src/common" }, + { "path": "../../../src/browser" } + ] +} diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json new file mode 100644 index 0000000000..14f4f6b6c8 --- /dev/null +++ b/addons/xterm-addon-serialize2/package.json @@ -0,0 +1,32 @@ +{ + "name": "xterm-addon-serialize2", + "version": "0.1.0", + "author": { + "name": "The xterm.js authors", + "url": "https://xtermjs.org/" + }, + "main": "lib/xterm-addon-serialize2.js", + "types": "typings/xterm-addon-serialize2.d.ts", + "repository": "https://github.com/xtermjs/xterm.js", + "license": "MIT", + "keywords": [ + "terminal", + "xterm", + "xterm.js" + ], + "scripts": { + "build": "../../node_modules/.bin/tsc -p . && inwasm out/*.wasm.js", + "prepackage": "npm run build", + "package": "../../node_modules/.bin/webpack", + "prepublishOnly": "npm run package", + "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json", + "benchmark-baseline": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --baseline out-benchmark/benchmark/*benchmark.js", + "benchmark-eval": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json --eval out-benchmark/benchmark/*benchmark.js" + }, + "peerDependencies": { + "xterm": "^5.0.0" + }, + "devDependencies": { + "inwasm": "^0.0.7" + } +} diff --git a/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts b/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts new file mode 100644 index 0000000000..cdd40392f3 --- /dev/null +++ b/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2018 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import jsdom = require('jsdom'); +import { assert } from 'chai'; +import { Serialize2Addon } from './Serialize2Addon'; +import { Terminal } from 'browser/public/Terminal'; +import { SelectionModel } from 'browser/selection/SelectionModel'; +import { IBufferService } from 'common/services/Services'; +import { OptionsService } from 'common/services/OptionsService'; +import { ThemeService } from 'browser/services/ThemeService'; + +function sgr(...seq: string[]): string { + return `\x1b[${seq.join(';')}m`; +} + +function writeP(terminal: Terminal, data: string | Uint8Array): Promise { + return new Promise(r => terminal.write(data, r)); +} + +class TestSelectionService { + private _model: SelectionModel; + private _hasSelection: boolean = false; + + constructor( + bufferService: IBufferService + ) { + this._model = new SelectionModel(bufferService); + } + + public get model(): SelectionModel { return this._model; } + + public get hasSelection(): boolean { return this._hasSelection; } + + public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; } + public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; } + + public setSelection(col: number, row: number, length: number): void { + this._model.selectionStart = [col, row]; + this._model.selectionStartLength = length; + this._hasSelection = true; + } +} + +describe('xterm-addon-serialize', () => { + let dom: jsdom.JSDOM; + let window: jsdom.DOMWindow; + + let serializeAddon: Serialize2Addon; + let terminal: Terminal; + + before(() => { + serializeAddon = new Serialize2Addon(); + }); + + beforeEach(() => { + dom = new jsdom.JSDOM(''); + window = dom.window; + + (window as any).HTMLCanvasElement.prototype.getContext = () => ({ + createLinearGradient(): any { + return null; + }, + + fillRect(): void { }, + + getImageData(): any { + return { data: [0, 0, 0, 0xFF] }; + } + }); + + terminal = new Terminal({ cols: 10, rows: 2, allowProposedApi: true }); + terminal.loadAddon(serializeAddon); + + (terminal as any)._core._themeService = new ThemeService(new OptionsService({})); + (terminal as any)._core._selectionService = new TestSelectionService((terminal as any)._core._bufferService); + }); + + describe('text', () => { + // TODO: wirte alot more test cases here... + it('restoring cursor styles', async () => { + await writeP(terminal, sgr('32') + '> ' + sgr('0')); + assert.equal(serializeAddon.serialize(), '\u001b[32m> \u001b[0m'); + }); + }); +}); diff --git a/addons/xterm-addon-serialize2/src/Serialize2Addon.ts b/addons/xterm-addon-serialize2/src/Serialize2Addon.ts new file mode 100644 index 0000000000..cbf34e75fa --- /dev/null +++ b/addons/xterm-addon-serialize2/src/Serialize2Addon.ts @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2023 The xterm.js authors. All rights reserved. + * @license MIT + * + * (EXPERIMENTAL) This Addon is still under development + */ + +import { serialize } from './serializer.wasm'; +import { Terminal, ITerminalAddon } from 'xterm'; + + +interface ISerializeOptions { + scrollback?: number; + excludeModes?: boolean; + excludeAltBuffer?: boolean; +} + + +export class Serialize2Addon implements ITerminalAddon { + private _terminal: Terminal | undefined; + + constructor() { } + + public activate(terminal: Terminal): void { + this._terminal = terminal; + } + + public serialize(options?: ISerializeOptions): string { + // TODO: Add combinedData support + if (!this._terminal) { + throw new Error('Cannot use addon until it has been loaded'); + } + + return serialize((this._terminal as any)._core); + } + + public dispose(): void { } +} + diff --git a/addons/xterm-addon-serialize2/src/serializer.wasm.ts b/addons/xterm-addon-serialize2/src/serializer.wasm.ts new file mode 100644 index 0000000000..32ab9c59e6 --- /dev/null +++ b/addons/xterm-addon-serialize2/src/serializer.wasm.ts @@ -0,0 +1,496 @@ +import { InWasm, OutputMode, OutputType } from 'inwasm'; +import { ITerminal } from 'browser/Types'; +import { IBufferLine, IExtendedAttrs } from 'common/Types'; + +interface IExtendedAttrsInternal extends IExtendedAttrs { + _urlId: number; + _ext: number; +} + +interface IBufferLineInternal extends IBufferLine { + _data: Uint32Array; + _extendedAttrs: {[index: number]: IExtendedAttrsInternal | undefined}; + _combined: {[index: number]: string}; +} + +const wasmSerialize = InWasm({ + name: 'serialize', + type: OutputType.INSTANCE, + mode: OutputMode.SYNC, + srctype: 'Clang-C', + imports: { + env: { + memory: new WebAssembly.Memory({ initial: 1 }), + single_combined: (dst: number, x: number) => 0, + load_link: (dst: number, linkId: number) => 0 + } + }, + exports: { + line16: (src: number, length: number, dst: number) => 0, + reset: (fg: number, bg: number, ul: number, link: number) => {} + }, + compile: { + switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first'] + }, + code: ` + /* write combined chars on JS side */ + __attribute__((import_module("env"), import_name("single_combined"))) void* single_combined(unsigned short* dst, int x); + __attribute__((import_module("env"), import_name("load_link"))) void* load_link(unsigned short* dst, int link); + + // FIXME: import mask values as template strings from JS + #define CODEPOINT_MASK 0x1FFFFF + #define IS_COMBINED_MASK 0x200000 + #define HAS_CONTENT_MASK 0x3FFFFF + #define WIDTH_MASK 0xC00000 + #define WIDTH_SHIFT 22 + + /* bit 1..8 blue in RGB, color in P256 and P16 */ + #define BLUE_MASK 0xFF + #define BLUE_SHIFT 0 + #define PCOLOR_MASK 0xFF + #define PCOLOR_SHIFT 0 + + /* bit 9..16 green in RGB */ + #define GREEN_MASK 0xFF00 + #define GREEN_SHIFT 8 + + /* bit 17..24 red in RGB */ + #define RED_MASK 0xFF0000 + #define RED_SHIFT 16 + + /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ + #define CM_MASK 0x3000000 + #define CM_DEFAULT 0 + #define CM_P16 0x1000000 + #define CM_P256 0x2000000 + #define CM_RGB 0x3000000 + + /* bit 1..24 RGB room */ + #define RGB_MASK 0xFFFFFF + #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ + + /* fg flags: bit 27..32 */ + #define INVERSE 0x4000000 + #define BOLD 0x8000000 + #define UNDERLINE 0x10000000 + #define BLINK 0x20000000 + #define INVISIBLE 0x40000000 + #define STRIKETHROUGH 0x80000000 + + /* bg flags: bit 27..32 (upper 2 unused) */ + #define ITALIC 0x4000000 + #define DIM 0x8000000 + #define HAS_EXTENDED 0x10000000 + #define PROTECTED 0x20000000 + + /* ext flags: bit 27..32 (upper 3 unused) */ + #define UNDERLINE_STYLE 0x1C000000 + + /* underline style */ + #define UL_NONE 0 + #define UL_SINGLE 1 + #define UL_DOUBLE 2 + #define UL_CURLY 3 + #define UL_DOTTED 4 + #define UL_DASHED 5 + + typedef struct __attribute__((packed, aligned(4))) { + unsigned int content; + unsigned int fg; + unsigned int bg; + } Cell; + + /** + * Optimized itoa implementation for unsigned short to utf16. + * + * Note: Clang compiles with the div instruction in wasm. + * Since tests with shift mul in source show no runtime difference, + * wasm engines prolly optimize the division on their own. + */ + unsigned int *LUT100 = (unsigned int*) 256; + + __attribute__((noinline)) + unsigned short* itoa16(unsigned short n, unsigned short *dst) { + if (n < 10) { + *dst++ = n + 48; + } else if (n < 100) { + *(unsigned int*) dst = LUT100[n]; + dst += 2; + } else if (n < 1000) { + int h = n / 100; + *dst++ = h + 48; + *(unsigned int*) dst = LUT100[n - h * 100]; + dst += 2; + } else if (n < 10000) { + int h = n / 100; + *(unsigned int*) dst = LUT100[h]; + *((unsigned int*) dst+1) = LUT100[n - h * 100]; + dst += 4; + } else { + int h = n / 10000; + *dst++ = h + 48; + n -= h * 10000; + h = n / 100; + *(unsigned int*) dst = LUT100[h]; + *((unsigned int*) dst+1) = LUT100[n - h * 100]; + dst += 4; + } + return dst; + } + + /* TODO: target support flags */ + #define S_SGR 1 /* include SGR flags */ + #define S_COLORS 2 /* include 256 indexed colors */ + #define S_RGB 4 /* include RGB colors */ + #define S_REMPTY 8 /* right truncate empty cells */ + #define S_CURSOR 16 /* include cursor move sequences */ + #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */ + #define S_DECAWM 64 /* dont break soft wraps */ + + + /** + * Set SGR colors for FG / BG / UL. + * c denotes the color target as {FG: '3', BG: '4', UL: '5'}. + */ + __attribute__((noinline)) + static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) { + int mode = v & CM_MASK; + if (mode == CM_DEFAULT) { + *(unsigned long long*) d = 0x3b00390000ULL | c; + d += 3; + } else if (mode == CM_P16) { + unsigned long long color = 48 + (v & 7); + if (v & 8) { + /* bright for FG | BG (no UL color here) */ + if (c == '3') { + *(unsigned long long*) d = 0x003b00000039ULL | color << 16; + d += 3; + } else if (c == '4') { + *(unsigned long long*) d = 0x003b000000300031ULL | color << 32; + d += 4; + } + } else { + /* handles normal FG | BG | UL */ + *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c; + d += 3; + } + } else if (mode == CM_P256) { + *d++ = c; + *(unsigned long long*) d = 0x3b0035003b0038ULL; + d += 4; + d = itoa16(v & 0xFF, d); + *d++ = ';'; + } else { + *d++ = c; + *(unsigned long long*) d = 0x3b0032003b0038ULL; + d += 4; + d = itoa16((v >> 16) & 0xFF, d); + *d++ = ';'; + d = itoa16((v >> 8) & 0xFF, d); + *d++ = ';'; + d = itoa16(v & 0xFF, d); + *d++ = ';'; + } + return d; + } + + // FIXME: any nicer way to express this? + #define SGR_FLAG(V, DIFF, FLAG, HI, LO) \ + if ((DIFF) & (FLAG)) { \ + if ((V) & (FLAG)) { \ + *(unsigned int*) d = 0x3b0000 | (HI); \ + d += 2; \ + } else { \ + *(unsigned long long*) d = 0x3b00000032ULL | (LO); \ + d += 3; \ + } \ + } \ + + #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; + + unsigned short* diff_attr16( + unsigned short* d, + unsigned int fg, + unsigned int bg, + unsigned int diff_fg, + unsigned int diff_bg, + unsigned int ul, + unsigned int diff_ul + ) { + W_CSI(d) + + if (!fg && !bg) { + *d++ = ';'; + } else { + /* fg flags */ + if (diff_fg >> 26) { + SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16) + SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16) + //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs + SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16) + SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16) + SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16) + } + /* fg color */ + if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3'); + + /* bg flags */ + if (diff_bg >> 26) { + SGR_FLAG(bg, diff_bg, ITALIC, '3', '3') + SGR_FLAG(bg, diff_bg, DIM, '2', '2') + } + /* bg color */ + if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4'); + + /* ul ext attributes */ + /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */ + if (bg & HAS_EXTENDED) { + if (diff_ul & UNDERLINE_STYLE) { + *d++ = '4'; + *d++ = ':'; + *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; + *d++ = ';'; + } + if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5'); + } + } + *(d - 1) = 'm'; + return d; + } + + unsigned int old_fg = 0; + unsigned int old_bg = 0; + unsigned int old_ul = 0; + unsigned int old_link = 0; + + void reset(int fg, int bg, int ul, int link) { + old_fg = fg; + old_bg = bg; + old_ul = ul; + old_link = link; + } + + // FIXME: how to get rid of the weird BCE hack? + void* line16(Cell *src, int length, unsigned short *dst) { + int cur_jmp = 0; + unsigned int bce = old_bg; + unsigned ul = old_ul; + unsigned link = old_link; + + for (int i = 0; i < length;) { + Cell cell = src[i]; + + /** + * apply SGR differences + * We have to nullify HAS_EXTENDED due to its overloaded meaning, + * otherwise we would introduce nonsense jump/erase sequences here. + * SGR ext attributes for UL are covered by the explicit comparison, + * URL/hyperlink entry needs a separate control path (TODO). + */ + unsigned bg = cell.bg & ~HAS_EXTENDED; + ul = *((unsigned int *) (4096 * 4) + i); + if (cell.fg != old_fg || bg != old_bg || ul != old_ul) { + /* + we are in the middle of jumped over cells, + thus still need to apply BG changes first + */ + if (cur_jmp) { + if (old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'C'; + cur_jmp = 0; + } + /* write new SGR sequence, advance fg/bg/ul colors */ + dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); + old_fg = cell.fg; + old_bg = bg; + old_ul = ul; + } + + /* OSC 8 link handling */ + link = *((unsigned int *) (4096 * 4) + (length + i)); + if (link != old_link) { + if (old_link) { + // simply close old link - OSC 8 ; ; BEL + *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC + dst += 4; + *(unsigned int*) dst = 0x0007003b; // BEL ; + dst += 2; + } + if ((cell.bg & HAS_EXTENDED) && link) { + dst = load_link(dst, link); + old_link = link; + } else { + old_link = 0; + } + } + + + /* text content handling */ + if (cell.content & HAS_CONTENT_MASK) { + /* + we are in the middle of jumped over cells, thus apply cursor jump + we have to check here again in case there were no SGR changes + */ + if (cur_jmp) { + if (old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'C'; + cur_jmp = 0; + } + /* combined chars are written from JS (expensive?) */ + if (cell.content & IS_COMBINED_MASK) { + // FIXME: preload combined in a single action similar to ext attribs? + dst = single_combined(dst, i); + } else { + /* utf32 to utf16 conversion */ + unsigned int cp = cell.content & 0x1FFFFF; + if (cp > 0xFFFF) { + cp -= 0x10000; + *dst++ = (cp >> 10) + 0xD800; + *dst++ = (cp % 0x400) + 0xDC00; + } else { + *dst++ = cp; + } + } + } else { + /* empty cells are treated by cursor jumps */ + cur_jmp++; + } + + /* advance cell read position by wcwidth or 1 */ + int width = cell.content >> WIDTH_SHIFT; + i += width ? width : 1; + } + + /* + clear cells to the right if we have jumped over cells + and bce color != current bg + */ + if (cur_jmp && old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + + return dst; + } + ` +}); + +// itoa LUT +// note: performant decimal conversion of numbers is actually a hard problem +// we use a LUT approach with 0-99 precomputed in utf16 for better performance +const LUT100 = new Uint32Array(100); +for (let i1 = 0; i1 < 10; ++i1) { + for (let i2 = 0; i2 < 10; ++i2) { + LUT100[i1 * 10 + i2] = (48 + i2) << 16 | (48 + i1); + } +} + +const mem = new WebAssembly.Memory({ initial: 3000 }); +const d16 = new Uint16Array(mem.buffer); +const d32 = new Uint32Array(mem.buffer); +let _single_comb: (dst: number, x: number) => number = (dst, x) => dst; +const single_combined: (dst: number, x: number) => number = (dst, x) => _single_comb(dst, x); +let _load_link: (dst: number, x: number) => number = (dst, x) => dst; +const load_link: (dst: number, x: number) => number = (dst, x) => _load_link(dst, x); +const inst = wasmSerialize({ env: { memory: mem, single_combined, load_link } }); +d32.set(LUT100, 64); +const td = new TextDecoder('UTF-16LE'); + + +export function serialize(t: ITerminal): string { + // reset FG/BG/ext + inst.exports.reset(0, 0, 0, 0); + + const buffer = t.buffer; + const len = t.buffer.lines.length; + let wPos = 150*65536; + + let line: IBufferLineInternal; + + // single call is pretty wasteful? preload combines once instead? + _single_comb = (dst, x) => { + let dp = dst >> 1; + const comb: string = (line as any)._combined[x] || ''; + for (let i = 0; i < comb.length; ++i) { + d16[dp++] = comb.charCodeAt(i); + } + return dp << 1; + }; + + // write link to buffer + _load_link = (dst, linkId) => { + const entry = (t as any)._oscLinkService._dataByLinkId.get(linkId); + if (!entry) { + return dst; + } + const osc8 = `\x1b]8;${entry.data.id ? 'id='+entry.data.id : ''};${entry.data.uri}\x07`; + let dp = dst >> 1; + for (let i = 0; i < osc8.length; ++i) { + d16[dp++] = osc8.charCodeAt(i); + } + return dp << 1; + }; + + const ext = d32.subarray(4096, 4096 + t.cols*2); + const ext2 = ext.subarray(t.cols); + let clearExt = false; + + for (let row = 0; row < len; ++row) { + line = buffer.lines.get(row) as IBufferLineInternal; + if (!line) break; + + // insert CRLF + if (!line.isWrapped) { + const wPos16 = wPos >> 1; + d16[wPos16] = 13; + d16[wPos16+1] = 10; + wPos += 4; + } + + // TODO: start of line hook goes here... + + // load extended attributes + if (clearExt) { + ext.fill(0); + clearExt = false; + } + const keys = Object.keys(line._extendedAttrs) as unknown as number[]; + if (keys.length) { + for (let k = 0; k < keys.length; ++k) { + const rk = keys[k]; + ext[rk] = line._extendedAttrs[rk]!._ext; // UL color & style + ext2[rk] = line._extendedAttrs[rk]!._urlId; // OSC 8 link + } + clearExt = true; + } + + d32.set(line._data, 16384); + wPos = inst.exports.line16(65536, t.cols, wPos); + + // TODO: end of line hook goes here... + } + const final_data = d16.subarray(75*65536, wPos >> 1); + // strip empty lines at bottom + let fdp = final_data.length - 1; + while (fdp && final_data[fdp] === 10 && final_data[fdp-1] === 13) fdp -= 2; + + // strip leading CRLF + const offset = final_data[0] === 13 && final_data[1] === 10 ? 2 : 0; + + // return as string + // TODO: compose from hook parts (needs to store wPos line offsets?) + return td.decode(d16.subarray(75*65536+offset, 75*65536+fdp+1)); +} diff --git a/addons/xterm-addon-serialize2/src/tsconfig.json b/addons/xterm-addon-serialize2/src/tsconfig.json new file mode 100644 index 0000000000..ba26f22e4a --- /dev/null +++ b/addons/xterm-addon-serialize2/src/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2015", + "lib": [ + "dom", + "es2015" + ], + "rootDir": ".", + "outDir": "../out", + "sourceMap": true, + "removeComments": true, + "baseUrl": ".", + "paths": { + "common/*": [ + "../../../src/common/*" + ], + "browser/*": [ + "../../../src/browser/*" + ] + }, + "strict": true, + "types": [ + "../../../node_modules/@types/mocha" + ] + }, + "include": [ + "./**/*", + "../../../typings/xterm.d.ts" + ], + "references": [ + { + "path": "../../../src/common" + }, + { + "path": "../../../src/browser" + } + ] +} diff --git a/addons/xterm-addon-serialize2/test/SerializeAddon.api.ts b/addons/xterm-addon-serialize2/test/SerializeAddon.api.ts new file mode 100644 index 0000000000..157b7072fc --- /dev/null +++ b/addons/xterm-addon-serialize2/test/SerializeAddon.api.ts @@ -0,0 +1,612 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { assert } from 'chai'; +import { openTerminal, writeSync, launchBrowser } from '../../../out-test/api/TestUtils'; +import { Browser, Page } from 'playwright'; + +const APP = 'http://127.0.0.1:3001/test'; + +let browser: Browser; +let page: Page; +const width = 800; +const height = 600; + +const writeRawSync = (page: any, str: string): Promise => writeSync(page, `' +` + JSON.stringify(str) + `+ '`); + +const testNormalScreenEqual = async (page: any, str: string): Promise => { + await writeRawSync(page, str); + const originalBuffer = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + const result = await page.evaluate(`serializeAddon.serialize();`) as string; + await page.evaluate(`term.reset();`); + await writeRawSync(page, result); + const newBuffer = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + // chai decides -0 and 0 are different number... + // and firefox have a bug that output -0 for unknown reason + assert.equal(JSON.stringify(originalBuffer), JSON.stringify(newBuffer)); +}; + +async function testSerializeEquals(writeContent: string, expectedSerialized: string): Promise { + await writeRawSync(page, writeContent); + const result = await page.evaluate(`serializeAddon.serialize();`) as string; + assert.strictEqual(result, expectedSerialized); +} + +describe('SerializeAddon', () => { + before(async function(): Promise { + browser = await launchBrowser(); + page = await (await browser.newContext()).newPage(); + await page.setViewportSize({ width, height }); + await page.goto(APP); + await openTerminal(page, { rows: 10, cols: 10 }); + await page.evaluate(` + window.serializeAddon = new SerializeAddon(); + window.term.loadAddon(window.serializeAddon); + window.inspectBuffer = (buffer) => { + const lines = []; + for (let i = 0; i < buffer.length; i++) { + // Do this intentionally to get content of underlining source + const bufferLine = buffer.getLine(i)._line; + lines.push(JSON.stringify(bufferLine)); + } + return { + x: buffer.cursorX, + y: buffer.cursorY, + data: lines + }; + } + `); + }); + + after(async () => await browser.close()); + beforeEach(async () => await page.evaluate(`window.term.reset()`)); + + it('produce different output when we call test util with different text', async function(): Promise { + await writeRawSync(page, '12345'); + const buffer1 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + await page.evaluate(`term.reset();`); + await writeRawSync(page, '67890'); + const buffer2 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + assert.throw(() => { + assert.equal(JSON.stringify(buffer1), JSON.stringify(buffer2)); + }); + }); + + it('produce different output when we call test util with different line wrap', async function(): Promise { + await writeRawSync(page, '1234567890\r\n12345'); + const buffer3 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + await page.evaluate(`term.reset();`); + await writeRawSync(page, '123456789012345'); + const buffer4 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + assert.throw(() => { + assert.equal(JSON.stringify(buffer3), JSON.stringify(buffer4)); + }); + }); + + it('empty content', async function(): Promise { + assert.equal(await page.evaluate(`serializeAddon.serialize();`), ''); + }); + + it('unwrap wrapped line', async function(): Promise { + const lines = ['123456789123456789']; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('does not unwrap non-wrapped line', async function(): Promise { + const lines = [ + '123456789', + '123456789' + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + + it('preserve last empty lines', async function(): Promise { + const cols = 10; + const lines = [ + '', + '', + digitsString(cols), + digitsString(cols), + '', + '', + digitsString(cols), + digitsString(cols), + '', + '', + '' + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('digits content', async function(): Promise { + const rows = 10; + const cols = 10; + const digitsLine = digitsString(cols); + const lines = newArray(digitsLine, rows); + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize with half of scrollback', async function(): Promise { + const rows = 20; + const scrollback = rows - 10; + const halfScrollback = scrollback / 2; + const cols = 10; + const lines = newArray((index: number) => digitsString(cols, index), rows); + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize({ scrollback: ${halfScrollback} });`), lines.slice(halfScrollback, rows).join('\r\n')); + }); + + it('serialize 0 rows of scrollback', async function(): Promise { + const rows = 20; + const cols = 10; + const lines = newArray((index: number) => digitsString(cols, index), rows); + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize({ scrollback: 0 });`), lines.slice(rows - 10, rows).join('\r\n')); + }); + + it('serialize exclude modes', async () => { + await writeSync(page, 'before\\x1b[?1hafter'); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), 'beforeafter\x1b[?1h'); + assert.equal(await page.evaluate(`serializeAddon.serialize({ excludeModes: true });`), 'beforeafter'); + }); + + it('serialize exclude alt buffer', async () => { + await writeSync(page, 'normal\\x1b[?1049h\\x1b[Halt'); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), 'normal\x1b[?1049h\x1b[Halt'); + assert.equal(await page.evaluate(`serializeAddon.serialize({ excludeAltBuffer: true });`), 'normal'); + }); + + it('serialize all rows of content with color16', async function(): Promise { + const cols = 10; + const color16 = [ + 30, 31, 32, 33, 34, 35, 36, 37, // Set foreground color + 90, 91, 92, 93, 94, 95, 96, 97, + 40, 41, 42, 43, 44, 45, 46, 47, // Set background color + 100, 101, 103, 104, 105, 106, 107 + ]; + const rows = color16.length; + const lines = newArray( + (index: number) => digitsString(cols, index, `\x1b[${color16[index % color16.length]}m`), + rows + ); + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with fg/bg flags', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_P16_GREEN) + line, // Workaround: If we clear all flags a the end, serialize will use \x1b[0m to clear instead of the sepcific disable sequence + sgr(INVERSE) + line, + sgr(BOLD) + line, + sgr(UNDERLINED) + line, + sgr(BLINK) + line, + sgr(INVISIBLE) + line, + sgr(STRIKETHROUGH) + line, + sgr(NO_INVERSE) + line, + sgr(NO_BOLD) + line, + sgr(NO_UNDERLINED) + line, + sgr(NO_BLINK) + line, + sgr(NO_INVISIBLE) + line, + sgr(NO_STRIKETHROUGH) + line + ]; + const rows = lines.length; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with color256', async function(): Promise { + const rows = 32; + const cols = 10; + const lines = newArray( + (index: number) => digitsString(cols, index, `\x1b[38;5;${16 + index}m`), + rows + ); + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with color16 and style separately', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_P16_RED) + line, // fg Red, + sgr(UNDERLINED) + line, // fg Red, Underlined + sgr(FG_P16_GREEN) + line, // fg Green, Underlined + sgr(INVERSE) + line, // fg Green, Underlined, Inverse + sgr(NO_INVERSE) + line, // fg Green, Underlined + sgr(INVERSE) + line, // fg Green, Underlined, Inverse + sgr(BG_P16_YELLOW) + line, // fg Green, bg Yellow, Underlined, Inverse + sgr(FG_RESET) + line, // bg Yellow, Underlined, Inverse + sgr(BG_RESET) + line, // Underlined, Inverse + sgr(NORMAL) + line // Back to normal + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with color16 and style together', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_P16_RED) + line, // fg Red + sgr(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow + sgr(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic + sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow + sgr(FG_RESET, ITALIC) + line, // bg Yellow, Italic + sgr(BG_RESET) + line, // Italic + sgr(NORMAL) + line, // Back to normal + sgr(FG_P16_RED) + line, // fg Red + sgr(FG_P16_GREEN, BG_P16_YELLOW) + line, // fg Green, bg Yellow + sgr(UNDERLINED, ITALIC) + line, // fg Green, bg Yellow, Underlined, Italic + sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green, bg Yellow + sgr(FG_RESET, ITALIC) + line, // bg Yellow, Italic + sgr(BG_RESET) + line // Italic + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with color256 and style separately', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_P256_RED) + line, // fg Red 256, + sgr(UNDERLINED) + line, // fg Red 256, Underlined + sgr(FG_P256_GREEN) + line, // fg Green 256, Underlined + sgr(INVERSE) + line, // fg Green 256, Underlined, Inverse + sgr(NO_INVERSE) + line, // fg Green 256, Underlined + sgr(INVERSE) + line, // fg Green 256, Underlined, Inverse + sgr(BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256, Underlined, Inverse + sgr(FG_RESET) + line, // bg Yellow 256, Underlined, Inverse + sgr(BG_RESET) + line, // Underlined, Inverse + sgr(NORMAL) + line // Back to normal + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with color256 and style together', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_P256_RED) + line, // fg Red 256 + sgr(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256 + sgr(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic + sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256 + sgr(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic + sgr(BG_RESET) + line, // Italic + sgr(NORMAL) + line, // Back to normal + sgr(FG_P256_RED) + line, // fg Red 256 + sgr(FG_P256_GREEN, BG_P256_YELLOW) + line, // fg Green 256, bg Yellow 256 + sgr(UNDERLINED, ITALIC) + line, // fg Green 256, bg Yellow 256, Underlined, Italic + sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green 256, bg Yellow 256 + sgr(FG_RESET, ITALIC) + line, // bg Yellow 256, Italic + sgr(BG_RESET) + line // Italic + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with colorRGB and style separately', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_RGB_RED) + line, // fg Red RGB, + sgr(UNDERLINED) + line, // fg Red RGB, Underlined + sgr(FG_RGB_GREEN) + line, // fg Green RGB, Underlined + sgr(INVERSE) + line, // fg Green RGB, Underlined, Inverse + sgr(NO_INVERSE) + line, // fg Green RGB, Underlined + sgr(INVERSE) + line, // fg Green RGB, Underlined, Inverse + sgr(BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB, Underlined, Inverse + sgr(FG_RESET) + line, // bg Yellow RGB, Underlined, Inverse + sgr(BG_RESET) + line, // Underlined, Inverse + sgr(NORMAL) + line // Back to normal + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize all rows of content with colorRGB and style together', async function(): Promise { + const cols = 10; + const line = '+'.repeat(cols); + const lines: string[] = [ + sgr(FG_RGB_RED) + line, // fg Red RGB + sgr(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB + sgr(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic + sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB + sgr(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic + sgr(BG_RESET) + line, // Italic + sgr(NORMAL) + line, // Back to normal + sgr(FG_RGB_RED) + line, // fg Red RGB + sgr(FG_RGB_GREEN, BG_RGB_YELLOW) + line, // fg Green RGB, bg Yellow RGB + sgr(UNDERLINED, ITALIC) + line, // fg Green RGB, bg Yellow RGB, Underlined, Italic + sgr(NO_UNDERLINED, NO_ITALIC) + line, // fg Green RGB, bg Yellow RGB + sgr(FG_RESET, ITALIC) + line, // bg Yellow RGB, Italic + sgr(BG_RESET) + line // Italic + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize tabs correctly', async () => { + const lines = [ + 'a\tb', + 'aa\tc', + 'aaa\td' + ]; + const expected = [ + 'a\x1b[7Cb', + 'aa\x1b[6Cc', + 'aaa\x1b[5Cd' + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n')); + }); + + it('serialize CJK correctly', async () => { + const lines = [ + '中文中文', + '12中文', + '中文12', + // This line is going to be wrapped at last character + // because it has line length of 11 (1+2*5). + // We concat it back without the null cell currently. + // But this may be incorrect. + // see also #3097 + '1中文中文中' + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('serialize CJK Mixed with tab correctly', async () => { + const lines = [ + '中文\t12' // CJK mixed with tab + ]; + const expected = [ + '中文\x1b[4C12' + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n')); + }); + + it('serialize with alt screen correctly', async () => { + const SMCUP = '\u001b[?1049h'; + const CUP = '\u001b[H'; + + const lines = [ + `1${SMCUP}${CUP}2` + ]; + const expected = [ + `1${SMCUP}${CUP}2` + ]; + + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`window.term.buffer.active.type`), 'alternate'); + assert.equal(JSON.stringify(await page.evaluate(`serializeAddon.serialize();`)), JSON.stringify(expected.join('\r\n'))); + }); + + it('serialize without alt screen correctly', async () => { + const SMCUP = '\u001b[?1049h'; + const RMCUP = '\u001b[?1049l'; + + const lines = [ + `1${SMCUP}2${RMCUP}` + ]; + const expected = [ + `1` + ]; + + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`window.term.buffer.active.type`), 'normal'); + assert.equal(JSON.stringify(await page.evaluate(`serializeAddon.serialize();`)), JSON.stringify(expected.join('\r\n'))); + }); + + it('serialize with background', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + + const lines = [ + `1\u001b[44m${CLEAR_RIGHT(5)}`, + `2${CLEAR_RIGHT(9)}` + ]; + + await testNormalScreenEqual(page, lines.join('\r\n')); + }); + + it('cause the BCE on scroll', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + + const padLines = newArray( + (index: number) => digitsString(10, index), + 10 + ); + + const lines = [ + ...padLines, + `\u001b[44m${CLEAR_RIGHT(5)}1111111111111111` + ]; + + await testNormalScreenEqual(page, lines.join('\r\n')); + }); + + it('handle invalid wrap before scroll', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + const MOVE_UP = (l: number): string => `\u001b[${l}A`; + const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; + const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; + + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. + const segments = [ + `123456789012345`, + MOVE_UP(1), + CLEAR_RIGHT(5), + MOVE_DOWN(1), + MOVE_LEFT(5), + CLEAR_RIGHT(5), + MOVE_UP(1), + '1' + ]; + + await testNormalScreenEqual(page, segments.join('')); + }); + + it('handle invalid wrap after scroll', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + const MOVE_UP = (l: number): string => `\u001b[${l}A`; + const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; + const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; + + const padLines = newArray( + (index: number) => digitsString(10, index), + 10 + ); + + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. + const lines = [ + padLines.join('\r\n'), + '\r\n', + `123456789012345`, + MOVE_UP(1), + CLEAR_RIGHT(5), + MOVE_DOWN(1), + MOVE_LEFT(5), + CLEAR_RIGHT(5), + MOVE_UP(1), + '1' + ]; + + await testNormalScreenEqual(page, lines.join('')); + }); + + describe('handle modes', () => { + it('applicationCursorKeysMode', async () => { + await testSerializeEquals('test\u001b[?1h', 'test\u001b[?1h'); + await testSerializeEquals('\u001b[?1l', 'test'); + }); + it('applicationKeypadMode', async () => { + await testSerializeEquals('test\u001b[?66h', 'test\u001b[?66h'); + await testSerializeEquals('\u001b[?66l', 'test'); + }); + it('bracketedPasteMode', async () => { + await testSerializeEquals('test\u001b[?2004h', 'test\u001b[?2004h'); + await testSerializeEquals('\u001b[?2004l', 'test'); + }); + it('insertMode', async () => { + await testSerializeEquals('test\u001b[4h', 'test\u001b[4h'); + await testSerializeEquals('\u001b[4l', 'test'); + }); + it('mouseTrackingMode', async () => { + await testSerializeEquals('test\u001b[?9h', 'test\u001b[?9h'); + await testSerializeEquals('\u001b[?9l', 'test'); + await testSerializeEquals('\u001b[?1000h', 'test\u001b[?1000h'); + await testSerializeEquals('\u001b[?1000l', 'test'); + await testSerializeEquals('\u001b[?1002h', 'test\u001b[?1002h'); + await testSerializeEquals('\u001b[?1002l', 'test'); + await testSerializeEquals('\u001b[?1003h', 'test\u001b[?1003h'); + await testSerializeEquals('\u001b[?1003l', 'test'); + }); + it('originMode', async () => { + // origin mode moves cursor to (0,0) + await testSerializeEquals('test\u001b[?6h', 'test\u001b[4D\u001b[?6h'); + await testSerializeEquals('\u001b[?6l', 'test\u001b[4D'); + }); + it('reverseWraparoundMode', async () => { + await testSerializeEquals('test\u001b[?45h', 'test\u001b[?45h'); + await testSerializeEquals('\u001b[?45l', 'test'); + }); + it('sendFocusMode', async () => { + await testSerializeEquals('test\u001b[?1004h', 'test\u001b[?1004h'); + await testSerializeEquals('\u001b[?1004l', 'test'); + }); + it('wraparoundMode', async () => { + await testSerializeEquals('test\u001b[?7l', 'test\u001b[?7l'); + await testSerializeEquals('\u001b[?7h', 'test'); + }); + }); +}); + +function newArray(initial: T | ((index: number) => T), count: number): T[] { + const array: T[] = new Array(count); + for (let i = 0; i < array.length; i++) { + if (typeof initial === 'function') { + array[i] = (initial as (index: number) => T)(i); + } else { + array[i] = initial as T; + } + } + return array; +} + +function digitsString(length: number, from: number = 0, sgr: string = ''): string { + let s = sgr; + for (let i = 0; i < length; i++) { + s += `${(from++) % 10}`; + } + return s; +} + +function sgr(...seq: string[]): string { + return `\x1b[${seq.join(';')}m`; +} + +const NORMAL = '0'; + +const FG_P16_RED = '31'; +const FG_P16_GREEN = '32'; +const FG_P16_YELLOW = '33'; +const FG_P256_RED = '38;5;196'; +const FG_P256_GREEN = '38;5;46'; +const FG_P256_YELLOW = '38;5;226'; +const FG_RGB_RED = '38;2;255;0;0'; +const FG_RGB_GREEN = '38;2;0;255;0'; +const FG_RGB_YELLOW = '38;2;255;255;0'; +const FG_RESET = '39'; + + +const BG_P16_RED = '41'; +const BG_P16_GREEN = '42'; +const BG_P16_YELLOW = '43'; +const BG_P256_RED = '48;5;196'; +const BG_P256_GREEN = '48;5;46'; +const BG_P256_YELLOW = '48;5;226'; +const BG_RGB_RED = '48;2;255;0;0'; +const BG_RGB_GREEN = '48;2;0;255;0'; +const BG_RGB_YELLOW = '48;2;255;255;0'; +const BG_RESET = '49'; + +const BOLD = '1'; +const DIM = '2'; +const ITALIC = '3'; +const UNDERLINED = '4'; +const BLINK = '5'; +const INVERSE = '7'; +const INVISIBLE = '8'; +const STRIKETHROUGH = '9'; + +const NO_BOLD = '22'; +const NO_DIM = '22'; +const NO_ITALIC = '23'; +const NO_UNDERLINED = '24'; +const NO_BLINK = '25'; +const NO_INVERSE = '27'; +const NO_INVISIBLE = '28'; +const NO_STRIKETHROUGH = '29'; diff --git a/addons/xterm-addon-serialize2/test/tsconfig.json b/addons/xterm-addon-serialize2/test/tsconfig.json new file mode 100644 index 0000000000..971f3e92f5 --- /dev/null +++ b/addons/xterm-addon-serialize2/test/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2015", + "lib": [ + "es2015" + ], + "rootDir": ".", + "outDir": "../out-test", + "sourceMap": true, + "removeComments": true, + "baseUrl": ".", + "paths": { + "common/*": [ + "../../../src/common/*" + ] + }, + "strict": true, + "types": [ + "../../../node_modules/@types/mocha", + "../../../node_modules/@types/node", + "../../../out-test/api/TestUtils" + ] + }, + "include": [ + "./**/*", + "../../../typings/xterm.d.ts" + ], + "references": [ + { + "path": "../../../src/common" + } + ] +} diff --git a/addons/xterm-addon-serialize2/tsconfig.json b/addons/xterm-addon-serialize2/tsconfig.json new file mode 100644 index 0000000000..0e7b5c3502 --- /dev/null +++ b/addons/xterm-addon-serialize2/tsconfig.json @@ -0,0 +1,9 @@ +{ + "files": [], + "include": [], + "references": [ + { "path": "./src" }, + { "path": "./test" }, + { "path": "./benchmark" } + ] +} diff --git a/addons/xterm-addon-serialize2/typings/xterm-addon-serialize.d.ts b/addons/xterm-addon-serialize2/typings/xterm-addon-serialize.d.ts new file mode 100644 index 0000000000..4cb6283db0 --- /dev/null +++ b/addons/xterm-addon-serialize2/typings/xterm-addon-serialize.d.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { Terminal, ITerminalAddon } from 'xterm'; + +declare module 'xterm-addon-serialize' { + /** + * An xterm.js addon that enables serialization of terminal contents. + */ + export class SerializeAddon implements ITerminalAddon { + + constructor(); + + /** + * Activates the addon. + * @param terminal The terminal the addon is being loaded in. + */ + public activate(terminal: Terminal): void; + + /** + * Serializes terminal rows into a string that can be written back to the terminal to restore + * the state. The cursor will also be positioned to the correct cell. When restoring a terminal + * it is best to do before `Terminal.open` is called to avoid wasting CPU cycles rendering + * incomplete frames. + * + * It's recommended that you write the serialized data into a terminal of the same size in which + * it originated from and then resize it after if needed. + * + * @param options Custom options to allow control over what gets serialized. + */ + public serialize(options?: ISerializeOptions): string; + + /** + * Serializes terminal content as HTML, which can be written to the clipboard using the + * `text/html` mimetype. For applications that support it, the pasted text should then retain + * its colors/styles. + * + * @param options Custom options to allow control over what gets serialized. + */ + public serializeAsHTML(options?: Partial): string; + + /** + * Disposes the addon. + */ + public dispose(): void; + } + + export interface ISerializeOptions { + /** + * The number of rows in the scrollback buffer to serialize, starting from the bottom of the + * scrollback buffer. When not specified, all available rows in the scrollback buffer will be + * serialized. + */ + scrollback?: number; + + /** + * Whether to exclude the terminal modes from the serialization. False by default. + */ + excludeModes?: boolean; + + /** + * Whether to exclude the alt buffer from the serialization. False by default. + */ + excludeAltBuffer?: boolean; + } + + export interface IHTMLSerializeOptions { + /** + * The number of rows in the scrollback buffer to serialize, starting from the bottom of the + * scrollback buffer. When not specified, all available rows in the scrollback buffer will be + * serialized. This setting is ignored if {@link IHTMLSerializeOptions.onlySelection} is true. + */ + scrollback: number; + + /** + * Whether to only serialize the selection. If false, the whole active buffer is serialized in HTML. + * False by default. + */ + onlySelection: boolean; + + /** + * Whether to include the global background of the terminal. False by default. + */ + includeGlobalBackground: boolean; + } +} diff --git a/addons/xterm-addon-serialize2/webpack.config.js b/addons/xterm-addon-serialize2/webpack.config.js new file mode 100644 index 0000000000..7c0ccd016a --- /dev/null +++ b/addons/xterm-addon-serialize2/webpack.config.js @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +const path = require('path'); + +const addonName = 'SerializeAddon'; +const mainFile = 'xterm-addon-serialize.js'; + +module.exports = { + entry: `./out/${addonName}.js`, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.js$/, + use: ["source-map-loader"], + enforce: "pre", + exclude: /node_modules/ + } + ] + }, + output: { + filename: mainFile, + path: path.resolve('./lib'), + library: addonName, + libraryTarget: 'umd', + globalObject: 'this' + }, + mode: 'production' +}; diff --git a/addons/xterm-addon-serialize2/yarn.lock b/addons/xterm-addon-serialize2/yarn.lock new file mode 100644 index 0000000000..485c69456c --- /dev/null +++ b/addons/xterm-addon-serialize2/yarn.lock @@ -0,0 +1,202 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/acorn@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" + integrity sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ== + dependencies: + "@types/estree" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +acorn-walk@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^8.8.2: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +colorette@^2.0.19: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.1.0.tgz#baa48c6a157cfa34ca7887f2a29c6156bc6b65f8" + integrity sha512-daGobsYuT0G4hng24B5LbeLNvwKZYRhWyDl3RvqqAGZjJnCopWWK6PWnAGBY1M/vdA63QE+jddhZcYp+74Bq6Q== + dependencies: + fs.realpath "^1.0.0" + minimatch "^9.0.0" + minipass "^5.0.0" + path-scurry "^1.7.0" + +inwasm@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.7.tgz#6b5241346e6a08ad98ac5ff664f279231fcf94e6" + integrity sha512-mKBYr/rsqJGbn/3uF8He8+zieNT0HIbg2qzbWZZ7HnB8Qa20kK7a9J7Xa4qmVdxG5grJoV/BJnJBBeUKlXogyA== + dependencies: + "@types/acorn" "^4.0.6" + acorn "^8.8.2" + acorn-walk "^8.2.0" + chokidar "^3.5.3" + colorette "^2.0.19" + glob "^10.0.0" + wabt "^1.0.32" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +lru-cache@^9.0.0: + version "9.0.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.3.tgz#8a04f282df5320227bb7215c55df2660d3e4e25b" + integrity sha512-cyjNRew29d4kbgnz1sjDqxg7qg8NW4s+HQzCGjeon7DV5T2yDije16W9HaUFV1dhVEMh+SjrOcK0TomBmf3Egg== + +minimatch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" + integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== + dependencies: + brace-expansion "^2.0.1" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +path-scurry@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db" + integrity sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg== + dependencies: + lru-cache "^9.0.0" + minipass "^5.0.0" + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +wabt@^1.0.32: + version "1.0.32" + resolved "https://registry.yarnpkg.com/wabt/-/wabt-1.0.32.tgz#a4611728e67f1c3f7c546fabb7bc4da3e84a67a7" + integrity sha512-1aHvkKaSrrl7qFtAbQ1RWVHLuJApRh7PtUdYvRtiUEKEhk0MOV0sTuz5cLF6jL5jPLRyifLbZcR65AEga/xBhQ== diff --git a/tsconfig.all.json b/tsconfig.all.json index 4d2df3066a..cf91391fde 100644 --- a/tsconfig.all.json +++ b/tsconfig.all.json @@ -12,6 +12,7 @@ { "path": "./addons/xterm-addon-ligatures" }, { "path": "./addons/xterm-addon-search" }, { "path": "./addons/xterm-addon-serialize" }, + { "path": "./addons/xterm-addon-serialize2" }, { "path": "./addons/xterm-addon-unicode11" }, { "path": "./addons/xterm-addon-web-links" }, { "path": "./addons/xterm-addon-webgl" } From dce2d2c644a9e6bff0c536a044a4ec5254c21b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 16 Apr 2023 22:42:19 +0200 Subject: [PATCH 02/14] run inwasm late --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 1423cab0ed..4bd0b3b099 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "prepare": "npm run setup", "setup": "npm run build", "presetup": "node ./bin/install-addons.js", + "postsetup": "cd addons/xterm-addon-serialize2 && npm run build", "prepublishOnly": "npm run package", "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput", "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", From e4952d474487072657e9d5576856f28c59aea733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 16 Apr 2023 22:55:18 +0200 Subject: [PATCH 03/14] fix test, disable linter for now --- .eslintrc.json | 2 ++ addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts | 4 ++-- addons/xterm-addon-serialize2/src/serializer.wasm.ts | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 822ee4bad6..a8e0810c6b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,6 +23,8 @@ "addons/xterm-addon-search/test/tsconfig.json", "addons/xterm-addon-serialize/src/tsconfig.json", "addons/xterm-addon-serialize/test/tsconfig.json", + "addons/xterm-addon-serialize2/src/tsconfig.json", + "addons/xterm-addon-serialize2/test/tsconfig.json", "addons/xterm-addon-serialize/benchmark/tsconfig.json", "addons/xterm-addon-unicode11/src/tsconfig.json", "addons/xterm-addon-unicode11/test/tsconfig.json", diff --git a/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts b/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts index cdd40392f3..9428aee39e 100644 --- a/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts +++ b/addons/xterm-addon-serialize2/src/Serialize2Addon.test.ts @@ -44,7 +44,7 @@ class TestSelectionService { } } -describe('xterm-addon-serialize', () => { +describe('xterm-addon-serialize2', () => { let dom: jsdom.JSDOM; let window: jsdom.DOMWindow; @@ -82,7 +82,7 @@ describe('xterm-addon-serialize', () => { // TODO: wirte alot more test cases here... it('restoring cursor styles', async () => { await writeP(terminal, sgr('32') + '> ' + sgr('0')); - assert.equal(serializeAddon.serialize(), '\u001b[32m> \u001b[0m'); + assert.equal(serializeAddon.serialize(), '\u001b[32m> \u001b[m'); }); }); }); diff --git a/addons/xterm-addon-serialize2/src/serializer.wasm.ts b/addons/xterm-addon-serialize2/src/serializer.wasm.ts index 32ab9c59e6..6d1ca17e92 100644 --- a/addons/xterm-addon-serialize2/src/serializer.wasm.ts +++ b/addons/xterm-addon-serialize2/src/serializer.wasm.ts @@ -1,3 +1,5 @@ +/* eslint-disable */ + import { InWasm, OutputMode, OutputType } from 'inwasm'; import { ITerminal } from 'browser/Types'; import { IBufferLine, IExtendedAttrs } from 'common/Types'; From f205855d4c35027b3776d7d9fdb6783c2eede118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 16 Apr 2023 23:01:45 +0200 Subject: [PATCH 04/14] avoid .. in npm script --- addons/xterm-addon-serialize2/package.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json index 14f4f6b6c8..a8c7acadf0 100644 --- a/addons/xterm-addon-serialize2/package.json +++ b/addons/xterm-addon-serialize2/package.json @@ -15,7 +15,7 @@ "xterm.js" ], "scripts": { - "build": "../../node_modules/.bin/tsc -p . && inwasm out/*.wasm.js", + "inwasm": "inwasm out/*.wasm.js", "prepackage": "npm run build", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", diff --git a/package.json b/package.json index 4bd0b3b099..a5116f72ca 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "prepare": "npm run setup", "setup": "npm run build", "presetup": "node ./bin/install-addons.js", - "postsetup": "cd addons/xterm-addon-serialize2 && npm run build", + "postsetup": "cd addons/xterm-addon-serialize2 && npm run inwasm", "prepublishOnly": "npm run package", "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput", "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", From ae1343dc82ed51b2c65ae375de3fecb01fd2a2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 16 Apr 2023 23:31:46 +0200 Subject: [PATCH 05/14] add compiled wasm to repo to save CI runtime --- addons/xterm-addon-serialize2/.gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/xterm-addon-serialize2/.gitignore b/addons/xterm-addon-serialize2/.gitignore index fece6c7fe0..1272307b8f 100644 --- a/addons/xterm-addon-serialize2/.gitignore +++ b/addons/xterm-addon-serialize2/.gitignore @@ -4,4 +4,3 @@ out-benchmark # skip inwasm folders inwasm-sdks/ -inwasm-builds/ From 27700c22d9dc5fc45c04e12c748eb0570322a145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Sun, 16 Apr 2023 23:35:52 +0200 Subject: [PATCH 06/14] add build files --- .../serializer.wasm.js/serialize/definition | 1 + .../serializer.wasm.js/serialize/final.wasm | Bin 0 -> 2291 bytes .../serializer.wasm.js/serialize/final.wat | 933 ++++++++++++++++++ .../serializer.wasm.js/serialize/serialize.c | 344 +++++++ .../serialize/serialize.wasm | Bin 0 -> 2291 bytes 5 files changed, 1278 insertions(+) create mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition create mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wasm create mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat create mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c create mode 100755 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.wasm diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition new file mode 100644 index 0000000000..f1ecfa1ca9 --- /dev/null +++ b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition @@ -0,0 +1 @@ +{"def":{"name":"serialize","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"\n /* write combined chars on JS side */\n __attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n __attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n // FIXME: import mask values as template strings from JS\n #define CODEPOINT_MASK 0x1FFFFF\n #define IS_COMBINED_MASK 0x200000\n #define HAS_CONTENT_MASK 0x3FFFFF\n #define WIDTH_MASK 0xC00000\n #define WIDTH_SHIFT 22\n\n /* bit 1..8 blue in RGB, color in P256 and P16 */\n #define BLUE_MASK 0xFF\n #define BLUE_SHIFT 0\n #define PCOLOR_MASK 0xFF\n #define PCOLOR_SHIFT 0\n \n /* bit 9..16 green in RGB */\n #define GREEN_MASK 0xFF00\n #define GREEN_SHIFT 8\n \n /* bit 17..24 red in RGB */\n #define RED_MASK 0xFF0000\n #define RED_SHIFT 16\n \n /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n #define CM_MASK 0x3000000\n #define CM_DEFAULT 0\n #define CM_P16 0x1000000\n #define CM_P256 0x2000000\n #define CM_RGB 0x3000000\n \n /* bit 1..24 RGB room */\n #define RGB_MASK 0xFFFFFF\n #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n /* fg flags: bit 27..32 */\n #define INVERSE 0x4000000\n #define BOLD 0x8000000\n #define UNDERLINE 0x10000000\n #define BLINK 0x20000000\n #define INVISIBLE 0x40000000\n #define STRIKETHROUGH 0x80000000\n\n /* bg flags: bit 27..32 (upper 2 unused) */\n #define ITALIC 0x4000000\n #define DIM 0x8000000\n #define HAS_EXTENDED 0x10000000\n #define PROTECTED 0x20000000\n \n /* ext flags: bit 27..32 (upper 3 unused) */\n #define UNDERLINE_STYLE 0x1C000000\n \n /* underline style */\n #define UL_NONE 0\n #define UL_SINGLE 1\n #define UL_DOUBLE 2\n #define UL_CURLY 3\n #define UL_DOTTED 4\n #define UL_DASHED 5\n\n typedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n } Cell;\n\n /**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\n unsigned int *LUT100 = (unsigned int*) 256;\n\n __attribute__((noinline))\n unsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n }\n\n /* TODO: target support flags */\n #define S_SGR 1 /* include SGR flags */\n #define S_COLORS 2 /* include 256 indexed colors */\n #define S_RGB 4 /* include RGB colors */\n #define S_REMPTY 8 /* right truncate empty cells */\n #define S_CURSOR 16 /* include cursor move sequences */\n #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n #define S_DECAWM 64 /* dont break soft wraps */\n\n\n /**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n __attribute__((noinline))\n static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n }\n\n // FIXME: any nicer way to express this?\n #define SGR_FLAG(V, DIFF, FLAG, HI, LO) if ((DIFF) & (FLAG)) { if ((V) & (FLAG)) { *(unsigned int*) d = 0x3b0000 | (HI); d += 2; } else { *(unsigned long long*) d = 0x3b00000032ULL | (LO); d += 3; } } \n #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\n unsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n ) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n }\n\n unsigned int old_fg = 0;\n unsigned int old_bg = 0;\n unsigned int old_ul = 0;\n unsigned int old_link = 0;\n\n void reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n }\n\n // FIXME: how to get rid of the weird BCE hack?\n void* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n }\n "},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"}} \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wasm b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wasm new file mode 100644 index 0000000000000000000000000000000000000000..b1869f7bc76fbe57441f4ef0cc0b0b03c4a56898 GIT binary patch literal 2291 zcmb_eOK)367@f!U^|kMHVj)o>pmeTRK_!Wshm+Dx#!_Drj~(08k83wKj#EEk(M^2= zRanBlEcgSJND87Ao1ku3RTW~vf<+}N(M6D0utb>i&9(EA%ZfzujA!PY?|kR`W)fEF zY;eXHe^uTP{XXyCkbN^)82U^sN*OeJsm5lhy4qN4t}{m8Cp&A+dLvk^Y;Kg-nn5*Q zwKsx|&Gv1=bfNj|*9nc`Z6 zlPCdcwem0 zFP5SxdaaeSXv|)PHD>0RSw%}{i)yF18|`mL(f(N?gJzTID2{>MMYKbl1X=N6bg=z&XaDSy zG?Mgyx8mYq0*Z8)6>#Vht+8Q24d$zHm~4;mr(iJRAKB7Q&k!8Z@!?rFO6Y$O#?th$nOy^EYG4Xw5d>V|56UuI1Hi1za3rFlQ7Da)5 zX-+tuxck)g48wxyKh?U71QOJg3f%?ZchLb1CJJXnQQV7uMNckCT1{gWuEg+tc7(?q z1XAxB0`#ScOvXqM{AdVhm6RP4MB<15b3^d$Sc0ElLSP*uuwnuy@VIPe}(PgCTGt_PBIXGwHz_$6UjFYjI+d*~7#@O9>ZnW8l?n?b&OppiW4jvC; zz(a2hdgSfi82H{8I2wGY(bfw_qcS`ZH;T}=L$%L%Y#F-7MvmV^FL(j{DOFm(-izNJ z>L+QMf%?@jQ_s+mMMJHb4=+`ME~2F-XmC_IE`V6qe^4h=!MRGs?__-8a*p=sLWwJ6 zzb~j$gsH^$(VGR~pl$9&e|-{tMz0YSz%z|Q_xpG*PAtaBG7vxtaIP=#A~;as;xAE> zG@CT(f_mm&|BrVRp2T-KNo6*1gi2*OG%!yb3SjZmZ+!?2f;8b>j~+_=84>0x{e3Rf z>7I_BvUQ)XnFz^`B+X%qvgkFSI3T5`M)S~x>l{pHdiX$Sas3S#oJ%QwErU1Hf-fdl zLo7{8@@;}J3_hc8H8ePmYx3TrEax1t6PqsO4+(i*K`cCcC9}J?O61f>L)2|8j`)lN(#zQn?XK*BhJV RQloR;>8#b8)4|8xe*wFT=$ill literal 0 HcmV?d00001 diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat new file mode 100644 index 0000000000..58c51ad7c7 --- /dev/null +++ b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat @@ -0,0 +1,933 @@ +(module + (type (;0;) (func (param i32 i32) (result i32))) + (type (;1;) (func (param i32 i32 i32) (result i32))) + (type (;2;) (func (param i32 i32 i32 i32))) + (import "env" "load_link" (func (;0;) (type 0))) + (import "env" "single_combined" (func (;1;) (type 0))) + (import "env" "memory" (memory (;0;) 1)) + (func (;2;) (type 0) (param i32 i32) (result i32) + (local i32 i32) + local.get 0 + i32.const 9 + i32.le_u + if ;; label = @1 + local.get 1 + local.get 0 + i32.const 48 + i32.add + i32.store16 + local.get 1 + i32.const 2 + i32.add + return + end + local.get 0 + i32.const 99 + i32.le_u + if ;; label = @1 + local.get 1 + local.get 0 + i32.const 2 + i32.shl + i32.const 256 + i32.add + i32.load + i32.store + local.get 1 + i32.const 4 + i32.add + return + end + local.get 0 + i32.const 999 + i32.le_u + if ;; label = @1 + local.get 1 + local.get 0 + i32.const 100 + i32.div_u + local.tee 2 + i32.const 48 + i32.add + i32.store16 + local.get 1 + local.get 2 + i32.const -100 + i32.mul + local.get 0 + i32.add + i32.const 2 + i32.shl + i32.const 256 + i32.add + i32.load + i32.store offset=2 + local.get 1 + i32.const 6 + i32.add + return + end + local.get 0 + i32.const 9999 + i32.le_u + if ;; label = @1 + local.get 1 + local.get 0 + i32.const 100 + i32.div_u + local.tee 2 + i32.const 2 + i32.shl + i32.const 256 + i32.add + i32.load + i32.store + local.get 1 + local.get 2 + i32.const -100 + i32.mul + local.get 0 + i32.add + i32.const 2 + i32.shl + i32.const 256 + i32.add + i32.load + i32.store offset=4 + local.get 1 + i32.const 8 + i32.add + return + end + local.get 1 + local.get 0 + local.get 0 + i32.const 10000 + i32.div_u + local.tee 0 + i32.const -10000 + i32.mul + i32.add + i32.const 65535 + i32.and + local.tee 2 + i32.const 100 + i32.div_u + local.tee 3 + i32.const 2 + i32.shl + i32.const 256 + i32.add + i32.load + i32.store offset=2 + local.get 1 + local.get 0 + i32.const 48 + i32.or + i32.store16 + local.get 1 + local.get 3 + i32.const -100 + i32.mul + local.get 2 + i32.add + i32.const 2 + i32.shl + i32.const 256 + i32.add + i32.load + i32.store offset=6 + local.get 1 + i32.const 10 + i32.add) + (func (;3;) (type 1) (param i32 i32 i32) (result i32) + (local i64 i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + local.get 1 + i32.const 50331648 + i32.and + local.tee 4 + i32.const 33554432 + i32.ne + if ;; label = @4 + local.get 4 + i32.const 16777216 + i32.ne + if ;; label = @5 + local.get 4 + br_if 2 (;@3;) + local.get 0 + local.get 2 + i64.extend_i32_u + i64.const 255 + i64.and + i64.const 253406806016 + i64.or + i64.store + br 4 (;@1;) + end + local.get 1 + i32.const 7 + i32.and + i32.const 48 + i32.or + i64.extend_i32_u + local.set 3 + local.get 1 + i32.const 8 + i32.and + if ;; label = @5 + block ;; label = @6 + block ;; label = @7 + local.get 2 + i32.const 51 + i32.sub + br_table 0 (;@7;) 1 (;@6;) 5 (;@2;) + end + local.get 0 + local.get 3 + i64.const 16 + i64.shl + i64.const 253403070521 + i64.or + i64.store + br 5 (;@1;) + end + local.get 0 + local.get 3 + i64.const 32 + i64.shl + i64.const 16607023629074481 + i64.or + i64.store + local.get 0 + i32.const 8 + i32.add + return + end + local.get 0 + local.get 2 + i64.extend_i32_u + i64.const 255 + i64.and + local.get 3 + i64.const 16 + i64.shl + i64.or + i64.const 253403070464 + i64.or + i64.store + br 3 (;@1;) + end + local.get 0 + i64.const 16607251263062072 + i64.store offset=2 + local.get 0 + local.get 2 + i32.const 255 + i32.and + i32.store16 + local.get 1 + i32.const 255 + i32.and + local.get 0 + i32.const 10 + i32.add + call 2 + local.tee 0 + i32.const 59 + i32.store16 + local.get 0 + i32.const 2 + i32.add + return + end + local.get 0 + i64.const 16607238378160184 + i64.store offset=2 + local.get 0 + local.get 2 + i32.const 255 + i32.and + i32.store16 + local.get 1 + i32.const 16 + i32.shr_u + i32.const 255 + i32.and + local.get 0 + i32.const 10 + i32.add + call 2 + local.tee 0 + i32.const 59 + i32.store16 + local.get 1 + i32.const 65280 + i32.and + i32.const 8 + i32.shr_u + local.get 0 + i32.const 2 + i32.add + call 2 + local.tee 0 + i32.const 59 + i32.store16 + local.get 1 + i32.const 255 + i32.and + local.get 0 + i32.const 2 + i32.add + call 2 + local.tee 0 + i32.const 59 + i32.store16 + local.get 0 + i32.const 2 + i32.add + local.set 0 + end + local.get 0 + return + end + local.get 0 + i32.const 6 + i32.add) + (func (;4;) (type 2) (param i32 i32 i32 i32) + i32.const 4 + local.get 1 + i32.store + i32.const 0 + local.get 0 + i32.store + i32.const 8 + local.get 2 + i32.store + i32.const 12 + local.get 3 + i32.store) + (func (;5;) (type 1) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + local.get 1 + i32.const 0 + i32.le_s + br_if 0 (;@1;) + i32.const 4 + i32.load + local.set 12 + loop ;; label = @2 + local.get 0 + local.get 9 + i32.const 12 + i32.mul + i32.add + local.tee 3 + i32.load offset=8 + local.tee 6 + i32.const -268435457 + i32.and + local.set 14 + local.get 9 + i32.const 2 + i32.shl + i32.const 16384 + i32.add + i32.load + local.set 10 + local.get 3 + i32.load + local.set 7 + i32.const 8 + i32.load + local.set 13 + i32.const 4 + i32.load + local.set 11 + block ;; label = @3 + block ;; label = @4 + local.get 3 + i32.load offset=4 + local.tee 5 + i32.const 0 + i32.load + local.tee 8 + i32.ne + br_if 0 (;@4;) + local.get 11 + local.get 14 + i32.ne + br_if 0 (;@4;) + local.get 10 + local.get 13 + i32.ne + br_if 0 (;@4;) + local.get 2 + local.set 3 + br 1 (;@3;) + end + local.get 4 + if ;; label = @4 + local.get 11 + local.get 12 + i32.ne + if ;; label = @5 + local.get 2 + i32.const 5963803 + i32.store + local.get 4 + i32.const 65535 + i32.and + local.get 2 + i32.const 4 + i32.add + call 2 + local.tee 2 + i32.const 88 + i32.store16 + local.get 2 + i32.const 2 + i32.add + local.set 2 + end + local.get 2 + i32.const 5963803 + i32.store + local.get 4 + i32.const 65535 + i32.and + local.get 2 + i32.const 4 + i32.add + call 2 + local.tee 2 + i32.const 67 + i32.store16 + i32.const 8 + i32.load + local.set 13 + i32.const 4 + i32.load + local.set 11 + i32.const 0 + i32.load + local.set 8 + local.get 2 + i32.const 2 + i32.add + local.set 2 + end + local.get 2 + i32.const 5963803 + i32.store + block ;; label = @4 + local.get 5 + local.get 6 + i32.or + i32.eqz + if ;; label = @5 + local.get 2 + i32.const 59 + i32.store16 offset=4 + local.get 2 + i32.const 6 + i32.add + local.set 3 + br 1 (;@4;) + end + local.get 2 + i32.const 4 + i32.add + local.set 3 + block ;; label = @5 + local.get 5 + local.get 8 + i32.xor + local.tee 4 + i32.const 67108864 + i32.lt_u + br_if 0 (;@5;) + block ;; label = @6 + local.get 4 + i32.const 67108864 + i32.and + i32.eqz + br_if 0 (;@6;) + local.get 5 + i32.const 67108864 + i32.and + if ;; label = @7 + local.get 2 + i32.const 3866679 + i32.store offset=4 + local.get 2 + i32.const 8 + i32.add + local.set 3 + br 1 (;@6;) + end + local.get 2 + i64.const 253406674994 + i64.store offset=4 + local.get 2 + i32.const 10 + i32.add + local.set 3 + end + block ;; label = @6 + local.get 4 + i32.const 134217728 + i32.and + i32.eqz + br_if 0 (;@6;) + local.get 5 + i32.const 134217728 + i32.and + if ;; label = @7 + local.get 3 + i32.const 3866673 + i32.store + local.get 3 + i32.const 4 + i32.add + local.set 3 + br 1 (;@6;) + end + local.get 3 + i64.const 253406347314 + i64.store + local.get 3 + i32.const 6 + i32.add + local.set 3 + end + block ;; label = @6 + local.get 4 + i32.const 536870912 + i32.and + i32.eqz + br_if 0 (;@6;) + local.get 5 + i32.const 536870912 + i32.and + if ;; label = @7 + local.get 3 + i32.const 3866677 + i32.store + local.get 3 + i32.const 4 + i32.add + local.set 3 + br 1 (;@6;) + end + local.get 3 + i64.const 253406543922 + i64.store + local.get 3 + i32.const 6 + i32.add + local.set 3 + end + block ;; label = @6 + local.get 4 + i32.const 1073741824 + i32.and + i32.eqz + br_if 0 (;@6;) + local.get 5 + i32.const 1073741824 + i32.and + if ;; label = @7 + local.get 3 + i32.const 3866680 + i32.store + local.get 3 + i32.const 4 + i32.add + local.set 3 + br 1 (;@6;) + end + local.get 3 + i64.const 253406740530 + i64.store + local.get 3 + i32.const 6 + i32.add + local.set 3 + end + local.get 4 + i32.const 0 + i32.ge_s + br_if 0 (;@5;) + local.get 5 + i32.const 0 + i32.lt_s + if ;; label = @6 + local.get 3 + i32.const 3866681 + i32.store + local.get 3 + i32.const 4 + i32.add + local.set 3 + br 1 (;@5;) + end + local.get 3 + i64.const 253406806066 + i64.store + local.get 3 + i32.const 6 + i32.add + local.set 3 + end + local.get 4 + i32.const 67108863 + i32.and + if ;; label = @5 + local.get 3 + local.get 5 + i32.const 51 + call 3 + local.set 3 + end + block ;; label = @5 + local.get 6 + local.get 11 + i32.xor + local.tee 2 + i32.const 67108864 + i32.lt_u + br_if 0 (;@5;) + block ;; label = @6 + local.get 2 + i32.const 67108864 + i32.and + i32.eqz + br_if 0 (;@6;) + local.get 6 + i32.const 67108864 + i32.and + if ;; label = @7 + local.get 3 + i32.const 3866675 + i32.store + local.get 3 + i32.const 4 + i32.add + local.set 3 + br 1 (;@6;) + end + local.get 3 + i64.const 253403070515 + i64.store + local.get 3 + i32.const 6 + i32.add + local.set 3 + end + local.get 2 + i32.const 134217728 + i32.and + i32.eqz + br_if 0 (;@5;) + local.get 6 + i32.const 134217728 + i32.and + if ;; label = @6 + local.get 3 + i32.const 3866674 + i32.store + local.get 3 + i32.const 4 + i32.add + local.set 3 + br 1 (;@5;) + end + local.get 3 + i64.const 253403070514 + i64.store + local.get 3 + i32.const 6 + i32.add + local.set 3 + end + local.get 2 + i32.const 67108863 + i32.and + if ;; label = @5 + local.get 3 + local.get 6 + i32.const 52 + call 3 + local.set 3 + end + local.get 6 + i32.const 268435456 + i32.and + i32.eqz + br_if 0 (;@4;) + local.get 10 + local.get 13 + i32.xor + local.tee 2 + i32.const 469762048 + i32.and + if ;; label = @5 + local.get 3 + i32.const 59 + i32.store16 offset=6 + local.get 3 + i32.const 3801140 + i32.store align=2 + local.get 3 + local.get 10 + i32.const 26 + i32.shr_u + i32.const 7 + i32.and + i32.const 48 + i32.or + i32.store16 offset=4 + local.get 3 + i32.const 8 + i32.add + local.set 3 + end + local.get 2 + i32.const 67108863 + i32.and + i32.eqz + br_if 0 (;@4;) + local.get 3 + local.get 10 + i32.const 53 + call 3 + local.set 3 + end + i32.const 0 + local.set 4 + i32.const 0 + local.get 5 + i32.store + i32.const 4 + local.get 14 + i32.store + i32.const 8 + local.get 10 + i32.store + local.get 3 + i32.const 2 + i32.sub + i32.const 109 + i32.store16 + end + block ;; label = @3 + local.get 1 + local.get 9 + i32.add + i32.const 2 + i32.shl + i32.const 16384 + i32.add + i32.load + local.tee 8 + i32.const 12 + i32.load + local.tee 2 + i32.eq + if ;; label = @4 + local.get 3 + local.set 2 + br 1 (;@3;) + end + local.get 2 + if ;; label = @4 + local.get 3 + i32.const 458811 + i32.store offset=8 + local.get 3 + i64.const 16607264150192155 + i64.store + local.get 3 + i32.const 12 + i32.add + local.set 3 + end + block (result i32) ;; label = @4 + block ;; label = @5 + local.get 6 + i32.const 268435456 + i32.and + i32.eqz + br_if 0 (;@5;) + local.get 8 + i32.eqz + br_if 0 (;@5;) + local.get 3 + local.get 8 + call 0 + br 1 (;@4;) + end + i32.const 0 + local.set 8 + local.get 3 + end + local.set 2 + i32.const 12 + local.get 8 + i32.store + end + block (result i32) ;; label = @3 + local.get 7 + i32.const 4194303 + i32.and + if ;; label = @4 + local.get 4 + if ;; label = @5 + local.get 12 + i32.const 4 + i32.load + i32.ne + if ;; label = @6 + local.get 2 + i32.const 5963803 + i32.store + local.get 4 + i32.const 65535 + i32.and + local.get 2 + i32.const 4 + i32.add + call 2 + local.tee 2 + i32.const 88 + i32.store16 + local.get 2 + i32.const 2 + i32.add + local.set 2 + end + local.get 2 + i32.const 5963803 + i32.store + local.get 4 + i32.const 65535 + i32.and + local.get 2 + i32.const 4 + i32.add + call 2 + local.tee 2 + i32.const 67 + i32.store16 + local.get 2 + i32.const 2 + i32.add + local.set 2 + end + local.get 7 + i32.const 2097152 + i32.and + if ;; label = @5 + local.get 2 + local.get 9 + call 1 + local.set 2 + i32.const 0 + br 2 (;@3;) + end + block (result i32) ;; label = @5 + local.get 7 + i32.const 2097151 + i32.and + local.tee 4 + i32.const 65536 + i32.ge_u + if ;; label = @6 + local.get 2 + local.get 7 + i32.const 1023 + i32.and + i32.const 56320 + i32.or + i32.store16 offset=2 + local.get 4 + i32.const 67043328 + i32.add + i32.const 10 + i32.shr_u + i32.const 10240 + i32.sub + local.set 4 + local.get 2 + i32.const 2 + i32.add + br 1 (;@5;) + end + local.get 7 + local.set 4 + local.get 2 + end + local.get 2 + local.get 4 + i32.store16 + i32.const 2 + i32.add + local.set 2 + i32.const 0 + br 1 (;@3;) + end + local.get 4 + i32.const 1 + i32.add + end + local.set 4 + i32.const 1 + local.get 7 + i32.const 22 + i32.shr_u + local.get 7 + i32.const 4194304 + i32.lt_u + select + local.get 9 + i32.add + local.tee 9 + local.get 1 + i32.lt_s + br_if 0 (;@2;) + end + local.get 4 + i32.eqz + br_if 0 (;@1;) + i32.const 4 + i32.load + local.get 12 + i32.eq + br_if 0 (;@1;) + local.get 2 + i32.const 5963803 + i32.store + local.get 4 + i32.const 65535 + i32.and + local.get 2 + i32.const 4 + i32.add + call 2 + local.tee 0 + i32.const 88 + i32.store16 + local.get 0 + i32.const 2 + i32.add + local.set 2 + end + local.get 2) + (export "reset" (func 4)) + (export "line16" (func 5)) + (data (;0;) (i32.const 0) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")) diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c new file mode 100644 index 0000000000..8b84a492da --- /dev/null +++ b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c @@ -0,0 +1,344 @@ + + /* write combined chars on JS side */ + __attribute__((import_module("env"), import_name("single_combined"))) void* single_combined(unsigned short* dst, int x); + __attribute__((import_module("env"), import_name("load_link"))) void* load_link(unsigned short* dst, int link); + + // FIXME: import mask values as template strings from JS + #define CODEPOINT_MASK 0x1FFFFF + #define IS_COMBINED_MASK 0x200000 + #define HAS_CONTENT_MASK 0x3FFFFF + #define WIDTH_MASK 0xC00000 + #define WIDTH_SHIFT 22 + + /* bit 1..8 blue in RGB, color in P256 and P16 */ + #define BLUE_MASK 0xFF + #define BLUE_SHIFT 0 + #define PCOLOR_MASK 0xFF + #define PCOLOR_SHIFT 0 + + /* bit 9..16 green in RGB */ + #define GREEN_MASK 0xFF00 + #define GREEN_SHIFT 8 + + /* bit 17..24 red in RGB */ + #define RED_MASK 0xFF0000 + #define RED_SHIFT 16 + + /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ + #define CM_MASK 0x3000000 + #define CM_DEFAULT 0 + #define CM_P16 0x1000000 + #define CM_P256 0x2000000 + #define CM_RGB 0x3000000 + + /* bit 1..24 RGB room */ + #define RGB_MASK 0xFFFFFF + #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ + + /* fg flags: bit 27..32 */ + #define INVERSE 0x4000000 + #define BOLD 0x8000000 + #define UNDERLINE 0x10000000 + #define BLINK 0x20000000 + #define INVISIBLE 0x40000000 + #define STRIKETHROUGH 0x80000000 + + /* bg flags: bit 27..32 (upper 2 unused) */ + #define ITALIC 0x4000000 + #define DIM 0x8000000 + #define HAS_EXTENDED 0x10000000 + #define PROTECTED 0x20000000 + + /* ext flags: bit 27..32 (upper 3 unused) */ + #define UNDERLINE_STYLE 0x1C000000 + + /* underline style */ + #define UL_NONE 0 + #define UL_SINGLE 1 + #define UL_DOUBLE 2 + #define UL_CURLY 3 + #define UL_DOTTED 4 + #define UL_DASHED 5 + + typedef struct __attribute__((packed, aligned(4))) { + unsigned int content; + unsigned int fg; + unsigned int bg; + } Cell; + + /** + * Optimized itoa implementation for unsigned short to utf16. + * + * Note: Clang compiles with the div instruction in wasm. + * Since tests with shift mul in source show no runtime difference, + * wasm engines prolly optimize the division on their own. + */ + unsigned int *LUT100 = (unsigned int*) 256; + + __attribute__((noinline)) + unsigned short* itoa16(unsigned short n, unsigned short *dst) { + if (n < 10) { + *dst++ = n + 48; + } else if (n < 100) { + *(unsigned int*) dst = LUT100[n]; + dst += 2; + } else if (n < 1000) { + int h = n / 100; + *dst++ = h + 48; + *(unsigned int*) dst = LUT100[n - h * 100]; + dst += 2; + } else if (n < 10000) { + int h = n / 100; + *(unsigned int*) dst = LUT100[h]; + *((unsigned int*) dst+1) = LUT100[n - h * 100]; + dst += 4; + } else { + int h = n / 10000; + *dst++ = h + 48; + n -= h * 10000; + h = n / 100; + *(unsigned int*) dst = LUT100[h]; + *((unsigned int*) dst+1) = LUT100[n - h * 100]; + dst += 4; + } + return dst; + } + + /* TODO: target support flags */ + #define S_SGR 1 /* include SGR flags */ + #define S_COLORS 2 /* include 256 indexed colors */ + #define S_RGB 4 /* include RGB colors */ + #define S_REMPTY 8 /* right truncate empty cells */ + #define S_CURSOR 16 /* include cursor move sequences */ + #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */ + #define S_DECAWM 64 /* dont break soft wraps */ + + + /** + * Set SGR colors for FG / BG / UL. + * c denotes the color target as {FG: '3', BG: '4', UL: '5'}. + */ + __attribute__((noinline)) + static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) { + int mode = v & CM_MASK; + if (mode == CM_DEFAULT) { + *(unsigned long long*) d = 0x3b00390000ULL | c; + d += 3; + } else if (mode == CM_P16) { + unsigned long long color = 48 + (v & 7); + if (v & 8) { + /* bright for FG | BG (no UL color here) */ + if (c == '3') { + *(unsigned long long*) d = 0x003b00000039ULL | color << 16; + d += 3; + } else if (c == '4') { + *(unsigned long long*) d = 0x003b000000300031ULL | color << 32; + d += 4; + } + } else { + /* handles normal FG | BG | UL */ + *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c; + d += 3; + } + } else if (mode == CM_P256) { + *d++ = c; + *(unsigned long long*) d = 0x3b0035003b0038ULL; + d += 4; + d = itoa16(v & 0xFF, d); + *d++ = ';'; + } else { + *d++ = c; + *(unsigned long long*) d = 0x3b0032003b0038ULL; + d += 4; + d = itoa16((v >> 16) & 0xFF, d); + *d++ = ';'; + d = itoa16((v >> 8) & 0xFF, d); + *d++ = ';'; + d = itoa16(v & 0xFF, d); + *d++ = ';'; + } + return d; + } + + // FIXME: any nicer way to express this? + #define SGR_FLAG(V, DIFF, FLAG, HI, LO) if ((DIFF) & (FLAG)) { if ((V) & (FLAG)) { *(unsigned int*) d = 0x3b0000 | (HI); d += 2; } else { *(unsigned long long*) d = 0x3b00000032ULL | (LO); d += 3; } } + #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; + + unsigned short* diff_attr16( + unsigned short* d, + unsigned int fg, + unsigned int bg, + unsigned int diff_fg, + unsigned int diff_bg, + unsigned int ul, + unsigned int diff_ul + ) { + W_CSI(d) + + if (!fg && !bg) { + *d++ = ';'; + } else { + /* fg flags */ + if (diff_fg >> 26) { + SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16) + SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16) + //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs + SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16) + SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16) + SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16) + } + /* fg color */ + if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3'); + + /* bg flags */ + if (diff_bg >> 26) { + SGR_FLAG(bg, diff_bg, ITALIC, '3', '3') + SGR_FLAG(bg, diff_bg, DIM, '2', '2') + } + /* bg color */ + if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4'); + + /* ul ext attributes */ + /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */ + if (bg & HAS_EXTENDED) { + if (diff_ul & UNDERLINE_STYLE) { + *d++ = '4'; + *d++ = ':'; + *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; + *d++ = ';'; + } + if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5'); + } + } + *(d - 1) = 'm'; + return d; + } + + unsigned int old_fg = 0; + unsigned int old_bg = 0; + unsigned int old_ul = 0; + unsigned int old_link = 0; + + void reset(int fg, int bg, int ul, int link) { + old_fg = fg; + old_bg = bg; + old_ul = ul; + old_link = link; + } + + // FIXME: how to get rid of the weird BCE hack? + void* line16(Cell *src, int length, unsigned short *dst) { + int cur_jmp = 0; + unsigned int bce = old_bg; + unsigned ul = old_ul; + unsigned link = old_link; + + for (int i = 0; i < length;) { + Cell cell = src[i]; + + /** + * apply SGR differences + * We have to nullify HAS_EXTENDED due to its overloaded meaning, + * otherwise we would introduce nonsense jump/erase sequences here. + * SGR ext attributes for UL are covered by the explicit comparison, + * URL/hyperlink entry needs a separate control path (TODO). + */ + unsigned bg = cell.bg & ~HAS_EXTENDED; + ul = *((unsigned int *) (4096 * 4) + i); + if (cell.fg != old_fg || bg != old_bg || ul != old_ul) { + /* + we are in the middle of jumped over cells, + thus still need to apply BG changes first + */ + if (cur_jmp) { + if (old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'C'; + cur_jmp = 0; + } + /* write new SGR sequence, advance fg/bg/ul colors */ + dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); + old_fg = cell.fg; + old_bg = bg; + old_ul = ul; + } + + /* OSC 8 link handling */ + link = *((unsigned int *) (4096 * 4) + (length + i)); + if (link != old_link) { + if (old_link) { + // simply close old link - OSC 8 ; ; BEL + *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC + dst += 4; + *(unsigned int*) dst = 0x0007003b; // BEL ; + dst += 2; + } + if ((cell.bg & HAS_EXTENDED) && link) { + dst = load_link(dst, link); + old_link = link; + } else { + old_link = 0; + } + } + + + /* text content handling */ + if (cell.content & HAS_CONTENT_MASK) { + /* + we are in the middle of jumped over cells, thus apply cursor jump + we have to check here again in case there were no SGR changes + */ + if (cur_jmp) { + if (old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'C'; + cur_jmp = 0; + } + /* combined chars are written from JS (expensive?) */ + if (cell.content & IS_COMBINED_MASK) { + // FIXME: preload combined in a single action similar to ext attribs? + dst = single_combined(dst, i); + } else { + /* utf32 to utf16 conversion */ + unsigned int cp = cell.content & 0x1FFFFF; + if (cp > 0xFFFF) { + cp -= 0x10000; + *dst++ = (cp >> 10) + 0xD800; + *dst++ = (cp % 0x400) + 0xDC00; + } else { + *dst++ = cp; + } + } + } else { + /* empty cells are treated by cursor jumps */ + cur_jmp++; + } + + /* advance cell read position by wcwidth or 1 */ + int width = cell.content >> WIDTH_SHIFT; + i += width ? width : 1; + } + + /* + clear cells to the right if we have jumped over cells + and bce color != current bg + */ + if (cur_jmp && old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + + return dst; + } + \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.wasm b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.wasm new file mode 100755 index 0000000000000000000000000000000000000000..b1869f7bc76fbe57441f4ef0cc0b0b03c4a56898 GIT binary patch literal 2291 zcmb_eOK)367@f!U^|kMHVj)o>pmeTRK_!Wshm+Dx#!_Drj~(08k83wKj#EEk(M^2= zRanBlEcgSJND87Ao1ku3RTW~vf<+}N(M6D0utb>i&9(EA%ZfzujA!PY?|kR`W)fEF zY;eXHe^uTP{XXyCkbN^)82U^sN*OeJsm5lhy4qN4t}{m8Cp&A+dLvk^Y;Kg-nn5*Q zwKsx|&Gv1=bfNj|*9nc`Z6 zlPCdcwem0 zFP5SxdaaeSXv|)PHD>0RSw%}{i)yF18|`mL(f(N?gJzTID2{>MMYKbl1X=N6bg=z&XaDSy zG?Mgyx8mYq0*Z8)6>#Vht+8Q24d$zHm~4;mr(iJRAKB7Q&k!8Z@!?rFO6Y$O#?th$nOy^EYG4Xw5d>V|56UuI1Hi1za3rFlQ7Da)5 zX-+tuxck)g48wxyKh?U71QOJg3f%?ZchLb1CJJXnQQV7uMNckCT1{gWuEg+tc7(?q z1XAxB0`#ScOvXqM{AdVhm6RP4MB<15b3^d$Sc0ElLSP*uuwnuy@VIPe}(PgCTGt_PBIXGwHz_$6UjFYjI+d*~7#@O9>ZnW8l?n?b&OppiW4jvC; zz(a2hdgSfi82H{8I2wGY(bfw_qcS`ZH;T}=L$%L%Y#F-7MvmV^FL(j{DOFm(-izNJ z>L+QMf%?@jQ_s+mMMJHb4=+`ME~2F-XmC_IE`V6qe^4h=!MRGs?__-8a*p=sLWwJ6 zzb~j$gsH^$(VGR~pl$9&e|-{tMz0YSz%z|Q_xpG*PAtaBG7vxtaIP=#A~;as;xAE> zG@CT(f_mm&|BrVRp2T-KNo6*1gi2*OG%!yb3SjZmZ+!?2f;8b>j~+_=84>0x{e3Rf z>7I_BvUQ)XnFz^`B+X%qvgkFSI3T5`M)S~x>l{pHdiX$Sas3S#oJ%QwErU1Hf-fdl zLo7{8@@;}J3_hc8H8ePmYx3TrEax1t6PqsO4+(i*K`cCcC9}J?O61f>L)2|8j`)lN(#zQn?XK*BhJV RQloR;>8#b8)4|8xe*wFT=$ill literal 0 HcmV?d00001 From 7a42219fb3eabe7e408c8b170b017af93a908a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 17 Apr 2023 10:58:32 +0200 Subject: [PATCH 07/14] fix benchmark, add force to pack step --- .../benchmark/SerializeAddon.benchmark.ts | 4 ++-- ...zeAddon.benchmark.ts_ => Serialize2Addon.benchmark.ts} | 8 ++++---- addons/xterm-addon-serialize2/benchmark/tsconfig.json | 2 +- addons/xterm-addon-serialize2/package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename addons/xterm-addon-serialize2/benchmark/{SerializeAddon.benchmark.ts_ => Serialize2Addon.benchmark.ts} (91%) diff --git a/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts b/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts index 87741b6367..65f12895ed 100644 --- a/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts +++ b/addons/xterm-addon-serialize/benchmark/SerializeAddon.benchmark.ts @@ -53,10 +53,10 @@ perfContext('Terminal: sh -c "dd if=/dev/urandom count=40 bs=1k | hexdump | lolc perfContext('serialize', () => { let terminal: TestTerminal; const serializeAddon = new SerializeAddon(); - before(() => { + before(async () => { terminal = new TestTerminal({ cols: 80, rows: 25, scrollback: 5000 }); serializeAddon.activate(terminal); - terminal.writeSync(content); + await new Promise(r => terminal.write(content, r)); }); new ThroughputRuntimeCase('', () => { return { payloadSize: serializeAddon.serialize().length }; diff --git a/addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ b/addons/xterm-addon-serialize2/benchmark/Serialize2Addon.benchmark.ts similarity index 91% rename from addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ rename to addons/xterm-addon-serialize2/benchmark/Serialize2Addon.benchmark.ts index 87741b6367..b4121fa96d 100644 --- a/addons/xterm-addon-serialize2/benchmark/SerializeAddon.benchmark.ts_ +++ b/addons/xterm-addon-serialize2/benchmark/Serialize2Addon.benchmark.ts @@ -8,7 +8,7 @@ import { perfContext, before, ThroughputRuntimeCase } from 'xterm-benchmark'; import { spawn } from 'node-pty'; import { Utf8ToUtf32, stringFromCodePoint } from 'common/input/TextDecoder'; import { Terminal } from 'browser/public/Terminal'; -import { SerializeAddon } from 'SerializeAddon'; +import { Serialize2Addon } from 'Serialize2Addon'; class TestTerminal extends Terminal { public writeSync(data: string): void { @@ -52,11 +52,11 @@ perfContext('Terminal: sh -c "dd if=/dev/urandom count=40 bs=1k | hexdump | lolc perfContext('serialize', () => { let terminal: TestTerminal; - const serializeAddon = new SerializeAddon(); - before(() => { + const serializeAddon = new Serialize2Addon(); + before(async () => { terminal = new TestTerminal({ cols: 80, rows: 25, scrollback: 5000 }); serializeAddon.activate(terminal); - terminal.writeSync(content); + await new Promise(r => terminal.write(content, r)); }); new ThroughputRuntimeCase('', () => { return { payloadSize: serializeAddon.serialize().length }; diff --git a/addons/xterm-addon-serialize2/benchmark/tsconfig.json b/addons/xterm-addon-serialize2/benchmark/tsconfig.json index bf5e335c2b..a4aba8e6b7 100644 --- a/addons/xterm-addon-serialize2/benchmark/tsconfig.json +++ b/addons/xterm-addon-serialize2/benchmark/tsconfig.json @@ -11,7 +11,7 @@ "paths": { "common/*": ["../../../src/common/*"], "browser/*": ["../../../src/browser/*"], - "SerializeAddon": ["../src/SerializeAddon"] + "Serialize2Addon": ["../src/Serialize2Addon"] } }, "include": ["../**/*", "../../../typings/xterm.d.ts"], diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json index a8c7acadf0..3ab4a8eceb 100644 --- a/addons/xterm-addon-serialize2/package.json +++ b/addons/xterm-addon-serialize2/package.json @@ -16,7 +16,7 @@ ], "scripts": { "inwasm": "inwasm out/*.wasm.js", - "prepackage": "npm run build", + "prepackage": "../../node_modules/.bin/tsc -p . && inwasm -f out/*.wasm.js", "package": "../../node_modules/.bin/webpack", "prepublishOnly": "npm run package", "benchmark": "NODE_PATH=../../out:./out:./out-benchmark/ ../../node_modules/.bin/xterm-benchmark -r 5 -c benchmark/benchmark.json", From 37ede6068b9bc6b1f27bea927616e8e2f7cfaa45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 17 Apr 2023 11:03:25 +0200 Subject: [PATCH 08/14] make linter happy --- .eslintrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index a8e0810c6b..3b4cd13489 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,9 +23,10 @@ "addons/xterm-addon-search/test/tsconfig.json", "addons/xterm-addon-serialize/src/tsconfig.json", "addons/xterm-addon-serialize/test/tsconfig.json", + "addons/xterm-addon-serialize/benchmark/tsconfig.json", "addons/xterm-addon-serialize2/src/tsconfig.json", "addons/xterm-addon-serialize2/test/tsconfig.json", - "addons/xterm-addon-serialize/benchmark/tsconfig.json", + "addons/xterm-addon-serialize2/benchmark/tsconfig.json", "addons/xterm-addon-unicode11/src/tsconfig.json", "addons/xterm-addon-unicode11/test/tsconfig.json", "addons/xterm-addon-web-links/src/tsconfig.json", From 8e51c7e839bb254b48d7e9001a8be5b95b1943a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 17 Apr 2023 20:32:13 +0200 Subject: [PATCH 09/14] upgrade inwasm --- .../serializer.wasm.js/serialize/definition | 2 +- addons/xterm-addon-serialize2/package.json | 2 +- addons/xterm-addon-serialize2/yarn.lock | 25 +++++-------------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition index f1ecfa1ca9..d37a7b2f64 100644 --- a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition +++ b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition @@ -1 +1 @@ -{"def":{"name":"serialize","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"\n /* write combined chars on JS side */\n __attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n __attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n // FIXME: import mask values as template strings from JS\n #define CODEPOINT_MASK 0x1FFFFF\n #define IS_COMBINED_MASK 0x200000\n #define HAS_CONTENT_MASK 0x3FFFFF\n #define WIDTH_MASK 0xC00000\n #define WIDTH_SHIFT 22\n\n /* bit 1..8 blue in RGB, color in P256 and P16 */\n #define BLUE_MASK 0xFF\n #define BLUE_SHIFT 0\n #define PCOLOR_MASK 0xFF\n #define PCOLOR_SHIFT 0\n \n /* bit 9..16 green in RGB */\n #define GREEN_MASK 0xFF00\n #define GREEN_SHIFT 8\n \n /* bit 17..24 red in RGB */\n #define RED_MASK 0xFF0000\n #define RED_SHIFT 16\n \n /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n #define CM_MASK 0x3000000\n #define CM_DEFAULT 0\n #define CM_P16 0x1000000\n #define CM_P256 0x2000000\n #define CM_RGB 0x3000000\n \n /* bit 1..24 RGB room */\n #define RGB_MASK 0xFFFFFF\n #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n /* fg flags: bit 27..32 */\n #define INVERSE 0x4000000\n #define BOLD 0x8000000\n #define UNDERLINE 0x10000000\n #define BLINK 0x20000000\n #define INVISIBLE 0x40000000\n #define STRIKETHROUGH 0x80000000\n\n /* bg flags: bit 27..32 (upper 2 unused) */\n #define ITALIC 0x4000000\n #define DIM 0x8000000\n #define HAS_EXTENDED 0x10000000\n #define PROTECTED 0x20000000\n \n /* ext flags: bit 27..32 (upper 3 unused) */\n #define UNDERLINE_STYLE 0x1C000000\n \n /* underline style */\n #define UL_NONE 0\n #define UL_SINGLE 1\n #define UL_DOUBLE 2\n #define UL_CURLY 3\n #define UL_DOTTED 4\n #define UL_DASHED 5\n\n typedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n } Cell;\n\n /**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\n unsigned int *LUT100 = (unsigned int*) 256;\n\n __attribute__((noinline))\n unsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n }\n\n /* TODO: target support flags */\n #define S_SGR 1 /* include SGR flags */\n #define S_COLORS 2 /* include 256 indexed colors */\n #define S_RGB 4 /* include RGB colors */\n #define S_REMPTY 8 /* right truncate empty cells */\n #define S_CURSOR 16 /* include cursor move sequences */\n #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n #define S_DECAWM 64 /* dont break soft wraps */\n\n\n /**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n __attribute__((noinline))\n static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n }\n\n // FIXME: any nicer way to express this?\n #define SGR_FLAG(V, DIFF, FLAG, HI, LO) if ((DIFF) & (FLAG)) { if ((V) & (FLAG)) { *(unsigned int*) d = 0x3b0000 | (HI); d += 2; } else { *(unsigned long long*) d = 0x3b00000032ULL | (LO); d += 3; } } \n #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\n unsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n ) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n }\n\n unsigned int old_fg = 0;\n unsigned int old_bg = 0;\n unsigned int old_ul = 0;\n unsigned int old_link = 0;\n\n void reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n }\n\n // FIXME: how to get rid of the weird BCE hack?\n void* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n }\n "},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"}} \ No newline at end of file +{"def":{"name":"serialize","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"\n /* write combined chars on JS side */\n __attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n __attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n // FIXME: import mask values as template strings from JS\n #define CODEPOINT_MASK 0x1FFFFF\n #define IS_COMBINED_MASK 0x200000\n #define HAS_CONTENT_MASK 0x3FFFFF\n #define WIDTH_MASK 0xC00000\n #define WIDTH_SHIFT 22\n\n /* bit 1..8 blue in RGB, color in P256 and P16 */\n #define BLUE_MASK 0xFF\n #define BLUE_SHIFT 0\n #define PCOLOR_MASK 0xFF\n #define PCOLOR_SHIFT 0\n \n /* bit 9..16 green in RGB */\n #define GREEN_MASK 0xFF00\n #define GREEN_SHIFT 8\n \n /* bit 17..24 red in RGB */\n #define RED_MASK 0xFF0000\n #define RED_SHIFT 16\n \n /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n #define CM_MASK 0x3000000\n #define CM_DEFAULT 0\n #define CM_P16 0x1000000\n #define CM_P256 0x2000000\n #define CM_RGB 0x3000000\n \n /* bit 1..24 RGB room */\n #define RGB_MASK 0xFFFFFF\n #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n /* fg flags: bit 27..32 */\n #define INVERSE 0x4000000\n #define BOLD 0x8000000\n #define UNDERLINE 0x10000000\n #define BLINK 0x20000000\n #define INVISIBLE 0x40000000\n #define STRIKETHROUGH 0x80000000\n\n /* bg flags: bit 27..32 (upper 2 unused) */\n #define ITALIC 0x4000000\n #define DIM 0x8000000\n #define HAS_EXTENDED 0x10000000\n #define PROTECTED 0x20000000\n \n /* ext flags: bit 27..32 (upper 3 unused) */\n #define UNDERLINE_STYLE 0x1C000000\n \n /* underline style */\n #define UL_NONE 0\n #define UL_SINGLE 1\n #define UL_DOUBLE 2\n #define UL_CURLY 3\n #define UL_DOTTED 4\n #define UL_DASHED 5\n\n typedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n } Cell;\n\n /**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\n unsigned int *LUT100 = (unsigned int*) 256;\n\n __attribute__((noinline))\n unsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n }\n\n /* TODO: target support flags */\n #define S_SGR 1 /* include SGR flags */\n #define S_COLORS 2 /* include 256 indexed colors */\n #define S_RGB 4 /* include RGB colors */\n #define S_REMPTY 8 /* right truncate empty cells */\n #define S_CURSOR 16 /* include cursor move sequences */\n #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n #define S_DECAWM 64 /* dont break soft wraps */\n\n\n /**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n __attribute__((noinline))\n static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n }\n\n // FIXME: any nicer way to express this?\n #define SGR_FLAG(V, DIFF, FLAG, HI, LO) if ((DIFF) & (FLAG)) { if ((V) & (FLAG)) { *(unsigned int*) d = 0x3b0000 | (HI); d += 2; } else { *(unsigned long long*) d = 0x3b00000032ULL | (LO); d += 3; } } \n #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\n unsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n ) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n }\n\n unsigned int old_fg = 0;\n unsigned int old_bg = 0;\n unsigned int old_ul = 0;\n unsigned int old_link = 0;\n\n void reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n }\n\n // FIXME: how to get rid of the weird BCE hack?\n void* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n }\n "},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"},"srcDef":"{\n name: 'serialize',\n type: 0,\n mode: 1,\n srctype: 'Clang-C',\n imports: {\n env: {\n memory: new WebAssembly.Memory({ initial: 1 }),\n single_combined: (dst, x) => 0,\n load_link: (dst, linkId) => 0\n }\n },\n exports: {\n line16: (src, length, dst) => 0,\n reset: (fg, bg, ul, link) => { }\n },\n compile: {\n switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first']\n },\n code: `\n /* write combined chars on JS side */\n __attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n __attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n // FIXME: import mask values as template strings from JS\n #define CODEPOINT_MASK 0x1FFFFF\n #define IS_COMBINED_MASK 0x200000\n #define HAS_CONTENT_MASK 0x3FFFFF\n #define WIDTH_MASK 0xC00000\n #define WIDTH_SHIFT 22\n\n /* bit 1..8 blue in RGB, color in P256 and P16 */\n #define BLUE_MASK 0xFF\n #define BLUE_SHIFT 0\n #define PCOLOR_MASK 0xFF\n #define PCOLOR_SHIFT 0\n \n /* bit 9..16 green in RGB */\n #define GREEN_MASK 0xFF00\n #define GREEN_SHIFT 8\n \n /* bit 17..24 red in RGB */\n #define RED_MASK 0xFF0000\n #define RED_SHIFT 16\n \n /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n #define CM_MASK 0x3000000\n #define CM_DEFAULT 0\n #define CM_P16 0x1000000\n #define CM_P256 0x2000000\n #define CM_RGB 0x3000000\n \n /* bit 1..24 RGB room */\n #define RGB_MASK 0xFFFFFF\n #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n /* fg flags: bit 27..32 */\n #define INVERSE 0x4000000\n #define BOLD 0x8000000\n #define UNDERLINE 0x10000000\n #define BLINK 0x20000000\n #define INVISIBLE 0x40000000\n #define STRIKETHROUGH 0x80000000\n\n /* bg flags: bit 27..32 (upper 2 unused) */\n #define ITALIC 0x4000000\n #define DIM 0x8000000\n #define HAS_EXTENDED 0x10000000\n #define PROTECTED 0x20000000\n \n /* ext flags: bit 27..32 (upper 3 unused) */\n #define UNDERLINE_STYLE 0x1C000000\n \n /* underline style */\n #define UL_NONE 0\n #define UL_SINGLE 1\n #define UL_DOUBLE 2\n #define UL_CURLY 3\n #define UL_DOTTED 4\n #define UL_DASHED 5\n\n typedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n } Cell;\n\n /**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\n unsigned int *LUT100 = (unsigned int*) 256;\n\n __attribute__((noinline))\n unsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n }\n\n /* TODO: target support flags */\n #define S_SGR 1 /* include SGR flags */\n #define S_COLORS 2 /* include 256 indexed colors */\n #define S_RGB 4 /* include RGB colors */\n #define S_REMPTY 8 /* right truncate empty cells */\n #define S_CURSOR 16 /* include cursor move sequences */\n #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n #define S_DECAWM 64 /* dont break soft wraps */\n\n\n /**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n __attribute__((noinline))\n static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n }\n\n // FIXME: any nicer way to express this?\n #define SGR_FLAG(V, DIFF, FLAG, HI, LO) \\\n if ((DIFF) & (FLAG)) { \\\n if ((V) & (FLAG)) { \\\n *(unsigned int*) d = 0x3b0000 | (HI); \\\n d += 2; \\\n } else { \\\n *(unsigned long long*) d = 0x3b00000032ULL | (LO); \\\n d += 3; \\\n } \\\n } \\\n\n #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\n unsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n ) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n }\n\n unsigned int old_fg = 0;\n unsigned int old_bg = 0;\n unsigned int old_ul = 0;\n unsigned int old_link = 0;\n\n void reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n }\n\n // FIXME: how to get rid of the weird BCE hack?\n void* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n }\n `\n}","mtimes":""} \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json index 3ab4a8eceb..4b9f25d32d 100644 --- a/addons/xterm-addon-serialize2/package.json +++ b/addons/xterm-addon-serialize2/package.json @@ -27,6 +27,6 @@ "xterm": "^5.0.0" }, "devDependencies": { - "inwasm": "^0.0.7" + "inwasm": "^0.0.10" } } diff --git a/addons/xterm-addon-serialize2/yarn.lock b/addons/xterm-addon-serialize2/yarn.lock index 485c69456c..39ee1ac144 100644 --- a/addons/xterm-addon-serialize2/yarn.lock +++ b/addons/xterm-addon-serialize2/yarn.lock @@ -2,18 +2,6 @@ # yarn lockfile v1 -"@types/acorn@^4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" - integrity sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ== - dependencies: - "@types/estree" "*" - -"@types/estree@*": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" @@ -71,7 +59,7 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -colorette@^2.0.19: +colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -110,16 +98,15 @@ glob@^10.0.0: minipass "^5.0.0" path-scurry "^1.7.0" -inwasm@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.7.tgz#6b5241346e6a08ad98ac5ff664f279231fcf94e6" - integrity sha512-mKBYr/rsqJGbn/3uF8He8+zieNT0HIbg2qzbWZZ7HnB8Qa20kK7a9J7Xa4qmVdxG5grJoV/BJnJBBeUKlXogyA== +inwasm@^0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.10.tgz#d2d15897ae65a7a2d14e6601494ca1bccc9ee1a0" + integrity sha512-PGAK9I5AdbaWCZbJ6NNUGVGf1knGNfke339De/ZV7U+uEBM80DMKc6DLpX7yKIzX+f2qNzbVDWIiDTsuhc7RfA== dependencies: - "@types/acorn" "^4.0.6" acorn "^8.8.2" acorn-walk "^8.2.0" chokidar "^3.5.3" - colorette "^2.0.19" + colorette "^2.0.20" glob "^10.0.0" wabt "^1.0.32" From d20db7fb5648789caddcebfda4cb5ee39a7e2574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Mon, 17 Apr 2023 20:43:02 +0200 Subject: [PATCH 10/14] preserve only needed built files --- addons/xterm-addon-serialize2/.gitignore | 3 + .../serializer.wasm.js/serialize/serialize.c | 344 ------------------ .../serialize/serialize.wasm | Bin 2291 -> 0 bytes 3 files changed, 3 insertions(+), 344 deletions(-) delete mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c delete mode 100755 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.wasm diff --git a/addons/xterm-addon-serialize2/.gitignore b/addons/xterm-addon-serialize2/.gitignore index 1272307b8f..02ef69d074 100644 --- a/addons/xterm-addon-serialize2/.gitignore +++ b/addons/xterm-addon-serialize2/.gitignore @@ -4,3 +4,6 @@ out-benchmark # skip inwasm folders inwasm-sdks/ +inwasm-builds/ +!inwasm-builds/**/final.wat +!inwasm-builds/**/final.wasm diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c deleted file mode 100644 index 8b84a492da..0000000000 --- a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.c +++ /dev/null @@ -1,344 +0,0 @@ - - /* write combined chars on JS side */ - __attribute__((import_module("env"), import_name("single_combined"))) void* single_combined(unsigned short* dst, int x); - __attribute__((import_module("env"), import_name("load_link"))) void* load_link(unsigned short* dst, int link); - - // FIXME: import mask values as template strings from JS - #define CODEPOINT_MASK 0x1FFFFF - #define IS_COMBINED_MASK 0x200000 - #define HAS_CONTENT_MASK 0x3FFFFF - #define WIDTH_MASK 0xC00000 - #define WIDTH_SHIFT 22 - - /* bit 1..8 blue in RGB, color in P256 and P16 */ - #define BLUE_MASK 0xFF - #define BLUE_SHIFT 0 - #define PCOLOR_MASK 0xFF - #define PCOLOR_SHIFT 0 - - /* bit 9..16 green in RGB */ - #define GREEN_MASK 0xFF00 - #define GREEN_SHIFT 8 - - /* bit 17..24 red in RGB */ - #define RED_MASK 0xFF0000 - #define RED_SHIFT 16 - - /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ - #define CM_MASK 0x3000000 - #define CM_DEFAULT 0 - #define CM_P16 0x1000000 - #define CM_P256 0x2000000 - #define CM_RGB 0x3000000 - - /* bit 1..24 RGB room */ - #define RGB_MASK 0xFFFFFF - #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ - - /* fg flags: bit 27..32 */ - #define INVERSE 0x4000000 - #define BOLD 0x8000000 - #define UNDERLINE 0x10000000 - #define BLINK 0x20000000 - #define INVISIBLE 0x40000000 - #define STRIKETHROUGH 0x80000000 - - /* bg flags: bit 27..32 (upper 2 unused) */ - #define ITALIC 0x4000000 - #define DIM 0x8000000 - #define HAS_EXTENDED 0x10000000 - #define PROTECTED 0x20000000 - - /* ext flags: bit 27..32 (upper 3 unused) */ - #define UNDERLINE_STYLE 0x1C000000 - - /* underline style */ - #define UL_NONE 0 - #define UL_SINGLE 1 - #define UL_DOUBLE 2 - #define UL_CURLY 3 - #define UL_DOTTED 4 - #define UL_DASHED 5 - - typedef struct __attribute__((packed, aligned(4))) { - unsigned int content; - unsigned int fg; - unsigned int bg; - } Cell; - - /** - * Optimized itoa implementation for unsigned short to utf16. - * - * Note: Clang compiles with the div instruction in wasm. - * Since tests with shift mul in source show no runtime difference, - * wasm engines prolly optimize the division on their own. - */ - unsigned int *LUT100 = (unsigned int*) 256; - - __attribute__((noinline)) - unsigned short* itoa16(unsigned short n, unsigned short *dst) { - if (n < 10) { - *dst++ = n + 48; - } else if (n < 100) { - *(unsigned int*) dst = LUT100[n]; - dst += 2; - } else if (n < 1000) { - int h = n / 100; - *dst++ = h + 48; - *(unsigned int*) dst = LUT100[n - h * 100]; - dst += 2; - } else if (n < 10000) { - int h = n / 100; - *(unsigned int*) dst = LUT100[h]; - *((unsigned int*) dst+1) = LUT100[n - h * 100]; - dst += 4; - } else { - int h = n / 10000; - *dst++ = h + 48; - n -= h * 10000; - h = n / 100; - *(unsigned int*) dst = LUT100[h]; - *((unsigned int*) dst+1) = LUT100[n - h * 100]; - dst += 4; - } - return dst; - } - - /* TODO: target support flags */ - #define S_SGR 1 /* include SGR flags */ - #define S_COLORS 2 /* include 256 indexed colors */ - #define S_RGB 4 /* include RGB colors */ - #define S_REMPTY 8 /* right truncate empty cells */ - #define S_CURSOR 16 /* include cursor move sequences */ - #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */ - #define S_DECAWM 64 /* dont break soft wraps */ - - - /** - * Set SGR colors for FG / BG / UL. - * c denotes the color target as {FG: '3', BG: '4', UL: '5'}. - */ - __attribute__((noinline)) - static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) { - int mode = v & CM_MASK; - if (mode == CM_DEFAULT) { - *(unsigned long long*) d = 0x3b00390000ULL | c; - d += 3; - } else if (mode == CM_P16) { - unsigned long long color = 48 + (v & 7); - if (v & 8) { - /* bright for FG | BG (no UL color here) */ - if (c == '3') { - *(unsigned long long*) d = 0x003b00000039ULL | color << 16; - d += 3; - } else if (c == '4') { - *(unsigned long long*) d = 0x003b000000300031ULL | color << 32; - d += 4; - } - } else { - /* handles normal FG | BG | UL */ - *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c; - d += 3; - } - } else if (mode == CM_P256) { - *d++ = c; - *(unsigned long long*) d = 0x3b0035003b0038ULL; - d += 4; - d = itoa16(v & 0xFF, d); - *d++ = ';'; - } else { - *d++ = c; - *(unsigned long long*) d = 0x3b0032003b0038ULL; - d += 4; - d = itoa16((v >> 16) & 0xFF, d); - *d++ = ';'; - d = itoa16((v >> 8) & 0xFF, d); - *d++ = ';'; - d = itoa16(v & 0xFF, d); - *d++ = ';'; - } - return d; - } - - // FIXME: any nicer way to express this? - #define SGR_FLAG(V, DIFF, FLAG, HI, LO) if ((DIFF) & (FLAG)) { if ((V) & (FLAG)) { *(unsigned int*) d = 0x3b0000 | (HI); d += 2; } else { *(unsigned long long*) d = 0x3b00000032ULL | (LO); d += 3; } } - #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; - - unsigned short* diff_attr16( - unsigned short* d, - unsigned int fg, - unsigned int bg, - unsigned int diff_fg, - unsigned int diff_bg, - unsigned int ul, - unsigned int diff_ul - ) { - W_CSI(d) - - if (!fg && !bg) { - *d++ = ';'; - } else { - /* fg flags */ - if (diff_fg >> 26) { - SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16) - SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16) - //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs - SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16) - SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16) - SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16) - } - /* fg color */ - if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3'); - - /* bg flags */ - if (diff_bg >> 26) { - SGR_FLAG(bg, diff_bg, ITALIC, '3', '3') - SGR_FLAG(bg, diff_bg, DIM, '2', '2') - } - /* bg color */ - if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4'); - - /* ul ext attributes */ - /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */ - if (bg & HAS_EXTENDED) { - if (diff_ul & UNDERLINE_STYLE) { - *d++ = '4'; - *d++ = ':'; - *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; - *d++ = ';'; - } - if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5'); - } - } - *(d - 1) = 'm'; - return d; - } - - unsigned int old_fg = 0; - unsigned int old_bg = 0; - unsigned int old_ul = 0; - unsigned int old_link = 0; - - void reset(int fg, int bg, int ul, int link) { - old_fg = fg; - old_bg = bg; - old_ul = ul; - old_link = link; - } - - // FIXME: how to get rid of the weird BCE hack? - void* line16(Cell *src, int length, unsigned short *dst) { - int cur_jmp = 0; - unsigned int bce = old_bg; - unsigned ul = old_ul; - unsigned link = old_link; - - for (int i = 0; i < length;) { - Cell cell = src[i]; - - /** - * apply SGR differences - * We have to nullify HAS_EXTENDED due to its overloaded meaning, - * otherwise we would introduce nonsense jump/erase sequences here. - * SGR ext attributes for UL are covered by the explicit comparison, - * URL/hyperlink entry needs a separate control path (TODO). - */ - unsigned bg = cell.bg & ~HAS_EXTENDED; - ul = *((unsigned int *) (4096 * 4) + i); - if (cell.fg != old_fg || bg != old_bg || ul != old_ul) { - /* - we are in the middle of jumped over cells, - thus still need to apply BG changes first - */ - if (cur_jmp) { - if (old_bg != bce) { - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'X'; - } - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'C'; - cur_jmp = 0; - } - /* write new SGR sequence, advance fg/bg/ul colors */ - dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); - old_fg = cell.fg; - old_bg = bg; - old_ul = ul; - } - - /* OSC 8 link handling */ - link = *((unsigned int *) (4096 * 4) + (length + i)); - if (link != old_link) { - if (old_link) { - // simply close old link - OSC 8 ; ; BEL - *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC - dst += 4; - *(unsigned int*) dst = 0x0007003b; // BEL ; - dst += 2; - } - if ((cell.bg & HAS_EXTENDED) && link) { - dst = load_link(dst, link); - old_link = link; - } else { - old_link = 0; - } - } - - - /* text content handling */ - if (cell.content & HAS_CONTENT_MASK) { - /* - we are in the middle of jumped over cells, thus apply cursor jump - we have to check here again in case there were no SGR changes - */ - if (cur_jmp) { - if (old_bg != bce) { - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'X'; - } - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'C'; - cur_jmp = 0; - } - /* combined chars are written from JS (expensive?) */ - if (cell.content & IS_COMBINED_MASK) { - // FIXME: preload combined in a single action similar to ext attribs? - dst = single_combined(dst, i); - } else { - /* utf32 to utf16 conversion */ - unsigned int cp = cell.content & 0x1FFFFF; - if (cp > 0xFFFF) { - cp -= 0x10000; - *dst++ = (cp >> 10) + 0xD800; - *dst++ = (cp % 0x400) + 0xDC00; - } else { - *dst++ = cp; - } - } - } else { - /* empty cells are treated by cursor jumps */ - cur_jmp++; - } - - /* advance cell read position by wcwidth or 1 */ - int width = cell.content >> WIDTH_SHIFT; - i += width ? width : 1; - } - - /* - clear cells to the right if we have jumped over cells - and bce color != current bg - */ - if (cur_jmp && old_bg != bce) { - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'X'; - } - - return dst; - } - \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.wasm b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/serialize.wasm deleted file mode 100755 index b1869f7bc76fbe57441f4ef0cc0b0b03c4a56898..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2291 zcmb_eOK)367@f!U^|kMHVj)o>pmeTRK_!Wshm+Dx#!_Drj~(08k83wKj#EEk(M^2= zRanBlEcgSJND87Ao1ku3RTW~vf<+}N(M6D0utb>i&9(EA%ZfzujA!PY?|kR`W)fEF zY;eXHe^uTP{XXyCkbN^)82U^sN*OeJsm5lhy4qN4t}{m8Cp&A+dLvk^Y;Kg-nn5*Q zwKsx|&Gv1=bfNj|*9nc`Z6 zlPCdcwem0 zFP5SxdaaeSXv|)PHD>0RSw%}{i)yF18|`mL(f(N?gJzTID2{>MMYKbl1X=N6bg=z&XaDSy zG?Mgyx8mYq0*Z8)6>#Vht+8Q24d$zHm~4;mr(iJRAKB7Q&k!8Z@!?rFO6Y$O#?th$nOy^EYG4Xw5d>V|56UuI1Hi1za3rFlQ7Da)5 zX-+tuxck)g48wxyKh?U71QOJg3f%?ZchLb1CJJXnQQV7uMNckCT1{gWuEg+tc7(?q z1XAxB0`#ScOvXqM{AdVhm6RP4MB<15b3^d$Sc0ElLSP*uuwnuy@VIPe}(PgCTGt_PBIXGwHz_$6UjFYjI+d*~7#@O9>ZnW8l?n?b&OppiW4jvC; zz(a2hdgSfi82H{8I2wGY(bfw_qcS`ZH;T}=L$%L%Y#F-7MvmV^FL(j{DOFm(-izNJ z>L+QMf%?@jQ_s+mMMJHb4=+`ME~2F-XmC_IE`V6qe^4h=!MRGs?__-8a*p=sLWwJ6 zzb~j$gsH^$(VGR~pl$9&e|-{tMz0YSz%z|Q_xpG*PAtaBG7vxtaIP=#A~;as;xAE> zG@CT(f_mm&|BrVRp2T-KNo6*1gi2*OG%!yb3SjZmZ+!?2f;8b>j~+_=84>0x{e3Rf z>7I_BvUQ)XnFz^`B+X%qvgkFSI3T5`M)S~x>l{pHdiX$Sas3S#oJ%QwErU1Hf-fdl zLo7{8@@;}J3_hc8H8ePmYx3TrEax1t6PqsO4+(i*K`cCcC9}J?O61f>L)2|8j`)lN(#zQn?XK*BhJV RQloR;>8#b8)4|8xe*wFT=$ill From 8f466fcd775a8018e80076214a2f5092d8cbd947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Tue, 18 Apr 2023 19:34:53 +0200 Subject: [PATCH 11/14] use external c file --- addons/xterm-addon-serialize2/.gitignore | 1 + .../serializer.wasm.js/serialize/definition | 1 - .../serialize/definition.json | 1 + addons/xterm-addon-serialize2/package.json | 2 +- .../src-wasm/serialize.c | 352 +++++++++++++++++ .../src/serializer.wasm.ts | 357 +----------------- addons/xterm-addon-serialize2/yarn.lock | 155 +++++++- 7 files changed, 503 insertions(+), 366 deletions(-) delete mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition create mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json create mode 100644 addons/xterm-addon-serialize2/src-wasm/serialize.c diff --git a/addons/xterm-addon-serialize2/.gitignore b/addons/xterm-addon-serialize2/.gitignore index 02ef69d074..10e27d6b85 100644 --- a/addons/xterm-addon-serialize2/.gitignore +++ b/addons/xterm-addon-serialize2/.gitignore @@ -7,3 +7,4 @@ inwasm-sdks/ inwasm-builds/ !inwasm-builds/**/final.wat !inwasm-builds/**/final.wasm +!inwasm-builds/**/definition.json diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition deleted file mode 100644 index d37a7b2f64..0000000000 --- a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition +++ /dev/null @@ -1 +0,0 @@ -{"def":{"name":"serialize","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"\n /* write combined chars on JS side */\n __attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n __attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n // FIXME: import mask values as template strings from JS\n #define CODEPOINT_MASK 0x1FFFFF\n #define IS_COMBINED_MASK 0x200000\n #define HAS_CONTENT_MASK 0x3FFFFF\n #define WIDTH_MASK 0xC00000\n #define WIDTH_SHIFT 22\n\n /* bit 1..8 blue in RGB, color in P256 and P16 */\n #define BLUE_MASK 0xFF\n #define BLUE_SHIFT 0\n #define PCOLOR_MASK 0xFF\n #define PCOLOR_SHIFT 0\n \n /* bit 9..16 green in RGB */\n #define GREEN_MASK 0xFF00\n #define GREEN_SHIFT 8\n \n /* bit 17..24 red in RGB */\n #define RED_MASK 0xFF0000\n #define RED_SHIFT 16\n \n /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n #define CM_MASK 0x3000000\n #define CM_DEFAULT 0\n #define CM_P16 0x1000000\n #define CM_P256 0x2000000\n #define CM_RGB 0x3000000\n \n /* bit 1..24 RGB room */\n #define RGB_MASK 0xFFFFFF\n #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n /* fg flags: bit 27..32 */\n #define INVERSE 0x4000000\n #define BOLD 0x8000000\n #define UNDERLINE 0x10000000\n #define BLINK 0x20000000\n #define INVISIBLE 0x40000000\n #define STRIKETHROUGH 0x80000000\n\n /* bg flags: bit 27..32 (upper 2 unused) */\n #define ITALIC 0x4000000\n #define DIM 0x8000000\n #define HAS_EXTENDED 0x10000000\n #define PROTECTED 0x20000000\n \n /* ext flags: bit 27..32 (upper 3 unused) */\n #define UNDERLINE_STYLE 0x1C000000\n \n /* underline style */\n #define UL_NONE 0\n #define UL_SINGLE 1\n #define UL_DOUBLE 2\n #define UL_CURLY 3\n #define UL_DOTTED 4\n #define UL_DASHED 5\n\n typedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n } Cell;\n\n /**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\n unsigned int *LUT100 = (unsigned int*) 256;\n\n __attribute__((noinline))\n unsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n }\n\n /* TODO: target support flags */\n #define S_SGR 1 /* include SGR flags */\n #define S_COLORS 2 /* include 256 indexed colors */\n #define S_RGB 4 /* include RGB colors */\n #define S_REMPTY 8 /* right truncate empty cells */\n #define S_CURSOR 16 /* include cursor move sequences */\n #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n #define S_DECAWM 64 /* dont break soft wraps */\n\n\n /**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n __attribute__((noinline))\n static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n }\n\n // FIXME: any nicer way to express this?\n #define SGR_FLAG(V, DIFF, FLAG, HI, LO) if ((DIFF) & (FLAG)) { if ((V) & (FLAG)) { *(unsigned int*) d = 0x3b0000 | (HI); d += 2; } else { *(unsigned long long*) d = 0x3b00000032ULL | (LO); d += 3; } } \n #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\n unsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n ) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n }\n\n unsigned int old_fg = 0;\n unsigned int old_bg = 0;\n unsigned int old_ul = 0;\n unsigned int old_link = 0;\n\n void reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n }\n\n // FIXME: how to get rid of the weird BCE hack?\n void* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n }\n "},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"},"srcDef":"{\n name: 'serialize',\n type: 0,\n mode: 1,\n srctype: 'Clang-C',\n imports: {\n env: {\n memory: new WebAssembly.Memory({ initial: 1 }),\n single_combined: (dst, x) => 0,\n load_link: (dst, linkId) => 0\n }\n },\n exports: {\n line16: (src, length, dst) => 0,\n reset: (fg, bg, ul, link) => { }\n },\n compile: {\n switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first']\n },\n code: `\n /* write combined chars on JS side */\n __attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n __attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n // FIXME: import mask values as template strings from JS\n #define CODEPOINT_MASK 0x1FFFFF\n #define IS_COMBINED_MASK 0x200000\n #define HAS_CONTENT_MASK 0x3FFFFF\n #define WIDTH_MASK 0xC00000\n #define WIDTH_SHIFT 22\n\n /* bit 1..8 blue in RGB, color in P256 and P16 */\n #define BLUE_MASK 0xFF\n #define BLUE_SHIFT 0\n #define PCOLOR_MASK 0xFF\n #define PCOLOR_SHIFT 0\n \n /* bit 9..16 green in RGB */\n #define GREEN_MASK 0xFF00\n #define GREEN_SHIFT 8\n \n /* bit 17..24 red in RGB */\n #define RED_MASK 0xFF0000\n #define RED_SHIFT 16\n \n /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n #define CM_MASK 0x3000000\n #define CM_DEFAULT 0\n #define CM_P16 0x1000000\n #define CM_P256 0x2000000\n #define CM_RGB 0x3000000\n \n /* bit 1..24 RGB room */\n #define RGB_MASK 0xFFFFFF\n #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n /* fg flags: bit 27..32 */\n #define INVERSE 0x4000000\n #define BOLD 0x8000000\n #define UNDERLINE 0x10000000\n #define BLINK 0x20000000\n #define INVISIBLE 0x40000000\n #define STRIKETHROUGH 0x80000000\n\n /* bg flags: bit 27..32 (upper 2 unused) */\n #define ITALIC 0x4000000\n #define DIM 0x8000000\n #define HAS_EXTENDED 0x10000000\n #define PROTECTED 0x20000000\n \n /* ext flags: bit 27..32 (upper 3 unused) */\n #define UNDERLINE_STYLE 0x1C000000\n \n /* underline style */\n #define UL_NONE 0\n #define UL_SINGLE 1\n #define UL_DOUBLE 2\n #define UL_CURLY 3\n #define UL_DOTTED 4\n #define UL_DASHED 5\n\n typedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n } Cell;\n\n /**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\n unsigned int *LUT100 = (unsigned int*) 256;\n\n __attribute__((noinline))\n unsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n }\n\n /* TODO: target support flags */\n #define S_SGR 1 /* include SGR flags */\n #define S_COLORS 2 /* include 256 indexed colors */\n #define S_RGB 4 /* include RGB colors */\n #define S_REMPTY 8 /* right truncate empty cells */\n #define S_CURSOR 16 /* include cursor move sequences */\n #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n #define S_DECAWM 64 /* dont break soft wraps */\n\n\n /**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n __attribute__((noinline))\n static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n }\n\n // FIXME: any nicer way to express this?\n #define SGR_FLAG(V, DIFF, FLAG, HI, LO) \\\n if ((DIFF) & (FLAG)) { \\\n if ((V) & (FLAG)) { \\\n *(unsigned int*) d = 0x3b0000 | (HI); \\\n d += 2; \\\n } else { \\\n *(unsigned long long*) d = 0x3b00000032ULL | (LO); \\\n d += 3; \\\n } \\\n } \\\n\n #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\n unsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n ) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n }\n\n unsigned int old_fg = 0;\n unsigned int old_bg = 0;\n unsigned int old_ul = 0;\n unsigned int old_link = 0;\n\n void reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n }\n\n // FIXME: how to get rid of the weird BCE hack?\n void* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n }\n `\n}","mtimes":""} \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json new file mode 100644 index 0000000000..4cb2255db9 --- /dev/null +++ b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json @@ -0,0 +1 @@ +{"def":{"name":"serialize","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"/* write combined chars on JS side */\n__attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n__attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n// FIXME: import mask values as template strings from JS\n#define CODEPOINT_MASK 0x1FFFFF\n#define IS_COMBINED_MASK 0x200000\n#define HAS_CONTENT_MASK 0x3FFFFF\n#define WIDTH_MASK 0xC00000\n#define WIDTH_SHIFT 22\n\n/* bit 1..8 blue in RGB, color in P256 and P16 */\n#define BLUE_MASK 0xFF\n#define BLUE_SHIFT 0\n#define PCOLOR_MASK 0xFF\n#define PCOLOR_SHIFT 0\n\n/* bit 9..16 green in RGB */\n#define GREEN_MASK 0xFF00\n#define GREEN_SHIFT 8\n\n/* bit 17..24 red in RGB */\n#define RED_MASK 0xFF0000\n#define RED_SHIFT 16\n\n/* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n#define CM_MASK 0x3000000\n#define CM_DEFAULT 0\n#define CM_P16 0x1000000\n#define CM_P256 0x2000000\n#define CM_RGB 0x3000000\n\n/* bit 1..24 RGB room */\n#define RGB_MASK 0xFFFFFF\n#define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n/* fg flags: bit 27..32 */\n#define INVERSE 0x4000000\n#define BOLD 0x8000000\n#define UNDERLINE 0x10000000\n#define BLINK 0x20000000\n#define INVISIBLE 0x40000000\n#define STRIKETHROUGH 0x80000000\n\n/* bg flags: bit 27..32 (upper 2 unused) */\n#define ITALIC 0x4000000\n#define DIM 0x8000000\n#define HAS_EXTENDED 0x10000000\n#define PROTECTED 0x20000000\n\n/* ext flags: bit 27..32 (upper 3 unused) */\n#define UNDERLINE_STYLE 0x1C000000\n\n/* underline style */\n#define UL_NONE 0\n#define UL_SINGLE 1\n#define UL_DOUBLE 2\n#define UL_CURLY 3\n#define UL_DOTTED 4\n#define UL_DASHED 5\n\ntypedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n} Cell;\n\n/**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\nunsigned int *LUT100 = (unsigned int*) 256;\n\n__attribute__((noinline))\nunsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n}\n\n/* TODO: target support flags */\n#define S_SGR 1 /* include SGR flags */\n#define S_COLORS 2 /* include 256 indexed colors */\n#define S_RGB 4 /* include RGB colors */\n#define S_REMPTY 8 /* right truncate empty cells */\n#define S_CURSOR 16 /* include cursor move sequences */\n#define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n#define S_DECAWM 64 /* dont break soft wraps */\n\n\n/**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n__attribute__((noinline))\nstatic unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n}\n\n// FIXME: any nicer way to express this?\n#define SGR_FLAG(V, DIFF, FLAG, HI, LO) \\\nif ((DIFF) & (FLAG)) { \\\n if ((V) & (FLAG)) { \\\n *(unsigned int*) d = 0x3b0000 | (HI); \\\n d += 2; \\\n } else { \\\n *(unsigned long long*) d = 0x3b00000032ULL | (LO); \\\n d += 3; \\\n } \\\n} \\\n\n#define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\nunsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n}\n\nunsigned int old_fg = 0;\nunsigned int old_bg = 0;\nunsigned int old_ul = 0;\nunsigned int old_link = 0;\n\nvoid reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n}\n\n// FIXME: how to get rid of the weird BCE hack?\nvoid* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n}\n","trackChanges":["src-wasm/serialize.c"],"trackMode":"content"},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"},"srcDef":"{\n name: 'serialize',\n type: 0,\n mode: 1,\n srctype: 'Clang-C',\n imports: {\n env: {\n memory: new WebAssembly.Memory({ initial: 1 }),\n single_combined: (dst, x) => 0,\n load_link: (dst, linkId) => 0\n }\n },\n exports: {\n line16: (src, length, dst) => 0,\n reset: (fg, bg, ul, link) => { }\n },\n compile: {\n switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first']\n },\n code: `${require('fs').readFileSync('src-wasm/serialize.c')}`,\n trackChanges: ['src-wasm/serialize.c'],\n trackMode: 'content'\n}","hash":"c8fb23f59fc2046f32fe781ab7f28d0413789c88a7860032d5ed1bdbe93aaea1"} \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json index 4b9f25d32d..14b44a5dd5 100644 --- a/addons/xterm-addon-serialize2/package.json +++ b/addons/xterm-addon-serialize2/package.json @@ -27,6 +27,6 @@ "xterm": "^5.0.0" }, "devDependencies": { - "inwasm": "^0.0.10" + "inwasm": "^0.0.11" } } diff --git a/addons/xterm-addon-serialize2/src-wasm/serialize.c b/addons/xterm-addon-serialize2/src-wasm/serialize.c new file mode 100644 index 0000000000..3222ec3444 --- /dev/null +++ b/addons/xterm-addon-serialize2/src-wasm/serialize.c @@ -0,0 +1,352 @@ +/* write combined chars on JS side */ +__attribute__((import_module("env"), import_name("single_combined"))) void* single_combined(unsigned short* dst, int x); +__attribute__((import_module("env"), import_name("load_link"))) void* load_link(unsigned short* dst, int link); + +// FIXME: import mask values as template strings from JS +#define CODEPOINT_MASK 0x1FFFFF +#define IS_COMBINED_MASK 0x200000 +#define HAS_CONTENT_MASK 0x3FFFFF +#define WIDTH_MASK 0xC00000 +#define WIDTH_SHIFT 22 + +/* bit 1..8 blue in RGB, color in P256 and P16 */ +#define BLUE_MASK 0xFF +#define BLUE_SHIFT 0 +#define PCOLOR_MASK 0xFF +#define PCOLOR_SHIFT 0 + +/* bit 9..16 green in RGB */ +#define GREEN_MASK 0xFF00 +#define GREEN_SHIFT 8 + +/* bit 17..24 red in RGB */ +#define RED_MASK 0xFF0000 +#define RED_SHIFT 16 + +/* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ +#define CM_MASK 0x3000000 +#define CM_DEFAULT 0 +#define CM_P16 0x1000000 +#define CM_P256 0x2000000 +#define CM_RGB 0x3000000 + +/* bit 1..24 RGB room */ +#define RGB_MASK 0xFFFFFF +#define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ + +/* fg flags: bit 27..32 */ +#define INVERSE 0x4000000 +#define BOLD 0x8000000 +#define UNDERLINE 0x10000000 +#define BLINK 0x20000000 +#define INVISIBLE 0x40000000 +#define STRIKETHROUGH 0x80000000 + +/* bg flags: bit 27..32 (upper 2 unused) */ +#define ITALIC 0x4000000 +#define DIM 0x8000000 +#define HAS_EXTENDED 0x10000000 +#define PROTECTED 0x20000000 + +/* ext flags: bit 27..32 (upper 3 unused) */ +#define UNDERLINE_STYLE 0x1C000000 + +/* underline style */ +#define UL_NONE 0 +#define UL_SINGLE 1 +#define UL_DOUBLE 2 +#define UL_CURLY 3 +#define UL_DOTTED 4 +#define UL_DASHED 5 + +typedef struct __attribute__((packed, aligned(4))) { + unsigned int content; + unsigned int fg; + unsigned int bg; +} Cell; + +/** + * Optimized itoa implementation for unsigned short to utf16. + * + * Note: Clang compiles with the div instruction in wasm. + * Since tests with shift mul in source show no runtime difference, + * wasm engines prolly optimize the division on their own. + */ +unsigned int *LUT100 = (unsigned int*) 256; + +__attribute__((noinline)) +unsigned short* itoa16(unsigned short n, unsigned short *dst) { + if (n < 10) { + *dst++ = n + 48; + } else if (n < 100) { + *(unsigned int*) dst = LUT100[n]; + dst += 2; + } else if (n < 1000) { + int h = n / 100; + *dst++ = h + 48; + *(unsigned int*) dst = LUT100[n - h * 100]; + dst += 2; + } else if (n < 10000) { + int h = n / 100; + *(unsigned int*) dst = LUT100[h]; + *((unsigned int*) dst+1) = LUT100[n - h * 100]; + dst += 4; + } else { + int h = n / 10000; + *dst++ = h + 48; + n -= h * 10000; + h = n / 100; + *(unsigned int*) dst = LUT100[h]; + *((unsigned int*) dst+1) = LUT100[n - h * 100]; + dst += 4; + } + return dst; +} + +/* TODO: target support flags */ +#define S_SGR 1 /* include SGR flags */ +#define S_COLORS 2 /* include 256 indexed colors */ +#define S_RGB 4 /* include RGB colors */ +#define S_REMPTY 8 /* right truncate empty cells */ +#define S_CURSOR 16 /* include cursor move sequences */ +#define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */ +#define S_DECAWM 64 /* dont break soft wraps */ + + +/** + * Set SGR colors for FG / BG / UL. + * c denotes the color target as {FG: '3', BG: '4', UL: '5'}. + */ +__attribute__((noinline)) +static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) { + int mode = v & CM_MASK; + if (mode == CM_DEFAULT) { + *(unsigned long long*) d = 0x3b00390000ULL | c; + d += 3; + } else if (mode == CM_P16) { + unsigned long long color = 48 + (v & 7); + if (v & 8) { + /* bright for FG | BG (no UL color here) */ + if (c == '3') { + *(unsigned long long*) d = 0x003b00000039ULL | color << 16; + d += 3; + } else if (c == '4') { + *(unsigned long long*) d = 0x003b000000300031ULL | color << 32; + d += 4; + } + } else { + /* handles normal FG | BG | UL */ + *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c; + d += 3; + } + } else if (mode == CM_P256) { + *d++ = c; + *(unsigned long long*) d = 0x3b0035003b0038ULL; + d += 4; + d = itoa16(v & 0xFF, d); + *d++ = ';'; + } else { + *d++ = c; + *(unsigned long long*) d = 0x3b0032003b0038ULL; + d += 4; + d = itoa16((v >> 16) & 0xFF, d); + *d++ = ';'; + d = itoa16((v >> 8) & 0xFF, d); + *d++ = ';'; + d = itoa16(v & 0xFF, d); + *d++ = ';'; + } + return d; +} + +// FIXME: any nicer way to express this? +#define SGR_FLAG(V, DIFF, FLAG, HI, LO) \ +if ((DIFF) & (FLAG)) { \ + if ((V) & (FLAG)) { \ + *(unsigned int*) d = 0x3b0000 | (HI); \ + d += 2; \ + } else { \ + *(unsigned long long*) d = 0x3b00000032ULL | (LO); \ + d += 3; \ + } \ +} \ + +#define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; + +unsigned short* diff_attr16( + unsigned short* d, + unsigned int fg, + unsigned int bg, + unsigned int diff_fg, + unsigned int diff_bg, + unsigned int ul, + unsigned int diff_ul +) { + W_CSI(d) + + if (!fg && !bg) { + *d++ = ';'; + } else { + /* fg flags */ + if (diff_fg >> 26) { + SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16) + SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16) + //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs + SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16) + SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16) + SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16) + } + /* fg color */ + if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3'); + + /* bg flags */ + if (diff_bg >> 26) { + SGR_FLAG(bg, diff_bg, ITALIC, '3', '3') + SGR_FLAG(bg, diff_bg, DIM, '2', '2') + } + /* bg color */ + if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4'); + + /* ul ext attributes */ + /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */ + if (bg & HAS_EXTENDED) { + if (diff_ul & UNDERLINE_STYLE) { + *d++ = '4'; + *d++ = ':'; + *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; + *d++ = ';'; + } + if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5'); + } + } + *(d - 1) = 'm'; + return d; +} + +unsigned int old_fg = 0; +unsigned int old_bg = 0; +unsigned int old_ul = 0; +unsigned int old_link = 0; + +void reset(int fg, int bg, int ul, int link) { + old_fg = fg; + old_bg = bg; + old_ul = ul; + old_link = link; +} + +// FIXME: how to get rid of the weird BCE hack? +void* line16(Cell *src, int length, unsigned short *dst) { + int cur_jmp = 0; + unsigned int bce = old_bg; + unsigned ul = old_ul; + unsigned link = old_link; + + for (int i = 0; i < length;) { + Cell cell = src[i]; + + /** + * apply SGR differences + * We have to nullify HAS_EXTENDED due to its overloaded meaning, + * otherwise we would introduce nonsense jump/erase sequences here. + * SGR ext attributes for UL are covered by the explicit comparison, + * URL/hyperlink entry needs a separate control path (TODO). + */ + unsigned bg = cell.bg & ~HAS_EXTENDED; + ul = *((unsigned int *) (4096 * 4) + i); + if (cell.fg != old_fg || bg != old_bg || ul != old_ul) { + /* + we are in the middle of jumped over cells, + thus still need to apply BG changes first + */ + if (cur_jmp) { + if (old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'C'; + cur_jmp = 0; + } + /* write new SGR sequence, advance fg/bg/ul colors */ + dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); + old_fg = cell.fg; + old_bg = bg; + old_ul = ul; + } + + /* OSC 8 link handling */ + link = *((unsigned int *) (4096 * 4) + (length + i)); + if (link != old_link) { + if (old_link) { + // simply close old link - OSC 8 ; ; BEL + *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC + dst += 4; + *(unsigned int*) dst = 0x0007003b; // BEL ; + dst += 2; + } + if ((cell.bg & HAS_EXTENDED) && link) { + dst = load_link(dst, link); + old_link = link; + } else { + old_link = 0; + } + } + + + /* text content handling */ + if (cell.content & HAS_CONTENT_MASK) { + /* + we are in the middle of jumped over cells, thus apply cursor jump + we have to check here again in case there were no SGR changes + */ + if (cur_jmp) { + if (old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'C'; + cur_jmp = 0; + } + /* combined chars are written from JS (expensive?) */ + if (cell.content & IS_COMBINED_MASK) { + // FIXME: preload combined in a single action similar to ext attribs? + dst = single_combined(dst, i); + } else { + /* utf32 to utf16 conversion */ + unsigned int cp = cell.content & 0x1FFFFF; + if (cp > 0xFFFF) { + cp -= 0x10000; + *dst++ = (cp >> 10) + 0xD800; + *dst++ = (cp % 0x400) + 0xDC00; + } else { + *dst++ = cp; + } + } + } else { + /* empty cells are treated by cursor jumps */ + cur_jmp++; + } + + /* advance cell read position by wcwidth or 1 */ + int width = cell.content >> WIDTH_SHIFT; + i += width ? width : 1; + } + + /* + clear cells to the right if we have jumped over cells + and bce color != current bg + */ + if (cur_jmp && old_bg != bce) { + W_CSI(dst) + dst = itoa16(cur_jmp, dst); + *dst++ = 'X'; + } + + return dst; +} diff --git a/addons/xterm-addon-serialize2/src/serializer.wasm.ts b/addons/xterm-addon-serialize2/src/serializer.wasm.ts index 6d1ca17e92..286281c631 100644 --- a/addons/xterm-addon-serialize2/src/serializer.wasm.ts +++ b/addons/xterm-addon-serialize2/src/serializer.wasm.ts @@ -34,360 +34,9 @@ const wasmSerialize = InWasm({ compile: { switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first'] }, - code: ` - /* write combined chars on JS side */ - __attribute__((import_module("env"), import_name("single_combined"))) void* single_combined(unsigned short* dst, int x); - __attribute__((import_module("env"), import_name("load_link"))) void* load_link(unsigned short* dst, int link); - - // FIXME: import mask values as template strings from JS - #define CODEPOINT_MASK 0x1FFFFF - #define IS_COMBINED_MASK 0x200000 - #define HAS_CONTENT_MASK 0x3FFFFF - #define WIDTH_MASK 0xC00000 - #define WIDTH_SHIFT 22 - - /* bit 1..8 blue in RGB, color in P256 and P16 */ - #define BLUE_MASK 0xFF - #define BLUE_SHIFT 0 - #define PCOLOR_MASK 0xFF - #define PCOLOR_SHIFT 0 - - /* bit 9..16 green in RGB */ - #define GREEN_MASK 0xFF00 - #define GREEN_SHIFT 8 - - /* bit 17..24 red in RGB */ - #define RED_MASK 0xFF0000 - #define RED_SHIFT 16 - - /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ - #define CM_MASK 0x3000000 - #define CM_DEFAULT 0 - #define CM_P16 0x1000000 - #define CM_P256 0x2000000 - #define CM_RGB 0x3000000 - - /* bit 1..24 RGB room */ - #define RGB_MASK 0xFFFFFF - #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ - - /* fg flags: bit 27..32 */ - #define INVERSE 0x4000000 - #define BOLD 0x8000000 - #define UNDERLINE 0x10000000 - #define BLINK 0x20000000 - #define INVISIBLE 0x40000000 - #define STRIKETHROUGH 0x80000000 - - /* bg flags: bit 27..32 (upper 2 unused) */ - #define ITALIC 0x4000000 - #define DIM 0x8000000 - #define HAS_EXTENDED 0x10000000 - #define PROTECTED 0x20000000 - - /* ext flags: bit 27..32 (upper 3 unused) */ - #define UNDERLINE_STYLE 0x1C000000 - - /* underline style */ - #define UL_NONE 0 - #define UL_SINGLE 1 - #define UL_DOUBLE 2 - #define UL_CURLY 3 - #define UL_DOTTED 4 - #define UL_DASHED 5 - - typedef struct __attribute__((packed, aligned(4))) { - unsigned int content; - unsigned int fg; - unsigned int bg; - } Cell; - - /** - * Optimized itoa implementation for unsigned short to utf16. - * - * Note: Clang compiles with the div instruction in wasm. - * Since tests with shift mul in source show no runtime difference, - * wasm engines prolly optimize the division on their own. - */ - unsigned int *LUT100 = (unsigned int*) 256; - - __attribute__((noinline)) - unsigned short* itoa16(unsigned short n, unsigned short *dst) { - if (n < 10) { - *dst++ = n + 48; - } else if (n < 100) { - *(unsigned int*) dst = LUT100[n]; - dst += 2; - } else if (n < 1000) { - int h = n / 100; - *dst++ = h + 48; - *(unsigned int*) dst = LUT100[n - h * 100]; - dst += 2; - } else if (n < 10000) { - int h = n / 100; - *(unsigned int*) dst = LUT100[h]; - *((unsigned int*) dst+1) = LUT100[n - h * 100]; - dst += 4; - } else { - int h = n / 10000; - *dst++ = h + 48; - n -= h * 10000; - h = n / 100; - *(unsigned int*) dst = LUT100[h]; - *((unsigned int*) dst+1) = LUT100[n - h * 100]; - dst += 4; - } - return dst; - } - - /* TODO: target support flags */ - #define S_SGR 1 /* include SGR flags */ - #define S_COLORS 2 /* include 256 indexed colors */ - #define S_RGB 4 /* include RGB colors */ - #define S_REMPTY 8 /* right truncate empty cells */ - #define S_CURSOR 16 /* include cursor move sequences */ - #define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */ - #define S_DECAWM 64 /* dont break soft wraps */ - - - /** - * Set SGR colors for FG / BG / UL. - * c denotes the color target as {FG: '3', BG: '4', UL: '5'}. - */ - __attribute__((noinline)) - static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) { - int mode = v & CM_MASK; - if (mode == CM_DEFAULT) { - *(unsigned long long*) d = 0x3b00390000ULL | c; - d += 3; - } else if (mode == CM_P16) { - unsigned long long color = 48 + (v & 7); - if (v & 8) { - /* bright for FG | BG (no UL color here) */ - if (c == '3') { - *(unsigned long long*) d = 0x003b00000039ULL | color << 16; - d += 3; - } else if (c == '4') { - *(unsigned long long*) d = 0x003b000000300031ULL | color << 32; - d += 4; - } - } else { - /* handles normal FG | BG | UL */ - *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c; - d += 3; - } - } else if (mode == CM_P256) { - *d++ = c; - *(unsigned long long*) d = 0x3b0035003b0038ULL; - d += 4; - d = itoa16(v & 0xFF, d); - *d++ = ';'; - } else { - *d++ = c; - *(unsigned long long*) d = 0x3b0032003b0038ULL; - d += 4; - d = itoa16((v >> 16) & 0xFF, d); - *d++ = ';'; - d = itoa16((v >> 8) & 0xFF, d); - *d++ = ';'; - d = itoa16(v & 0xFF, d); - *d++ = ';'; - } - return d; - } - - // FIXME: any nicer way to express this? - #define SGR_FLAG(V, DIFF, FLAG, HI, LO) \ - if ((DIFF) & (FLAG)) { \ - if ((V) & (FLAG)) { \ - *(unsigned int*) d = 0x3b0000 | (HI); \ - d += 2; \ - } else { \ - *(unsigned long long*) d = 0x3b00000032ULL | (LO); \ - d += 3; \ - } \ - } \ - - #define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; - - unsigned short* diff_attr16( - unsigned short* d, - unsigned int fg, - unsigned int bg, - unsigned int diff_fg, - unsigned int diff_bg, - unsigned int ul, - unsigned int diff_ul - ) { - W_CSI(d) - - if (!fg && !bg) { - *d++ = ';'; - } else { - /* fg flags */ - if (diff_fg >> 26) { - SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16) - SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16) - //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs - SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16) - SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16) - SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16) - } - /* fg color */ - if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3'); - - /* bg flags */ - if (diff_bg >> 26) { - SGR_FLAG(bg, diff_bg, ITALIC, '3', '3') - SGR_FLAG(bg, diff_bg, DIM, '2', '2') - } - /* bg color */ - if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4'); - - /* ul ext attributes */ - /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */ - if (bg & HAS_EXTENDED) { - if (diff_ul & UNDERLINE_STYLE) { - *d++ = '4'; - *d++ = ':'; - *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; - *d++ = ';'; - } - if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5'); - } - } - *(d - 1) = 'm'; - return d; - } - - unsigned int old_fg = 0; - unsigned int old_bg = 0; - unsigned int old_ul = 0; - unsigned int old_link = 0; - - void reset(int fg, int bg, int ul, int link) { - old_fg = fg; - old_bg = bg; - old_ul = ul; - old_link = link; - } - - // FIXME: how to get rid of the weird BCE hack? - void* line16(Cell *src, int length, unsigned short *dst) { - int cur_jmp = 0; - unsigned int bce = old_bg; - unsigned ul = old_ul; - unsigned link = old_link; - - for (int i = 0; i < length;) { - Cell cell = src[i]; - - /** - * apply SGR differences - * We have to nullify HAS_EXTENDED due to its overloaded meaning, - * otherwise we would introduce nonsense jump/erase sequences here. - * SGR ext attributes for UL are covered by the explicit comparison, - * URL/hyperlink entry needs a separate control path (TODO). - */ - unsigned bg = cell.bg & ~HAS_EXTENDED; - ul = *((unsigned int *) (4096 * 4) + i); - if (cell.fg != old_fg || bg != old_bg || ul != old_ul) { - /* - we are in the middle of jumped over cells, - thus still need to apply BG changes first - */ - if (cur_jmp) { - if (old_bg != bce) { - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'X'; - } - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'C'; - cur_jmp = 0; - } - /* write new SGR sequence, advance fg/bg/ul colors */ - dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); - old_fg = cell.fg; - old_bg = bg; - old_ul = ul; - } - - /* OSC 8 link handling */ - link = *((unsigned int *) (4096 * 4) + (length + i)); - if (link != old_link) { - if (old_link) { - // simply close old link - OSC 8 ; ; BEL - *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC - dst += 4; - *(unsigned int*) dst = 0x0007003b; // BEL ; - dst += 2; - } - if ((cell.bg & HAS_EXTENDED) && link) { - dst = load_link(dst, link); - old_link = link; - } else { - old_link = 0; - } - } - - - /* text content handling */ - if (cell.content & HAS_CONTENT_MASK) { - /* - we are in the middle of jumped over cells, thus apply cursor jump - we have to check here again in case there were no SGR changes - */ - if (cur_jmp) { - if (old_bg != bce) { - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'X'; - } - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'C'; - cur_jmp = 0; - } - /* combined chars are written from JS (expensive?) */ - if (cell.content & IS_COMBINED_MASK) { - // FIXME: preload combined in a single action similar to ext attribs? - dst = single_combined(dst, i); - } else { - /* utf32 to utf16 conversion */ - unsigned int cp = cell.content & 0x1FFFFF; - if (cp > 0xFFFF) { - cp -= 0x10000; - *dst++ = (cp >> 10) + 0xD800; - *dst++ = (cp % 0x400) + 0xDC00; - } else { - *dst++ = cp; - } - } - } else { - /* empty cells are treated by cursor jumps */ - cur_jmp++; - } - - /* advance cell read position by wcwidth or 1 */ - int width = cell.content >> WIDTH_SHIFT; - i += width ? width : 1; - } - - /* - clear cells to the right if we have jumped over cells - and bce color != current bg - */ - if (cur_jmp && old_bg != bce) { - W_CSI(dst) - dst = itoa16(cur_jmp, dst); - *dst++ = 'X'; - } - - return dst; - } - ` + code: `${require('fs').readFileSync('src-wasm/serialize.c')}`, + trackChanges: ['src-wasm/serialize.c'], + trackMode: 'content' }); // itoa LUT diff --git a/addons/xterm-addon-serialize2/yarn.lock b/addons/xterm-addon-serialize2/yarn.lock index 39ee1ac144..620e647df1 100644 --- a/addons/xterm-addon-serialize2/yarn.lock +++ b/addons/xterm-addon-serialize2/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + acorn-walk@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" @@ -12,6 +17,18 @@ acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -59,11 +76,46 @@ chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +cliui@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -71,6 +123,14 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -89,19 +149,21 @@ glob-parent@~5.1.2: is-glob "^4.0.1" glob@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.1.0.tgz#baa48c6a157cfa34ca7887f2a29c6156bc6b65f8" - integrity sha512-daGobsYuT0G4hng24B5LbeLNvwKZYRhWyDl3RvqqAGZjJnCopWWK6PWnAGBY1M/vdA63QE+jddhZcYp+74Bq6Q== + version "10.2.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.2.1.tgz#44288e9186b5cd5baa848728533ba21a94aa8f33" + integrity sha512-ngom3wq2UhjdbmRE/krgkD8BQyi1KZ5l+D2dVm4+Yj+jJIBp74/ZGunL6gNGc/CYuQmvUBiavWEXIotRiv5R6A== dependencies: + foreground-child "^3.1.0" fs.realpath "^1.0.0" + jackspeak "^2.0.3" minimatch "^9.0.0" minipass "^5.0.0" path-scurry "^1.7.0" -inwasm@^0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.10.tgz#d2d15897ae65a7a2d14e6601494ca1bccc9ee1a0" - integrity sha512-PGAK9I5AdbaWCZbJ6NNUGVGf1knGNfke339De/ZV7U+uEBM80DMKc6DLpX7yKIzX+f2qNzbVDWIiDTsuhc7RfA== +inwasm@^0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.11.tgz#738eceac0599342de8236bd5f6ea0bdea45a10c9" + integrity sha512-pfGP6mE7eBk8VolnJzOScb+085gDEt5xcAT9/iwUUosrEzuiIbhAmNxmVJ4Po1bbc5UPmiUOXQ+a0iLtdXXI1Q== dependencies: acorn "^8.8.2" acorn-walk "^8.2.0" @@ -122,6 +184,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -134,10 +201,24 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.0.3.tgz#672eb397b97744a265b5862d7762b96e8dad6e61" + integrity sha512-0Jud3OMUdMbrlr3PyUMKESq51LXVAB+a239Ywdvd+Kgxj3MaBRml/nVRxf8tQFyfthMjuRkxkv7Vg58pmIMfuQ== + dependencies: + cliui "^7.0.4" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + lru-cache@^9.0.0: - version "9.0.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.3.tgz#8a04f282df5320227bb7215c55df2660d3e4e25b" - integrity sha512-cyjNRew29d4kbgnz1sjDqxg7qg8NW4s+HQzCGjeon7DV5T2yDije16W9HaUFV1dhVEMh+SjrOcK0TomBmf3Egg== + version "9.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.0.tgz#19efafa9d08d1c08eb8efd78876075f0b8b1b07b" + integrity sha512-qFXQEwchrZcMVen2uIDceR8Tii6kCJak5rzDStfEM0qA3YLMswaxIEZO0DhIbJ3aqaJiDjt+3crlplOb0tDtKQ== minimatch@^9.0.0: version "9.0.0" @@ -156,6 +237,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-scurry@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db" @@ -176,6 +262,39 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.1.tgz#96a61033896120ec9335d96851d902cc98f0ba2a" + integrity sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw== + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -187,3 +306,19 @@ wabt@^1.0.32: version "1.0.32" resolved "https://registry.yarnpkg.com/wabt/-/wabt-1.0.32.tgz#a4611728e67f1c3f7c546fabb7bc4da3e84a67a7" integrity sha512-1aHvkKaSrrl7qFtAbQ1RWVHLuJApRh7PtUdYvRtiUEKEhk0MOV0sTuz5cLF6jL5jPLRyifLbZcR65AEga/xBhQ== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" From 334f9f2ac7ef8ca88a34704b640b621d58e9a326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Tue, 18 Apr 2023 22:48:37 +0200 Subject: [PATCH 12/14] use -S flag to avoid sdk pulling in CI steps --- addons/xterm-addon-serialize2/package.json | 2 +- addons/xterm-addon-serialize2/yarn.lock | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json index 14b44a5dd5..5e8210c711 100644 --- a/addons/xterm-addon-serialize2/package.json +++ b/addons/xterm-addon-serialize2/package.json @@ -27,6 +27,6 @@ "xterm": "^5.0.0" }, "devDependencies": { - "inwasm": "^0.0.11" + "inwasm": "^0.0.12" } } diff --git a/addons/xterm-addon-serialize2/yarn.lock b/addons/xterm-addon-serialize2/yarn.lock index 620e647df1..e2ff007b2f 100644 --- a/addons/xterm-addon-serialize2/yarn.lock +++ b/addons/xterm-addon-serialize2/yarn.lock @@ -160,10 +160,10 @@ glob@^10.0.0: minipass "^5.0.0" path-scurry "^1.7.0" -inwasm@^0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.11.tgz#738eceac0599342de8236bd5f6ea0bdea45a10c9" - integrity sha512-pfGP6mE7eBk8VolnJzOScb+085gDEt5xcAT9/iwUUosrEzuiIbhAmNxmVJ4Po1bbc5UPmiUOXQ+a0iLtdXXI1Q== +inwasm@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.12.tgz#22dfcc69a512f5ce8e704200e0a567aba405bcc3" + integrity sha512-f+coaZsZ0lYPKWvaPTzKR/fMEjCjGxTYJ06Sqx5wohmJ6gR6DUScLOLCLdeyX7KX0dh47T+XnhRi22B4zdNr9g== dependencies: acorn "^8.8.2" acorn-walk "^8.2.0" diff --git a/package.json b/package.json index a5116f72ca..878fc579f1 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "prepare": "npm run setup", "setup": "npm run build", "presetup": "node ./bin/install-addons.js", - "postsetup": "cd addons/xterm-addon-serialize2 && npm run inwasm", + "postsetup": "cd addons/xterm-addon-serialize2 && npm run inwasm -- -S", "prepublishOnly": "npm run package", "watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput", "benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json", From 8c15eb2890607e3d244a50a46860378ae5dfb974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Wed, 19 Apr 2023 17:12:01 +0200 Subject: [PATCH 13/14] upgrade inwasm, remove nonsense files in builddir --- .../serialize/definition.json | 1 - .../serializer.wasm.js/serialize/final.wat | 933 ------------------ addons/xterm-addon-serialize2/package.json | 2 +- addons/xterm-addon-serialize2/yarn.lock | 8 +- 4 files changed, 5 insertions(+), 939 deletions(-) delete mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json delete mode 100644 addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json deleted file mode 100644 index 4cb2255db9..0000000000 --- a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/definition.json +++ /dev/null @@ -1 +0,0 @@ -{"def":{"name":"serialize","type":0,"mode":1,"srctype":"Clang-C","imports":{"env":{"memory":{}}},"exports":{},"compile":{"switches":["-Wl,-z,stack-size=0","-Wl,--stack-first"]},"code":"/* write combined chars on JS side */\n__attribute__((import_module(\"env\"), import_name(\"single_combined\"))) void* single_combined(unsigned short* dst, int x);\n__attribute__((import_module(\"env\"), import_name(\"load_link\"))) void* load_link(unsigned short* dst, int link);\n\n// FIXME: import mask values as template strings from JS\n#define CODEPOINT_MASK 0x1FFFFF\n#define IS_COMBINED_MASK 0x200000\n#define HAS_CONTENT_MASK 0x3FFFFF\n#define WIDTH_MASK 0xC00000\n#define WIDTH_SHIFT 22\n\n/* bit 1..8 blue in RGB, color in P256 and P16 */\n#define BLUE_MASK 0xFF\n#define BLUE_SHIFT 0\n#define PCOLOR_MASK 0xFF\n#define PCOLOR_SHIFT 0\n\n/* bit 9..16 green in RGB */\n#define GREEN_MASK 0xFF00\n#define GREEN_SHIFT 8\n\n/* bit 17..24 red in RGB */\n#define RED_MASK 0xFF0000\n#define RED_SHIFT 16\n\n/* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */\n#define CM_MASK 0x3000000\n#define CM_DEFAULT 0\n#define CM_P16 0x1000000\n#define CM_P256 0x2000000\n#define CM_RGB 0x3000000\n\n/* bit 1..24 RGB room */\n#define RGB_MASK 0xFFFFFF\n#define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */\n\n/* fg flags: bit 27..32 */\n#define INVERSE 0x4000000\n#define BOLD 0x8000000\n#define UNDERLINE 0x10000000\n#define BLINK 0x20000000\n#define INVISIBLE 0x40000000\n#define STRIKETHROUGH 0x80000000\n\n/* bg flags: bit 27..32 (upper 2 unused) */\n#define ITALIC 0x4000000\n#define DIM 0x8000000\n#define HAS_EXTENDED 0x10000000\n#define PROTECTED 0x20000000\n\n/* ext flags: bit 27..32 (upper 3 unused) */\n#define UNDERLINE_STYLE 0x1C000000\n\n/* underline style */\n#define UL_NONE 0\n#define UL_SINGLE 1\n#define UL_DOUBLE 2\n#define UL_CURLY 3\n#define UL_DOTTED 4\n#define UL_DASHED 5\n\ntypedef struct __attribute__((packed, aligned(4))) {\n unsigned int content;\n unsigned int fg;\n unsigned int bg;\n} Cell;\n\n/**\n * Optimized itoa implementation for unsigned short to utf16.\n *\n * Note: Clang compiles with the div instruction in wasm.\n * Since tests with shift mul in source show no runtime difference,\n * wasm engines prolly optimize the division on their own.\n */\nunsigned int *LUT100 = (unsigned int*) 256;\n\n__attribute__((noinline))\nunsigned short* itoa16(unsigned short n, unsigned short *dst) {\n if (n < 10) {\n *dst++ = n + 48;\n } else if (n < 100) {\n *(unsigned int*) dst = LUT100[n];\n dst += 2;\n } else if (n < 1000) {\n int h = n / 100;\n *dst++ = h + 48;\n *(unsigned int*) dst = LUT100[n - h * 100];\n dst += 2;\n } else if (n < 10000) {\n int h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n } else {\n int h = n / 10000;\n *dst++ = h + 48;\n n -= h * 10000;\n h = n / 100;\n *(unsigned int*) dst = LUT100[h];\n *((unsigned int*) dst+1) = LUT100[n - h * 100];\n dst += 4;\n }\n return dst;\n}\n\n/* TODO: target support flags */\n#define S_SGR 1 /* include SGR flags */\n#define S_COLORS 2 /* include 256 indexed colors */\n#define S_RGB 4 /* include RGB colors */\n#define S_REMPTY 8 /* right truncate empty cells */\n#define S_CURSOR 16 /* include cursor move sequences */\n#define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */\n#define S_DECAWM 64 /* dont break soft wraps */\n\n\n/**\n * Set SGR colors for FG / BG / UL.\n * c denotes the color target as {FG: '3', BG: '4', UL: '5'}.\n */\n__attribute__((noinline))\nstatic unsigned short* set_color16(unsigned short *d, unsigned int v, char c) {\n int mode = v & CM_MASK;\n if (mode == CM_DEFAULT) {\n *(unsigned long long*) d = 0x3b00390000ULL | c;\n d += 3;\n } else if (mode == CM_P16) {\n unsigned long long color = 48 + (v & 7);\n if (v & 8) {\n /* bright for FG | BG (no UL color here) */\n if (c == '3') {\n *(unsigned long long*) d = 0x003b00000039ULL | color << 16;\n d += 3;\n } else if (c == '4') {\n *(unsigned long long*) d = 0x003b000000300031ULL | color << 32;\n d += 4;\n }\n } else {\n /* handles normal FG | BG | UL */\n *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c;\n d += 3;\n }\n } else if (mode == CM_P256) {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0035003b0038ULL;\n d += 4;\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n } else {\n *d++ = c;\n *(unsigned long long*) d = 0x3b0032003b0038ULL;\n d += 4;\n d = itoa16((v >> 16) & 0xFF, d);\n *d++ = ';';\n d = itoa16((v >> 8) & 0xFF, d);\n *d++ = ';';\n d = itoa16(v & 0xFF, d);\n *d++ = ';';\n }\n return d;\n}\n\n// FIXME: any nicer way to express this?\n#define SGR_FLAG(V, DIFF, FLAG, HI, LO) \\\nif ((DIFF) & (FLAG)) { \\\n if ((V) & (FLAG)) { \\\n *(unsigned int*) d = 0x3b0000 | (HI); \\\n d += 2; \\\n } else { \\\n *(unsigned long long*) d = 0x3b00000032ULL | (LO); \\\n d += 3; \\\n } \\\n} \\\n\n#define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2;\n\nunsigned short* diff_attr16(\n unsigned short* d,\n unsigned int fg,\n unsigned int bg,\n unsigned int diff_fg,\n unsigned int diff_bg,\n unsigned int ul,\n unsigned int diff_ul\n) {\n W_CSI(d)\n\n if (!fg && !bg) {\n *d++ = ';';\n } else {\n /* fg flags */\n if (diff_fg >> 26) {\n SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16)\n SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16)\n //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs\n SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16)\n SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16)\n SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16)\n }\n /* fg color */\n if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3');\n\n /* bg flags */\n if (diff_bg >> 26) {\n SGR_FLAG(bg, diff_bg, ITALIC, '3', '3')\n SGR_FLAG(bg, diff_bg, DIM, '2', '2')\n }\n /* bg color */\n if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4');\n\n /* ul ext attributes */\n /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */\n if (bg & HAS_EXTENDED) {\n if (diff_ul & UNDERLINE_STYLE) {\n *d++ = '4';\n *d++ = ':';\n *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48;\n *d++ = ';';\n }\n if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5');\n }\n }\n *(d - 1) = 'm';\n return d;\n}\n\nunsigned int old_fg = 0;\nunsigned int old_bg = 0;\nunsigned int old_ul = 0;\nunsigned int old_link = 0;\n\nvoid reset(int fg, int bg, int ul, int link) {\n old_fg = fg;\n old_bg = bg;\n old_ul = ul;\n old_link = link;\n}\n\n// FIXME: how to get rid of the weird BCE hack?\nvoid* line16(Cell *src, int length, unsigned short *dst) {\n int cur_jmp = 0;\n unsigned int bce = old_bg;\n unsigned ul = old_ul;\n unsigned link = old_link;\n\n for (int i = 0; i < length;) {\n Cell cell = src[i];\n\n /**\n * apply SGR differences\n * We have to nullify HAS_EXTENDED due to its overloaded meaning,\n * otherwise we would introduce nonsense jump/erase sequences here.\n * SGR ext attributes for UL are covered by the explicit comparison,\n * URL/hyperlink entry needs a separate control path (TODO).\n */\n unsigned bg = cell.bg & ~HAS_EXTENDED;\n ul = *((unsigned int *) (4096 * 4) + i);\n if (cell.fg != old_fg || bg != old_bg || ul != old_ul) {\n /*\n we are in the middle of jumped over cells,\n thus still need to apply BG changes first\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* write new SGR sequence, advance fg/bg/ul colors */\n dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul);\n old_fg = cell.fg;\n old_bg = bg;\n old_ul = ul;\n }\n\n /* OSC 8 link handling */\n link = *((unsigned int *) (4096 * 4) + (length + i));\n if (link != old_link) {\n if (old_link) {\n // simply close old link - OSC 8 ; ; BEL\n *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC\n dst += 4;\n *(unsigned int*) dst = 0x0007003b; // BEL ;\n dst += 2;\n }\n if ((cell.bg & HAS_EXTENDED) && link) {\n dst = load_link(dst, link);\n old_link = link;\n } else {\n old_link = 0;\n }\n }\n\n\n /* text content handling */\n if (cell.content & HAS_CONTENT_MASK) {\n /*\n we are in the middle of jumped over cells, thus apply cursor jump\n we have to check here again in case there were no SGR changes\n */\n if (cur_jmp) {\n if (old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'C';\n cur_jmp = 0;\n }\n /* combined chars are written from JS (expensive?) */\n if (cell.content & IS_COMBINED_MASK) {\n // FIXME: preload combined in a single action similar to ext attribs?\n dst = single_combined(dst, i);\n } else {\n /* utf32 to utf16 conversion */\n unsigned int cp = cell.content & 0x1FFFFF;\n if (cp > 0xFFFF) {\n cp -= 0x10000;\n *dst++ = (cp >> 10) + 0xD800;\n *dst++ = (cp % 0x400) + 0xDC00;\n } else {\n *dst++ = cp;\n }\n }\n } else {\n /* empty cells are treated by cursor jumps */\n cur_jmp++;\n }\n\n /* advance cell read position by wcwidth or 1 */\n int width = cell.content >> WIDTH_SHIFT;\n i += width ? width : 1;\n }\n\n /*\n clear cells to the right if we have jumped over cells\n and bce color != current bg\n */\n if (cur_jmp && old_bg != bce) {\n W_CSI(dst)\n dst = itoa16(cur_jmp, dst);\n *dst++ = 'X';\n }\n\n return dst;\n}\n","trackChanges":["src-wasm/serialize.c"],"trackMode":"content"},"memorySettings":{"descriptor":{"initial":1,"shared":false},"mode":"imported"},"srcDef":"{\n name: 'serialize',\n type: 0,\n mode: 1,\n srctype: 'Clang-C',\n imports: {\n env: {\n memory: new WebAssembly.Memory({ initial: 1 }),\n single_combined: (dst, x) => 0,\n load_link: (dst, linkId) => 0\n }\n },\n exports: {\n line16: (src, length, dst) => 0,\n reset: (fg, bg, ul, link) => { }\n },\n compile: {\n switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first']\n },\n code: `${require('fs').readFileSync('src-wasm/serialize.c')}`,\n trackChanges: ['src-wasm/serialize.c'],\n trackMode: 'content'\n}","hash":"c8fb23f59fc2046f32fe781ab7f28d0413789c88a7860032d5ed1bdbe93aaea1"} \ No newline at end of file diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat deleted file mode 100644 index 58c51ad7c7..0000000000 --- a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wat +++ /dev/null @@ -1,933 +0,0 @@ -(module - (type (;0;) (func (param i32 i32) (result i32))) - (type (;1;) (func (param i32 i32 i32) (result i32))) - (type (;2;) (func (param i32 i32 i32 i32))) - (import "env" "load_link" (func (;0;) (type 0))) - (import "env" "single_combined" (func (;1;) (type 0))) - (import "env" "memory" (memory (;0;) 1)) - (func (;2;) (type 0) (param i32 i32) (result i32) - (local i32 i32) - local.get 0 - i32.const 9 - i32.le_u - if ;; label = @1 - local.get 1 - local.get 0 - i32.const 48 - i32.add - i32.store16 - local.get 1 - i32.const 2 - i32.add - return - end - local.get 0 - i32.const 99 - i32.le_u - if ;; label = @1 - local.get 1 - local.get 0 - i32.const 2 - i32.shl - i32.const 256 - i32.add - i32.load - i32.store - local.get 1 - i32.const 4 - i32.add - return - end - local.get 0 - i32.const 999 - i32.le_u - if ;; label = @1 - local.get 1 - local.get 0 - i32.const 100 - i32.div_u - local.tee 2 - i32.const 48 - i32.add - i32.store16 - local.get 1 - local.get 2 - i32.const -100 - i32.mul - local.get 0 - i32.add - i32.const 2 - i32.shl - i32.const 256 - i32.add - i32.load - i32.store offset=2 - local.get 1 - i32.const 6 - i32.add - return - end - local.get 0 - i32.const 9999 - i32.le_u - if ;; label = @1 - local.get 1 - local.get 0 - i32.const 100 - i32.div_u - local.tee 2 - i32.const 2 - i32.shl - i32.const 256 - i32.add - i32.load - i32.store - local.get 1 - local.get 2 - i32.const -100 - i32.mul - local.get 0 - i32.add - i32.const 2 - i32.shl - i32.const 256 - i32.add - i32.load - i32.store offset=4 - local.get 1 - i32.const 8 - i32.add - return - end - local.get 1 - local.get 0 - local.get 0 - i32.const 10000 - i32.div_u - local.tee 0 - i32.const -10000 - i32.mul - i32.add - i32.const 65535 - i32.and - local.tee 2 - i32.const 100 - i32.div_u - local.tee 3 - i32.const 2 - i32.shl - i32.const 256 - i32.add - i32.load - i32.store offset=2 - local.get 1 - local.get 0 - i32.const 48 - i32.or - i32.store16 - local.get 1 - local.get 3 - i32.const -100 - i32.mul - local.get 2 - i32.add - i32.const 2 - i32.shl - i32.const 256 - i32.add - i32.load - i32.store offset=6 - local.get 1 - i32.const 10 - i32.add) - (func (;3;) (type 1) (param i32 i32 i32) (result i32) - (local i64 i32) - block ;; label = @1 - block ;; label = @2 - block ;; label = @3 - local.get 1 - i32.const 50331648 - i32.and - local.tee 4 - i32.const 33554432 - i32.ne - if ;; label = @4 - local.get 4 - i32.const 16777216 - i32.ne - if ;; label = @5 - local.get 4 - br_if 2 (;@3;) - local.get 0 - local.get 2 - i64.extend_i32_u - i64.const 255 - i64.and - i64.const 253406806016 - i64.or - i64.store - br 4 (;@1;) - end - local.get 1 - i32.const 7 - i32.and - i32.const 48 - i32.or - i64.extend_i32_u - local.set 3 - local.get 1 - i32.const 8 - i32.and - if ;; label = @5 - block ;; label = @6 - block ;; label = @7 - local.get 2 - i32.const 51 - i32.sub - br_table 0 (;@7;) 1 (;@6;) 5 (;@2;) - end - local.get 0 - local.get 3 - i64.const 16 - i64.shl - i64.const 253403070521 - i64.or - i64.store - br 5 (;@1;) - end - local.get 0 - local.get 3 - i64.const 32 - i64.shl - i64.const 16607023629074481 - i64.or - i64.store - local.get 0 - i32.const 8 - i32.add - return - end - local.get 0 - local.get 2 - i64.extend_i32_u - i64.const 255 - i64.and - local.get 3 - i64.const 16 - i64.shl - i64.or - i64.const 253403070464 - i64.or - i64.store - br 3 (;@1;) - end - local.get 0 - i64.const 16607251263062072 - i64.store offset=2 - local.get 0 - local.get 2 - i32.const 255 - i32.and - i32.store16 - local.get 1 - i32.const 255 - i32.and - local.get 0 - i32.const 10 - i32.add - call 2 - local.tee 0 - i32.const 59 - i32.store16 - local.get 0 - i32.const 2 - i32.add - return - end - local.get 0 - i64.const 16607238378160184 - i64.store offset=2 - local.get 0 - local.get 2 - i32.const 255 - i32.and - i32.store16 - local.get 1 - i32.const 16 - i32.shr_u - i32.const 255 - i32.and - local.get 0 - i32.const 10 - i32.add - call 2 - local.tee 0 - i32.const 59 - i32.store16 - local.get 1 - i32.const 65280 - i32.and - i32.const 8 - i32.shr_u - local.get 0 - i32.const 2 - i32.add - call 2 - local.tee 0 - i32.const 59 - i32.store16 - local.get 1 - i32.const 255 - i32.and - local.get 0 - i32.const 2 - i32.add - call 2 - local.tee 0 - i32.const 59 - i32.store16 - local.get 0 - i32.const 2 - i32.add - local.set 0 - end - local.get 0 - return - end - local.get 0 - i32.const 6 - i32.add) - (func (;4;) (type 2) (param i32 i32 i32 i32) - i32.const 4 - local.get 1 - i32.store - i32.const 0 - local.get 0 - i32.store - i32.const 8 - local.get 2 - i32.store - i32.const 12 - local.get 3 - i32.store) - (func (;5;) (type 1) (param i32 i32 i32) (result i32) - (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) - block ;; label = @1 - local.get 1 - i32.const 0 - i32.le_s - br_if 0 (;@1;) - i32.const 4 - i32.load - local.set 12 - loop ;; label = @2 - local.get 0 - local.get 9 - i32.const 12 - i32.mul - i32.add - local.tee 3 - i32.load offset=8 - local.tee 6 - i32.const -268435457 - i32.and - local.set 14 - local.get 9 - i32.const 2 - i32.shl - i32.const 16384 - i32.add - i32.load - local.set 10 - local.get 3 - i32.load - local.set 7 - i32.const 8 - i32.load - local.set 13 - i32.const 4 - i32.load - local.set 11 - block ;; label = @3 - block ;; label = @4 - local.get 3 - i32.load offset=4 - local.tee 5 - i32.const 0 - i32.load - local.tee 8 - i32.ne - br_if 0 (;@4;) - local.get 11 - local.get 14 - i32.ne - br_if 0 (;@4;) - local.get 10 - local.get 13 - i32.ne - br_if 0 (;@4;) - local.get 2 - local.set 3 - br 1 (;@3;) - end - local.get 4 - if ;; label = @4 - local.get 11 - local.get 12 - i32.ne - if ;; label = @5 - local.get 2 - i32.const 5963803 - i32.store - local.get 4 - i32.const 65535 - i32.and - local.get 2 - i32.const 4 - i32.add - call 2 - local.tee 2 - i32.const 88 - i32.store16 - local.get 2 - i32.const 2 - i32.add - local.set 2 - end - local.get 2 - i32.const 5963803 - i32.store - local.get 4 - i32.const 65535 - i32.and - local.get 2 - i32.const 4 - i32.add - call 2 - local.tee 2 - i32.const 67 - i32.store16 - i32.const 8 - i32.load - local.set 13 - i32.const 4 - i32.load - local.set 11 - i32.const 0 - i32.load - local.set 8 - local.get 2 - i32.const 2 - i32.add - local.set 2 - end - local.get 2 - i32.const 5963803 - i32.store - block ;; label = @4 - local.get 5 - local.get 6 - i32.or - i32.eqz - if ;; label = @5 - local.get 2 - i32.const 59 - i32.store16 offset=4 - local.get 2 - i32.const 6 - i32.add - local.set 3 - br 1 (;@4;) - end - local.get 2 - i32.const 4 - i32.add - local.set 3 - block ;; label = @5 - local.get 5 - local.get 8 - i32.xor - local.tee 4 - i32.const 67108864 - i32.lt_u - br_if 0 (;@5;) - block ;; label = @6 - local.get 4 - i32.const 67108864 - i32.and - i32.eqz - br_if 0 (;@6;) - local.get 5 - i32.const 67108864 - i32.and - if ;; label = @7 - local.get 2 - i32.const 3866679 - i32.store offset=4 - local.get 2 - i32.const 8 - i32.add - local.set 3 - br 1 (;@6;) - end - local.get 2 - i64.const 253406674994 - i64.store offset=4 - local.get 2 - i32.const 10 - i32.add - local.set 3 - end - block ;; label = @6 - local.get 4 - i32.const 134217728 - i32.and - i32.eqz - br_if 0 (;@6;) - local.get 5 - i32.const 134217728 - i32.and - if ;; label = @7 - local.get 3 - i32.const 3866673 - i32.store - local.get 3 - i32.const 4 - i32.add - local.set 3 - br 1 (;@6;) - end - local.get 3 - i64.const 253406347314 - i64.store - local.get 3 - i32.const 6 - i32.add - local.set 3 - end - block ;; label = @6 - local.get 4 - i32.const 536870912 - i32.and - i32.eqz - br_if 0 (;@6;) - local.get 5 - i32.const 536870912 - i32.and - if ;; label = @7 - local.get 3 - i32.const 3866677 - i32.store - local.get 3 - i32.const 4 - i32.add - local.set 3 - br 1 (;@6;) - end - local.get 3 - i64.const 253406543922 - i64.store - local.get 3 - i32.const 6 - i32.add - local.set 3 - end - block ;; label = @6 - local.get 4 - i32.const 1073741824 - i32.and - i32.eqz - br_if 0 (;@6;) - local.get 5 - i32.const 1073741824 - i32.and - if ;; label = @7 - local.get 3 - i32.const 3866680 - i32.store - local.get 3 - i32.const 4 - i32.add - local.set 3 - br 1 (;@6;) - end - local.get 3 - i64.const 253406740530 - i64.store - local.get 3 - i32.const 6 - i32.add - local.set 3 - end - local.get 4 - i32.const 0 - i32.ge_s - br_if 0 (;@5;) - local.get 5 - i32.const 0 - i32.lt_s - if ;; label = @6 - local.get 3 - i32.const 3866681 - i32.store - local.get 3 - i32.const 4 - i32.add - local.set 3 - br 1 (;@5;) - end - local.get 3 - i64.const 253406806066 - i64.store - local.get 3 - i32.const 6 - i32.add - local.set 3 - end - local.get 4 - i32.const 67108863 - i32.and - if ;; label = @5 - local.get 3 - local.get 5 - i32.const 51 - call 3 - local.set 3 - end - block ;; label = @5 - local.get 6 - local.get 11 - i32.xor - local.tee 2 - i32.const 67108864 - i32.lt_u - br_if 0 (;@5;) - block ;; label = @6 - local.get 2 - i32.const 67108864 - i32.and - i32.eqz - br_if 0 (;@6;) - local.get 6 - i32.const 67108864 - i32.and - if ;; label = @7 - local.get 3 - i32.const 3866675 - i32.store - local.get 3 - i32.const 4 - i32.add - local.set 3 - br 1 (;@6;) - end - local.get 3 - i64.const 253403070515 - i64.store - local.get 3 - i32.const 6 - i32.add - local.set 3 - end - local.get 2 - i32.const 134217728 - i32.and - i32.eqz - br_if 0 (;@5;) - local.get 6 - i32.const 134217728 - i32.and - if ;; label = @6 - local.get 3 - i32.const 3866674 - i32.store - local.get 3 - i32.const 4 - i32.add - local.set 3 - br 1 (;@5;) - end - local.get 3 - i64.const 253403070514 - i64.store - local.get 3 - i32.const 6 - i32.add - local.set 3 - end - local.get 2 - i32.const 67108863 - i32.and - if ;; label = @5 - local.get 3 - local.get 6 - i32.const 52 - call 3 - local.set 3 - end - local.get 6 - i32.const 268435456 - i32.and - i32.eqz - br_if 0 (;@4;) - local.get 10 - local.get 13 - i32.xor - local.tee 2 - i32.const 469762048 - i32.and - if ;; label = @5 - local.get 3 - i32.const 59 - i32.store16 offset=6 - local.get 3 - i32.const 3801140 - i32.store align=2 - local.get 3 - local.get 10 - i32.const 26 - i32.shr_u - i32.const 7 - i32.and - i32.const 48 - i32.or - i32.store16 offset=4 - local.get 3 - i32.const 8 - i32.add - local.set 3 - end - local.get 2 - i32.const 67108863 - i32.and - i32.eqz - br_if 0 (;@4;) - local.get 3 - local.get 10 - i32.const 53 - call 3 - local.set 3 - end - i32.const 0 - local.set 4 - i32.const 0 - local.get 5 - i32.store - i32.const 4 - local.get 14 - i32.store - i32.const 8 - local.get 10 - i32.store - local.get 3 - i32.const 2 - i32.sub - i32.const 109 - i32.store16 - end - block ;; label = @3 - local.get 1 - local.get 9 - i32.add - i32.const 2 - i32.shl - i32.const 16384 - i32.add - i32.load - local.tee 8 - i32.const 12 - i32.load - local.tee 2 - i32.eq - if ;; label = @4 - local.get 3 - local.set 2 - br 1 (;@3;) - end - local.get 2 - if ;; label = @4 - local.get 3 - i32.const 458811 - i32.store offset=8 - local.get 3 - i64.const 16607264150192155 - i64.store - local.get 3 - i32.const 12 - i32.add - local.set 3 - end - block (result i32) ;; label = @4 - block ;; label = @5 - local.get 6 - i32.const 268435456 - i32.and - i32.eqz - br_if 0 (;@5;) - local.get 8 - i32.eqz - br_if 0 (;@5;) - local.get 3 - local.get 8 - call 0 - br 1 (;@4;) - end - i32.const 0 - local.set 8 - local.get 3 - end - local.set 2 - i32.const 12 - local.get 8 - i32.store - end - block (result i32) ;; label = @3 - local.get 7 - i32.const 4194303 - i32.and - if ;; label = @4 - local.get 4 - if ;; label = @5 - local.get 12 - i32.const 4 - i32.load - i32.ne - if ;; label = @6 - local.get 2 - i32.const 5963803 - i32.store - local.get 4 - i32.const 65535 - i32.and - local.get 2 - i32.const 4 - i32.add - call 2 - local.tee 2 - i32.const 88 - i32.store16 - local.get 2 - i32.const 2 - i32.add - local.set 2 - end - local.get 2 - i32.const 5963803 - i32.store - local.get 4 - i32.const 65535 - i32.and - local.get 2 - i32.const 4 - i32.add - call 2 - local.tee 2 - i32.const 67 - i32.store16 - local.get 2 - i32.const 2 - i32.add - local.set 2 - end - local.get 7 - i32.const 2097152 - i32.and - if ;; label = @5 - local.get 2 - local.get 9 - call 1 - local.set 2 - i32.const 0 - br 2 (;@3;) - end - block (result i32) ;; label = @5 - local.get 7 - i32.const 2097151 - i32.and - local.tee 4 - i32.const 65536 - i32.ge_u - if ;; label = @6 - local.get 2 - local.get 7 - i32.const 1023 - i32.and - i32.const 56320 - i32.or - i32.store16 offset=2 - local.get 4 - i32.const 67043328 - i32.add - i32.const 10 - i32.shr_u - i32.const 10240 - i32.sub - local.set 4 - local.get 2 - i32.const 2 - i32.add - br 1 (;@5;) - end - local.get 7 - local.set 4 - local.get 2 - end - local.get 2 - local.get 4 - i32.store16 - i32.const 2 - i32.add - local.set 2 - i32.const 0 - br 1 (;@3;) - end - local.get 4 - i32.const 1 - i32.add - end - local.set 4 - i32.const 1 - local.get 7 - i32.const 22 - i32.shr_u - local.get 7 - i32.const 4194304 - i32.lt_u - select - local.get 9 - i32.add - local.tee 9 - local.get 1 - i32.lt_s - br_if 0 (;@2;) - end - local.get 4 - i32.eqz - br_if 0 (;@1;) - i32.const 4 - i32.load - local.get 12 - i32.eq - br_if 0 (;@1;) - local.get 2 - i32.const 5963803 - i32.store - local.get 4 - i32.const 65535 - i32.and - local.get 2 - i32.const 4 - i32.add - call 2 - local.tee 0 - i32.const 88 - i32.store16 - local.get 0 - i32.const 2 - i32.add - local.set 2 - end - local.get 2) - (export "reset" (func 4)) - (export "line16" (func 5)) - (data (;0;) (i32.const 0) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")) diff --git a/addons/xterm-addon-serialize2/package.json b/addons/xterm-addon-serialize2/package.json index 5e8210c711..6582441c0a 100644 --- a/addons/xterm-addon-serialize2/package.json +++ b/addons/xterm-addon-serialize2/package.json @@ -27,6 +27,6 @@ "xterm": "^5.0.0" }, "devDependencies": { - "inwasm": "^0.0.12" + "inwasm": "^0.0.13" } } diff --git a/addons/xterm-addon-serialize2/yarn.lock b/addons/xterm-addon-serialize2/yarn.lock index e2ff007b2f..73e9bfed35 100644 --- a/addons/xterm-addon-serialize2/yarn.lock +++ b/addons/xterm-addon-serialize2/yarn.lock @@ -160,10 +160,10 @@ glob@^10.0.0: minipass "^5.0.0" path-scurry "^1.7.0" -inwasm@^0.0.12: - version "0.0.12" - resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.12.tgz#22dfcc69a512f5ce8e704200e0a567aba405bcc3" - integrity sha512-f+coaZsZ0lYPKWvaPTzKR/fMEjCjGxTYJ06Sqx5wohmJ6gR6DUScLOLCLdeyX7KX0dh47T+XnhRi22B4zdNr9g== +inwasm@^0.0.13: + version "0.0.13" + resolved "https://registry.yarnpkg.com/inwasm/-/inwasm-0.0.13.tgz#cbbbbc566b86d876edc1a385cfae32864a4bcaab" + integrity sha512-gmULhw1wfF3tQ19y0TvcNH6A5jN7IuTD51kbZuy+ittUU59d+ZTQMb53wbGQuciMrledgagL3/ohnjUj5qJikQ== dependencies: acorn "^8.8.2" acorn-walk "^8.2.0" From 840974dfcf6b9d1e59f72691290ad84426e1227b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Breitbart?= Date: Wed, 19 Apr 2023 20:11:39 +0200 Subject: [PATCH 14/14] some initial cleanup --- .../serializer.wasm.js/serialize/final.wasm | Bin 2291 -> 2287 bytes .../src-wasm/serialize.c | 405 +++++++++++------- .../src/serializer.wasm.ts | 105 ++++- 3 files changed, 335 insertions(+), 175 deletions(-) diff --git a/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wasm b/addons/xterm-addon-serialize2/inwasm-builds/out/serializer.wasm.js/serialize/final.wasm index b1869f7bc76fbe57441f4ef0cc0b0b03c4a56898..b4a53720993d676ce48d666652dfca447583a3a2 100644 GIT binary patch delta 59 zcmew?_+D^=f~+w!b8220XL(U(Nvcm~UN!>*h|de=J5RKi=NDjNElMp;En#3`$;r%1 J-I(9P0RT3C5^(?k delta 63 zcmaDa_*rm*g1jj+b8220XHI@%N_*h|gb~nU|iE8lODTR$fquiM1%TIJJa< Pg$<}8)zEBXb_)jphPxA} diff --git a/addons/xterm-addon-serialize2/src-wasm/serialize.c b/addons/xterm-addon-serialize2/src-wasm/serialize.c index 3222ec3444..c54f2c0611 100644 --- a/addons/xterm-addon-serialize2/src-wasm/serialize.c +++ b/addons/xterm-addon-serialize2/src-wasm/serialize.c @@ -1,81 +1,167 @@ -/* write combined chars on JS side */ -__attribute__((import_module("env"), import_name("single_combined"))) void* single_combined(unsigned short* dst, int x); -__attribute__((import_module("env"), import_name("load_link"))) void* load_link(unsigned short* dst, int link); - -// FIXME: import mask values as template strings from JS -#define CODEPOINT_MASK 0x1FFFFF -#define IS_COMBINED_MASK 0x200000 -#define HAS_CONTENT_MASK 0x3FFFFF -#define WIDTH_MASK 0xC00000 -#define WIDTH_SHIFT 22 - -/* bit 1..8 blue in RGB, color in P256 and P16 */ -#define BLUE_MASK 0xFF -#define BLUE_SHIFT 0 -#define PCOLOR_MASK 0xFF -#define PCOLOR_SHIFT 0 - -/* bit 9..16 green in RGB */ -#define GREEN_MASK 0xFF00 -#define GREEN_SHIFT 8 - -/* bit 17..24 red in RGB */ -#define RED_MASK 0xFF0000 -#define RED_SHIFT 16 - -/* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ -#define CM_MASK 0x3000000 -#define CM_DEFAULT 0 -#define CM_P16 0x1000000 -#define CM_P256 0x2000000 -#define CM_RGB 0x3000000 - -/* bit 1..24 RGB room */ -#define RGB_MASK 0xFFFFFF -#define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ - -/* fg flags: bit 27..32 */ -#define INVERSE 0x4000000 -#define BOLD 0x8000000 -#define UNDERLINE 0x10000000 -#define BLINK 0x20000000 -#define INVISIBLE 0x40000000 -#define STRIKETHROUGH 0x80000000 - -/* bg flags: bit 27..32 (upper 2 unused) */ -#define ITALIC 0x4000000 -#define DIM 0x8000000 -#define HAS_EXTENDED 0x10000000 -#define PROTECTED 0x20000000 - -/* ext flags: bit 27..32 (upper 3 unused) */ -#define UNDERLINE_STYLE 0x1C000000 - -/* underline style */ -#define UL_NONE 0 -#define UL_SINGLE 1 -#define UL_DOUBLE 2 -#define UL_CURLY 3 -#define UL_DOTTED 4 -#define UL_DASHED 5 +/** + * @file serialize.c + * @brief Wasm terminal line serializer + * @version 0.1 + * @copyright Copyright (c) 2023 The xterm.js authors. All rights reserved. + * @license MIT + */ + +/** + * TODO: proper memory layout + * + * memory layout: + * 0 - 16: global state variables size: 4 * int32 + * 16 - 256: free + * 256 - 656: LUT100 size: 100 * int32 + * 656 - 1024: free + * 1024 - ????: extended attribs + urlId size: 2 * int32 * cols = 8 * cols + * ???? - ????: line data (src) size: 3 * int32 * cols = 12 * cols + * ???? - ????: dst + */ + + +#ifndef TS_OVERRIDE + /** + * Note on the defines here: + * Simply copied over from TS sources and unmaintained here. + * The defines are still in place here just to make the editor happy. + * + * They get overloaded with real values imported on TS side. + */ + + #define CODEPOINT_MASK 0x1FFFFF + #define IS_COMBINED_MASK 0x200000 + #define HAS_CONTENT_MASK 0x3FFFFF + #define WIDTH_MASK 0xC00000 + #define WIDTH_SHIFT 22 + + /* bit 1..8 blue in RGB, color in P256 and P16 */ + #define BLUE_MASK 0xFF + #define BLUE_SHIFT 0 + #define PCOLOR_MASK 0xFF + #define PCOLOR_SHIFT 0 + + /* bit 9..16 green in RGB */ + #define GREEN_MASK 0xFF00 + #define GREEN_SHIFT 8 + + /* bit 17..24 red in RGB */ + #define RED_MASK 0xFF0000 + #define RED_SHIFT 16 + + /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ + #define CM_MASK 0x3000000 + #define CM_DEFAULT 0 + #define CM_P16 0x1000000 + #define CM_P256 0x2000000 + #define CM_RGB 0x3000000 + + /* bit 1..24 RGB room */ + #define RGB_MASK 0xFFFFFF + #define COLOR_MASK 0x3FFFFFF /* == CM_MASK | RGB_MASK */ + + /* fg flags: bit 27..32 */ + #define INVERSE 0x4000000 + #define BOLD 0x8000000 + #define UNDERLINE 0x10000000 + #define BLINK 0x20000000 + #define INVISIBLE 0x40000000 + #define STRIKETHROUGH 0x80000000 + + /* bg flags: bit 27..32 (upper 2 unused) */ + #define ITALIC 0x4000000 + #define DIM 0x8000000 + #define HAS_EXTENDED 0x10000000 + #define PROTECTED 0x20000000 + + /* ext flags: bit 27..32 (upper 3 unused) */ + #define UNDERLINE_STYLE 0x1C000000 + + /* underline style */ + #define UL_NONE 0 + #define UL_SINGLE 1 + #define UL_DOUBLE 2 + #define UL_CURLY 3 + #define UL_DOTTED 4 + #define UL_DASHED 5 + + + /* memory locations */ + #define P_LUT100 256 + #define P_EXT 16384 + +#endif /* TS_OVERRIDE */ + + +/** + * Imported functions from JS side. + */ + +/** + * Write combined string data for a single cell on JS side. + * The callback should write all codepoints of the combined string + * for cell `x` beginning at `dst` and return the new write position. + */ +__attribute__((import_module("env"), import_name("writeCombined"))) +unsigned short* js_write_combined(unsigned short* dst, int x); + +/** + * Write URL sequence on JS side. + * The callback should write the URL sequence beginning at `dst` + * for the URL with the urlID `link` (from OscLinkService), + * and return the new write position. + */ +__attribute__((import_module("env"), import_name("writeLink"))) +unsigned short* js_write_link(unsigned short* dst, int link); + + +/** + * Cell struct as defined in Bufferline.ts + * FIXME: Enhance with bitfield unions? (might save some bit juggling further down...) + */ typedef struct __attribute__((packed, aligned(4))) { unsigned int content; unsigned int fg; unsigned int bg; } Cell; + +// FIXME: any nicer way to express this? +#define SGR_FLAG(V, DIFF, FLAG, HI, LO) \ +if ((DIFF) & (FLAG)) { \ + if ((V) & (FLAG)) { \ + *(unsigned int*) dst = 0x3b0000 | (HI); \ + dst += 2; \ + } else { \ + *(unsigned long long*) dst = 0x3b00000032ULL | (LO); \ + dst += 3; \ + } \ +} \ + +#define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; + + +/** + * State to be preserve between multiple lines. + */ +unsigned int old_fg = 0; +unsigned int old_bg = 0; +unsigned int old_ul = 0; +unsigned int old_link = 0; + + /** - * Optimized itoa implementation for unsigned short to utf16. + * itoa implementation for unsigned short to utf16. * - * Note: Clang compiles with the div instruction in wasm. + * Note: Clang compiles with the div instruction into wasm. * Since tests with shift mul in source show no runtime difference, * wasm engines prolly optimize the division on their own. */ -unsigned int *LUT100 = (unsigned int*) 256; +const unsigned int * LUT100 = (unsigned int *) P_LUT100; __attribute__((noinline)) -unsigned short* itoa16(unsigned short n, unsigned short *dst) { +unsigned short* itoa(unsigned short n, unsigned short *dst) { if (n < 10) { *dst++ = n + 48; } else if (n < 100) { @@ -103,78 +189,61 @@ unsigned short* itoa16(unsigned short n, unsigned short *dst) { return dst; } -/* TODO: target support flags */ -#define S_SGR 1 /* include SGR flags */ -#define S_COLORS 2 /* include 256 indexed colors */ -#define S_RGB 4 /* include RGB colors */ -#define S_REMPTY 8 /* right truncate empty cells */ -#define S_CURSOR 16 /* include cursor move sequences */ -#define S_ALT_SWITCH 32 /* include normal buffer, if on alternate */ -#define S_DECAWM 64 /* dont break soft wraps */ - /** * Set SGR colors for FG / BG / UL. * c denotes the color target as {FG: '3', BG: '4', UL: '5'}. */ __attribute__((noinline)) -static unsigned short* set_color16(unsigned short *d, unsigned int v, char c) { +static unsigned short* color(unsigned short *dst, unsigned int v, char c) { int mode = v & CM_MASK; if (mode == CM_DEFAULT) { - *(unsigned long long*) d = 0x3b00390000ULL | c; - d += 3; + /* default is 39; | 49; | 59; */ + *(unsigned long long*) dst = 0x3b00390000ULL | c; + dst += 3; } else if (mode == CM_P16) { unsigned long long color = 48 + (v & 7); if (v & 8) { /* bright for FG | BG (no UL color here) */ if (c == '3') { - *(unsigned long long*) d = 0x003b00000039ULL | color << 16; - d += 3; + *(unsigned long long*) dst = 0x003b00000039ULL | color << 16; + dst += 3; } else if (c == '4') { - *(unsigned long long*) d = 0x003b000000300031ULL | color << 32; - d += 4; + *(unsigned long long*) dst = 0x003b000000300031ULL | color << 32; + dst += 4; } } else { /* handles normal FG | BG | UL */ - *(unsigned long long*) d = 0x3b00000000ULL | color << 16 | c; - d += 3; + *(unsigned long long*) dst = 0x3b00000000ULL | color << 16 | c; + dst += 3; } } else if (mode == CM_P256) { - *d++ = c; - *(unsigned long long*) d = 0x3b0035003b0038ULL; - d += 4; - d = itoa16(v & 0xFF, d); - *d++ = ';'; + /* 256 indexed written in ; notation */ + *dst++ = c; + *(unsigned long long*) dst = 0x3b0035003b0038ULL; + dst += 4; + dst = itoa(v & 0xFF, dst); + *dst++ = ';'; } else { - *d++ = c; - *(unsigned long long*) d = 0x3b0032003b0038ULL; - d += 4; - d = itoa16((v >> 16) & 0xFF, d); - *d++ = ';'; - d = itoa16((v >> 8) & 0xFF, d); - *d++ = ';'; - d = itoa16(v & 0xFF, d); - *d++ = ';'; + /* RGB written in ; notation */ + *dst++ = c; + *(unsigned long long*) dst = 0x3b0032003b0038ULL; + dst += 4; + dst = itoa((v >> 16) & 0xFF, dst); + *dst++ = ';'; + dst = itoa((v >> 8) & 0xFF, dst); + *dst++ = ';'; + dst = itoa(v & 0xFF, dst); + *dst++ = ';'; } - return d; + return dst; } -// FIXME: any nicer way to express this? -#define SGR_FLAG(V, DIFF, FLAG, HI, LO) \ -if ((DIFF) & (FLAG)) { \ - if ((V) & (FLAG)) { \ - *(unsigned int*) d = 0x3b0000 | (HI); \ - d += 2; \ - } else { \ - *(unsigned long long*) d = 0x3b00000032ULL | (LO); \ - d += 3; \ - } \ -} \ - -#define W_CSI(dst) *(unsigned int*) dst = 0x5b001b; dst += 2; - -unsigned short* diff_attr16( - unsigned short* d, +/** + * Write SGR sequence into `dst` from FG, BG and UL diffs. + */ +unsigned short* sgr( + unsigned short* dst, unsigned int fg, unsigned int bg, unsigned int diff_fg, @@ -182,22 +251,23 @@ unsigned short* diff_attr16( unsigned int ul, unsigned int diff_ul ) { - W_CSI(d) + W_CSI(dst) if (!fg && !bg) { - *d++ = ';'; + /* SGR 0 */ + *dst++ = ';'; } else { /* fg flags */ if (diff_fg >> 26) { SGR_FLAG(fg, diff_fg, INVERSE, '7', '7' << 16) SGR_FLAG(fg, diff_fg, BOLD, '1', '2' << 16) - //SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // covered by ext ul attribs + // SGR_FLAG(fg, diff_fg, UNDERLINE, '4', '4' << 16) // commented out: covered by ext ul attribs SGR_FLAG(fg, diff_fg, BLINK, '5', '5' << 16) SGR_FLAG(fg, diff_fg, INVISIBLE, '8', '8' << 16) SGR_FLAG(fg, diff_fg, STRIKETHROUGH, '9', '9' << 16) } /* fg color */ - if (diff_fg & COLOR_MASK) d = set_color16(d, fg, '3'); + if (diff_fg & COLOR_MASK) dst = color(dst, fg, '3'); /* bg flags */ if (diff_bg >> 26) { @@ -205,29 +275,40 @@ unsigned short* diff_attr16( SGR_FLAG(bg, diff_bg, DIM, '2', '2') } /* bg color */ - if (diff_bg & COLOR_MASK) d = set_color16(d, bg, '4'); + if (diff_bg & COLOR_MASK) dst = color(dst, bg, '4'); /* ul ext attributes */ /* safety measure: check against HAS_EXTENDED in case we have spurious ext attrib values */ if (bg & HAS_EXTENDED) { if (diff_ul & UNDERLINE_STYLE) { - *d++ = '4'; - *d++ = ':'; - *d++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; - *d++ = ';'; + *dst++ = '4'; + *dst++ = ':'; + *dst++ = ((ul & UNDERLINE_STYLE) >> 26) + 48; + *dst++ = ';'; } - if (diff_ul & COLOR_MASK) d = set_color16(d, ul, '5'); + if (diff_ul & COLOR_MASK) dst = color(dst, ul, '5'); } } - *(d - 1) = 'm'; - return d; + + /* all params above are added with final ';', overwrite last one hereby -1 */ + *(dst - 1) = 'm'; + return dst; } -unsigned int old_fg = 0; -unsigned int old_bg = 0; -unsigned int old_ul = 0; -unsigned int old_link = 0; +/** + * Exported functions. + */ + + +/** + * @brief Reset internal state for FG, BG, UL and link. + * + * Should b called at the beginning of a serialization. + * FG, BG and UL should be set to the terminal's default values (null cell), + * happens to be 0 for all. + * `link` is the urlId for a link as in the OscLinkService, 0 for unset. + */ void reset(int fg, int bg, int ul, int link) { old_fg = fg; old_bg = bg; @@ -235,8 +316,19 @@ void reset(int fg, int bg, int ul, int link) { old_link = link; } + +/** + * @brief Serialize terminal bufferline data with SGR attributes, + * extended attributes and OSC8 hyperlinks. + * + * `src` is the start pointer, where Bufferline._data got copied to, + * `length` denotes the Cell-length (Bufferline.length). + * + * The function will write the serialized line data to `dst` as 2-byte UTF-16 + * without additional size check (make sure to have enough space on TS side). + */ // FIXME: how to get rid of the weird BCE hack? -void* line16(Cell *src, int length, unsigned short *dst) { +void* line(Cell *src, int length, unsigned short *dst) { int cur_jmp = 0; unsigned int bce = old_bg; unsigned ul = old_ul; @@ -253,70 +345,74 @@ void* line16(Cell *src, int length, unsigned short *dst) { * URL/hyperlink entry needs a separate control path (TODO). */ unsigned bg = cell.bg & ~HAS_EXTENDED; - ul = *((unsigned int *) (4096 * 4) + i); + ul = *((unsigned int *) P_EXT + i); if (cell.fg != old_fg || bg != old_bg || ul != old_ul) { - /* - we are in the middle of jumped over cells, - thus still need to apply BG changes first - */ if (cur_jmp) { + /** + * We are in the middle of jumped over cells, + * thus still need to apply BG changes first. + */ if (old_bg != bce) { W_CSI(dst) - dst = itoa16(cur_jmp, dst); + dst = itoa(cur_jmp, dst); *dst++ = 'X'; } W_CSI(dst) - dst = itoa16(cur_jmp, dst); + dst = itoa(cur_jmp, dst); *dst++ = 'C'; cur_jmp = 0; } /* write new SGR sequence, advance fg/bg/ul colors */ - dst = diff_attr16(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); + dst = sgr(dst, cell.fg, cell.bg, cell.fg ^ old_fg, cell.bg ^ old_bg, ul, ul ^ old_ul); old_fg = cell.fg; old_bg = bg; old_ul = ul; } /* OSC 8 link handling */ - link = *((unsigned int *) (4096 * 4) + (length + i)); + link = *((unsigned int *) P_EXT + (length + i)); // FIXME: merge memory segment with UL above if (link != old_link) { if (old_link) { - // simply close old link - OSC 8 ; ; BEL + /* close old link */ *(unsigned long long*) dst = 0x003b0038005d001bULL; // ; 8 ] ESC dst += 4; *(unsigned int*) dst = 0x0007003b; // BEL ; dst += 2; } + /** + * safety measure: check against HAS_EXTENDED in case + * we have a spurious ext attrib object on JS side + */ if ((cell.bg & HAS_EXTENDED) && link) { - dst = load_link(dst, link); + /* compose and write url sequence on JS side */ + dst = js_write_link(dst, link); old_link = link; } else { + /* we have spurious ext object, explicitly nullify here */ old_link = 0; } } - /* text content handling */ if (cell.content & HAS_CONTENT_MASK) { - /* - we are in the middle of jumped over cells, thus apply cursor jump - we have to check here again in case there were no SGR changes - */ if (cur_jmp) { + /** + * We are in the middle of jumped over cells, thus apply cursor jump. + * We have to check again in case there were no SGR changes before. + */ if (old_bg != bce) { W_CSI(dst) - dst = itoa16(cur_jmp, dst); + dst = itoa(cur_jmp, dst); *dst++ = 'X'; } W_CSI(dst) - dst = itoa16(cur_jmp, dst); + dst = itoa(cur_jmp, dst); *dst++ = 'C'; cur_jmp = 0; } - /* combined chars are written from JS (expensive?) */ if (cell.content & IS_COMBINED_MASK) { - // FIXME: preload combined in a single action similar to ext attribs? - dst = single_combined(dst, i); + /* combined chars are written from JS */ + dst = js_write_combined(dst, i); } else { /* utf32 to utf16 conversion */ unsigned int cp = cell.content & 0x1FFFFF; @@ -338,13 +434,10 @@ void* line16(Cell *src, int length, unsigned short *dst) { i += width ? width : 1; } - /* - clear cells to the right if we have jumped over cells - and bce color != current bg - */ + /* clear cells if we have jumped over cells and bce color != current bg */ if (cur_jmp && old_bg != bce) { W_CSI(dst) - dst = itoa16(cur_jmp, dst); + dst = itoa(cur_jmp, dst); *dst++ = 'X'; } diff --git a/addons/xterm-addon-serialize2/src/serializer.wasm.ts b/addons/xterm-addon-serialize2/src/serializer.wasm.ts index 286281c631..3cf1c07290 100644 --- a/addons/xterm-addon-serialize2/src/serializer.wasm.ts +++ b/addons/xterm-addon-serialize2/src/serializer.wasm.ts @@ -1,9 +1,10 @@ -/* eslint-disable */ import { InWasm, OutputMode, OutputType } from 'inwasm'; import { ITerminal } from 'browser/Types'; import { IBufferLine, IExtendedAttrs } from 'common/Types'; +import { Attributes, BgFlags, Content, ExtFlags, FgFlags, UnderlineStyle } from 'common/buffer/Constants'; +/* eslint-disable */ interface IExtendedAttrsInternal extends IExtendedAttrs { _urlId: number; _ext: number; @@ -14,6 +15,8 @@ interface IBufferLineInternal extends IBufferLine { _extendedAttrs: {[index: number]: IExtendedAttrsInternal | undefined}; _combined: {[index: number]: string}; } +/* eslint-enable */ + const wasmSerialize = InWasm({ name: 'serialize', @@ -23,20 +26,84 @@ const wasmSerialize = InWasm({ imports: { env: { memory: new WebAssembly.Memory({ initial: 1 }), - single_combined: (dst: number, x: number) => 0, - load_link: (dst: number, linkId: number) => 0 + writeCombined: (dst: number, x: number) => 0, + writeLink: (dst: number, linkId: number) => 0 } }, exports: { - line16: (src: number, length: number, dst: number) => 0, + line: (src: number, length: number, dst: number) => 0, reset: (fg: number, bg: number, ul: number, link: number) => {} }, compile: { switches: ['-Wl,-z,stack-size=0', '-Wl,--stack-first'] }, - code: `${require('fs').readFileSync('src-wasm/serialize.c')}`, trackChanges: ['src-wasm/serialize.c'], - trackMode: 'content' + code: ` + #define TS_OVERRIDE + + #define CODEPOINT_MASK ${Content.CODEPOINT_MASK} + #define IS_COMBINED_MASK ${Content.IS_COMBINED_MASK} + #define HAS_CONTENT_MASK ${Content.HAS_CONTENT_MASK} + #define WIDTH_MASK ${Content.WIDTH_MASK} + #define WIDTH_SHIFT ${Content.WIDTH_SHIFT} + + /* bit 1..8 blue in RGB, color in P256 and P16 */ + #define BLUE_MASK ${Attributes.BLUE_MASK} + #define BLUE_SHIFT ${Attributes.BLUE_SHIFT} + #define PCOLOR_MASK ${Attributes.PCOLOR_MASK} + #define PCOLOR_SHIFT ${Attributes.PCOLOR_SHIFT} + + /* bit 9..16 green in RGB */ + #define GREEN_MASK ${Attributes.GREEN_MASK} + #define GREEN_SHIFT ${Attributes.GREEN_SHIFT} + + /* bit 17..24 red in RGB */ + #define RED_MASK ${Attributes.RED_MASK} + #define RED_SHIFT ${Attributes.RED_SHIFT} + + /* bit 25..26 color mode: DEFAULT (0) | P16 (1) | P256 (2) | RGB (3) */ + #define CM_MASK ${Attributes.CM_MASK} + #define CM_DEFAULT ${Attributes.CM_DEFAULT} + #define CM_P16 ${Attributes.CM_P16} + #define CM_P256 ${Attributes.CM_P256} + #define CM_RGB ${Attributes.CM_RGB} + + /* bit 1..24 RGB room */ + #define RGB_MASK ${Attributes.RGB_MASK} + #define COLOR_MASK ${Attributes.CM_MASK | Attributes.RGB_MASK} + + /* fg flags: bit 27..32 */ + #define INVERSE ${FgFlags.INVERSE} + #define BOLD ${FgFlags.BOLD} + #define UNDERLINE ${FgFlags.UNDERLINE} + #define BLINK ${FgFlags.BLINK} + #define INVISIBLE ${FgFlags.INVISIBLE} + #define STRIKETHROUGH ${FgFlags.STRIKETHROUGH} + + /* bg flags: bit 27..32 (upper 2 unused) */ + #define ITALIC ${BgFlags.ITALIC} + #define DIM ${BgFlags.DIM} + #define HAS_EXTENDED ${BgFlags.HAS_EXTENDED} + #define PROTECTED ${BgFlags.PROTECTED} + + /* ext flags: bit 27..32 (upper 3 unused) */ + #define UNDERLINE_STYLE ${ExtFlags.UNDERLINE_STYLE} + + /* underline style */ + #define UL_NONE ${UnderlineStyle.NONE} + #define UL_SINGLE ${UnderlineStyle.SINGLE} + #define UL_DOUBLE ${UnderlineStyle.DOUBLE} + #define UL_CURLY ${UnderlineStyle.CURLY} + #define UL_DOTTED ${UnderlineStyle.DOTTED} + #define UL_DASHED ${UnderlineStyle.DASHED} + + + /* memory locations */ + #define P_LUT100 256 + #define P_EXT 16384 + + ${require('fs').readFileSync('src-wasm/serialize.c')} + ` }); // itoa LUT @@ -52,11 +119,11 @@ for (let i1 = 0; i1 < 10; ++i1) { const mem = new WebAssembly.Memory({ initial: 3000 }); const d16 = new Uint16Array(mem.buffer); const d32 = new Uint32Array(mem.buffer); -let _single_comb: (dst: number, x: number) => number = (dst, x) => dst; -const single_combined: (dst: number, x: number) => number = (dst, x) => _single_comb(dst, x); -let _load_link: (dst: number, x: number) => number = (dst, x) => dst; -const load_link: (dst: number, x: number) => number = (dst, x) => _load_link(dst, x); -const inst = wasmSerialize({ env: { memory: mem, single_combined, load_link } }); +let writeCombinedOverload: (dst: number, x: number) => number = (dst, x) => dst; +const writeCombined: (dst: number, x: number) => number = (dst, x) => writeCombinedOverload(dst, x); +let writeLinkOverload: (dst: number, x: number) => number = (dst, x) => dst; +const writeLink: (dst: number, x: number) => number = (dst, x) => writeLinkOverload(dst, x); +const inst = wasmSerialize({ env: { memory: mem, writeCombined, writeLink } }); d32.set(LUT100, 64); const td = new TextDecoder('UTF-16LE'); @@ -70,9 +137,9 @@ export function serialize(t: ITerminal): string { let wPos = 150*65536; let line: IBufferLineInternal; - + // single call is pretty wasteful? preload combines once instead? - _single_comb = (dst, x) => { + writeCombinedOverload = (dst, x) => { let dp = dst >> 1; const comb: string = (line as any)._combined[x] || ''; for (let i = 0; i < comb.length; ++i) { @@ -82,7 +149,7 @@ export function serialize(t: ITerminal): string { }; // write link to buffer - _load_link = (dst, linkId) => { + writeLinkOverload = (dst, linkId) => { const entry = (t as any)._oscLinkService._dataByLinkId.get(linkId); if (!entry) { return dst; @@ -129,17 +196,17 @@ export function serialize(t: ITerminal): string { } d32.set(line._data, 16384); - wPos = inst.exports.line16(65536, t.cols, wPos); + wPos = inst.exports.line(65536, t.cols, wPos); // TODO: end of line hook goes here... } - const final_data = d16.subarray(75*65536, wPos >> 1); + const finalData = d16.subarray(75*65536, wPos >> 1); // strip empty lines at bottom - let fdp = final_data.length - 1; - while (fdp && final_data[fdp] === 10 && final_data[fdp-1] === 13) fdp -= 2; + let fdp = finalData.length - 1; + while (fdp && finalData[fdp] === 10 && finalData[fdp-1] === 13) fdp -= 2; // strip leading CRLF - const offset = final_data[0] === 13 && final_data[1] === 10 ? 2 : 0; + const offset = finalData[0] === 13 && finalData[1] === 10 ? 2 : 0; // return as string // TODO: compose from hook parts (needs to store wPos line offsets?)