import { BufferLocation, BufferSize, Char, Color } from "./base.ts"; import { Display } from "./display.ts"; /** * ANSI terminal display */ export class AnsiTerminalDisplay implements Display { private palette_bg: readonly Uint8Array[] = []; private palette_fg: readonly Uint8Array[] = []; constructor( private writer: Deno.Writer = Deno.stdout, private reader: Deno.Reader = Deno.stdin, ) { } async getSize(): Promise { const size = Deno.consoleSize(Deno.stdout.rid); return { w: size.columns, h: size.rows, }; } async setupPalette(colors: readonly Color[]): Promise { // TODO handle not fully rgb compatible terminals const cr = (x: number) => Math.round(x * 255); this.palette_bg = colors.map((col) => escape(`[48;2;${cr(col.r)};${cr(col.g)};${cr(col.b)}m`) ); this.palette_fg = colors.map((col) => escape(`[38;2;${cr(col.r)};${cr(col.g)};${cr(col.b)}m`) ); return colors; } async clear(): Promise { await this.writer.write(CLEAR); } async setChar(at: BufferLocation, char: Char): Promise { // TODO do not move the cursor if already at good location // TODO do not change the color if already good const fg = this.palette_fg[char.fg]; const bg = this.palette_bg[char.bg]; await this.writer.write(fg); await this.writer.write(bg); await this.writer.write(escape(`[${at.y};${at.x}H${char.ch}`)); } } function escape(sequence: string): Uint8Array { return new Uint8Array([0x1B, ...new TextEncoder().encode(sequence)]); } const CLEAR = escape("[2J");