@xterm/addon-image 0.7.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +231 -0
- package/lib/addon-image.js +3 -0
- package/lib/addon-image.js.LICENSE.txt +24 -0
- package/lib/addon-image.js.map +1 -0
- package/out/IIPHandler.js +148 -0
- package/out/IIPHandler.js.map +1 -0
- package/out/IIPHeaderParser.js +156 -0
- package/out/IIPHeaderParser.js.map +1 -0
- package/out/IIPHeaderParser.test.js +137 -0
- package/out/IIPHeaderParser.test.js.map +1 -0
- package/out/IIPMetrics.js +71 -0
- package/out/IIPMetrics.js.map +1 -0
- package/out/IIPMetrics.test.js +38 -0
- package/out/IIPMetrics.test.js.map +1 -0
- package/out/ImageAddon.js +261 -0
- package/out/ImageAddon.js.map +1 -0
- package/out/ImageRenderer.js +330 -0
- package/out/ImageRenderer.js.map +1 -0
- package/out/ImageStorage.js +552 -0
- package/out/ImageStorage.js.map +1 -0
- package/out/SixelHandler.js +140 -0
- package/out/SixelHandler.js.map +1 -0
- package/package.json +31 -0
- package/src/IIPHandler.ts +161 -0
- package/src/IIPHeaderParser.test.ts +138 -0
- package/src/IIPHeaderParser.ts +186 -0
- package/src/IIPMetrics.test.ts +43 -0
- package/src/IIPMetrics.ts +81 -0
- package/src/ImageAddon.ts +317 -0
- package/src/ImageRenderer.ts +379 -0
- package/src/ImageStorage.ts +592 -0
- package/src/SixelHandler.ts +151 -0
- package/src/Types.d.ts +108 -0
- package/typings/addon-image.d.ts +120 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from 'chai';
|
|
7
|
+
import { imageType, IMetrics } from './IIPMetrics';
|
|
8
|
+
|
|
9
|
+
// fix missing nodejs decl
|
|
10
|
+
declare const require: (s: string) => any;
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
const TEST_IMAGES: [string, IMetrics][] = [
|
|
15
|
+
['w3c_home_256.gif', { mime: 'image/gif', width: 72, height: 48 }],
|
|
16
|
+
['w3c_home_256.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
|
|
17
|
+
['w3c_home_256.png', { mime: 'image/png', width: 72, height: 48 }],
|
|
18
|
+
['w3c_home_2.gif', { mime: 'image/gif', width: 72, height: 48 }],
|
|
19
|
+
['w3c_home_2.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
|
|
20
|
+
['w3c_home_2.png', { mime: 'image/png', width: 72, height: 48 }],
|
|
21
|
+
['w3c_home_animation.gif', { mime: 'image/gif', width: 72, height: 48 }],
|
|
22
|
+
['w3c_home.gif', { mime: 'image/gif', width: 72, height: 48 }],
|
|
23
|
+
['w3c_home_gray.gif', { mime: 'image/gif', width: 72, height: 48 }],
|
|
24
|
+
['w3c_home_gray.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
|
|
25
|
+
['w3c_home_gray.png', { mime: 'image/png', width: 72, height: 48 }],
|
|
26
|
+
['w3c_home.jpg', { mime: 'image/jpeg', width: 72, height: 48 }],
|
|
27
|
+
['w3c_home.png', { mime: 'image/png', width: 72, height: 48 }],
|
|
28
|
+
['spinfox.png', { mime: 'image/png', width: 148, height: 148 }],
|
|
29
|
+
['iphone_hdr_YES.jpg', { mime: 'image/jpeg', width: 3264, height: 2448 }],
|
|
30
|
+
['nikon-e950.jpg', { mime: 'image/jpeg', width: 800, height: 600 }],
|
|
31
|
+
['agfa-makernotes.jpg', { mime: 'image/jpeg', width: 8, height: 8 }],
|
|
32
|
+
['sony-alpha-6000.jpg', { mime: 'image/jpeg', width: 6000, height: 4000 }]
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
describe('IIPMetrics', () => {
|
|
37
|
+
it('bunch of testimages', () => {
|
|
38
|
+
for (let i = 0; i < TEST_IMAGES.length; ++i) {
|
|
39
|
+
const imageData = fs.readFileSync('./addons/addon-image/fixture/testimages/' + TEST_IMAGES[i][0]);
|
|
40
|
+
assert.deepStrictEqual(imageType(imageData), TEST_IMAGES[i][1]);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export type ImageType = 'image/png' | 'image/jpeg' | 'image/gif' | 'unsupported' | '';
|
|
8
|
+
|
|
9
|
+
export interface IMetrics {
|
|
10
|
+
mime: ImageType;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const UNSUPPORTED_TYPE: IMetrics = {
|
|
16
|
+
mime: 'unsupported',
|
|
17
|
+
width: 0,
|
|
18
|
+
height: 0
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function imageType(d: Uint8Array): IMetrics {
|
|
22
|
+
if (d.length < 24) {
|
|
23
|
+
return UNSUPPORTED_TYPE;
|
|
24
|
+
}
|
|
25
|
+
const d32 = new Uint32Array(d.buffer, d.byteOffset, 6);
|
|
26
|
+
// PNG: 89 50 4E 47 0D 0A 1A 0A (8 first bytes == magic number for PNG)
|
|
27
|
+
// + first chunk must be IHDR
|
|
28
|
+
if (d32[0] === 0x474E5089 && d32[1] === 0x0A1A0A0D && d32[3] === 0x52444849) {
|
|
29
|
+
return {
|
|
30
|
+
mime: 'image/png',
|
|
31
|
+
width: d[16] << 24 | d[17] << 16 | d[18] << 8 | d[19],
|
|
32
|
+
height: d[20] << 24 | d[21] << 16 | d[22] << 8 | d[23]
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// JPEG: FF D8 FF E0 xx xx JFIF or FF D8 FF E1 xx xx Exif 00 00
|
|
36
|
+
if ((d32[0] === 0xE0FFD8FF || d32[0] === 0xE1FFD8FF)
|
|
37
|
+
&& (
|
|
38
|
+
(d[6] === 0x4a && d[7] === 0x46 && d[8] === 0x49 && d[9] === 0x46)
|
|
39
|
+
|| (d[6] === 0x45 && d[7] === 0x78 && d[8] === 0x69 && d[9] === 0x66)
|
|
40
|
+
)
|
|
41
|
+
) {
|
|
42
|
+
const [width, height] = jpgSize(d);
|
|
43
|
+
return { mime: 'image/jpeg', width, height };
|
|
44
|
+
}
|
|
45
|
+
// GIF: GIF87a or GIF89a
|
|
46
|
+
if (d32[0] === 0x38464947 && (d[4] === 0x37 || d[4] === 0x39) && d[5] === 0x61) {
|
|
47
|
+
return {
|
|
48
|
+
mime: 'image/gif',
|
|
49
|
+
width: d[7] << 8 | d[6],
|
|
50
|
+
height: d[9] << 8 | d[8]
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return UNSUPPORTED_TYPE;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function jpgSize(d: Uint8Array): [number, number] {
|
|
57
|
+
const len = d.length;
|
|
58
|
+
let i = 4;
|
|
59
|
+
let blockLength = d[i] << 8 | d[i + 1];
|
|
60
|
+
while (true) {
|
|
61
|
+
i += blockLength;
|
|
62
|
+
if (i >= len) {
|
|
63
|
+
// exhausted without size info
|
|
64
|
+
return [0, 0];
|
|
65
|
+
}
|
|
66
|
+
if (d[i] !== 0xFF) {
|
|
67
|
+
return [0, 0];
|
|
68
|
+
}
|
|
69
|
+
if (d[i + 1] === 0xC0 || d[i + 1] === 0xC2) {
|
|
70
|
+
if (i + 8 < len) {
|
|
71
|
+
return [
|
|
72
|
+
d[i + 7] << 8 | d[i + 8],
|
|
73
|
+
d[i + 5] << 8 | d[i + 6]
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
return [0, 0];
|
|
77
|
+
}
|
|
78
|
+
i += 2;
|
|
79
|
+
blockLength = d[i] << 8 | d[i + 1];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { IIPHandler } from './IIPHandler';
|
|
7
|
+
import { ITerminalAddon, IDisposable } from '@xterm/xterm';
|
|
8
|
+
import { ImageRenderer } from './ImageRenderer';
|
|
9
|
+
import { ImageStorage, CELL_SIZE_DEFAULT } from './ImageStorage';
|
|
10
|
+
import { SixelHandler } from './SixelHandler';
|
|
11
|
+
import { ITerminalExt, IImageAddonOptions, IResetHandler } from './Types';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// default values of addon ctor options
|
|
15
|
+
const DEFAULT_OPTIONS: IImageAddonOptions = {
|
|
16
|
+
enableSizeReports: true,
|
|
17
|
+
pixelLimit: 16777216, // limit to 4096 * 4096 pixels
|
|
18
|
+
sixelSupport: true,
|
|
19
|
+
sixelScrolling: true,
|
|
20
|
+
sixelPaletteLimit: 256,
|
|
21
|
+
sixelSizeLimit: 25000000,
|
|
22
|
+
storageLimit: 128,
|
|
23
|
+
showPlaceholder: true,
|
|
24
|
+
iipSupport: true,
|
|
25
|
+
iipSizeLimit: 20000000
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// max palette size supported by the sixel lib (compile time setting)
|
|
29
|
+
const MAX_SIXEL_PALETTE_SIZE = 4096;
|
|
30
|
+
|
|
31
|
+
// definitions for _xtermGraphicsAttributes sequence
|
|
32
|
+
const enum GaItem {
|
|
33
|
+
COLORS = 1,
|
|
34
|
+
SIXEL_GEO = 2,
|
|
35
|
+
REGIS_GEO = 3
|
|
36
|
+
}
|
|
37
|
+
const enum GaAction {
|
|
38
|
+
READ = 1,
|
|
39
|
+
SET_DEFAULT = 2,
|
|
40
|
+
SET = 3,
|
|
41
|
+
READ_MAX = 4
|
|
42
|
+
}
|
|
43
|
+
const enum GaStatus {
|
|
44
|
+
SUCCESS = 0,
|
|
45
|
+
ITEM_ERROR = 1,
|
|
46
|
+
ACTION_ERROR = 2,
|
|
47
|
+
FAILURE = 3
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export class ImageAddon implements ITerminalAddon {
|
|
52
|
+
private _opts: IImageAddonOptions;
|
|
53
|
+
private _defaultOpts: IImageAddonOptions;
|
|
54
|
+
private _storage: ImageStorage | undefined;
|
|
55
|
+
private _renderer: ImageRenderer | undefined;
|
|
56
|
+
private _disposables: IDisposable[] = [];
|
|
57
|
+
private _terminal: ITerminalExt | undefined;
|
|
58
|
+
private _handlers: Map<String, IResetHandler> = new Map();
|
|
59
|
+
|
|
60
|
+
constructor(opts?: Partial<IImageAddonOptions>) {
|
|
61
|
+
this._opts = Object.assign({}, DEFAULT_OPTIONS, opts);
|
|
62
|
+
this._defaultOpts = Object.assign({}, DEFAULT_OPTIONS, opts);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public dispose(): void {
|
|
66
|
+
for (const obj of this._disposables) {
|
|
67
|
+
obj.dispose();
|
|
68
|
+
}
|
|
69
|
+
this._disposables.length = 0;
|
|
70
|
+
this._handlers.clear();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private _disposeLater(...args: IDisposable[]): void {
|
|
74
|
+
for (const obj of args) {
|
|
75
|
+
this._disposables.push(obj);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public activate(terminal: ITerminalExt): void {
|
|
80
|
+
this._terminal = terminal;
|
|
81
|
+
|
|
82
|
+
// internal data structures
|
|
83
|
+
this._renderer = new ImageRenderer(terminal);
|
|
84
|
+
this._storage = new ImageStorage(terminal, this._renderer, this._opts);
|
|
85
|
+
|
|
86
|
+
// enable size reports
|
|
87
|
+
if (this._opts.enableSizeReports) {
|
|
88
|
+
// const windowOptions = terminal.getOption('windowOptions');
|
|
89
|
+
// windowOptions.getWinSizePixels = true;
|
|
90
|
+
// windowOptions.getCellSizePixels = true;
|
|
91
|
+
// windowOptions.getWinSizeChars = true;
|
|
92
|
+
// terminal.setOption('windowOptions', windowOptions);
|
|
93
|
+
const windowOps = terminal.options.windowOptions || {};
|
|
94
|
+
windowOps.getWinSizePixels = true;
|
|
95
|
+
windowOps.getCellSizePixels = true;
|
|
96
|
+
windowOps.getWinSizeChars = true;
|
|
97
|
+
terminal.options.windowOptions = windowOps;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
this._disposeLater(
|
|
101
|
+
this._renderer,
|
|
102
|
+
this._storage,
|
|
103
|
+
|
|
104
|
+
// DECSET/DECRST/DA1/XTSMGRAPHICS handlers
|
|
105
|
+
terminal.parser.registerCsiHandler({ prefix: '?', final: 'h' }, params => this._decset(params)),
|
|
106
|
+
terminal.parser.registerCsiHandler({ prefix: '?', final: 'l' }, params => this._decrst(params)),
|
|
107
|
+
terminal.parser.registerCsiHandler({ final: 'c' }, params => this._da1(params)),
|
|
108
|
+
terminal.parser.registerCsiHandler({ prefix: '?', final: 'S' }, params => this._xtermGraphicsAttributes(params)),
|
|
109
|
+
|
|
110
|
+
// render hook
|
|
111
|
+
terminal.onRender(range => this._storage?.render(range)),
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* reset handlers covered:
|
|
115
|
+
* - DECSTR
|
|
116
|
+
* - RIS
|
|
117
|
+
* - Terminal.reset()
|
|
118
|
+
*/
|
|
119
|
+
terminal.parser.registerCsiHandler({ intermediates: '!', final: 'p' }, () => this.reset()),
|
|
120
|
+
terminal.parser.registerEscHandler({ final: 'c' }, () => this.reset()),
|
|
121
|
+
terminal._core._inputHandler.onRequestReset(() => this.reset()),
|
|
122
|
+
|
|
123
|
+
// wipe canvas and delete alternate images on buffer switch
|
|
124
|
+
terminal.buffer.onBufferChange(() => this._storage?.wipeAlternate()),
|
|
125
|
+
|
|
126
|
+
// extend images to the right on resize
|
|
127
|
+
terminal.onResize(metrics => this._storage?.viewportResize(metrics))
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// SIXEL handler
|
|
131
|
+
if (this._opts.sixelSupport) {
|
|
132
|
+
const sixelHandler = new SixelHandler(this._opts, this._storage!, terminal);
|
|
133
|
+
this._handlers.set('sixel', sixelHandler);
|
|
134
|
+
this._disposeLater(
|
|
135
|
+
terminal._core._inputHandler._parser.registerDcsHandler({ final: 'q' }, sixelHandler)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// iTerm IIP handler
|
|
140
|
+
if (this._opts.iipSupport) {
|
|
141
|
+
const iipHandler = new IIPHandler(this._opts, this._renderer!, this._storage!, terminal);
|
|
142
|
+
this._handlers.set('iip', iipHandler);
|
|
143
|
+
this._disposeLater(
|
|
144
|
+
terminal._core._inputHandler._parser.registerOscHandler(1337, iipHandler)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Note: storageLimit is skipped here to not intoduce a surprising side effect.
|
|
150
|
+
public reset(): boolean {
|
|
151
|
+
// reset options customizable by sequences to defaults
|
|
152
|
+
this._opts.sixelScrolling = this._defaultOpts.sixelScrolling;
|
|
153
|
+
this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
|
|
154
|
+
// also clear image storage
|
|
155
|
+
this._storage?.reset();
|
|
156
|
+
// reset protocol handlers
|
|
157
|
+
for (const handler of this._handlers.values()) {
|
|
158
|
+
handler.reset();
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
public get storageLimit(): number {
|
|
164
|
+
return this._storage?.getLimit() || -1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public set storageLimit(limit: number) {
|
|
168
|
+
this._storage?.setLimit(limit);
|
|
169
|
+
this._opts.storageLimit = limit;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public get storageUsage(): number {
|
|
173
|
+
if (this._storage) {
|
|
174
|
+
return this._storage.getUsage();
|
|
175
|
+
}
|
|
176
|
+
return -1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public get showPlaceholder(): boolean {
|
|
180
|
+
return this._opts.showPlaceholder;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
public set showPlaceholder(value: boolean) {
|
|
184
|
+
this._opts.showPlaceholder = value;
|
|
185
|
+
this._renderer?.showPlaceholder(value);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public getImageAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined {
|
|
189
|
+
return this._storage?.getImageAtBufferCell(x, y);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public extractTileAtBufferCell(x: number, y: number): HTMLCanvasElement | undefined {
|
|
193
|
+
return this._storage?.extractTileAtBufferCell(x, y);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private _report(s: string): void {
|
|
197
|
+
this._terminal?._core.coreService.triggerDataEvent(s);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private _decset(params: (number | number[])[]): boolean {
|
|
201
|
+
for (let i = 0; i < params.length; ++i) {
|
|
202
|
+
switch (params[i]) {
|
|
203
|
+
case 80:
|
|
204
|
+
this._opts.sixelScrolling = false;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private _decrst(params: (number | number[])[]): boolean {
|
|
212
|
+
for (let i = 0; i < params.length; ++i) {
|
|
213
|
+
switch (params[i]) {
|
|
214
|
+
case 80:
|
|
215
|
+
this._opts.sixelScrolling = true;
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// overload DA to return something more appropriate
|
|
223
|
+
private _da1(params: (number | number[])[]): boolean {
|
|
224
|
+
if (params[0]) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
// reported features:
|
|
228
|
+
// 62 - VT220
|
|
229
|
+
// 4 - SIXEL support
|
|
230
|
+
// 9 - charsets
|
|
231
|
+
// 22 - ANSI colors
|
|
232
|
+
if (this._opts.sixelSupport) {
|
|
233
|
+
this._report(`\x1b[?62;4;9;22c`);
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Implementation of xterm's graphics attribute sequence.
|
|
241
|
+
*
|
|
242
|
+
* Supported features:
|
|
243
|
+
* - read/change palette limits (max 4096 by sixel lib)
|
|
244
|
+
* - read SIXEL canvas geometry (reports current window canvas or
|
|
245
|
+
* squared pixelLimit if canvas > pixel limit)
|
|
246
|
+
*
|
|
247
|
+
* Everything else is deactivated.
|
|
248
|
+
*/
|
|
249
|
+
private _xtermGraphicsAttributes(params: (number | number[])[]): boolean {
|
|
250
|
+
if (params.length < 2) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
if (params[0] === GaItem.COLORS) {
|
|
254
|
+
switch (params[1]) {
|
|
255
|
+
case GaAction.READ:
|
|
256
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${this._opts.sixelPaletteLimit}S`);
|
|
257
|
+
return true;
|
|
258
|
+
case GaAction.SET_DEFAULT:
|
|
259
|
+
this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
|
|
260
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${this._opts.sixelPaletteLimit}S`);
|
|
261
|
+
// also reset protocol handlers for now
|
|
262
|
+
for (const handler of this._handlers.values()) {
|
|
263
|
+
handler.reset();
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
case GaAction.SET:
|
|
267
|
+
if (params.length > 2 && !(params[2] instanceof Array) && params[2] <= MAX_SIXEL_PALETTE_SIZE) {
|
|
268
|
+
this._opts.sixelPaletteLimit = params[2];
|
|
269
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${this._opts.sixelPaletteLimit}S`);
|
|
270
|
+
} else {
|
|
271
|
+
this._report(`\x1b[?${params[0]};${GaStatus.ACTION_ERROR}S`);
|
|
272
|
+
}
|
|
273
|
+
return true;
|
|
274
|
+
case GaAction.READ_MAX:
|
|
275
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${MAX_SIXEL_PALETTE_SIZE}S`);
|
|
276
|
+
return true;
|
|
277
|
+
default:
|
|
278
|
+
this._report(`\x1b[?${params[0]};${GaStatus.ACTION_ERROR}S`);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (params[0] === GaItem.SIXEL_GEO) {
|
|
283
|
+
switch (params[1]) {
|
|
284
|
+
// we only implement read and read_max here
|
|
285
|
+
case GaAction.READ:
|
|
286
|
+
let width = this._renderer?.dimensions?.css.canvas.width;
|
|
287
|
+
let height = this._renderer?.dimensions?.css.canvas.height;
|
|
288
|
+
if (!width || !height) {
|
|
289
|
+
// for some reason we have no working image renderer
|
|
290
|
+
// --> fallback to default cell size
|
|
291
|
+
const cellSize = CELL_SIZE_DEFAULT;
|
|
292
|
+
width = (this._terminal?.cols || 80) * cellSize.width;
|
|
293
|
+
height = (this._terminal?.rows || 24) * cellSize.height;
|
|
294
|
+
}
|
|
295
|
+
if (width * height < this._opts.pixelLimit) {
|
|
296
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${width.toFixed(0)};${height.toFixed(0)}S`);
|
|
297
|
+
} else {
|
|
298
|
+
// if we overflow pixelLimit report that squared instead
|
|
299
|
+
const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
|
|
300
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${x};${x}S`);
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
case GaAction.READ_MAX:
|
|
304
|
+
// read_max returns pixelLimit as square area
|
|
305
|
+
const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
|
|
306
|
+
this._report(`\x1b[?${params[0]};${GaStatus.SUCCESS};${x};${x}S`);
|
|
307
|
+
return true;
|
|
308
|
+
default:
|
|
309
|
+
this._report(`\x1b[?${params[0]};${GaStatus.ACTION_ERROR}S`);
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// exit with error on ReGIS or any other requests
|
|
314
|
+
this._report(`\x1b[?${params[0]};${GaStatus.ITEM_ERROR}S`);
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
}
|