1313// limitations under the License.
1414
1515#include < thread>
16+ #include < cstdio>
1617
1718#if defined(_WIN32) || defined(_WIN64)
1819 #define NOMINMAX
2829#include < cpp_utils/exception/InitializationException.hpp>
2930#include < cpp_utils/Log.hpp>
3031
32+ namespace {
33+ #if defined(_WIN32) || defined(_WIN64)
34+ inline bool consume_tilde_if_present ()
35+ {
36+ // Windows special keys don’t have a trailing '~'
37+ return true ;
38+ }
39+
40+ #else
41+ inline bool consume_tilde_if_present ()
42+ {
43+ // POSIX: ESC [ <num> ~ → read and verify '~'
44+ int t = ::getchar ();
45+ return t == ' ~' ;
46+ }
47+
48+ #endif // if defined(_WIN32) || defined(_WIN64)
49+ } // namespace
50+
3151namespace eprosima {
3252namespace utils {
3353namespace event {
@@ -256,19 +276,19 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
256276 std::string read_str;
257277 size_t cursor_index = 0 ;
258278#if defined(_WIN32) || defined(_WIN64)
259- bool use_getchar = GetFileType (GetStdHandle (STD_INPUT_HANDLE )) == FILE_TYPE_PIPE ;
279+ bool use_::getchar = GetFileType (GetStdHandle (STD_INPUT_HANDLE )) == FILE_TYPE_PIPE ;
260280#endif // if defined(_WIN32) || defined(_WIN64)
261281 while (true )
262282 {
263283
264284#if defined(_WIN32) || defined(_WIN64)
265285 int c;
266286 // Read first character
267- // Use getchar() for pipes to avoid blocking
287+ // Use :: getchar() for pipes to avoid blocking
268288 // Use _getch() for console input to avoid echoing characters
269- if (use_getchar )
289+ if (use_::getchar )
270290 {
271- c = getchar ();
291+ c = :: getchar ();
272292 }
273293 else
274294 {
@@ -282,11 +302,11 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
282302 case 72 : // Arrow up
283303#else
284304 char c;
285- c = getchar (); // Read first character
305+ c = :: getchar (); // Read first character
286306 if (c == ' \033 ' )
287307 {
288- getchar (); // Ignore next character '['
289- switch (getchar ())
308+ :: getchar (); // Ignore next character '['
309+ switch (:: getchar ())
290310 {
291311 case ' A' :
292312#endif // if defined(_WIN32) || defined(_WIN64)
@@ -375,6 +395,122 @@ void StdinEventHandler::stdin_listener_thread_routine_() noexcept
375395 break ;
376396 }
377397
398+ #if defined(_WIN32) || defined(_WIN64)
399+ case 82 : // Insert
400+ #else
401+ case ' 2' : // Insert: ESC [ 2 ~
402+ #endif // if defined(_WIN32) || defined(_WIN64)
403+ {
404+ int c3 = ::getchar (); (void )c3; // swallow trailing '~'
405+ // Minimal behavior: do nothing
406+ break ;
407+ }
408+
409+ #if defined(_WIN32) || defined(_WIN64)
410+ case 83 : // Supr
411+ #else
412+ case ' 3' : // Supr: ESC [ 3 ~
413+ #endif // if defined(_WIN32) || defined(_WIN64)
414+ {
415+ if (consume_tilde_if_present ())
416+ {
417+ // delete-forward (char at cursor), if any
418+ if (cursor_index < read_str.size ())
419+ {
420+ update_line (" >> " , read_str, cursor_index,
421+ [](std::string& line, size_t & index)
422+ {
423+ if (index < line.size ())
424+ {
425+ line.erase (line.begin () + index);
426+ }
427+ });
428+ }
429+ }
430+ // do nothing.
431+ break ;
432+ }
433+
434+ #if defined(_WIN32) || defined(_WIN64)
435+ case 73 : // Page Up
436+ #else
437+ case ' 5' : // Page Up: ESC [ 5 ~ (history prev)
438+ #endif // if defined(_WIN32) || defined(_WIN64)
439+ {
440+ if (consume_tilde_if_present ())
441+ {
442+ std::string prev = history_handler_.get_previous_command ();
443+ if (!prev.empty ())
444+ {
445+ std::string prompt = " >> " ;
446+ int term_width = get_terminal_width ();
447+ int lines = compute_lines_needed (prompt, read_str, term_width);
448+ clear_lines (lines);
449+ read_str = prev;
450+ cursor_index = read_str.size ();
451+ std::cout << " \033 [38;5;82m" << prompt << " \033 [0m" << read_str << std::flush;
452+ }
453+ }
454+ break ;
455+ }
456+
457+
458+ #if defined(_WIN32) || defined(_WIN64)
459+ case 81 : // Page Down
460+ #else
461+ case ' 6' : // Page Down: ESC [ 6 ~ (history next)
462+ #endif // if defined(_WIN32) || defined(_WIN64)
463+ {
464+ if (consume_tilde_if_present ())
465+ {
466+ std::string next = history_handler_.get_next_command ();
467+ std::string prompt = " >> " ;
468+ int term_width = get_terminal_width ();
469+ int lines = compute_lines_needed (prompt, read_str, term_width);
470+ clear_lines (lines);
471+ if (!next.empty ())
472+ {
473+ read_str = next;
474+ cursor_index = read_str.size ();
475+ std::cout << " \033 [38;5;82m" << prompt << " \033 [0m" << read_str << std::flush;
476+ }
477+ else
478+ {
479+ read_str.clear ();
480+ cursor_index = 0 ;
481+ std::cout << " \033 [38;5;82m" << prompt << " \033 [0m" << std::flush;
482+ }
483+ }
484+ break ;
485+ }
486+
487+ #if defined(_WIN32) || defined(_WIN64)
488+ case 71 : // Home
489+ #else
490+ case ' H' : // Home: ESC [ H
491+ #endif // if defined(_WIN32) || defined(_WIN64)
492+ {
493+ update_line (" >> " , read_str, cursor_index,
494+ [](std::string&, size_t & index)
495+ {
496+ index = 0 ;
497+ });
498+ break ;
499+ }
500+
501+ #if defined(_WIN32) || defined(_WIN64)
502+ case 79 : // End
503+ #else
504+ case ' F' : // End: ESC [ F
505+ #endif // if defined(_WIN32) || defined(_WIN64)
506+ {
507+ update_line (" >> " , read_str, cursor_index,
508+ [](std::string& line, size_t & index)
509+ {
510+ index = line.size ();
511+ });
512+ break ;
513+ }
378514 }
379515 }
380516 else if (c == ' \n ' || c == ' \r ' )
0 commit comments