diff --git a/demos_terminal.cpp b/demos_terminal.cpp index 370211f..9186e24 100644 --- a/demos_terminal.cpp +++ b/demos_terminal.cpp @@ -1,9 +1,17 @@ // 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. +// 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" @@ -13,31 +21,64 @@ #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-close +// 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 — spawn bash, ejecuta comandos, auto-close."); + "Emulador TTY embebible. Demo 1: one-shot — auto-launch al primer frame, cross-platform."); static fn_term::TerminalPanel s_term; - static bool s_started = false; + static bool s_launched = false; + static bool s_shell_missing = 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; - } + 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; } @@ -50,10 +91,21 @@ void demo_terminal_oneshot() { if (ImGui::Button("Reset demo")) { if (s_term.is_open()) fn_term::close(s_term); - s_started = false; + 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" @@ -62,55 +114,115 @@ void demo_terminal_oneshot() { "// En cada frame ImGui:\n" "fn_term::render(term);\n" "if (term.process_exited.load()) fn_term::close(term);" +#endif ); } // --------------------------------------------------------------------------- -// Demo 2: Interactivo +// Demo 2: Interactivo con auto-launch // --------------------------------------------------------------------------- void demo_terminal_interactive() { demo_header("terminal_panel", "v1.0.0", - "Demo 2: terminal interactivo — bash -i (Linux) / cmd.exe (Windows)."); + "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; - if (!s_term.is_open()) { - if (ImGui::Button("Open shell")) { + // Auto-launch en el primer frame que se renderiza esta demo. + if (!s_launched && !s_shell_missing) { #ifdef _WIN32 - s_term.shell = "cmd.exe"; + const char* sh = "cmd.exe"; #else - s_term.shell = "/bin/bash"; + 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 (ImGui::Button("Close shell")) { - fn_term::close(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 +// Demo 3: Readonly tail (Linux) / mensaje informativo (Windows) // --------------------------------------------------------------------------- void demo_terminal_readonly() { demo_header("terminal_panel", "v1.0.0", - "Demo 3: readonly — tail de /tmp/fn_gallery_test.log (creado al abrir)."); + "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; - // 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++) @@ -153,6 +265,7 @@ void demo_terminal_readonly() { "// En cada frame:\n" "fn_term::render(term);" ); +#endif } // ---------------------------------------------------------------------------