260 lines
11 KiB
JavaScript
260 lines
11 KiB
JavaScript
"use strict";
|
|
/**
|
|
* Copyright (c) 2020 The xterm.js authors. All rights reserved.
|
|
* @license MIT
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.ImageAddon = void 0;
|
|
const IIPHandler_1 = require("./IIPHandler");
|
|
const ImageRenderer_1 = require("./ImageRenderer");
|
|
const ImageStorage_1 = require("./ImageStorage");
|
|
const SixelHandler_1 = require("./SixelHandler");
|
|
// default values of addon ctor options
|
|
const DEFAULT_OPTIONS = {
|
|
enableSizeReports: true,
|
|
pixelLimit: 16777216,
|
|
sixelSupport: true,
|
|
sixelScrolling: true,
|
|
sixelPaletteLimit: 256,
|
|
sixelSizeLimit: 25000000,
|
|
storageLimit: 128,
|
|
showPlaceholder: true,
|
|
iipSupport: true,
|
|
iipSizeLimit: 20000000
|
|
};
|
|
// max palette size supported by the sixel lib (compile time setting)
|
|
const MAX_SIXEL_PALETTE_SIZE = 4096;
|
|
class ImageAddon {
|
|
constructor(opts) {
|
|
this._disposables = [];
|
|
this._handlers = new Map();
|
|
this._opts = Object.assign({}, DEFAULT_OPTIONS, opts);
|
|
this._defaultOpts = Object.assign({}, DEFAULT_OPTIONS, opts);
|
|
}
|
|
dispose() {
|
|
for (const obj of this._disposables) {
|
|
obj.dispose();
|
|
}
|
|
this._disposables.length = 0;
|
|
this._handlers.clear();
|
|
}
|
|
_disposeLater(...args) {
|
|
for (const obj of args) {
|
|
this._disposables.push(obj);
|
|
}
|
|
}
|
|
activate(terminal) {
|
|
this._terminal = terminal;
|
|
// internal data structures
|
|
this._renderer = new ImageRenderer_1.ImageRenderer(terminal);
|
|
this._storage = new ImageStorage_1.ImageStorage(terminal, this._renderer, this._opts);
|
|
// enable size reports
|
|
if (this._opts.enableSizeReports) {
|
|
// const windowOptions = terminal.getOption('windowOptions');
|
|
// windowOptions.getWinSizePixels = true;
|
|
// windowOptions.getCellSizePixels = true;
|
|
// windowOptions.getWinSizeChars = true;
|
|
// terminal.setOption('windowOptions', windowOptions);
|
|
const windowOps = terminal.options.windowOptions || {};
|
|
windowOps.getWinSizePixels = true;
|
|
windowOps.getCellSizePixels = true;
|
|
windowOps.getWinSizeChars = true;
|
|
terminal.options.windowOptions = windowOps;
|
|
}
|
|
this._disposeLater(this._renderer, this._storage,
|
|
// DECSET/DECRST/DA1/XTSMGRAPHICS handlers
|
|
terminal.parser.registerCsiHandler({ prefix: '?', final: 'h' }, params => this._decset(params)), terminal.parser.registerCsiHandler({ prefix: '?', final: 'l' }, params => this._decrst(params)), terminal.parser.registerCsiHandler({ final: 'c' }, params => this._da1(params)), terminal.parser.registerCsiHandler({ prefix: '?', final: 'S' }, params => this._xtermGraphicsAttributes(params)),
|
|
// render hook
|
|
terminal.onRender(range => { var _a; return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.render(range); }),
|
|
/**
|
|
* reset handlers covered:
|
|
* - DECSTR
|
|
* - RIS
|
|
* - Terminal.reset()
|
|
*/
|
|
terminal.parser.registerCsiHandler({ intermediates: '!', final: 'p' }, () => this.reset()), terminal.parser.registerEscHandler({ final: 'c' }, () => this.reset()), terminal._core._inputHandler.onRequestReset(() => this.reset()),
|
|
// wipe canvas and delete alternate images on buffer switch
|
|
terminal.buffer.onBufferChange(() => { var _a; return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.wipeAlternate(); }),
|
|
// extend images to the right on resize
|
|
terminal.onResize(metrics => { var _a; return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.viewportResize(metrics); }));
|
|
// SIXEL handler
|
|
if (this._opts.sixelSupport) {
|
|
const sixelHandler = new SixelHandler_1.SixelHandler(this._opts, this._storage, terminal);
|
|
this._handlers.set('sixel', sixelHandler);
|
|
this._disposeLater(terminal._core._inputHandler._parser.registerDcsHandler({ final: 'q' }, sixelHandler));
|
|
}
|
|
// iTerm IIP handler
|
|
if (this._opts.iipSupport) {
|
|
const iipHandler = new IIPHandler_1.IIPHandler(this._opts, this._renderer, this._storage, terminal);
|
|
this._handlers.set('iip', iipHandler);
|
|
this._disposeLater(terminal._core._inputHandler._parser.registerOscHandler(1337, iipHandler));
|
|
}
|
|
}
|
|
// Note: storageLimit is skipped here to not intoduce a surprising side effect.
|
|
reset() {
|
|
var _a;
|
|
// reset options customizable by sequences to defaults
|
|
this._opts.sixelScrolling = this._defaultOpts.sixelScrolling;
|
|
this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
|
|
// also clear image storage
|
|
(_a = this._storage) === null || _a === void 0 ? void 0 : _a.reset();
|
|
// reset protocol handlers
|
|
for (const handler of this._handlers.values()) {
|
|
handler.reset();
|
|
}
|
|
return false;
|
|
}
|
|
get storageLimit() {
|
|
var _a;
|
|
return ((_a = this._storage) === null || _a === void 0 ? void 0 : _a.getLimit()) || -1;
|
|
}
|
|
set storageLimit(limit) {
|
|
var _a;
|
|
(_a = this._storage) === null || _a === void 0 ? void 0 : _a.setLimit(limit);
|
|
this._opts.storageLimit = limit;
|
|
}
|
|
get storageUsage() {
|
|
if (this._storage) {
|
|
return this._storage.getUsage();
|
|
}
|
|
return -1;
|
|
}
|
|
get showPlaceholder() {
|
|
return this._opts.showPlaceholder;
|
|
}
|
|
set showPlaceholder(value) {
|
|
var _a;
|
|
this._opts.showPlaceholder = value;
|
|
(_a = this._renderer) === null || _a === void 0 ? void 0 : _a.showPlaceholder(value);
|
|
}
|
|
getImageAtBufferCell(x, y) {
|
|
var _a;
|
|
return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.getImageAtBufferCell(x, y);
|
|
}
|
|
extractTileAtBufferCell(x, y) {
|
|
var _a;
|
|
return (_a = this._storage) === null || _a === void 0 ? void 0 : _a.extractTileAtBufferCell(x, y);
|
|
}
|
|
_report(s) {
|
|
var _a;
|
|
(_a = this._terminal) === null || _a === void 0 ? void 0 : _a._core.coreService.triggerDataEvent(s);
|
|
}
|
|
_decset(params) {
|
|
for (let i = 0; i < params.length; ++i) {
|
|
switch (params[i]) {
|
|
case 80:
|
|
this._opts.sixelScrolling = false;
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
_decrst(params) {
|
|
for (let i = 0; i < params.length; ++i) {
|
|
switch (params[i]) {
|
|
case 80:
|
|
this._opts.sixelScrolling = true;
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// overload DA to return something more appropriate
|
|
_da1(params) {
|
|
if (params[0]) {
|
|
return true;
|
|
}
|
|
// reported features:
|
|
// 62 - VT220
|
|
// 4 - SIXEL support
|
|
// 9 - charsets
|
|
// 22 - ANSI colors
|
|
if (this._opts.sixelSupport) {
|
|
this._report(`\x1b[?62;4;9;22c`);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Implementation of xterm's graphics attribute sequence.
|
|
*
|
|
* Supported features:
|
|
* - read/change palette limits (max 4096 by sixel lib)
|
|
* - read SIXEL canvas geometry (reports current window canvas or
|
|
* squared pixelLimit if canvas > pixel limit)
|
|
*
|
|
* Everything else is deactivated.
|
|
*/
|
|
_xtermGraphicsAttributes(params) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
if (params.length < 2) {
|
|
return true;
|
|
}
|
|
if (params[0] === 1 /* GaItem.COLORS */) {
|
|
switch (params[1]) {
|
|
case 1 /* GaAction.READ */:
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${this._opts.sixelPaletteLimit}S`);
|
|
return true;
|
|
case 2 /* GaAction.SET_DEFAULT */:
|
|
this._opts.sixelPaletteLimit = this._defaultOpts.sixelPaletteLimit;
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${this._opts.sixelPaletteLimit}S`);
|
|
// also reset protocol handlers for now
|
|
for (const handler of this._handlers.values()) {
|
|
handler.reset();
|
|
}
|
|
return true;
|
|
case 3 /* GaAction.SET */:
|
|
if (params.length > 2 && !(params[2] instanceof Array) && params[2] <= MAX_SIXEL_PALETTE_SIZE) {
|
|
this._opts.sixelPaletteLimit = params[2];
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${this._opts.sixelPaletteLimit}S`);
|
|
}
|
|
else {
|
|
this._report(`\x1b[?${params[0]};${2 /* GaStatus.ACTION_ERROR */}S`);
|
|
}
|
|
return true;
|
|
case 4 /* GaAction.READ_MAX */:
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${MAX_SIXEL_PALETTE_SIZE}S`);
|
|
return true;
|
|
default:
|
|
this._report(`\x1b[?${params[0]};${2 /* GaStatus.ACTION_ERROR */}S`);
|
|
return true;
|
|
}
|
|
}
|
|
if (params[0] === 2 /* GaItem.SIXEL_GEO */) {
|
|
switch (params[1]) {
|
|
// we only implement read and read_max here
|
|
case 1 /* GaAction.READ */:
|
|
let width = (_b = (_a = this._renderer) === null || _a === void 0 ? void 0 : _a.dimensions) === null || _b === void 0 ? void 0 : _b.css.canvas.width;
|
|
let height = (_d = (_c = this._renderer) === null || _c === void 0 ? void 0 : _c.dimensions) === null || _d === void 0 ? void 0 : _d.css.canvas.height;
|
|
if (!width || !height) {
|
|
// for some reason we have no working image renderer
|
|
// --> fallback to default cell size
|
|
const cellSize = ImageStorage_1.CELL_SIZE_DEFAULT;
|
|
width = (((_e = this._terminal) === null || _e === void 0 ? void 0 : _e.cols) || 80) * cellSize.width;
|
|
height = (((_f = this._terminal) === null || _f === void 0 ? void 0 : _f.rows) || 24) * cellSize.height;
|
|
}
|
|
if (width * height < this._opts.pixelLimit) {
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${width.toFixed(0)};${height.toFixed(0)}S`);
|
|
}
|
|
else {
|
|
// if we overflow pixelLimit report that squared instead
|
|
const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${x};${x}S`);
|
|
}
|
|
return true;
|
|
case 4 /* GaAction.READ_MAX */:
|
|
// read_max returns pixelLimit as square area
|
|
const x = Math.floor(Math.sqrt(this._opts.pixelLimit));
|
|
this._report(`\x1b[?${params[0]};${0 /* GaStatus.SUCCESS */};${x};${x}S`);
|
|
return true;
|
|
default:
|
|
this._report(`\x1b[?${params[0]};${2 /* GaStatus.ACTION_ERROR */}S`);
|
|
return true;
|
|
}
|
|
}
|
|
// exit with error on ReGIS or any other requests
|
|
this._report(`\x1b[?${params[0]};${1 /* GaStatus.ITEM_ERROR */}S`);
|
|
return true;
|
|
}
|
|
}
|
|
exports.ImageAddon = ImageAddon; |