#pragma once // terminal_panel — emulador TTY embebible en ImGui. // // Arranca un proceso hijo via PTY (Linux: forkpty) o ConPTY (Windows) y // renderiza su output en un child window ImGui con soporte basico de ANSI: // colores FG/BG 16-color, bold, cursor pos, clear screen/line. // // Uso basico: // static fn_term::TerminalPanel term; // term.shell = "/bin/bash"; // // if (!term.is_open()) fn_term::open(term); // fn_term::render(term); // if (!term.readonly) fn_term::send(term, "ls\n"); // // Al cerrar: // fn_term::close(term); // // Thread-safety: open/render/send/close deben llamarse desde el hilo ImGui. // El reader thread interno es gestionado por la implementacion. // // Plataformas: // Linux/macOS: terminal_panel_linux.cpp (forkpty + read no-blocking en thread) // Windows: terminal_panel_windows.cpp (ConPTY CreatePseudoConsole) #include "core/ansi_parser.h" #include #include #include #include #include #include namespace fn_term { // Una linea del scrollback: vector de celdas ya parseadas. using TermLine = std::vector; // Configuracion y estado del panel. struct TerminalPanel { // --- Config (set antes de open(), no cambiar en vivo) --- std::string shell; // "" → auto-detect (/bin/bash linux, cmd.exe windows) std::string cwd; // "" → directorio actual del proceso padre std::vector env; // KEY=VAL adicionales al entorno heredado int scrollback_lines = 5000; // max filas en el ring buffer bool readonly = false; // si true, no reenvía input del teclado // --- Estado interno (gestionado por open/close/render) --- // No modificar directamente. // Proceso hijo int child_pid = -1; // Linux: PID del hijo; -1 si no abierto int master_fd = -1; // Linux: fd del extremo master del PTY void* proc_handle = nullptr; // Windows: HANDLE del proceso hijo (HANDLE) void* pty_handle = nullptr; // Windows: HPCON (ConPTY handle) void* pipe_read = nullptr; // Windows: HANDLE pipe de lectura void* pipe_write = nullptr; // Windows: HANDLE pipe de escritura (→ stdin del hijo) // Reader thread std::thread reader_thread; std::atomic reader_running{false}; // Scrollback buffer (protegido por mutex) mutable std::mutex buf_mutex; std::vector lines; // buffer circular de lineas int cur_row = 0; // fila del cursor dentro de `lines` int cur_col = 0; // columna del cursor bool scroll_to_bottom = true; // Parser ANSI (solo lo toca el reader thread) AnsiParser parser; // Flag: proceso hijo terminó std::atomic process_exited{false}; int exit_code = 0; // ctor/dtor TerminalPanel(); ~TerminalPanel(); TerminalPanel(const TerminalPanel&) = delete; TerminalPanel& operator=(const TerminalPanel&) = delete; bool is_open() const { return master_fd >= 0 || pipe_read != nullptr; } }; // Abre el proceso hijo y arranca el reader thread. // Llama una sola vez antes del primer render. // Si falla, loguea via fn_log::log_error y deja is_open() == false. void open(TerminalPanel& panel); // Renderiza el terminal en el area disponible de ImGui. // Debe llamarse dentro de un frame ImGui activo. // Dibuja toolbar (clear, copy, reset, scroll-lock) + scrollback + input. void render(TerminalPanel& panel); // Envía texto al stdin del proceso hijo. // No-op si !is_open() o readonly. void send(TerminalPanel& panel, const std::string& text); // Cierra el proceso hijo, espera al reader thread y libera recursos. void close(TerminalPanel& panel); // ---- Internals usados por los backends Linux/Windows ---- // (No llamar directamente desde apps.) // Procesa un chunk de bytes del PTY y los añade al scrollback. // Llamado desde el reader thread. Thread-safe via buf_mutex. void process_output(TerminalPanel& panel, const char* data, size_t n); } // namespace fn_term