diff --git a/ChangeLog b/ChangeLog index 46d762d8..22dc10a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,14 @@ +2016-11-20 Markus Gans + * Improve tty settings + 2016-11-13 Markus Gans * Do not draw shadows on a linux console if not all characters are available 2016-11-12 Markus Gans * Better support for Linux terminals with 8 colors - * Optimized input cursor positioning for terminals without hidden cursor + * Optimized input cursor positioning for terminals without + hidden cursor * Switch locale name from "en_US" to "C" * Fix FString toLong() diff --git a/src/fapp.cpp b/src/fapp.cpp index 0b331aa8..83856cf6 100644 --- a/src/fapp.cpp +++ b/src/fapp.cpp @@ -33,7 +33,7 @@ FApplication::eventQueue* FApplication::event_queue = 0; // constructors and destructor //---------------------------------------------------------------------- -FApplication::FApplication (int& _argc, char**& _argv) +FApplication::FApplication (int& _argc, char* _argv[]) : FWidget(0) , app_argc(_argc) , app_argv(_argv) @@ -59,8 +59,8 @@ FApplication::FApplication (int& _argc, char**& _argv) if ( ! (_argc && _argv) ) { static char* empty = const_cast(""); - _argc = 0; - _argv = static_cast(&empty); + app_argc = 0; + app_argv = static_cast(&empty); } init(); diff --git a/src/fapp.h b/src/fapp.h index 8f05a330..3d743fbc 100644 --- a/src/fapp.h +++ b/src/fapp.h @@ -55,7 +55,7 @@ class FApplication : public FWidget { public: // Constructor - FApplication (int&, char**& ); + FApplication (int&, char*[]); // Destructor virtual ~FApplication(); diff --git a/src/fstring.cpp b/src/fstring.cpp index 86dbc4ef..1abdacc1 100644 --- a/src/fstring.cpp +++ b/src/fstring.cpp @@ -690,7 +690,7 @@ long FString::toLong() const p++; } - while ( std::iswdigit(*p) ) + while ( std::iswdigit(wint_t(*p)) ) { register uChar d = uChar((*p) - L'0'); @@ -704,7 +704,7 @@ long FString::toLong() const p++; } - if ( *p != L'\0' && ! std::iswdigit(*p) ) + if ( *p != L'\0' && ! std::iswdigit(wint_t(*p)) ) throw std::invalid_argument ("no valid number"); return num; @@ -736,7 +736,7 @@ uLong FString::toULong() const p++; } - while ( std::iswdigit(*p) ) + while ( std::iswdigit(wint_t(*p)) ) { register uChar d = uChar((*p) - L'0'); @@ -750,7 +750,7 @@ uLong FString::toULong() const p++; } - if ( *p != L'\0' && ! std::iswdigit(*p) ) + if ( *p != L'\0' && ! std::iswdigit(wint_t(*p)) ) throw std::invalid_argument ("no valid number"); return num; diff --git a/src/fterm.cpp b/src/fterm.cpp index 26fb61e4..ebb3a149 100644 --- a/src/fterm.cpp +++ b/src/fterm.cpp @@ -128,6 +128,14 @@ FTerm::~FTerm() // destructor // public methods of FTerm +//---------------------------------------------------------------------- +termios FTerm::getTTY() +{ + struct termios t; + tcgetattr (stdin_no, &t); + return t; +} + //---------------------------------------------------------------------- int FTerm::getLineNumber() { @@ -228,52 +236,77 @@ void FTerm::setConsoleCursor (fc::consoleCursorStyle style, bool hidden) std::fflush(stdout); } +//---------------------------------------------------------------------- +void FTerm::setTTY (termios& t) +{ + tcsetattr (stdin_no, TCSADRAIN, &t); +} + +//---------------------------------------------------------------------- +void FTerm::noHardwareEcho() +{ + // Info under: man 3 termios + struct termios t; + tcgetattr (stdin_no, &t); + + // local mode + t.c_lflag &= uInt(~(ECHO | ECHONL)); + + // input mode + t.c_iflag &= uInt(~(ICRNL | INLCR | IGNCR)); + + // output mode + t.c_oflag &= uInt(~ONLCR); + + // set the new termios settings + setTTY (t); +} + //---------------------------------------------------------------------- bool FTerm::setRawMode (bool on) { + // set + unset flags for raw mode if ( on == raw_mode ) return raw_mode; - std::fflush(stdout); - if ( on ) { // Info under: man 3 termios struct termios t; tcgetattr (stdin_no, &t); - /* set + unset flags for raw mode */ - // input mode - t.c_iflag &= uInt(~(IGNBRK | BRKINT | PARMRK | ISTRIP - | INLCR | IGNCR | ICRNL | IXON)); - // output mode - t.c_oflag &= uInt(~OPOST); - // local mode #if DEBUG // Exit with ctrl-c only if compiled with "DEBUG" option - t.c_lflag &= uInt(~(ECHO | ECHONL | ICANON | IEXTEN)); + t.c_lflag &= uInt(~(ICANON | IEXTEN)); #else - // Plus disable signals. - t.c_lflag &= uInt(~(ECHO | ECHONL | ICANON | IEXTEN | ISIG)); + t.c_lflag &= uInt(~(ICANON | ISIG | IEXTEN)); #endif - // control mode - t.c_cflag &= uInt(~(CSIZE | PARENB)); - t.c_cflag |= uInt(CS8); + // input mode + t.c_iflag &= uInt(~(IXON | BRKINT | PARMRK)); // defines the terminal special characters for noncanonical read t.c_cc[VTIME] = 0; // Timeout in deciseconds t.c_cc[VMIN] = 1; // Minimum number of characters // set the new termios settings - tcsetattr (stdin_no, TCSAFLUSH, &t); + setTTY (t); raw_mode = true; } else { - // restore termios settings - tcsetattr (stdin_no, TCSAFLUSH, &term_init); + struct termios t; + tcgetattr (stdin_no, &t); + + // local mode + t.c_lflag |= uInt(ISIG | ICANON | (term_init.c_lflag & IEXTEN)); + + // input mode + t.c_iflag |= uInt(IXON | BRKINT | PARMRK); + + // set the new termios settings + setTTY (t); raw_mode = false; } @@ -328,7 +361,7 @@ bool FTerm::setNonBlockingInput (bool on) } //---------------------------------------------------------------------- -int FTerm::parseKeyString ( char* buffer +int FTerm::parseKeyString ( char buffer[] , int buf_size , timeval* time_keypressed ) { @@ -762,24 +795,32 @@ const FString FTerm::getXTermFont() if ( xterm_terminal || screen_terminal || FTermcap::osc_support ) { - if ( raw_mode && non_blocking_stdin ) + fd_set ifds; + struct timeval tv; + char temp[150] = {}; + + oscPrefix(); + putstring (OSC "50;?" BEL); // get font + oscPostfix(); + std::fflush(stdout); + + FD_ZERO(&ifds); + FD_SET(stdin_no, &ifds); + tv.tv_sec = 0; + tv.tv_usec = 150000; // 150 ms + + // read the terminal answer + if ( select (stdin_no+1, &ifds, 0, 0, &tv) > 0) { - int n; - char temp[150] = {}; - oscPrefix(); - putstring (OSC "50;?" BEL); // get font - oscPostfix(); - std::fflush(stdout); - usleep(150000); // wait 150 ms - - // read the terminal answer - n = int(read(fileno(stdin), &temp, sizeof(temp)-1)); - - // BEL + '\0' = string terminator - if ( n >= 6 && temp[n-1] == BEL[0] && temp[n] == '\0' ) + if ( std::scanf("\033]50;%[^\n]s", temp) == 1 ) { - temp[n-1] = '\0'; - font = static_cast(temp + 5); + size_t n = std::strlen(temp); + + // BEL + '\0' = string terminator + if ( n >= 5 && temp[n-1] == BEL[0] && temp[n] == '\0' ) + temp[n-1] = '\0'; + + font = temp; } } } @@ -795,22 +836,30 @@ const FString FTerm::getXTermTitle() if ( kde_konsole ) return title; - if ( raw_mode && non_blocking_stdin ) + fd_set ifds; + struct timeval tv; + char temp[512] = {}; + + putstring (CSI "21t"); // get title + std::fflush(stdout); + + FD_ZERO(&ifds); + FD_SET(stdin_no, &ifds); + tv.tv_sec = 0; + tv.tv_usec = 150000; // 150 ms + + // read the terminal answer + if ( select (stdin_no+1, &ifds, 0, 0, &tv) > 0) { - int n; - char temp[512] = {}; - putstring (CSI "21t"); // get title - std::fflush(stdout); - usleep(150000); // wait 150 ms - - // read the terminal answer - n = int(read(fileno(stdin), &temp, sizeof(temp)-1)); - - // Esc + \ = OSC string terminator - if ( n >= 5 && temp[n-1] == '\\' && temp[n-2] == ESC[0] ) + if ( std::scanf("\033]l%[^\n]s", temp) == 1 ) { - temp[n-2] = '\0'; - title = static_cast(temp + 3); + size_t n = std::strlen(temp); + + // Esc + \ = OSC string terminator + if ( n >= 4 && temp[n-1] == '\\' && temp[n-2] == ESC[0] ) + temp[n-2] = '\0'; + + title = temp; } } @@ -822,28 +871,39 @@ const FString FTerm::getXTermColorName (int color) { FString color_str(""); - if ( raw_mode && non_blocking_stdin ) + fd_set ifds; + struct timeval tv; + int c = color; + int digits = 0; + + while ( c /= 10 ) + digits++; + + char temp[512] = {}; + putstringf (OSC "4;%d;?" BEL, color); // get color + std::fflush(stdout); + + FD_ZERO(&ifds); + FD_SET(stdin_no, &ifds); + tv.tv_sec = 0; + tv.tv_usec = 150000; // 150 ms + + // read the terminal answer + if ( select (stdin_no+1, &ifds, 0, 0, &tv) > 0) { - int n; - int c = color; - int digits = 0; - - while ( c /= 10 ) - digits++; - - char temp[512] = {}; - putstringf (OSC "4;%d;?" BEL, color); // get color - std::fflush(stdout); - usleep(150000); // wait 150 ms - - // read the terminal answer - n = int(read(fileno(stdin), &temp, sizeof(temp)-1)); - - // BEL + '\0' = string terminator - if ( n >= 6 && temp[n-1] == BEL[0] && temp[n] == '\0' ) + if ( std::scanf("\033]4;%d;%[^\n]s", &color, temp) == 2 ) { - temp[n-1] = '\0'; - color_str = static_cast(temp + 6 + digits); + size_t n = std::strlen(temp); + + // BEL + '\0' = string terminator + if ( n >= 6 && temp[n-1] == BEL[0] && temp[n] == '\0' ) + temp[n-1] = '\0'; + + // Esc + \ = OSC string terminator (mintty) + if ( n >= 6 && temp[n-1] == '\\' && temp[n-2] == ESC[0] ) + temp[n-2] = '\0'; + + color_str = temp; } } @@ -1294,33 +1354,22 @@ const FString FTerm::getAnswerbackMsg() { FString answerback = ""; - if ( raw_mode ) - { - ssize_t n; - fd_set ifds; - struct timeval tv; - char temp[10] = {}; + fd_set ifds; + struct timeval tv; + char temp[10] = {}; - FD_ZERO(&ifds); - FD_SET(stdin_no, &ifds); - tv.tv_sec = 0; - tv.tv_usec = 150000; // 150 ms + std::putchar (ENQ[0]); // send enquiry character + std::fflush(stdout); - std::putchar (ENQ[0]); // send enquiry character - std::fflush(stdout); + FD_ZERO(&ifds); + FD_SET(stdin_no, &ifds); + tv.tv_sec = 0; + tv.tv_usec = 150000; // 150 ms - // read the answerback message - if ( select (stdin_no+1, &ifds, 0, 0, &tv) > 0) - { - n = read(fileno(stdin), &temp, sizeof(temp)-1); - - if ( n > 0 ) - { - temp[n] = '\0'; - answerback = temp; - } - } - } + // read the answerback message + if ( select (stdin_no+1, &ifds, 0, 0, &tv) > 0) + if ( std::fgets (temp, sizeof(temp)-1, stdin) != 0 ) + answerback = temp; return answerback; } @@ -1330,39 +1379,23 @@ const FString FTerm::getSecDA() { FString sec_da_str = ""; - if ( raw_mode ) - { - ssize_t n; - fd_set ifds; - struct timeval tv; - char temp[16] = {}; + int a=0, b=0, c=0; + fd_set ifds; + struct timeval tv; - FD_ZERO(&ifds); - FD_SET(stdin_no, &ifds); - tv.tv_sec = 0; - tv.tv_usec = 550000; // 150 ms + // get the secondary device attributes + putstring (SECDA); + std::fflush(stdout); - // get the secondary device attributes - std::putchar (SECDA[0]); - std::putchar (SECDA[1]); - std::putchar (SECDA[2]); - std::putchar (SECDA[3]); + FD_ZERO(&ifds); + FD_SET(stdin_no, &ifds); + tv.tv_sec = 0; + tv.tv_usec = 600000;; // 600 ms - std::fflush(stdout); - usleep(150000); // min. wait time 150 ms (need for mintty) - - // read the answer - if ( select (stdin_no+1, &ifds, 0, 0, &tv) > 0 ) - { - n = read(fileno(stdin), &temp, sizeof(temp)-1); - - if ( n > 0 ) - { - temp[n] = '\0'; - sec_da_str = temp; - } - } - } + // read the answer + if ( select (stdin_no+1, &ifds, 0, 0, &tv) == 1 ) + if ( std::scanf("\033[>%d;%d;%dc", &a, &b, &c) == 3 ) + sec_da_str.sprintf("\033[>%d;%d;%dc", a, b, c); return sec_da_str; } @@ -1436,7 +1469,7 @@ int FTerm::putchar_UTF8 (register int c) } //---------------------------------------------------------------------- -int FTerm::UTF8decode(char* utf8) +int FTerm::UTF8decode (const char utf8[]) { register int ucs=0; @@ -1883,6 +1916,20 @@ void FTerm::identifyTermType() std::strncpy (termtype, const_cast("vt100"), 6); } +//---------------------------------------------------------------------- +void FTerm::storeTTYsettings() +{ + // store termios settings + term_init = getTTY(); +} + +//---------------------------------------------------------------------- +void FTerm::restoreTTYsettings() +{ + // restore termios settings + setTTY (term_init); +} + //---------------------------------------------------------------------- int FTerm::getScreenFont() { @@ -2224,8 +2271,9 @@ char* FTerm::parseAnswerbackMsg (char*& current_termtype) else putty_terminal = false; + // cygwin needs a backspace to delete the '♣' char if ( cygwin_terminal ) - std::putchar (BS[0]); // cygwin needs a backspace to delete the '♣' char + putstring (BS " " BS); return new_termtype; } @@ -2998,8 +3046,8 @@ void FTerm::init() xterm_default_colors = false; // Preset to true - cursor_optimisation = \ - terminal_detection = true; + cursor_optimisation = \ + terminal_detection = true; // assertion: programm start in cooked mode raw_mode = \ @@ -3021,11 +3069,8 @@ void FTerm::init() // initialize terminal and Linux console init_console(); - // make stdin non-blocking - setNonBlockingInput(); - // save termios settings - tcgetattr (stdin_no, &term_init); + storeTTYsettings(); // get output baud rate baudrate = getBaudRate(&term_init); @@ -3057,7 +3102,13 @@ void FTerm::init() // terminal detection if ( terminal_detection ) { - setRawMode(); + struct termios t; + tcgetattr (STDIN_FILENO, &t); + t.c_lflag &= uInt(~(ICANON | ECHO)); + t.c_cc[VTIME] = 1; // Timeout in deciseconds + t.c_cc[VMIN] = 0; // Minimum number of characters + tcsetattr (STDIN_FILENO, TCSANOW, &t); + // Identify the terminal via the answerback-message new_termtype = parseAnswerbackMsg (new_termtype); @@ -3066,7 +3117,11 @@ void FTerm::init() new_termtype = parseSecDA (new_termtype); // Determine xterm maximum number of colors via OSC 4 - if ( ! color256 && ! tera_terminal && getXTermColorName(0) != "" ) + if ( ! color256 + && ! cygwin_terminal + && ! tera_terminal + && ! linux_terminal + && getXTermColorName(0) != "" ) { if ( getXTermColorName(256) != "" ) { @@ -3107,12 +3162,10 @@ void FTerm::init() if ( linux_terminal && getFramebuffer_bpp() >= 4 ) FTermcap::max_color = 16; - unsetRawMode(); + t.c_lflag |= uInt(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSADRAIN, &t); } - // stop non-blocking stdin - unsetNonBlockingInput(); - // Test if the terminal is a xterm if ( std::strncmp(termtype, const_cast("xterm"), 5) == 0 || std::strncmp(termtype, const_cast("Eterm"), 4) == 0 ) @@ -3217,14 +3270,18 @@ void FTerm::init() std::fflush(stdout); } - setRawMode(); - if ( (xterm_terminal || urxvt_terminal) && ! rxvt_terminal ) { - setNonBlockingInput(); + struct termios t; + tcgetattr (STDIN_FILENO, &t); + t.c_lflag &= uInt(~(ICANON | ECHO)); + tcsetattr (STDIN_FILENO, TCSANOW, &t); + xterm_font = new FString(getXTermFont()); xterm_title = new FString(getXTermTitle()); - unsetNonBlockingInput(); + + t.c_lflag |= uInt(ICANON | ECHO); + tcsetattr (STDIN_FILENO, TCSADRAIN, &t); } if ( kde_konsole ) @@ -3266,6 +3323,12 @@ void FTerm::init() signal(SIGILL, FTerm::signal_handler); // Illegal Instruction signal(SIGSEGV, FTerm::signal_handler); // Invalid memory reference signal(SIGWINCH, FTerm::signal_handler); // Window resize signal + + // turn off hardware echo + noHardwareEcho(); + + // switch to the raw mode + setRawMode(); } //---------------------------------------------------------------------- @@ -3283,7 +3346,8 @@ void FTerm::finish() if ( xterm_title && xterm_terminal && ! rxvt_terminal ) setXTermTitle (*xterm_title); - setCookedMode(); // leave raw mode + // restore the saved termios settings + restoreTTYsettings(); // turn off all attributes if ( tcap[fc::t_exit_attribute_mode].string ) diff --git a/src/fterm.h b/src/fterm.h index 1e7f32bc..b31d7325 100644 --- a/src/fterm.h +++ b/src/fterm.h @@ -103,6 +103,7 @@ class FTerm // Accessors virtual const char* getClassName() const; + static termios getTTY(); static int getLineNumber(); static int getColumnNumber(); static FString getKeyName (int); @@ -144,6 +145,8 @@ class FTerm static bool setCursorOptimisation (bool); static void setXTermDefaultColors (bool); static void setConsoleCursor (fc::consoleCursorStyle, bool); + static void setTTY (termios&); + static void noHardwareEcho(); static bool setRawMode (bool); static bool setRawMode(); static bool unsetRawMode(); @@ -156,7 +159,7 @@ class FTerm static bool unsetNonBlockingInput(); // Methods - static int parseKeyString (char*, int, timeval*); + static int parseKeyString (char[], int, timeval*); static bool& unprocessedInput(); static bool setVGAFont(); static bool setNewFont(); @@ -216,7 +219,7 @@ class FTerm static void putstring (const char*, int = 1); static int putchar_ASCII (register int); static int putchar_UTF8 (register int); - static int UTF8decode (char*); + static int UTF8decode (const char[]); protected: // Typedefs @@ -246,20 +249,21 @@ class FTerm static void setMousePos (short, short); // Data Members - static int stdin_no; - static int stdout_no; - static bool NewFont; - static bool VGAFont; - static bool no_shadow_character; - static bool no_half_block_character; - static bool cursor_optimisation; - static bool xterm_default_colors; - static fc::encoding Encoding; - static char exit_message[8192]; + static int stdin_no; + static int stdout_no; + static bool NewFont; + static bool VGAFont; + static bool no_shadow_character; + static bool no_half_block_character; + static bool cursor_optimisation; + static bool xterm_default_colors; + static fc::encoding Encoding; + static char exit_message[8192]; private: // Typedefs - typedef FTermcap::tcap_map termcap_map; + typedef FTermcap::tcap_map termcap_map; + typedef struct { uChar red; @@ -289,6 +293,8 @@ class FTerm static int openConsole(); static int closeConsole(); static void identifyTermType(); + static void storeTTYsettings(); + static void restoreTTYsettings(); static int getScreenFont(); static int setScreenFont (uChar*, uInt, uInt, uInt, bool = false); static int setUnicodeMap (struct unimapdesc*); diff --git a/src/fvterm.cpp b/src/fvterm.cpp index 085aa577..fff8627f 100644 --- a/src/fvterm.cpp +++ b/src/fvterm.cpp @@ -550,7 +550,8 @@ int FVTerm::print (term_area* area, FString& s) nc.inherit_bg = next_attribute.inherit_bg; if ( area - && ax >= 0 && ay >= 0 + && area->cursor_x > 0 + && area->cursor_y > 0 && ax < area->width + area->right_shadow && ay < area->height + area->bottom_shadow ) { @@ -664,7 +665,8 @@ int FVTerm::print (term_area* area, register int c) nc.trans_shadow = next_attribute.trans_shadow; nc.inherit_bg = next_attribute.inherit_bg; - if ( ax >= 0 && ay >= 0 + if ( area->cursor_x > 0 + && area->cursor_y > 0 && ax < area->width + area->right_shadow && ay < area->height + area->bottom_shadow ) {