import { STDIN_FILENO, TCSANOW, ITermios, TERMIOS_SIZE } from "../common/termios"; import { cfmakeraw, tcgetattr, tcsetattr, ptr } from "./ffi"; /** * Implementation to toggle raw mode with Bun runtime */ export class Termios implements ITermios { /** * Are we in raw mode? * @private */ #inRawMode:boolean; /** * The saved version of the termios struct for cooked/canonical mode * @private */ #cookedTermios: Uint8Array; /** * The data for the termios struct we are manipulating * @private */ #termios: Uint8Array; /** * The pointer to the termios struct * @private // this.#ptr = ptr(this.#termios); */ #ptr: any; constructor() { this.#inRawMode = false; // These are the TypedArrays linked to the raw pointer data this.#cookedTermios = new Uint8Array(TERMIOS_SIZE); this.#termios = new Uint8Array(TERMIOS_SIZE); // The current pointer for C this.#ptr = ptr(this.#termios); } get inRawMode() { return this.#inRawMode; } enableRawMode() { if (this.#inRawMode) { throw new Error('Can not enable raw mode when in raw mode'); } // Get the current termios settings tcgetattr(STDIN_FILENO, this.#ptr); // The #ptr property is pointing to the #termios TypedArray. As the pointer // is manipulated, the TypedArray is as well. We will use this to save // the original canonical/cooked terminal settings for disabling raw mode later. this.#cookedTermios = new Uint8Array(this.#termios, 0, 60); // Update termios struct with raw settings cfmakeraw(this.#ptr); // Actually set the new termios settings tcsetattr(STDIN_FILENO, TCSANOW, this.#ptr); this.#inRawMode = true; } disableRawMode() { // Don't even bother throwing an error if we try to disable raw mode // and aren't in raw mode. It just doesn't really matter. if (!this.#inRawMode) { return; } const oldTermiosPtr = ptr(this.#cookedTermios); tcsetattr(STDIN_FILENO, TCSANOW, oldTermiosPtr); this.#inRawMode = false; } }