|
| 1 | +// Some constants for clarity |
| 2 | +const ESC = `\x1B`; |
| 3 | +const DCS = `${ESC}P`; |
| 4 | +const ST = `${ESC}\\`; |
| 5 | +const ONE = `1`.charCodeAt(0); |
| 6 | +const EQUALS = `=`.charCodeAt(0); |
| 7 | + |
| 8 | +/** |
| 9 | + * Returns the DCS query string (XTGETTCAP). Only supports a single query. |
| 10 | + * |
| 11 | + * > Request Termcap/Terminfo String (XTGETTCAP) |
| 12 | + * |
| 13 | + * @param query {string} - The query to make. |
| 14 | + * @returns The encoded DCS query string |
| 15 | + * @see <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html> |
| 16 | + */ |
| 17 | +function dcsQuery(query) { |
| 18 | + const hex = Buffer.from(query, 'utf8').toString('hex'); |
| 19 | + return `${DCS}+q${hex}${ST}`; |
| 20 | +} |
| 21 | + |
| 22 | +/** |
| 23 | + * Returns the DCS answer (to the {@link dcsQuery}). (The part after the `=`.) |
| 24 | + * |
| 25 | + * > DCS 1 + r Pt ST for valid requests, adding to Pt an = , and |
| 26 | + * > the value of the corresponding string that xterm would send, |
| 27 | + * > or |
| 28 | + * > DCS 0 + r ST for invalid requests. |
| 29 | + * |
| 30 | + * @param response {Buffer} - The encoded DCS response. |
| 31 | + * @returns The answer for valid requests and `undefined` for invalid requests |
| 32 | + * @see <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html> |
| 33 | + */ |
| 34 | +function dcsAnswer(response) { |
| 35 | + const success = response[2]; |
| 36 | + if (success !== ONE) { |
| 37 | + return undefined; // Invalid request |
| 38 | + } |
| 39 | + |
| 40 | + const index = response.lastIndexOf(EQUALS); |
| 41 | + if (index < 0) { |
| 42 | + return undefined; // No `=` found, so invalid format |
| 43 | + } |
| 44 | + |
| 45 | + const answer = response.subarray(index + 1, -1); |
| 46 | + // Answer is an ASCII (UTF-8) string of Hex characters |
| 47 | + return Buffer.from(answer.toString(), 'hex').toString(); |
| 48 | +} |
| 49 | + |
| 50 | +/** |
| 51 | + * Query the terminal for Termcap/Terminfo capabilities (XTGETTCAP). |
| 52 | + * |
| 53 | + * @param query {string} - The requested capability. |
| 54 | + * @returns The answer for valid requests and `undefined` for invalid requests |
| 55 | + */ |
| 56 | +export async function queryTerminal(query) { |
| 57 | + return new Promise(function (resolve, reject) { |
| 58 | + try { |
| 59 | + // Configure stdin to watch for the response |
| 60 | + /** @type {import('node:stream').Readable} */ |
| 61 | + const stdin = process.stdin; |
| 62 | + stdin.setRawMode(true); |
| 63 | + stdin.resume(); // Listen |
| 64 | + stdin.on('data', (data) => { |
| 65 | + stdin.pause(); // Stop listening |
| 66 | + resolve(dcsAnswer(data)); |
| 67 | + }); |
| 68 | + |
| 69 | + // XTGETTCAP means writing the DCS query to the terminal |
| 70 | + process.stdout.write(dcsQuery(query)); |
| 71 | + |
| 72 | + } catch (error) { |
| 73 | + reject(error); |
| 74 | + } |
| 75 | + }); |
| 76 | +} |
0 commit comments