mirror of
				https://github.com/ArthurSonzogni/FTXUI.git
				synced 2025-10-31 02:28:11 +08:00 
			
		
		
		
	Tweak implementation and documentation.
This commit is contained in:
		| @@ -1,5 +1,9 @@ | |||||||
| # POSIX Piped Input in FTXUI | # POSIX Piped Input in FTXUI | ||||||
|  |  | ||||||
|  | > [!WARNING] | ||||||
|  | > This feature works only on Linux and macOS. It is not supported on | ||||||
|  | > Windows and WebAssembly. | ||||||
|  |  | ||||||
| ## What is a POSIX Pipe? | ## What is a POSIX Pipe? | ||||||
|  |  | ||||||
| A POSIX pipe is a way for two separate programs to communicate. One program sends its output directly as input to another program. Think of it like a one-way tube for data. | A POSIX pipe is a way for two separate programs to communicate. One program sends its output directly as input to another program. Think of it like a one-way tube for data. | ||||||
| @@ -40,7 +44,6 @@ auto screen = ScreenInteractive::Fullscreen(); | |||||||
| screen.Loop(component); | screen.Loop(component); | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| **Note:** This feature works only on Linux and macOS. It does nothing on Windows. |  | ||||||
|  |  | ||||||
| ## Turning Off Piped Input | ## Turning Off Piped Input | ||||||
|  |  | ||||||
| @@ -52,4 +55,4 @@ To disable it, call `HandlePipedInput(false)` before starting your application's | |||||||
| auto screen = ScreenInteractive::Fullscreen(); | auto screen = ScreenInteractive::Fullscreen(); | ||||||
| screen.HandlePipedInput(false); // Turn off piped input handling | screen.HandlePipedInput(false); // Turn off piped input handling | ||||||
| screen.Loop(component); | screen.Loop(component); | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ class ScreenInteractive : public Screen { | |||||||
|   static ScreenInteractive TerminalOutput(); |   static ScreenInteractive TerminalOutput(); | ||||||
|  |  | ||||||
|   // Destructor. |   // Destructor. | ||||||
|   ~ScreenInteractive(); |   ~ScreenInteractive() override; | ||||||
|  |  | ||||||
|   // Options. Must be called before Loop(). |   // Options. Must be called before Loop(). | ||||||
|   void TrackMouse(bool enable = true); |   void TrackMouse(bool enable = true); | ||||||
| @@ -101,6 +101,8 @@ class ScreenInteractive : public Screen { | |||||||
|   void Draw(Component component); |   void Draw(Component component); | ||||||
|   void ResetCursorPosition(); |   void ResetCursorPosition(); | ||||||
|  |  | ||||||
|  |   void InstallPipedInputHandling(); | ||||||
|  |  | ||||||
|   void Signal(int signal); |   void Signal(int signal); | ||||||
|  |  | ||||||
|   void FetchTerminalEvents(); |   void FetchTerminalEvents(); | ||||||
| @@ -118,6 +120,7 @@ class ScreenInteractive : public Screen { | |||||||
|                     int dimx, |                     int dimx, | ||||||
|                     int dimy, |                     int dimy, | ||||||
|                     bool use_alternative_screen); |                     bool use_alternative_screen); | ||||||
|  |  | ||||||
|   const Dimension dimension_; |   const Dimension dimension_; | ||||||
|   const bool use_alternative_screen_; |   const bool use_alternative_screen_; | ||||||
|  |  | ||||||
| @@ -142,12 +145,8 @@ class ScreenInteractive : public Screen { | |||||||
|   bool force_handle_ctrl_c_ = true; |   bool force_handle_ctrl_c_ = true; | ||||||
|   bool force_handle_ctrl_z_ = true; |   bool force_handle_ctrl_z_ = true; | ||||||
|  |  | ||||||
| #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) |  | ||||||
|   // Piped input handling state (POSIX only) |   // Piped input handling state (POSIX only) | ||||||
|   bool handle_piped_input_ = true; |   bool handle_piped_input_ = true; | ||||||
|   bool stdin_was_redirected_ = false; |  | ||||||
|   int original_stdin_fd_ = -1; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   // The style of the cursor to restore on exit. |   // The style of the cursor to restore on exit. | ||||||
|   int cursor_reset_shape_ = 1; |   int cursor_reset_shape_ = 1; | ||||||
|   | |||||||
| @@ -373,22 +373,16 @@ void ScreenInteractive::TrackMouse(bool enable) { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// @brief Enable or disable automatic piped input handling. | /// @brief Enable or disable automatic piped input handling. | ||||||
| /// When enabled, FTXUI will detect piped input and redirect stdin to /dev/tty | /// When enabled, FTXUI will detect piped input and redirect stdin from /dev/tty | ||||||
| /// for keyboard input, allowing applications to read piped data while still | /// for keyboard input, allowing applications to read piped data while still | ||||||
| /// receiving interactive keyboard events. | /// receiving interactive keyboard events. | ||||||
| /// @param enable Whether to enable piped input handling. Default is true. | /// @param enable Whether to enable piped input handling. Default is true. | ||||||
| /// @note This must be called before Loop(). | /// @note This must be called before Loop(). | ||||||
| /// @note This feature is enabled by default. | /// @note This feature is enabled by default. | ||||||
| /// @note This feature is only available on POSIX systems (Linux/macOS). | /// @note This feature is only available on POSIX systems (Linux/macOS). | ||||||
| #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) |  | ||||||
| void ScreenInteractive::HandlePipedInput(bool enable) { | void ScreenInteractive::HandlePipedInput(bool enable) { | ||||||
|   handle_piped_input_ = enable; |   handle_piped_input_ = enable; | ||||||
| } | } | ||||||
| #else |  | ||||||
| void ScreenInteractive::HandlePipedInput(bool /*enable*/) { |  | ||||||
|   // This feature is not supported on this platform. |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| /// @brief Add a task to the main loop. | /// @brief Add a task to the main loop. | ||||||
| /// It will be executed later, after every other scheduled tasks. | /// It will be executed later, after every other scheduled tasks. | ||||||
| @@ -676,47 +670,58 @@ void ScreenInteractive::Install() { | |||||||
|   // ensure it is fully applied: |   // ensure it is fully applied: | ||||||
|   Flush(); |   Flush(); | ||||||
|  |  | ||||||
| #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) |   // Redirect the true terminal to stdin, so that we can read keyboard input | ||||||
|   // Handle piped input redirection if explicitly enabled by the application. |   // directly from stdin, even if the input is piped from a file or another | ||||||
|   // This allows applications to read data from stdin while still receiving |   // process. | ||||||
|   // keyboard input from the terminal for interactive use. |   // | ||||||
|   if (handle_piped_input_ && !stdin_was_redirected_ && !isatty(STDIN_FILENO)) { |   // TODO: Instead of redirecting stdin, we could define the file descriptor to | ||||||
|     // Save the current stdin so we can restore it later |   // read from, and use it in the TerminalInputParser. | ||||||
|     original_stdin_fd_ = dup(STDIN_FILENO); |   InstallPipedInputHandling(); | ||||||
|     if (original_stdin_fd_ >= 0) { |  | ||||||
|       // Redirect stdin to the controlling terminal for keyboard input |  | ||||||
|       if (freopen("/dev/tty", "r", stdin) != nullptr) { |  | ||||||
|         stdin_was_redirected_ = true; |  | ||||||
|       } else { |  | ||||||
|         // Failed to open /dev/tty (containers, headless systems, etc.) |  | ||||||
|         // Clean up and continue without redirection |  | ||||||
|         close(original_stdin_fd_); |  | ||||||
|         original_stdin_fd_ = -1; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     // If dup() failed, we silently continue without redirection |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   quit_ = false; |   quit_ = false; | ||||||
|  |  | ||||||
|   PostAnimationTask(); |   PostAnimationTask(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void ScreenInteractive::InstallPipedInputHandling() { | ||||||
|  | #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) | ||||||
|  |   // Handle piped input redirection if explicitly enabled by the application. | ||||||
|  |   // This allows applications to read data from stdin while still receiving | ||||||
|  |   // keyboard input from the terminal for interactive use. | ||||||
|  |   if (!handle_piped_input_) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // If stdin is a terminal, we don't need to redirect it. | ||||||
|  |   if (isatty(STDIN_FILENO)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Save the current stdin so we can restore it later. | ||||||
|  |   int original_fd = dup(STDIN_FILENO); | ||||||
|  |   if (original_fd < 0) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Redirect stdin to the controlling terminal for keyboard input. | ||||||
|  |   if (std::freopen("/dev/tty", "r", stdin) == nullptr) { | ||||||
|  |     // Failed to open /dev/tty (containers, headless systems, etc.) | ||||||
|  |     // Clean up and continue without redirection | ||||||
|  |     close(original_fd); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // Restore the original stdin file descriptor on exit. | ||||||
|  |   on_exit_functions.emplace([=] { | ||||||
|  |     dup2(original_fd, STDIN_FILENO); | ||||||
|  |     close(original_fd); | ||||||
|  |   }); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
| // private | // private | ||||||
| void ScreenInteractive::Uninstall() { | void ScreenInteractive::Uninstall() { | ||||||
|   ExitNow(); |   ExitNow(); | ||||||
|    |  | ||||||
| #if !defined(_WIN32) && !defined(__EMSCRIPTEN__) |  | ||||||
|   // Restore stdin to its original state if we redirected it |  | ||||||
|   if (stdin_was_redirected_ && original_stdin_fd_ >= 0) { |  | ||||||
|     dup2(original_stdin_fd_, STDIN_FILENO); |  | ||||||
|     close(original_stdin_fd_); |  | ||||||
|     original_stdin_fd_ = -1; |  | ||||||
|     stdin_was_redirected_ = false; |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
|    |  | ||||||
|   OnExit(); |   OnExit(); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ArthurSonzogni
					ArthurSonzogni