// 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 #include // 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 static bool shell_exists(const char* path) { return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES; } #else # include 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