feat(0132): cpp terminal_panel module + ansi_parser
Nuevo modulo reutilizable terminal_panel (fn_term) para ImGui: Sub-fn ansi_parser_cpp_core (cpp/functions/core/): - Parser ANSI/VT100 byte-a-byte sin heap allocs por evento - SGR colores FG/BG 16-color + bold + reset - Cursor moves CUU/CUD/CUF/CUB + CUP absoluto - Erase ED(2)/EL(2), CR/LF/BS - Statemachine 4 estados, thread-unsafe por diseno - 21 tests unitarios (57 assertions), todos pasan terminal_panel_cpp_viz (cpp/functions/viz/terminal_panel/): - terminal_panel.cpp: render ImGui + process_output con list clipper - terminal_panel_linux.cpp: forkpty + reader thread no-blocking - terminal_panel_windows.cpp: ConPTY CreatePseudoConsole (SDK >= 17763) - Scrollback circular configurable (default 5000 lineas) - Toolbar: clear, copy, reset, scroll-lock + status indicator - readonly mode: sin input box, send() es no-op - uses_functions: ansi_parser_cpp_core, logger_cpp_core Tests: - test_ansi_parser.cpp: 21 test cases, 57 assertions (PASS) - test_terminal_panel_smoke.cpp: 3 test cases (PASS: spawn echo hello, process exits cleanly, readonly ignores send) CMake: - cpp/tests/CMakeLists.txt: add test_ansi_parser + test_terminal_panel_smoke - primitives_gallery (sub-repo): ver commit separado en apps/primitives_gallery Pendiente (anti-scope v1): - Windows ConPTY: stub funcional que compila; join() del reader thread via std::thread no implementado (usa CreateThread detached) - ANSI 256/24-bit color, italics, Unicode wide - Curses pesados (vim, htop, top) — cursor visible basic solo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
#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 <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace fn_term {
|
||||
|
||||
// Una linea del scrollback: vector de celdas ya parseadas.
|
||||
using TermLine = std::vector<AnsiCell>;
|
||||
|
||||
// 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<std::string> 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<bool> reader_running{false};
|
||||
|
||||
// Scrollback buffer (protegido por mutex)
|
||||
mutable std::mutex buf_mutex;
|
||||
std::vector<TermLine> 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<bool> 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
|
||||
Reference in New Issue
Block a user