]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Minor] Rework replxx to make in compatible with Rspamd again
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 24 Aug 2021 14:53:32 +0000 (15:53 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 24 Aug 2021 14:53:32 +0000 (15:53 +0100)
contrib/DEPENDENCY_INFO.md
contrib/replxx/CMakeLists.txt
contrib/replxx/src/conversion.cxx
contrib/replxx/src/terminal.cxx [moved from contrib/replxx/src/io.cxx with 78% similarity]
contrib/replxx/src/terminal.hxx [moved from contrib/replxx/src/io.hxx with 79% similarity]

index 41c53aadca1171165246b7fccf2d6a02e4037f56..8b3b898289db7b3ee13c8ca08006b5f0bb6dca8e 100644 (file)
@@ -10,7 +10,7 @@
 | libottery     | ?       | Public Domain / CC0 | YES     | many changes       |
 | librdns       | ?       | BSD-2-Clause        | YES     |                    |
 | libucl        | ?       | BSD-2-Clause        | YES     |                    |
-| replxx        | 0.0.2   | BSD-2-Clause        | YES     | libicu usage       |
+| replxx        |  6d93360 | BSD-2-Clause       | YES     | libicu usage       |
 | lua-argparse  | 0.7.0   | MIT                 | NO      |                    |
 | lua-bit       | 1.0.2   | MIT                 | YES     | build fixes        |
 | lua-fun       | ?       | MIT                 | YES     | rspamd text        |
@@ -35,4 +35,4 @@
 | frozen        | 1.0.1   | Apache 2            | NO      |                    |
 | fmt           | 7.1.3   | MIT                 | NO      |                    |
 | doctest       | 2.4.5   | MIT                 | NO      |                    |
-| function2     | 4.1.0   | Boost               | NO      |                    |
\ No newline at end of file
+| function2     | 4.1.0   | Boost               | NO      |                    |
index 749e61208328b8ad31c75584064ca4ce8f2614d0..da6e8d86511dd45db99dec569e6423910c339028 100644 (file)
@@ -55,11 +55,11 @@ set(
        src/escape.cxx
        src/history.cxx
        src/replxx_impl.cxx
-       src/io.cxx
        src/prompt.cxx
        src/replxx.cxx
        src/util.cxx
        src/wcwidth.cpp
+       src/terminal.cxx
        src/windows.cxx
 )
 
index bcdbe048ec878f130fd72a35268306a24caca9ff..f629f910e4f5a68177ca7253f7bb7c55afb2c000 100644 (file)
@@ -2,8 +2,9 @@
 #include <string>
 #include <cstring>
 #include <cctype>
-#include <locale.h>
+#include <clocale>
 
+#include "unicode/utf8.h"
 #include "conversion.hxx"
 
 #ifdef _WIN32
@@ -44,20 +45,38 @@ bool is8BitEncoding( is_8bit_encoding() );
 ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, const char* src) {
        ConversionResult res = ConversionResult::conversionOK;
        if ( ! locale::is8BitEncoding ) {
-               const UTF8* sourceStart = reinterpret_cast<const UTF8*>(src);
-               const UTF8* sourceEnd = sourceStart + strlen(src);
-               UTF32* targetStart = reinterpret_cast<UTF32*>(dst);
-               UTF32* targetEnd = targetStart + dstSize;
-
-               res = ConvertUTF8toUTF32(
-                               &sourceStart, sourceEnd, &targetStart, targetEnd, lenientConversion);
+               auto sourceStart = reinterpret_cast<const unsigned char*>(src);
+               auto slen = strlen(src);
+               auto targetStart = reinterpret_cast<UChar32*>(dst);
+               int i = 0, j = 0;
+
+               while (i < slen && j < dstSize) {
+                       UChar32 uc;
+                       auto prev_i = i;
+                       U8_NEXT (sourceStart, i, slen, uc);
+
+                       if (uc <= 0) {
+                               if (U8_IS_LEAD (sourceStart[prev_i])) {
+                                       auto lead_byte = sourceStart[prev_i];
+                                       auto trailing_bytes = (((uint8_t)(lead_byte)>=0xc2)+
+                                                                                  ((uint8_t)(lead_byte)>=0xe0)+
+                                                                                  ((uint8_t)(lead_byte)>=0xf0));
+
+                                       if (trailing_bytes + i > slen) {
+                                               return ConversionResult::sourceExhausted;
+                                       }
+                               }
+
+                               /* Replace with 0xFFFD */
+                               uc = 0x0000FFFD;
+                       }
+                       targetStart[j++] = uc;
+               }
 
-               if (res == conversionOK) {
-                       dstCount = static_cast<int>( targetStart - reinterpret_cast<UTF32*>( dst ) );
+               dstCount = j;
 
-                       if (dstCount < dstSize) {
-                               *targetStart = 0;
-                       }
+               if (j < dstSize) {
+                       targetStart[j] = 0;
                }
        } else {
                for ( dstCount = 0; ( dstCount < dstSize ) && src[dstCount]; ++ dstCount ) {
@@ -69,26 +88,32 @@ ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, cons
 
 ConversionResult copyString8to32(char32_t* dst, int dstSize, int& dstCount, const char8_t* src) {
        return copyString8to32(
-               dst, dstSize, dstCount, reinterpret_cast<const char*>(src)
+                       dst, dstSize, dstCount, reinterpret_cast<const char*>(src)
        );
 }
 
-int copyString32to8( char* dst, int dstSize, const char32_t* src, int srcSize ) {
-       int resCount( 0 );
+int copyString32to8(
+               char* dst, int dstSize, const char32_t* src, int srcSize
+) {
+       int resCount = 0;
+
        if ( ! locale::is8BitEncoding ) {
-               const UTF32* sourceStart = reinterpret_cast<const UTF32*>(src);
-               const UTF32* sourceEnd = sourceStart + srcSize;
-               UTF8* targetStart = reinterpret_cast<UTF8*>(dst);
-               UTF8* targetEnd = targetStart + dstSize;
-
-               ConversionResult res = ConvertUTF32toUTF8(
-                       &sourceStart, sourceEnd, &targetStart, targetEnd, lenientConversion
-               );
-
-               if ( res == conversionOK ) {
-                       resCount = static_cast<int>( targetStart - reinterpret_cast<UTF8*>( dst ) );
-                       if ( resCount < dstSize ) {
-                               *targetStart = 0;
+               int j = 0;
+               UBool is_error = 0;
+
+               for (auto i = 0; i < srcSize; i ++) {
+                       U8_APPEND ((uint8_t *)dst, j, dstSize, src[i], is_error);
+
+                       if (is_error) {
+                               break;
+                       }
+               }
+
+               if (!is_error) {
+                       resCount = j;
+
+                       if (j < dstSize) {
+                               dst[j] = '\0';
                        }
                }
        } else {
@@ -101,7 +126,8 @@ int copyString32to8( char* dst, int dstSize, const char32_t* src, int srcSize )
                        dst[i] = 0;
                }
        }
-       return ( resCount );
+
+       return resCount;
 }
 
 }
similarity index 78%
rename from contrib/replxx/src/io.cxx
rename to contrib/replxx/src/terminal.cxx
index 8df176d1ce27ccfe34e463547e571927e80572f2..e618219e5dbf184ab2cc34b77487af69c4933b10 100644 (file)
 #define write _write
 #define STDIN_FILENO 0
 
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+static DWORD const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
+#endif
+
 #include "windows.hxx"
 
 #else /* _WIN32 */
 #include <sys/ioctl.h>
 #include <sys/select.h>
 #include <fcntl.h>
+#include <signal.h>
 
 #endif /* _WIN32 */
 
-#include "io.hxx"
+#include "terminal.hxx"
 #include "conversion.hxx"
 #include "escape.hxx"
 #include "replxx.hxx"
@@ -65,21 +70,35 @@ bool out( is_a_tty( 1 ) );
 
 }
 
+#ifndef _WIN32
+Terminal* _terminal_ = nullptr;
+static void WindowSizeChanged( int ) {
+       if ( ! _terminal_ ) {
+               return;
+       }
+       _terminal_->notify_event( Terminal::EVENT_TYPE::RESIZE );
+}
+#endif
+
+
 Terminal::Terminal( void )
 #ifdef _WIN32
        : _consoleOut( INVALID_HANDLE_VALUE )
        , _consoleIn( INVALID_HANDLE_VALUE )
-       , _oldMode()
+       , _origOutMode()
+       , _origInMode()
        , _oldDisplayAttribute()
        , _inputCodePage( GetConsoleCP() )
        , _outputCodePage( GetConsoleOutputCP() )
        , _interrupt( INVALID_HANDLE_VALUE )
        , _events()
+       , _empty()
 #else
        : _origTermios()
        , _interrupt()
 #endif
-       , _rawMode( false ) {
+       , _rawMode( false )
+       , _utf8() {
 #ifdef _WIN32
        _interrupt = CreateEvent( nullptr, true, false, TEXT( "replxx_interrupt_event" ) );
 #else
@@ -100,26 +119,20 @@ Terminal::~Terminal( void ) {
 }
 
 void Terminal::write32( char32_t const* text32, int len32 ) {
-       int len8 = 4 * len32 + 1;
-       unique_ptr<char[]> text8(new char[len8]);
-       int count8 = 0;
-
-       copyString32to8(text8.get(), len8, text32, len32, &count8);
-       int nWritten( 0 );
-#ifdef _WIN32
-       nWritten = win_write( text8.get(), count8 );
-#else
-       nWritten = write( 1, text8.get(), count8 );
-#endif
-       if ( nWritten != count8 ) {
-               throw std::runtime_error( "write failed" );
-       }
+       _utf8.assign( text32, len32 );
+       write8( _utf8.get(), _utf8.size() );
        return;
 }
 
 void Terminal::write8( char const* data_, int size_ ) {
 #ifdef _WIN32
-       int nWritten( win_write( data_, size_ ) );
+       if ( ! _rawMode ) {
+               enable_out();
+       }
+       int nWritten( win_write( _consoleOut, _autoEscape, data_, size_ ) );
+       if ( ! _rawMode ) {
+               disable_out();
+       }
 #else
        int nWritten( write( 1, data_, size_ ) );
 #endif
@@ -164,18 +177,45 @@ inline int notty( void ) {
 }
 }
 
+void Terminal::enable_out( void ) {
+#ifdef _WIN32
+       _consoleOut = GetStdHandle( STD_OUTPUT_HANDLE );
+       SetConsoleOutputCP( 65001 );
+       GetConsoleMode( _consoleOut, &_origOutMode );
+       _autoEscape = SetConsoleMode( _consoleOut, _origOutMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING ) != 0;
+#endif
+}
+
+void Terminal::disable_out( void ) {
+#ifdef _WIN32
+       SetConsoleMode( _consoleOut, _origOutMode );
+       SetConsoleOutputCP( _outputCodePage );
+       _consoleOut = INVALID_HANDLE_VALUE;
+       _autoEscape = false;
+#endif
+}
+
+void Terminal::enable_bracketed_paste( void ) {
+       static char const  BRACK_PASTE_INIT[] = "\033[?2004h";
+       write8( BRACK_PASTE_INIT, sizeof ( BRACK_PASTE_INIT ) - 1 );
+}
+
+void Terminal::disable_bracketed_paste( void ) {
+       static char const  BRACK_PASTE_DISABLE[] = "\033[?2004l";
+       write8( BRACK_PASTE_DISABLE, sizeof ( BRACK_PASTE_DISABLE ) - 1 );
+}
+
 int Terminal::enable_raw_mode( void ) {
        if ( ! _rawMode ) {
 #ifdef _WIN32
                _consoleIn = GetStdHandle( STD_INPUT_HANDLE );
-               _consoleOut = GetStdHandle( STD_OUTPUT_HANDLE );
                SetConsoleCP( 65001 );
-               SetConsoleOutputCP( 65001 );
-               GetConsoleMode( _consoleIn, &_oldMode );
+               GetConsoleMode( _consoleIn, &_origInMode );
                SetConsoleMode(
                        _consoleIn,
-                       _oldMode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT )
+                       _origInMode & ~( ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT )
                );
+               enable_out();
 #else
                struct termios raw;
 
@@ -208,21 +248,22 @@ int Terminal::enable_raw_mode( void ) {
                if ( tcsetattr(0, TCSADRAIN, &raw) < 0 ) {
                        return ( notty() );
                }
+               _terminal_ = this;
 #endif
                _rawMode = true;
        }
-       return 0;
+       return ( 0 );
 }
 
 void Terminal::disable_raw_mode(void) {
        if ( _rawMode ) {
 #ifdef _WIN32
-               SetConsoleMode( _consoleIn, _oldMode );
+               disable_out();
+               SetConsoleMode( _consoleIn, _origInMode );
                SetConsoleCP( _inputCodePage );
-               SetConsoleOutputCP( _outputCodePage );
                _consoleIn = INVALID_HANDLE_VALUE;
-               _consoleOut = INVALID_HANDLE_VALUE;
 #else
+               _terminal_ = nullptr;
                if ( tcsetattr( 0, TCSADRAIN, &_origTermios ) == -1 ) {
                        return;
                }
@@ -320,31 +361,23 @@ char32_t Terminal::read_char( void ) {
                        }
                }
 #endif
-               if (rec.EventType != KEY_EVENT) {
+               if ( rec.EventType != KEY_EVENT ) {
                        continue;
                }
-               // Windows provides for entry of characters that are not on your keyboard by
-               // sending the
-               // Unicode characters as a "key up" with virtual keycode 0x12 (VK_MENU ==
-               // Alt key) ...
+               // Windows provides for entry of characters that are not on your keyboard by sending the
+               // Unicode characters as a "key up" with virtual keycode 0x12 (VK_MENU == Alt key) ...
                // accept these characters, otherwise only process characters on "key down"
-               if (!rec.Event.KeyEvent.bKeyDown &&
-                               rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU) {
+               if ( !rec.Event.KeyEvent.bKeyDown && ( rec.Event.KeyEvent.wVirtualKeyCode != VK_MENU ) ) {
                        continue;
                }
                modifierKeys = 0;
-               // AltGr is encoded as ( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED ), so don't
-               // treat this
-               // combination as either CTRL or META we just turn off those two bits, so it
-               // is still
-               // possible to combine CTRL and/or META with an AltGr key by using
-               // right-Ctrl and/or
+               // AltGr is encoded as ( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED ), so don't treat this
+               // combination as either CTRL or META we just turn off those two bits, so it is still
+               // possible to combine CTRL and/or META with an AltGr key by using right-Ctrl and/or
                // left-Alt
-               if ((rec.Event.KeyEvent.dwControlKeyState &
-                                (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) ==
-                               (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)) {
-                       rec.Event.KeyEvent.dwControlKeyState &=
-                                       ~(LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
+               DWORD const AltGr( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED );
+               if ( ( rec.Event.KeyEvent.dwControlKeyState & AltGr ) == AltGr ) {
+                       rec.Event.KeyEvent.dwControlKeyState &= ~( LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED );
                }
                if ( rec.Event.KeyEvent.dwControlKeyState & ( RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED ) ) {
                        modifierKeys |= Replxx::KEY::BASE_CONTROL;
@@ -352,7 +385,7 @@ char32_t Terminal::read_char( void ) {
                if ( rec.Event.KeyEvent.dwControlKeyState & ( RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED ) ) {
                        modifierKeys |= Replxx::KEY::BASE_META;
                }
-               if (escSeen) {
+               if ( escSeen ) {
                        modifierKeys |= Replxx::KEY::BASE_META;
                }
                int key( rec.Event.KeyEvent.uChar.UnicodeChar );
@@ -417,7 +450,7 @@ char32_t Terminal::read_char( void ) {
                                key += 0x10000;
                        }
                        if ( is_control_code( key ) ) {
-                               key += 0x40;
+                               key = control_to_human( key );
                                modifierKeys |= Replxx::KEY::BASE_CONTROL;
                        }
                        key |= modifierKeys;
@@ -474,7 +507,7 @@ char32_t Terminal::read_char( void ) {
                                        friendlyTextPtr = const_cast<char*>("DEL");
                                } else {
                                        friendlyTextBuf[0] = '^';
-                                       friendlyTextBuf[1] = keyCopy + 0x40;
+                                       friendlyTextBuf[1] = control_to_human( keyCopy );
                                        friendlyTextBuf[2] = 0;
                                        friendlyTextPtr = friendlyTextBuf;
                                }
@@ -494,7 +527,7 @@ char32_t Terminal::read_char( void ) {
 
        c = EscapeSequenceProcessing::doDispatch(c);
        if ( is_control_code( c ) ) {
-               c = Replxx::KEY::control( c + 0x40 );
+               c = Replxx::KEY::control( control_to_human( c ) );
        }
 #endif // #_WIN32
        return ( c );
@@ -504,7 +537,7 @@ Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) {
 #ifdef _WIN32
        std::array<HANDLE,2> handles = { _consoleIn, _interrupt };
        while ( true ) {
-               DWORD event( WaitForMultipleObjects( handles.size (), handles.data(), false, timeout_ > 0 ? timeout_ : INFINITE ) );
+               DWORD event( WaitForMultipleObjects( static_cast<DWORD>( handles.size() ), handles.data(), false, timeout_ > 0 ? timeout_ : INFINITE ) );
                switch ( event ) {
                        case ( WAIT_OBJECT_0 + 0 ): {
                                // peek events that will be skipped
@@ -580,6 +613,9 @@ Terminal::EVENT_TYPE Terminal::wait_for_input( int long timeout_ ) {
                        if ( data == 'm' ) {
                                return ( EVENT_TYPE::MESSAGE );
                        }
+                       if ( data == 'r' ) {
+                               return ( EVENT_TYPE::RESIZE );
+                       }
                }
                if ( FD_ISSET( 0, &fdSet ) ) {
                        return ( EVENT_TYPE::KEY_PRESS );
@@ -593,7 +629,7 @@ void Terminal::notify_event( EVENT_TYPE eventType_ ) {
        _events.push_back( eventType_ );
        SetEvent( _interrupt );
 #else
-       char data( eventType_ == EVENT_TYPE::KEY_PRESS ? 'k' : 'm' );
+       char data( ( eventType_ == EVENT_TYPE::KEY_PRESS ) ? 'k' : ( eventType_ == EVENT_TYPE::MESSAGE ? 'm' : 'r' ) );
        static_cast<void>( write( _interrupt[1], &data, 1 ) == 1 );
 #endif
 }
@@ -603,31 +639,38 @@ void Terminal::notify_event( EVENT_TYPE eventType_ ) {
  */
 void Terminal::clear_screen( CLEAR_SCREEN clearScreen_ ) {
 #ifdef _WIN32
+       if ( _autoEscape ) {
+#endif
+               if ( clearScreen_ == CLEAR_SCREEN::WHOLE ) {
+                       char const clearCode[] = "\033c\033[H\033[2J\033[0m";
+                       static_cast<void>( write(1, clearCode, sizeof ( clearCode ) - 1) >= 0 );
+               } else {
+                       char const clearCode[] = "\033[J";
+                       static_cast<void>( write(1, clearCode, sizeof ( clearCode ) - 1) >= 0 );
+               }
+               return;
+#ifdef _WIN32
+       }
        COORD coord = { 0, 0 };
        CONSOLE_SCREEN_BUFFER_INFO inf;
-       bool toEnd( clearScreen_ == CLEAR_SCREEN::TO_END );
        HANDLE consoleOut( _consoleOut != INVALID_HANDLE_VALUE ? _consoleOut : GetStdHandle( STD_OUTPUT_HANDLE ) );
        GetConsoleScreenBufferInfo( consoleOut, &inf );
-       if ( ! toEnd ) {
-               SetConsoleCursorPosition( consoleOut, coord );
-       } else {
+       if ( clearScreen_ == CLEAR_SCREEN::TO_END ) {
                coord = inf.dwCursorPosition;
-       }
-       DWORD nWritten( 0 );
-       DWORD toWrite(
-               toEnd
-                       ? ( inf.dwSize.Y - inf.dwCursorPosition.Y ) * inf.dwSize.X - inf.dwCursorPosition.X
-                       : inf.dwSize.X * inf.dwSize.Y
-       );
-       FillConsoleOutputCharacterA( consoleOut, ' ', toWrite, coord, &nWritten );
-#else
-       if ( clearScreen_ == CLEAR_SCREEN::WHOLE ) {
-               char const clearCode[] = "\033c\033[H\033[2J\033[0m";
-               static_cast<void>( write(1, clearCode, sizeof ( clearCode ) - 1) >= 0 );
+               DWORD nWritten( 0 );
+               SHORT height( inf.srWindow.Bottom - inf.srWindow.Top );
+               DWORD yPos( inf.dwCursorPosition.Y - inf.srWindow.Top );
+               DWORD toWrite( ( height + 1 - yPos ) * inf.dwSize.X - inf.dwCursorPosition.X );
+//             FillConsoleOutputCharacterA( consoleOut, ' ', toWrite, coord, &nWritten );
+               _empty.resize( toWrite - 1, ' ' );
+               WriteConsoleA( consoleOut, _empty.data(), toWrite - 1, &nWritten, nullptr );
        } else {
-               char const clearCode[] = "\033[J";
-               static_cast<void>( write(1, clearCode, sizeof ( clearCode ) - 1) >= 0 );
+               COORD scrollTarget = { 0, -inf.dwSize.Y };
+               CHAR_INFO fill{ TEXT( ' ' ), inf.wAttributes };
+               SMALL_RECT scrollRect = { 0, 0, inf.dwSize.X, inf.dwSize.Y };
+               ScrollConsoleScreenBuffer( consoleOut, &scrollRect, nullptr, scrollTarget, &fill );
        }
+       SetConsoleCursorPosition( consoleOut, coord );
 #endif
 }
 
@@ -653,6 +696,18 @@ void Terminal::jump_cursor( int xPos_, int yOffset_ ) {
 #endif
 }
 
+#ifdef _WIN32
+void Terminal::set_cursor_visible( bool visible_ ) {
+       CONSOLE_CURSOR_INFO     cursorInfo;
+       GetConsoleCursorInfo( _consoleOut, &cursorInfo );
+       cursorInfo.bVisible = visible_;
+       SetConsoleCursorInfo( _consoleOut, &cursorInfo );
+       return;
+}
+#else
+void Terminal::set_cursor_visible( bool ) {}
+#endif
+
 #ifndef _WIN32
 int Terminal::read_verbatim( char32_t* buffer_, int size_ ) {
        int len( 0 );
@@ -669,6 +724,18 @@ int Terminal::read_verbatim( char32_t* buffer_, int size_ ) {
        ::fcntl( STDIN_FILENO, F_SETFL, statusFlags );
        return ( len );
 }
+
+int Terminal::install_window_change_handler( void ) {
+       struct sigaction sa;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = 0;
+       sa.sa_handler = &WindowSizeChanged;
+
+       if (sigaction(SIGWINCH, &sa, nullptr) == -1) {
+               return errno;
+       }
+       return 0;
+}
 #endif
 
 }
similarity index 79%
rename from contrib/replxx/src/io.hxx
rename to contrib/replxx/src/terminal.hxx
index 42d8bd5b3ad8fab86b0ec73c6f73e625fc5bcc7d..e6a25786b91b298f24256a82ac50291a7b76d07b 100644 (file)
@@ -4,11 +4,14 @@
 #include <deque>
 
 #ifdef _WIN32
+#include <vector>
 #include <windows.h>
 #else
 #include <termios.h>
 #endif
 
+#include "utf8string.hxx"
+
 namespace replxx {
 
 class Terminal {
@@ -16,24 +19,29 @@ public:
        enum class EVENT_TYPE {
                KEY_PRESS,
                MESSAGE,
-               TIMEOUT
+               TIMEOUT,
+               RESIZE
        };
 private:
 #ifdef _WIN32
        HANDLE _consoleOut;
        HANDLE _consoleIn;
-       DWORD _oldMode;
+       DWORD _origOutMode;
+       DWORD _origInMode;
+       bool _autoEscape;
        WORD _oldDisplayAttribute;
        UINT const _inputCodePage;
        UINT const _outputCodePage;
        HANDLE _interrupt;
        typedef std::deque<EVENT_TYPE> events_t;
        events_t _events;
+       std::vector<char> _empty;
 #else
        struct termios _origTermios; /* in order to restore at exit */
        int _interrupt[2];
 #endif
        bool _rawMode; /* for destructor to check if restore is needed */
+       Utf8String _utf8;
 public:
        enum class CLEAR_SCREEN {
                WHOLE,
@@ -46,6 +54,8 @@ public:
        void write8( char const*, int );
        int get_screen_columns(void);
        int get_screen_rows(void);
+       void enable_bracketed_paste( void );
+       void disable_bracketed_paste( void );
        int enable_raw_mode(void);
        void disable_raw_mode(void);
        char32_t read_char(void);
@@ -53,9 +63,14 @@ public:
        EVENT_TYPE wait_for_input( int long = 0 );
        void notify_event( EVENT_TYPE );
        void jump_cursor( int, int );
+       void set_cursor_visible( bool );
 #ifndef _WIN32
        int read_verbatim( char32_t*, int );
+       int install_window_change_handler( void );
 #endif
+private:
+       void enable_out( void );
+       void disable_out( void );
 private:
        Terminal( Terminal const& ) = delete;
        Terminal& operator = ( Terminal const& ) = delete;