@@ -114,9 +114,17 @@ bool unicode_capable();
114114// the space bar, and `wasd` as a second set of arrows.
115115enum class Key { Up, Down, Left, Right, Space };
116116
117+ // The keys in `pending`, the bytes a POSIX terminal sent: an arrow is
118+ // `ESC [ A` to `ESC [ D`, `ESC O A` to `ESC O D` in application mode, or
119+ // `ESC [ 1 ; 5 A` and the like with a modifier; a sequence for any other key
120+ // is skipped whole. An incomplete sequence at the end is left in `pending`.
121+ std::vector<Key> decode_keys (std::string& pending);
122+
117123// KEYS READ FROM THE TERMINAL FOR THE LIFETIME OF THE OBJECT, without echo and
118124// without waiting for a line end; Ctrl-C still interrupts. Active only when
119- // standard input and standard output are both terminals. The mode the
125+ // standard input and standard output are both terminals and, on POSIX, mcpp
126+ // is in the terminal's foreground process group: a background job that
127+ // changed the terminal's mode would be stopped by SIGTTOU. The mode the
120128// terminal had is restored when the object is destroyed, and by the signal
121129// handler if a signal ends mcpp first (POSIX: `unixproc::guard_terminal_mode`;
122130// Windows: a console control handler).
@@ -368,28 +376,32 @@ BOOL WINAPI restore_input_mode(DWORD) {
368376}
369377#endif
370378
371- // The keys in `bytes` (POSIX): an arrow is `ESC [ A` to `ESC [ D`, or with
372- // `O` in place of `[` in application mode. An incomplete sequence at the end
373- // is left in `pending`.
379+ } // namespace
380+
374381std::vector<Key> decode_keys (std::string& pending) {
375382 std::vector<Key> keys;
376383 std::size_t i = 0 ;
377384 while (i < pending.size ()) {
378385 const char c = pending[i];
379386 if (c == ' \x1b ' ) {
380- if (i + 2 >= pending.size ()) break ; // wait for the rest
381- if (pending[i + 1 ] == ' [' || pending[i + 1 ] == ' O' ) {
382- switch (pending[i + 2 ]) {
383- case ' A' : keys.push_back (Key::Up); break ;
384- case ' B' : keys.push_back (Key::Down); break ;
385- case ' C' : keys.push_back (Key::Right); break ;
386- case ' D' : keys.push_back (Key::Left); break ;
387- default : break ;
388- }
389- i += 3 ;
390- continue ;
387+ if (i + 1 >= pending.size ()) break ; // wait for the rest
388+ if (pending[i + 1 ] != ' [' && pending[i + 1 ] != ' O' ) { ++i; continue ; }
389+ // Parameter and intermediate bytes (0x20 to 0x3F), then one final
390+ // byte (0x40 to 0x7E) that names the key.
391+ std::size_t end = i + 2 ;
392+ while (end < pending.size ()
393+ && static_cast <unsigned char >(pending[end]) >= 0x20
394+ && static_cast <unsigned char >(pending[end]) <= 0x3F )
395+ ++end;
396+ if (end >= pending.size ()) break ; // wait for the final byte
397+ switch (pending[end]) {
398+ case ' A' : keys.push_back (Key::Up); break ;
399+ case ' B' : keys.push_back (Key::Down); break ;
400+ case ' C' : keys.push_back (Key::Right); break ;
401+ case ' D' : keys.push_back (Key::Left); break ;
402+ default : break ;
391403 }
392- ++i ;
404+ i = end + 1 ;
393405 continue ;
394406 }
395407 switch (c) {
@@ -406,8 +418,6 @@ std::vector<Key> decode_keys(std::string& pending) {
406418 return keys;
407419}
408420
409- } // namespace
410-
411421KeyInput::KeyInput () {
412422 if (!is_terminal (Stream::Out)) return ;
413423#if defined(_WIN32)
@@ -422,7 +432,7 @@ KeyInput::KeyInput() {
422432 if (!::SetConsoleMode (h, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT ))) return ;
423433 active_ = true ;
424434#elif defined(__unix__) || defined(__APPLE__)
425- if (::isatty (0 ) == 0 ) return ;
435+ if (::isatty (0 ) == 0 || :: tcgetpgrp ( 0 ) != :: getpgrp () ) return ;
426436 struct termios mode{};
427437 if (::tcgetattr (0 , &mode) != 0 ) return ;
428438 mcpp::platform::unixproc::guard_terminal_mode (0 );
0 commit comments