"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;