import { importForRuntime } from './index.ts'; export const STDIN_FILENO = 0; export const STOUT_FILENO = 1; export const TCSANOW = 0; export const TCSAFLUSH = 2; export const TERMIOS_SIZE = 60; /** * Common interface for setting Termios properties */ export interface ITermios { /** * Are we currently in raw mode? */ inRawMode: boolean; /** * Toggles on raw mode */ enableRawMode(): void; /** * Restores canonical mode */ disableRawMode(): void; } export const getTermios = async () => { // Get the runtime-specific ffi wrappers const { tcgetattr, tcsetattr, cfmakeraw, getPointer } = await importForRuntime('ffi'); /** * Implementation to toggle raw mode with Bun runtime */ 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 */ readonly #termios: Uint8Array; /** * The pointer to the termios struct * @private */ readonly #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 = getPointer(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 = getPointer(this.#cookedTermios); tcsetattr(STDIN_FILENO, TCSANOW, oldTermiosPtr); this.#inRawMode = false; } } return new Termios(); };