diff --git a/src/bun/ffi.ts b/src/bun/ffi.ts index 8cf24d1..38eda62 100644 --- a/src/bun/ffi.ts +++ b/src/bun/ffi.ts @@ -37,4 +37,4 @@ try { } export const { tcgetattr, tcsetattr, cfmakeraw } = cStdLib.symbols; -export { ptr }; \ No newline at end of file +export const getPointer = ptr; diff --git a/src/bun/index.ts b/src/bun/index.ts index 35d6cf1..67daabc 100644 --- a/src/bun/index.ts +++ b/src/bun/index.ts @@ -2,10 +2,10 @@ * The main entrypoint when using Bun as the runtime */ -import {Termios} from './termios' +import { getTermios } from "../common/termios"; export async function main(): Promise { - const t = new Termios(); + const t = await getTermios(); t.enableRawMode(); const decoder = new TextDecoder(); diff --git a/src/bun/termios.ts b/src/bun/termios.ts deleted file mode 100644 index ab8a9cd..0000000 --- a/src/bun/termios.ts +++ /dev/null @@ -1,81 +0,0 @@ -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; - } -} - diff --git a/src/common/index.ts b/src/common/index.ts index ae680d9..d2a252f 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -30,7 +30,7 @@ export const getRuntime = (): RunTime => { */ export const importForRuntime = async (path: string) => { const runtime = getRuntime(); - const suffix = (runtime === RunTime.Deno) ? '.ts' : ''; + const suffix = '.ts'; const base = `../${runtime}/`; const pathParts = path.split('/') diff --git a/src/common/termios.ts b/src/common/termios.ts index d023d6c..3228ce8 100644 --- a/src/common/termios.ts +++ b/src/common/termios.ts @@ -1,3 +1,5 @@ +import { importForRuntime } from "./index.ts"; + export const STDIN_FILENO = 0; export const STOUT_FILENO = 1; export const TCSANOW = 0; @@ -23,4 +25,89 @@ export interface ITermios { * 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(); } \ No newline at end of file diff --git a/src/deno/index.ts b/src/deno/index.ts index e176f53..9dacf97 100644 --- a/src/deno/index.ts +++ b/src/deno/index.ts @@ -1,10 +1,10 @@ /** * The main entrypoint when using Deno as the runtime */ -import { Termios } from './termios.ts' +import {getTermios} from "../common/termios.ts"; export async function main(): Promise { - const t = new Termios(); + const t = await getTermios() t.enableRawMode(); const decoder = new TextDecoder(); diff --git a/src/deno/termios.ts b/src/deno/termios.ts deleted file mode 100644 index f8c7b59..0000000 --- a/src/deno/termios.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { STDIN_FILENO, TCSANOW, ITermios, TERMIOS_SIZE } from "../common/termios.ts"; -import { cfmakeraw, tcgetattr, tcsetattr, getPointer } from "./ffi.ts"; - -/** - * Implementation to toggle raw mode with Deno 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 - */ - #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; - } -} -