feat(0132): add terminal_panel demo (oneshot, interactive, readonly)
Add demos_terminal.cpp con 3 demos del modulo terminal_panel: - Demo 1: one-shot spawn bash, echo hello; date; ls /tmp - Demo 2: terminal interactivo bash/cmd.exe con input box - Demo 3: readonly tail -f con boton append Actualiza CMakeLists.txt con ansi_parser + terminal_panel + -lutil (Linux). Actualiza demos.h + main.cpp con entry "Terminal" en el sidebar. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,12 @@ add_imgui_app(primitives_gallery
|
||||
${CMAKE_SOURCE_DIR}/functions/gfx/mesh_gpu.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/core/orbit_camera.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/viz/mesh_viewer.cpp
|
||||
# terminal_panel module (issue 0132)
|
||||
demos_terminal.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/core/ansi_parser.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/viz/terminal_panel/terminal_panel.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/viz/terminal_panel/terminal_panel_linux.cpp
|
||||
${CMAKE_SOURCE_DIR}/functions/viz/terminal_panel/terminal_panel_windows.cpp
|
||||
)
|
||||
target_include_directories(primitives_gallery PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/vendor/imgui_text_edit
|
||||
@@ -108,3 +114,8 @@ endif()
|
||||
if(WIN32)
|
||||
set_target_properties(primitives_gallery PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
endif()
|
||||
|
||||
# terminal_panel: forkpty requiere -lutil en Linux.
|
||||
if(NOT WIN32)
|
||||
target_link_libraries(primitives_gallery PRIVATE util)
|
||||
endif()
|
||||
|
||||
@@ -53,4 +53,7 @@ void demo_shader_canvas();
|
||||
void demo_gl_texture(); // wave 1, issue 0026
|
||||
void demo_gl_info(); // issue 0049b — runtime GL version + 4.3 caps
|
||||
|
||||
// --- Terminal (issue 0132) ---
|
||||
void demo_terminal();
|
||||
|
||||
} // namespace gallery
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
// demos_terminal.cpp — demos del modulo terminal_panel (issue 0132).
|
||||
//
|
||||
// Demo 1: One-shot — spawn bash que ejecuta "echo hello; date; ls /tmp | head"
|
||||
// con auto-close cuando el proceso termina. Muestra el scrollback.
|
||||
// Demo 2: Interactivo — bash -i (Linux) / cmd.exe (Windows) con input box.
|
||||
// Demo 3: Readonly — tail simulado de un archivo de log.
|
||||
|
||||
#include "demos.h"
|
||||
#include "demo.h"
|
||||
#include "viz/terminal_panel/terminal_panel.h"
|
||||
#include "imgui.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace gallery {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Demo 1: One-shot con auto-close
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void demo_terminal_oneshot() {
|
||||
demo_header("terminal_panel", "v1.0.0",
|
||||
"Emulador TTY embebible. Demo 1: one-shot — spawn bash, ejecuta comandos, auto-close.");
|
||||
|
||||
static fn_term::TerminalPanel s_term;
|
||||
static bool s_started = false;
|
||||
|
||||
if (!s_started) {
|
||||
ImGui::TextUnformatted("Pulsa 'Run' para lanzar el one-shot.");
|
||||
if (ImGui::Button("Run")) {
|
||||
if (!s_term.is_open()) {
|
||||
s_term.shell = "/bin/bash";
|
||||
s_term.scrollback_lines = 200;
|
||||
s_term.readonly = true; // solo output, sin input
|
||||
fn_term::open(s_term);
|
||||
fn_term::send(s_term, "echo hello; date; ls /tmp | head -5; exit 0\n");
|
||||
s_started = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn_term::render(s_term);
|
||||
|
||||
if (s_term.process_exited.load() && s_term.is_open()) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("[done — exit %d]", s_term.exit_code);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Reset demo")) {
|
||||
if (s_term.is_open()) fn_term::close(s_term);
|
||||
s_started = false;
|
||||
}
|
||||
|
||||
code_block(
|
||||
"fn_term::TerminalPanel term;\n"
|
||||
"term.shell = \"/bin/bash\";\n"
|
||||
"term.readonly = true;\n"
|
||||
"fn_term::open(term);\n"
|
||||
"fn_term::send(term, \"echo hello; date; exit 0\\n\");\n"
|
||||
"// En cada frame ImGui:\n"
|
||||
"fn_term::render(term);\n"
|
||||
"if (term.process_exited.load()) fn_term::close(term);"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Demo 2: Interactivo
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void demo_terminal_interactive() {
|
||||
demo_header("terminal_panel", "v1.0.0",
|
||||
"Demo 2: terminal interactivo — bash -i (Linux) / cmd.exe (Windows).");
|
||||
|
||||
static fn_term::TerminalPanel s_term;
|
||||
|
||||
if (!s_term.is_open()) {
|
||||
if (ImGui::Button("Open shell")) {
|
||||
#ifdef _WIN32
|
||||
s_term.shell = "cmd.exe";
|
||||
#else
|
||||
s_term.shell = "/bin/bash";
|
||||
#endif
|
||||
s_term.scrollback_lines = 1000;
|
||||
s_term.readonly = false;
|
||||
fn_term::open(s_term);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn_term::render(s_term);
|
||||
|
||||
if (ImGui::Button("Close shell")) {
|
||||
fn_term::close(s_term);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Demo 3: Readonly tail
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void demo_terminal_readonly() {
|
||||
demo_header("terminal_panel", "v1.0.0",
|
||||
"Demo 3: readonly — tail de /tmp/fn_gallery_test.log (creado al abrir).");
|
||||
|
||||
static fn_term::TerminalPanel s_term;
|
||||
static bool s_log_created = false;
|
||||
|
||||
// Crear el archivo de log de prueba si no existe.
|
||||
if (!s_log_created) {
|
||||
if (ImGui::Button("Start tail")) {
|
||||
// Escribir unas líneas de muestra al log.
|
||||
FILE* f = fopen("/tmp/fn_gallery_test.log", "w");
|
||||
if (f) {
|
||||
for (int i = 0; i < 5; i++)
|
||||
fprintf(f, "[sample] log line %d\n", i);
|
||||
fclose(f);
|
||||
}
|
||||
s_term.shell = "/bin/bash";
|
||||
s_term.readonly = true;
|
||||
s_term.scrollback_lines = 500;
|
||||
fn_term::open(s_term);
|
||||
fn_term::send(s_term, "tail -f /tmp/fn_gallery_test.log\n");
|
||||
s_log_created = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn_term::render(s_term);
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Append line")) {
|
||||
FILE* f = fopen("/tmp/fn_gallery_test.log", "a");
|
||||
if (f) {
|
||||
static int s_count = 0;
|
||||
fprintf(f, "[appended] line %d\n", s_count++);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Stop")) {
|
||||
if (s_term.is_open()) fn_term::close(s_term);
|
||||
s_log_created = false;
|
||||
}
|
||||
|
||||
code_block(
|
||||
"fn_term::TerminalPanel term;\n"
|
||||
"term.shell = \"/bin/bash\";\n"
|
||||
"term.readonly = true; // sin input box\n"
|
||||
"fn_term::open(term);\n"
|
||||
"fn_term::send(term, \"tail -f /tmp/agent.log\\n\");\n"
|
||||
"// En cada frame:\n"
|
||||
"fn_term::render(term);"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Entry point unificado (llamado desde main.cpp via demos.h)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void demo_terminal() {
|
||||
static int s_tab = 0;
|
||||
if (ImGui::BeginTabBar("##term_tabs")) {
|
||||
if (ImGui::BeginTabItem("One-shot")) {
|
||||
s_tab = 0;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Interactive")) {
|
||||
s_tab = 1;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Readonly tail")) {
|
||||
s_tab = 2;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
switch (s_tab) {
|
||||
case 0: demo_terminal_oneshot(); break;
|
||||
case 1: demo_terminal_interactive(); break;
|
||||
case 2: demo_terminal_readonly(); break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gallery
|
||||
@@ -81,6 +81,8 @@ static const DemoEntry k_demos[] = {
|
||||
{"shader_canvas", "shader_canvas", "Gfx", &gallery::demo_shader_canvas},
|
||||
{"gl_texture", "gl_texture_load", "Gfx", &gallery::demo_gl_texture}, // wave 1
|
||||
{"gl_info", "gl_info", "Gfx", &gallery::demo_gl_info}, // issue 0049b
|
||||
// Terminal (issue 0132)
|
||||
{"terminal_panel", "terminal_panel", "Terminal", &gallery::demo_terminal},
|
||||
};
|
||||
static constexpr int k_demo_count = sizeof(k_demos) / sizeof(k_demos[0]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user