#!/bin/env -S deno run --allow-net --allow-run import { MsgToClient, MsgToServer, mtc } from "../common/mod.ts"; import { run } from "../common/utils.ts"; import { channel, launch_caught, log_from, parsed_stream, Receiver, Sender, serialized_stream, wait, } from "../common/utils.ts"; const log = log_from(import.meta); async function main() { log("Client starting."); const interface_ = await ServerInterface.connect(9999); new InputHandler(interface_.outputs).spin(); const display = new DisplayHandler(interface_.outputs); display.spin(); new MsgHandler(interface_.inputs, display).spin(); log("Connected."); interface_.outputs.send({ kind: "ping", content: { message: "Machin." } }); log("Sent ping."); } class MsgHandler { receiver; display; constructor(receiver: Receiver, display: DisplayHandler) { this.receiver = receiver; this.display = display; } async spin() { for await (const event of this.receiver.iter()) { if (event.kind === "ping_response") log("Received ping response :", event.content); if (event.kind === "display") this.display.refresh(event.content.raw); } } } class InputHandler { outputs; constructor(outputs: Sender) { this.outputs = outputs; } async read_one_char() { const args = ["-i0", "sh", "-c", "read -n 1 OUT; echo -n $OUT"]; const command = new Deno.Command("stdbuf", { args, stdin: "inherit", stdout: "piped" }); const output = await command.output(); const result = new TextDecoder().decode(output.stdout); console.log(`${String.fromCharCode(0x08)} ${String.fromCharCode(0o33)}[A`); return result[0]; } async spin() { while (true) { const read = await this.read_one_char(); if (read === "z") this.outputs.send({ kind: "input", content: { control: "up" } }); if (read === "s") this.outputs.send({ kind: "input", content: { control: "down" } }); if (read === "q") this.outputs.send({ kind: "input", content: { control: "left" } }); if (read === "d") this.outputs.send({ kind: "input", content: { control: "right" } }); } } } class DisplayHandler { outputs; constructor(outputs: Sender) { this.outputs = outputs; } async spin() { while (true) { await wait(100); const [width, height] = await DisplayHandler.get_res(); this.outputs.send({ kind: "request_display", content: { width, height } }); } } static async get_res() { const output = await run("stty", "size"); const [raw_height, raw_width] = output.split(" "); const width = Math.floor(parseInt(raw_width) / 2) - 2; // note : tiles are 2 char wide const height = parseInt(raw_height) - 2; return [width, height] as const; } refresh(raw: string) { const clear_sequence = String.fromCharCode(0o033) + String.fromCharCode(0o143); console.log(clear_sequence + raw); } } class ServerInterface { inputs; outputs; constructor(inputs: Receiver, outputs: Sender) { this.inputs = inputs; this.outputs = outputs; } static async connect(port: number) { const connection = await Deno.connect({ port }); return await ServerInterface.init(connection); } // deno-lint-ignore require-await static async init(connection: Deno.Conn) { // TODO : handshake ? const [input_sender, input_receiver] = channel(); const [output_sender, output_receiver] = channel(); launch_caught(() => input_sender.send_all(parsed_stream(mtc.message_to_client_parser())(connection.readable))); launch_caught(() => serialized_stream(output_receiver.iter())(connection.writable)); return new ServerInterface(input_receiver, output_sender); } } if (import.meta.main) await main(); Deno.test("res_check", async () => { const ENABLED = false; // note : toggle before running test to know current resolution. while (ENABLED) { await wait(100); const res = await DisplayHandler.get_res(); console.clear(); console.log("res", res); } });