a3928cf2db
- Demo 1 (one-shot): auto-launch al primer frame, sin boton Run. Linux: bash; Windows: cmd.exe con "echo hello && date /t && dir /b %TEMP%" - Demo 2 (interactive): auto-launch al entrar al tab. Sin boton Open shell. Linux: bash; Windows: cmd.exe. Boton Close/Reopen post-exit. - Demo 3 (readonly): Linux tail -f igual que antes. Windows: panel readonly con cmd.exe one-shot + mensaje informativo. - shell_exists() helper para mostrar error si el binario no existe. - Render: fondo negro + prompt "$ " implementados en terminal_panel.cpp. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
301 lines
9.1 KiB
C++
301 lines
9.1 KiB
C++
// demos_terminal.cpp — demos del modulo terminal_panel (issue 0132).
|
|
//
|
|
// Demo 1: One-shot — auto-launch al primer frame. Cross-platform:
|
|
// Linux → bash "echo hello; date; ls /tmp | head"
|
|
// Windows → cmd.exe "echo hello && date /t && dir /b %TEMP%"
|
|
// Demo 2: Interactivo — auto-launch al entrar al tab.
|
|
// Linux → bash -i
|
|
// Windows → cmd.exe
|
|
// Demo 3: Readonly — tail simulado en Linux / mensaje en Windows.
|
|
//
|
|
// Cambios issue 0132:
|
|
// - Fondo negro + prompt input: en terminal_panel.cpp.
|
|
// - Cross-platform: _WIN32 en todos los demos.
|
|
// - Auto-launch: sin boton "Open shell" / "Run" en Demo 1 y Demo 2.
|
|
|
|
#include "demos.h"
|
|
#include "demo.h"
|
|
#include "viz/terminal_panel/terminal_panel.h"
|
|
#include "imgui.h"
|
|
|
|
#include <cstring>
|
|
#include <string>
|
|
|
|
// Para verificar que el binario del shell existe antes de abrir.
|
|
#ifdef _WIN32
|
|
# ifndef WIN32_LEAN_AND_MEAN
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# endif
|
|
# include <windows.h>
|
|
static bool shell_exists(const char* path) {
|
|
return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
|
|
}
|
|
#else
|
|
# include <unistd.h>
|
|
static bool shell_exists(const char* path) {
|
|
return access(path, X_OK) == 0;
|
|
}
|
|
#endif
|
|
|
|
namespace gallery {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Demo 1: One-shot con auto-launch
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void demo_terminal_oneshot() {
|
|
demo_header("terminal_panel", "v1.0.0",
|
|
"Emulador TTY embebible. Demo 1: one-shot — auto-launch al primer frame, cross-platform.");
|
|
|
|
static fn_term::TerminalPanel s_term;
|
|
static bool s_launched = false;
|
|
static bool s_shell_missing = false;
|
|
|
|
if (!s_launched && !s_shell_missing) {
|
|
#ifdef _WIN32
|
|
const char* sh = "cmd.exe";
|
|
const char* cmd = "echo hello && date /t && dir /b %TEMP% 2>nul\r\n";
|
|
#else
|
|
const char* sh = "/bin/bash";
|
|
const char* cmd = "echo hello; date; ls /tmp | head -5; exit 0\n";
|
|
#endif
|
|
if (!shell_exists(sh)) {
|
|
s_shell_missing = true;
|
|
} else {
|
|
s_term.shell = sh;
|
|
s_term.scrollback_lines = 200;
|
|
s_term.readonly = true;
|
|
fn_term::open(s_term);
|
|
fn_term::send(s_term, cmd);
|
|
s_launched = true;
|
|
}
|
|
}
|
|
|
|
if (s_shell_missing) {
|
|
#ifdef _WIN32
|
|
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
|
|
"Shell not available: cmd.exe");
|
|
#else
|
|
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
|
|
"Shell not available: /bin/bash");
|
|
#endif
|
|
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_launched = false;
|
|
s_shell_missing = false;
|
|
}
|
|
|
|
code_block(
|
|
#ifdef _WIN32
|
|
"fn_term::TerminalPanel term;\n"
|
|
"term.shell = \"cmd.exe\";\n"
|
|
"term.readonly = true;\n"
|
|
"fn_term::open(term);\n"
|
|
"fn_term::send(term, \"echo hello && date /t\\r\\n\");\n"
|
|
"// En cada frame ImGui:\n"
|
|
"fn_term::render(term);\n"
|
|
"if (term.process_exited.load()) fn_term::close(term);"
|
|
#else
|
|
"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);"
|
|
#endif
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Demo 2: Interactivo con auto-launch
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void demo_terminal_interactive() {
|
|
demo_header("terminal_panel", "v1.0.0",
|
|
"Demo 2: terminal interactivo — auto-launch al entrar al tab. "
|
|
"Linux: bash -i / Windows: cmd.exe.");
|
|
|
|
static fn_term::TerminalPanel s_term;
|
|
static bool s_launched = false;
|
|
static bool s_shell_missing = false;
|
|
|
|
// Auto-launch en el primer frame que se renderiza esta demo.
|
|
if (!s_launched && !s_shell_missing) {
|
|
#ifdef _WIN32
|
|
const char* sh = "cmd.exe";
|
|
#else
|
|
const char* sh = "/bin/bash";
|
|
#endif
|
|
if (!shell_exists(sh)) {
|
|
s_shell_missing = true;
|
|
} else {
|
|
s_term.shell = sh;
|
|
s_term.scrollback_lines = 1000;
|
|
s_term.readonly = false;
|
|
fn_term::open(s_term);
|
|
s_launched = true;
|
|
}
|
|
}
|
|
|
|
if (s_shell_missing) {
|
|
#ifdef _WIN32
|
|
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
|
|
"Shell not available: cmd.exe");
|
|
#else
|
|
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
|
|
"Shell not available: /bin/bash");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
fn_term::render(s_term);
|
|
|
|
if (s_term.is_open()) {
|
|
ImGui::Spacing();
|
|
if (ImGui::Button("Close shell")) {
|
|
fn_term::close(s_term);
|
|
s_launched = false;
|
|
}
|
|
} else if (s_launched) {
|
|
ImGui::TextDisabled("[shell exited — click Reopen]");
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Reopen")) {
|
|
s_launched = false;
|
|
s_shell_missing = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Demo 3: Readonly tail (Linux) / mensaje informativo (Windows)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void demo_terminal_readonly() {
|
|
demo_header("terminal_panel", "v1.0.0",
|
|
"Demo 3: readonly — "
|
|
#ifdef _WIN32
|
|
"demo disponible solo en Linux (tail -f). En Windows se muestra un mensaje.");
|
|
#else
|
|
"tail de /tmp/fn_gallery_test.log (creado al abrir).");
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
// En Windows nativo, tail -f no existe y /tmp tampoco.
|
|
// Mostramos un panel readonly con mensaje estático para seguir ejercitando el render.
|
|
static fn_term::TerminalPanel s_term;
|
|
static bool s_launched = false;
|
|
|
|
if (!s_launched) {
|
|
s_term.shell = "cmd.exe";
|
|
s_term.readonly = true;
|
|
s_term.scrollback_lines = 100;
|
|
fn_term::open(s_term);
|
|
// Imprimir unas líneas de muestra y terminar el proceso.
|
|
fn_term::send(s_term,
|
|
"echo [terminal_panel demo 3 - readonly] && "
|
|
"echo This demo uses tail -f on Linux. && "
|
|
"echo On Windows the panel renders static output. && "
|
|
"exit 0\r\n");
|
|
s_launched = true;
|
|
}
|
|
|
|
fn_term::render(s_term);
|
|
|
|
if (ImGui::Button("Reset demo")) {
|
|
if (s_term.is_open()) fn_term::close(s_term);
|
|
s_launched = false;
|
|
}
|
|
#else
|
|
static fn_term::TerminalPanel s_term;
|
|
static bool s_log_created = false;
|
|
|
|
if (!s_log_created) {
|
|
if (ImGui::Button("Start tail")) {
|
|
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);"
|
|
);
|
|
#endif
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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
|