From aa434a9754a56a224afb7ddfd02bb2481ff4777a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 17 Jun 2022 18:21:15 -0300 Subject: [PATCH] eterm: More WIP. --- projects/linux/ee.files | 3 - src/tools/eterm/eterminaldisplay.cpp | 34 +-- src/tools/eterm/eterminaldisplay.hpp | 17 +- src/tools/eterm/system/autohandle.cpp | 13 +- src/tools/eterm/system/autohandle.hpp | 2 +- src/tools/eterm/system/ipipe.hpp | 8 + src/tools/eterm/system/iprocess.hpp | 12 +- src/tools/eterm/system/iprocessfactory.hpp | 16 +- src/tools/eterm/system/pipe.hpp | 9 +- src/tools/eterm/system/pipe.win32.cpp | 7 +- src/tools/eterm/system/process.cpp | 246 +++++++++++++++- src/tools/eterm/system/process.hpp | 4 + src/tools/eterm/system/process.win32.cpp | 266 ------------------ src/tools/eterm/system/processfactory.cpp | 2 +- src/tools/eterm/system/processfactory.hpp | 17 +- src/tools/eterm/terminal/config.def.hpp | 97 ------- src/tools/eterm/terminal/ipseudoterminal.hpp | 8 + src/tools/eterm/terminal/pseudoterminal.cpp | 119 +++++++- src/tools/eterm/terminal/pseudoterminal.hpp | 11 +- .../eterm/terminal/pseudoterminal.win32.cpp | 135 --------- src/tools/eterm/terminal/terminaldisplay.cpp | 17 +- src/tools/eterm/terminal/terminaldisplay.hpp | 4 +- src/tools/eterm/terminal/terminalemulator.cpp | 131 +++++---- src/tools/eterm/terminal/terminalemulator.hpp | 14 +- src/tools/eterm/terminal/types.hpp | 6 +- src/tools/eterm/terminal/windowserrors.hpp | 5 +- 26 files changed, 560 insertions(+), 643 deletions(-) delete mode 100644 src/tools/eterm/system/process.win32.cpp delete mode 100644 src/tools/eterm/terminal/config.def.hpp delete mode 100644 src/tools/eterm/terminal/pseudoterminal.win32.cpp diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 6bc33b6c4..8c493bf7e 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -1144,17 +1144,14 @@ ../../src/tools/eterm/system/pipe.win32.cpp ../../src/tools/eterm/system/process.cpp ../../src/tools/eterm/system/process.hpp -../../src/tools/eterm/system/process.win32.cpp ../../src/tools/eterm/system/processfactory.cpp ../../src/tools/eterm/system/processfactory.hpp ../../src/tools/eterm/terminal/boxdraw.hpp ../../src/tools/eterm/terminal/boxdrawdata.hpp -../../src/tools/eterm/terminal/config.def.hpp ../../src/tools/eterm/terminal/ipseudoterminal.hpp ../../src/tools/eterm/terminal/nonspacing.hpp ../../src/tools/eterm/terminal/pseudoterminal.cpp ../../src/tools/eterm/terminal/pseudoterminal.hpp -../../src/tools/eterm/terminal/pseudoterminal.win32.cpp ../../src/tools/eterm/terminal/terminaldisplay.cpp ../../src/tools/eterm/terminal/terminaldisplay.hpp ../../src/tools/eterm/terminal/terminalemulator.cpp diff --git a/src/tools/eterm/eterminaldisplay.cpp b/src/tools/eterm/eterminaldisplay.cpp index 19ff60e6e..efa92f1a0 100644 --- a/src/tools/eterm/eterminaldisplay.cpp +++ b/src/tools/eterm/eterminaldisplay.cpp @@ -214,6 +214,7 @@ TerminalKeyMap terminalKeyMap{ keys, eeARRAY_SIZE( keys ), platformKeys, eeARRAY_SIZE( platformKeys ), shortcuts, eeARRAY_SIZE( shortcuts ) }; +/* Terminal colors (16 first used in escape sequence) */ // This is the customizable colorscheme const char* colornames[256] = { "#1e2127", "#e06c75", "#98c379", "#d19a66", "#61afef", "#c678dd", "#56b6c2", "#abb2bf", "#5c6370", "#e06c75", "#98c379", "#d19a66", @@ -310,7 +311,7 @@ static const Color colormapped[256] = { std::shared_ptr ETerminalDisplay::Create( EE::Window::Window* window, Font* font, - std::shared_ptr&& terminalEmulator, + std::shared_ptr&& terminalEmulator, TerminalConfig* config ) { std::shared_ptr terminal = std::shared_ptr( new ETerminalDisplay( window, font, terminalEmulator->GetNumColumns(), @@ -369,7 +370,7 @@ ETerminalDisplay::ETerminalDisplay( EE::Window::Window* window, Font* font, int m_pasteNewlineFix = true; } - Hexe::Terminal::Glyph defaultGlyph; + TerminalGlyph defaultGlyph; defaultGlyph.mode = ATTR_INVISIBLE; auto defaultColor = std::make_pair( 0U, "" ); m_cursorg = defaultGlyph; @@ -450,7 +451,7 @@ const char* ETerminalDisplay::GetClipboard() const { bool ETerminalDisplay::DrawBegin( int columns, int rows ) { if ( columns != m_columns || rows != m_rows ) { - Hexe::Terminal::Glyph defaultGlyph{}; + TerminalGlyph defaultGlyph{}; m_buffer.resize( columns * rows, defaultGlyph ); m_columns = columns; m_rows = rows; @@ -462,7 +463,7 @@ bool ETerminalDisplay::DrawBegin( int columns, int rows ) { void ETerminalDisplay::DrawLine( Line line, int x1, int y, int x2 ) { m_checkDirty = true; - memcpy( &m_buffer[y * m_columns + x1], line, ( x2 - x1 ) * sizeof( Hexe::Terminal::Glyph ) ); + memcpy( &m_buffer[y * m_columns + x1], line, ( x2 - x1 ) * sizeof( TerminalGlyph ) ); for ( int i = x1; i < x2; i++ ) { if ( m_terminal->selected( i, y ) ) { m_buffer[y * m_columns + i].mode |= ATTR_REVERSE; @@ -470,8 +471,7 @@ void ETerminalDisplay::DrawLine( Line line, int x1, int y, int x2 ) { } } -void ETerminalDisplay::DrawCursor( int cx, int cy, Hexe::Terminal::Glyph g, int, int, - Hexe::Terminal::Glyph ) { +void ETerminalDisplay::DrawCursor( int cx, int cy, TerminalGlyph g, int, int, TerminalGlyph ) { m_cursorx = cx; m_cursory = cy; m_cursorg = g; @@ -508,13 +508,8 @@ static inline Color GetCol( unsigned int terminalColor, terminalColor & 0xFF, ( ~( ( terminalColor >> 25 ) & 0xFF ) ) & 0xFF ); } -#define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) -#define MAX( a, b ) ( ( a ) < ( b ) ? ( b ) : ( a ) ) -#define LEN( a ) ( sizeof( a ) / sizeof( a )[0] ) #define BETWEEN( x, a, b ) ( ( a ) <= ( x ) && ( x ) <= ( b ) ) #define IS_SET( flag ) ( ( mMode & ( flag ) ) != 0 ) -#define DIV( n, d ) ( ( ( n ) + ( d ) / 2.0f ) / ( d ) ) -#define DIVI( n, d ) ( ( ( n ) + ( d ) / 2 ) / ( d ) ) #define COL32_R_SHIFT 24 #define COL32_G_SHIFT 16 #define COL32_B_SHIFT 8 @@ -697,25 +692,14 @@ void ETerminalDisplay::Draw( Vector2i pos, const Sizei& clip_rect, bool /*hasFoc continue; } - /*Text t( mFont, mFontSize ); - t.setDisableCacheWidth( true ); - t.setColor( fg ); - t.setStyle( 0 | ( glyph.mode & ATTR_BOLD ? Text::Style::Bold : 0 ) | - ( glyph.mode & ATTR_ITALIC ? Text::Style::Italic : 0 ) | - ( glyph.mode & ATTR_UNDERLINE ? Text::Style::Underlined : 0 ) ); - String str; - str.push_back( glyph.u ); - t.setString( str ); - t.draw( x, y );*/ auto* gd = mFont->getGlyphDrawable( glyph.u, mFontSize, glyph.mode & ATTR_BOLD ); gd->setColor( fg ); gd->setDrawMode( GlyphDrawable::DrawMode::Text ); gd->draw( { x, y } ); /*if ( glyph.mode & ATTR_BOXDRAW && m_useBoxDrawing ) { - auto bd = boxdrawindex( &glyph ); - drawbox( x, y, advanceX, line_height, fg, bg, bd, vtx_write, idx_write, - vtx_current_idx, drawList->_Data->TexUvWhitePixel ); + auto bd = TerminalEmulator::boxdrawindex( &glyph ); + drawbox( x, y, advanceX, line_height, fg, bg, bd, vtx_write ); } if ( glyph.mode & ATTR_STRUCK ) { @@ -796,7 +780,7 @@ void ETerminalDisplay::Draw( Vector2i pos, const Sizei& clip_rect, bool /*hasFoc // xdrawglyph(g, cx, cy); // break; case 3: /* Blinking Underline */ - case Hexe::Terminal::MAX_CURSOR: + case cursor_mode::MAX_CURSOR: case 4: /* Steady Underline */ p.drawRectangle( Rectf( { pos.x + borderpx + m_cursorx * spaceCharAdvanceX, diff --git a/src/tools/eterm/eterminaldisplay.hpp b/src/tools/eterm/eterminaldisplay.hpp index a73eaf56f..8913bb620 100644 --- a/src/tools/eterm/eterminaldisplay.hpp +++ b/src/tools/eterm/eterminaldisplay.hpp @@ -112,8 +112,7 @@ class ETerminalDisplay : public TerminalDisplay { public: static std::shared_ptr Create( EE::Window::Window* window, Font* font, - std::shared_ptr&& terminalEmulator, - TerminalConfig* config = 0 ); + std::shared_ptr&& terminalEmulator, TerminalConfig* config = 0 ); static std::shared_ptr Create( EE::Window::Window* window, Font* font, int columns, int rows, @@ -132,8 +131,7 @@ class ETerminalDisplay : public TerminalDisplay { virtual bool DrawBegin( int columns, int rows ); virtual void DrawLine( Line line, int x1, int y, int x2 ); - virtual void DrawCursor( int cx, int cy, Hexe::Terminal::Glyph g, int ox, int oy, - Hexe::Terminal::Glyph og ); + virtual void DrawCursor( int cx, int cy, TerminalGlyph g, int ox, int oy, TerminalGlyph og ); virtual void DrawEnd(); virtual void Update(); @@ -144,21 +142,20 @@ class ETerminalDisplay : public TerminalDisplay { void Draw( const Rectf& contentArea ); - void Draw( Vector2i pos, const Sizei& clip_rect, bool hasFocus ); - virtual void onTextInput( const Uint32& chr ); virtual void onKeyDown( const Keycode& keyCode, const Uint32& chr, const Uint32& mod, const Scancode& scancode ); Float getFontSize() const; + void setFontSize( Float FontSize ); protected: EE::Window::Window* mWindow; - std::vector m_buffer; + std::vector m_buffer; std::vector> m_colors; - std::shared_ptr m_terminal; + std::shared_ptr m_terminal; mutable std::string mClipboard; Font* mFont; @@ -169,7 +166,7 @@ class ETerminalDisplay : public TerminalDisplay { bool m_checkDirty; int m_cursorx; int m_cursory; - Hexe::Terminal::Glyph m_cursorg; + TerminalGlyph m_cursorg; bool m_useBoxDrawing; bool m_useColorEmoji; bool m_pasteNewlineFix; @@ -177,6 +174,8 @@ class ETerminalDisplay : public TerminalDisplay { ETerminalDisplay( EE::Window::Window* window, Font* font, int columns, int rows, TerminalConfig* config ); + + void Draw( Vector2i pos, const Sizei& clip_rect, bool hasFocus ); }; #endif // ETERMINALDISPLAY_HPP diff --git a/src/tools/eterm/system/autohandle.cpp b/src/tools/eterm/system/autohandle.cpp index 825a75bcc..515b3b019 100644 --- a/src/tools/eterm/system/autohandle.cpp +++ b/src/tools/eterm/system/autohandle.cpp @@ -21,11 +21,9 @@ // DEALINGS IN THE SOFTWARE. #include "autohandle.hpp" -#ifndef WIN32 +#ifndef _WIN32 #include -#endif - -#ifdef WIN32 +#else #include #endif @@ -46,7 +44,7 @@ Hexe::AutoHandle& Hexe::AutoHandle::operator=( AutoHandle&& other ) { return *this; } -#ifdef WIN32 +#ifdef _WIN32 Hexe::AutoHandle::AutoHandle( HANDLE handle ) : m_hHandle( handle ) {} void Hexe::AutoHandle::Release() const { @@ -63,7 +61,6 @@ void Hexe::AutoHandle::Release() const { close( m_hHandle ); } } - #endif Hexe::AutoHandle::operator bool() const noexcept { @@ -82,11 +79,11 @@ Hexe::AutoHandle::operator Hexe::AutoHandle::type() const noexcept { return m_hHandle; } -#ifdef WIN32 +#ifdef _WIN32 // On Windows we deal with the Windows API, which means HANDLE is used as the handle to files // and pipes constexpr Hexe::AutoHandle::type Hexe::AutoHandle::invalid_value() { - return INVALID_HANDLE_VALUE; + return static_cast( 0 ); } #else // On non-Windows OS'es we deal with file descriptors diff --git a/src/tools/eterm/system/autohandle.hpp b/src/tools/eterm/system/autohandle.hpp index ebd2c7518..6769f1796 100644 --- a/src/tools/eterm/system/autohandle.hpp +++ b/src/tools/eterm/system/autohandle.hpp @@ -26,7 +26,7 @@ namespace Hexe { // This class is designed to automatically free a resource handle when destructed class AutoHandle final { public: -#ifdef WIN32 +#ifdef _WIN32 // On Windows we deal with the Windows API, which means HANDLE is used as the handle to files // and pipes using type = void*; diff --git a/src/tools/eterm/system/ipipe.hpp b/src/tools/eterm/system/ipipe.hpp index 0ce345da7..18352e5ed 100644 --- a/src/tools/eterm/system/ipipe.hpp +++ b/src/tools/eterm/system/ipipe.hpp @@ -25,18 +25,26 @@ #include namespace Hexe { namespace System { + class IPipe { public: IPipe() = default; + IPipe( IPipe&& ) = delete; + IPipe( const IPipe& ) = delete; + IPipe& operator=( IPipe&& ) = delete; + IPipe& operator=( const IPipe& ) = delete; + virtual ~IPipe() = default; virtual bool IsTTY() const = 0; virtual int Write( const char* s, size_t n ) = 0; + virtual int Read( char* buf, size_t n, bool block = false ) = 0; }; + }} // namespace Hexe::System diff --git a/src/tools/eterm/system/iprocess.hpp b/src/tools/eterm/system/iprocess.hpp index 7a62a4622..854738acf 100644 --- a/src/tools/eterm/system/iprocess.hpp +++ b/src/tools/eterm/system/iprocess.hpp @@ -22,22 +22,32 @@ #pragma once namespace Hexe { namespace System { + enum class ProcessStatus { RUNNING = 0, EXITED }; class IProcess { public: IProcess() = default; + virtual ~IProcess() = default; + IProcess( const IProcess& ) = delete; + IProcess( IProcess&& ) = delete; + IProcess& operator=( const IProcess& ) = delete; + IProcess& operator=( IProcess&& ) = delete; virtual void CheckExitStatus() = 0; + virtual bool HasExited() const = 0; + virtual int GetExitCode() const = 0; virtual void Terminate() = 0; + virtual void WaitForExit() = 0; }; -}} // namespace Hexe::System \ No newline at end of file + +}} // namespace Hexe::System diff --git a/src/tools/eterm/system/iprocessfactory.hpp b/src/tools/eterm/system/iprocessfactory.hpp index 73b5517a5..8fb1a9dbb 100644 --- a/src/tools/eterm/system/iprocessfactory.hpp +++ b/src/tools/eterm/system/iprocessfactory.hpp @@ -28,14 +28,22 @@ #include #include +using namespace Hexe::Terminal; + namespace Hexe { namespace System { + class IProcessFactory { public: IProcessFactory() = default; + virtual ~IProcessFactory() = default; + IProcessFactory( const IProcessFactory& ) = delete; + IProcessFactory( IProcessFactory&& ) = delete; + IProcessFactory& operator=( const IProcessFactory& ) = delete; + IProcessFactory& operator=( IProcessFactory&& ) = delete; virtual std::unique_ptr CreateWithStdioPipe( const std::string& program, @@ -44,10 +52,10 @@ class IProcessFactory { std::unique_ptr& outPipe, bool withStderr = true ) = 0; - virtual std::unique_ptr CreateWithPseudoTerminal( - const std::string& program, const std::vector& args, - const std::string& workingDirectory, int numColumns, int numRows, - std::unique_ptr& outPseudoTerminal ) = 0; + virtual std::unique_ptr + CreateWithPseudoTerminal( const std::string& program, const std::vector& args, + const std::string& workingDirectory, int numColumns, int numRows, + std::unique_ptr& outPseudoTerminal ) = 0; }; }} // namespace Hexe::System diff --git a/src/tools/eterm/system/pipe.hpp b/src/tools/eterm/system/pipe.hpp index 719497b32..75572b8fb 100644 --- a/src/tools/eterm/system/pipe.hpp +++ b/src/tools/eterm/system/pipe.hpp @@ -21,8 +21,8 @@ // DEALINGS IN THE SOFTWARE. #pragma once -#include "autohandle.hpp" #include "../system/ipipe.hpp" +#include "autohandle.hpp" #include namespace Hexe { namespace System { @@ -30,7 +30,7 @@ class Process; class Pipe : public IPipe { private: friend class Process; -#ifdef WIN32 +#ifdef _WIN32 AutoHandle m_hInput; AutoHandle m_hOutput; @@ -42,14 +42,19 @@ class Pipe : public IPipe { #endif public: Pipe( Pipe&& ) = delete; + Pipe( const Pipe& ) = delete; + Pipe& operator=( Pipe&& ) = delete; + Pipe& operator=( const Pipe& ) = delete; + virtual ~Pipe() = default; virtual bool IsTTY() const override; virtual int Write( const char* s, size_t n ) override; + virtual int Read( char* buf, size_t n, bool block = false ) override; static bool CreatePipePair( std::unique_ptr& outPipeA, std::unique_ptr& outPipeB ); diff --git a/src/tools/eterm/system/pipe.win32.cpp b/src/tools/eterm/system/pipe.win32.cpp index 3d62b711e..aed06b163 100644 --- a/src/tools/eterm/system/pipe.win32.cpp +++ b/src/tools/eterm/system/pipe.win32.cpp @@ -1,6 +1,7 @@ -#ifdef WIN32 -#include "../system/pipe.h" -#include "windowserrors.h" +#ifdef _WIN32 +#include "pipe.hpp" +#include "../terminal/windowserrors.hpp" +#include using namespace Hexe::System; diff --git a/src/tools/eterm/system/process.cpp b/src/tools/eterm/system/process.cpp index ab47aabb0..a476c3baf 100644 --- a/src/tools/eterm/system/process.cpp +++ b/src/tools/eterm/system/process.cpp @@ -20,7 +20,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#ifndef WIN32 +#ifndef _WIN32 #include "process.hpp" #include #include @@ -187,4 +187,248 @@ Process::CreateWithPseudoTerminal( const std::string& program, const std::vector return nullptr; } +#else + +#include "../terminal/windowserrors.hpp" +#include "pipe.hpp" +#include "process.hpp" +#include +#include +#include + +using namespace Hexe::System; +using namespace Hexe; + +static std::wstring stringToWideString( const std::string& str ) { + if ( str.empty() ) + return std::wstring(); + int size_needed = MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0 ); + std::wstring wstrTo( size_needed, 0 ); + MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed ); + return wstrTo; +} + +#define HANDLE_WIN_ERR( err ) HRESULT_FROM_WIN32( err ), PrintWinApiError( err ) + +static HRESULT InitializeStartupInfoAttachedToPseudoConsole( STARTUPINFOEXW* pStartupInfo, + void* hPC ) { + HRESULT hr{ E_UNEXPECTED }; + + if ( pStartupInfo ) { + PSIZE_T attrListSize{}; + + pStartupInfo->StartupInfo.cb = sizeof( STARTUPINFOEXW ); + pStartupInfo->StartupInfo.dwFlags = STARTF_USESTDHANDLES; + + // Get the size of the thread attribute list. + InitializeProcThreadAttributeList( NULL, 1, 0, attrListSize ); + + // Allocate a thread attribute list of the correct size + pStartupInfo->lpAttributeList = reinterpret_cast( + HeapAlloc( GetProcessHeap(), 0, *attrListSize ) ); + + // Initialize thread attribute list + if ( pStartupInfo->lpAttributeList && + InitializeProcThreadAttributeList( pStartupInfo->lpAttributeList, 1, 0, + attrListSize ) ) { + // Set Pseudo Console attribute + hr = UpdateProcThreadAttribute( pStartupInfo->lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC, sizeof( hPC ), + NULL, NULL ) + ? S_OK + : HANDLE_WIN_ERR( GetLastError() ); + } else { + hr = HANDLE_WIN_ERR( GetLastError() ); + } + } + return hr; +} + +Process::Process( AutoHandle&& hProcess, LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList ) : + m_status( ProcessStatus::RUNNING ), + m_leaveRunning( false ), + m_exitCode( 1 ), + m_hProcess( std::move( hProcess ) ), + m_lpAttributeList( lpAttributeList ) {} + +Process::~Process() { + if ( m_lpAttributeList ) { + DeleteProcThreadAttributeList( m_lpAttributeList ); + HeapFree( GetProcessHeap(), 0, m_lpAttributeList ); + } + if ( !m_leaveRunning ) { + Terminate(); + } +} + +void Process::CheckExitStatus() { + if ( m_status == ProcessStatus::RUNNING ) { + DWORD exitCode; + if ( GetExitCodeProcess( (HANDLE)m_hProcess, &exitCode ) && exitCode != STILL_ACTIVE ) { + m_status = ProcessStatus::EXITED; + m_exitCode = (int)exitCode; + } + } +} + +bool Process::HasExited() const { + return m_status == ProcessStatus::EXITED; +} + +int Process::GetExitCode() const { + return m_status == ProcessStatus::EXITED ? m_exitCode : STILL_ACTIVE; +} + +void Process::Terminate() { + if ( m_status == ProcessStatus::RUNNING ) { + TerminateProcess( (HANDLE)m_hProcess, EXIT_FAILURE ); + m_exitCode = EXIT_FAILURE; + m_status = ProcessStatus::EXITED; + m_hProcess.Release(); + } +} + +void Process::WaitForExit() { + CheckExitStatus(); + + if ( m_status == ProcessStatus::RUNNING ) { + WaitForSingleObject( (HANDLE)m_hProcess, INFINITE ); + } +} + +std::unique_ptr Process::CreateWithPipe( const std::string& program, + const std::vector& args, + const std::string& workingDirectory, + std::unique_ptr& outPipe, + bool withStderr ) { + outPipe = nullptr; + + SECURITY_ATTRIBUTES saAttr; + ZeroMemory( &saAttr, sizeof( saAttr ) ); + saAttr.nLength = sizeof( saAttr ); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + AutoHandle g_hChildStd_IN_Rd{}; + AutoHandle g_hChildStd_IN_Wr{}; + AutoHandle g_hChildStd_OUT_Rd{}; + AutoHandle g_hChildStd_OUT_Wr{}; + + if ( !CreatePipe( g_hChildStd_OUT_Rd.Get(), g_hChildStd_OUT_Wr.Get(), &saAttr, 0 ) ) { + fprintf( stderr, "Failed to create pipe (Stdout)\n" ); + return nullptr; + } + + if ( !SetHandleInformation( (HANDLE)g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0 ) ) { + fprintf( stderr, "Failed to set handle information (Stdout)\n" ); + return nullptr; + } + + if ( !CreatePipe( g_hChildStd_IN_Rd.Get(), g_hChildStd_IN_Wr.Get(), &saAttr, 0 ) ) { + fprintf( stderr, "Failed to create pipe (Stdin)\n" ); + return nullptr; + } + + if ( !SetHandleInformation( (HANDLE)g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0 ) ) { + fprintf( stderr, "Failed to set handle information (Stdin)\n" ); + return nullptr; + } + + PROCESS_INFORMATION piProcInfo; + STARTUPINFOW siStartInfo; + + ZeroMemory( &piProcInfo, sizeof( PROCESS_INFORMATION ) ); + ZeroMemory( &siStartInfo, sizeof( STARTUPINFOW ) ); + + siStartInfo.cb = sizeof( STARTUPINFOW ); + siStartInfo.hStdError = withStderr ? (HANDLE)g_hChildStd_OUT_Wr : INVALID_HANDLE_VALUE; + siStartInfo.hStdOutput = (HANDLE)g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = (HANDLE)g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + std::ostringstream oss; + oss << std::quoted( program ); + for ( auto& arg : args ) { + oss << ' ' << arg; + } + std::wstring commandLine = stringToWideString( oss.str() ); + // TODO: If workingDirectory is relative, make it absolute (relative to the + // current process working directory) + std::wstring workingDir = stringToWideString( workingDirectory ); + + if ( CreateProcessW( NULL, (LPWSTR)commandLine.c_str(), NULL, NULL, TRUE, 0, NULL, + workingDir.empty() ? NULL : workingDir.c_str(), &siStartInfo, + &piProcInfo ) ) { + std::unique_ptr pipe = std::unique_ptr( + new Pipe( std::move( g_hChildStd_OUT_Rd ), std::move( g_hChildStd_IN_Wr ) ) ); + outPipe = std::move( pipe ); + + AutoHandle hProcess{ piProcInfo.hProcess }; + AutoHandle hThread{ piProcInfo.hThread }; + + return std::unique_ptr( new Process( std::move( hProcess ), NULL ) ); + } + + PrintWinApiError( GetLastError() ); + + return nullptr; +} + +std::unique_ptr +Process::CreateWithPseudoTerminal( const std::string& program, const std::vector& args, + const std::string& workingDirectory, + Terminal::PseudoTerminal& pseudoTerminal ) { + HRESULT hr{ E_UNEXPECTED }; + + AutoHandle hProcess{}; + AutoHandle hThread{}; + + STARTUPINFOEXW startupInfo{}; + PROCESS_INFORMATION piClient{}; + + std::ostringstream oss; + oss << std::quoted( program ); + for ( auto& arg : args ) { + oss << ' ' << arg; + } + std::wstring commandLine = stringToWideString( oss.str() ); + // TODO: If workingDirectory is relative, make it absolute (relative to the + // current process working directory) + std::wstring workingDir = stringToWideString( workingDirectory ); + + if ( ( hr = InitializeStartupInfoAttachedToPseudoConsole( &startupInfo, + pseudoTerminal.m_phPC ) ) != S_OK ) { + PrintErrorResult( hr ); + goto fail; + } + + hr = CreateProcessW( NULL, // No module name - use Command Line + (wchar_t*)commandLine.c_str(), // Command Line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Inherit handles + EXTENDED_STARTUPINFO_PRESENT, // Creation flags + NULL, // Use parent's environment block + workingDir.empty() ? NULL + : workingDir.c_str(), // Use parent's starting directory + &startupInfo.StartupInfo, // Pointer to STARTUPINFO + &piClient ) // Pointer to PROCESS_INFORMATION + ? S_OK + : HANDLE_WIN_ERR( GetLastError() ); + + if ( hr != S_OK ) { + goto fail; + } + + hProcess = AutoHandle( piClient.hProcess ); + hThread = AutoHandle( piClient.hThread ); + + pseudoTerminal.m_attached = true; + + return std::unique_ptr( + new Process( std::move( hProcess ), startupInfo.lpAttributeList ) ); +fail: + return std::unique_ptr(); +} + #endif diff --git a/src/tools/eterm/system/process.hpp b/src/tools/eterm/system/process.hpp index 5442dd0ce..b7e10e24d 100644 --- a/src/tools/eterm/system/process.hpp +++ b/src/tools/eterm/system/process.hpp @@ -31,9 +31,13 @@ #ifndef WIN32 #include #include +#else +#define NTDDI_VERSION NTDDI_WIN10_RS5 +#include #endif namespace Hexe { namespace System { + class Process final : public IProcess { private: ProcessStatus m_status; diff --git a/src/tools/eterm/system/process.win32.cpp b/src/tools/eterm/system/process.win32.cpp deleted file mode 100644 index 109cb082f..000000000 --- a/src/tools/eterm/system/process.win32.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2020 Fredrik A. Kristiansen - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#ifdef WIN32 -#include "../system/process.h" -#include "../system/pipe.h" -#include "windowserrors.h" -#include -#include -#include - -using namespace Hexe::System; -using namespace Hexe; - -static std::wstring stringToWideString( const std::string& str ) { - if ( str.empty() ) - return std::wstring(); - int size_needed = MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0 ); - std::wstring wstrTo( size_needed, 0 ); - MultiByteToWideChar( CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed ); - return wstrTo; -} - -#define HANDLE_WIN_ERR( err ) HRESULT_FROM_WIN32( err ), PrintWinApiError( err ) - -static HRESULT InitializeStartupInfoAttachedToPseudoConsole( STARTUPINFOEXW* pStartupInfo, - HPCON hPC ) { - HRESULT hr{ E_UNEXPECTED }; - - if ( pStartupInfo ) { - size_t attrListSize{}; - - pStartupInfo->StartupInfo.cb = sizeof( STARTUPINFOEXW ); - pStartupInfo->StartupInfo.dwFlags = STARTF_USESTDHANDLES; - - // Get the size of the thread attribute list. - InitializeProcThreadAttributeList( NULL, 1, 0, &attrListSize ); - - // Allocate a thread attribute list of the correct size - pStartupInfo->lpAttributeList = reinterpret_cast( - HeapAlloc( GetProcessHeap(), 0, attrListSize ) ); - - // Initialize thread attribute list - if ( pStartupInfo->lpAttributeList && - InitializeProcThreadAttributeList( pStartupInfo->lpAttributeList, 1, 0, - &attrListSize ) ) { - // Set Pseudo Console attribute - hr = UpdateProcThreadAttribute( pStartupInfo->lpAttributeList, 0, - PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, hPC, sizeof( hPC ), - NULL, NULL ) - ? S_OK - : HANDLE_WIN_ERR( GetLastError() ); - } else { - hr = HANDLE_WIN_ERR( GetLastError() ); - } - } - return hr; -} - -Process::Process( AutoHandle&& hProcess, LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList ) : - m_status( ProcessStatus::RUNNING ), - m_leaveRunning( false ), - m_exitCode( 1 ), - m_hProcess( std::move( hProcess ) ), - m_lpAttributeList( lpAttributeList ) {} - -Process::~Process() { - if ( m_lpAttributeList ) { - DeleteProcThreadAttributeList( m_lpAttributeList ); - HeapFree( GetProcessHeap(), 0, m_lpAttributeList ); - } - if ( !m_leaveRunning ) { - Terminate(); - } -} - -void Process::CheckExitStatus() { - if ( m_status == ProcessStatus::RUNNING ) { - DWORD exitCode; - if ( GetExitCodeProcess( (HANDLE)m_hProcess, &exitCode ) && exitCode != STILL_ACTIVE ) { - m_status = ProcessStatus::EXITED; - m_exitCode = (int)exitCode; - } - } -} - -bool Process::HasExited() const { - return m_status == ProcessStatus::EXITED; -} - -int Process::GetExitCode() const { - return m_status == ProcessStatus::EXITED ? m_exitCode : STILL_ACTIVE; -} - -void Process::Terminate() { - if ( m_status == ProcessStatus::RUNNING ) { - TerminateProcess( (HANDLE)m_hProcess, EXIT_FAILURE ); - m_exitCode = EXIT_FAILURE; - m_status = ProcessStatus::EXITED; - m_hProcess.Release(); - } -} - -void Process::WaitForExit() { - CheckExitStatus(); - - if ( m_status == ProcessStatus::RUNNING ) { - WaitForSingleObject( (HANDLE)m_hProcess, INFINITE ); - } -} - -std::unique_ptr Process::CreateWithPipe( const std::string& program, - const std::vector& args, - const std::string& workingDirectory, - std::unique_ptr& outPipe, - bool withStderr ) { - outPipe = nullptr; - - SECURITY_ATTRIBUTES saAttr; - ZeroMemory( &saAttr, sizeof( saAttr ) ); - saAttr.nLength = sizeof( saAttr ); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - AutoHandle g_hChildStd_IN_Rd{}; - AutoHandle g_hChildStd_IN_Wr{}; - AutoHandle g_hChildStd_OUT_Rd{}; - AutoHandle g_hChildStd_OUT_Wr{}; - - if ( !CreatePipe( g_hChildStd_OUT_Rd.Get(), g_hChildStd_OUT_Wr.Get(), &saAttr, 0 ) ) { - fprintf( stderr, "Failed to create pipe (Stdout)\n" ); - return nullptr; - } - - if ( !SetHandleInformation( (HANDLE)g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0 ) ) { - fprintf( stderr, "Failed to set handle information (Stdout)\n" ); - return nullptr; - } - - if ( !CreatePipe( g_hChildStd_IN_Rd.Get(), g_hChildStd_IN_Wr.Get(), &saAttr, 0 ) ) { - fprintf( stderr, "Failed to create pipe (Stdin)\n" ); - return nullptr; - } - - if ( !SetHandleInformation( (HANDLE)g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0 ) ) { - fprintf( stderr, "Failed to set handle information (Stdin)\n" ); - return nullptr; - } - - PROCESS_INFORMATION piProcInfo; - STARTUPINFOW siStartInfo; - - ZeroMemory( &piProcInfo, sizeof( PROCESS_INFORMATION ) ); - ZeroMemory( &siStartInfo, sizeof( STARTUPINFOW ) ); - - siStartInfo.cb = sizeof( STARTUPINFOW ); - siStartInfo.hStdError = withStderr ? (HANDLE)g_hChildStd_OUT_Wr : INVALID_HANDLE_VALUE; - siStartInfo.hStdOutput = (HANDLE)g_hChildStd_OUT_Wr; - siStartInfo.hStdInput = (HANDLE)g_hChildStd_IN_Rd; - siStartInfo.dwFlags |= STARTF_USESTDHANDLES; - - std::ostringstream oss; - oss << std::quoted( program ); - for ( auto& arg : args ) { - oss << ' ' << arg; - } - std::wstring commandLine = stringToWideString( oss.str() ); - // TODO: If workingDirectory is relative, make it absolute (relative to the - // current process working directory) - std::wstring workingDir = stringToWideString( workingDirectory ); - - if ( CreateProcessW( NULL, (LPWSTR)commandLine.c_str(), NULL, NULL, TRUE, 0, NULL, - workingDir.empty() ? NULL : workingDir.c_str(), &siStartInfo, - &piProcInfo ) ) { - std::unique_ptr pipe = std::unique_ptr( - new Pipe( std::move( g_hChildStd_OUT_Rd ), std::move( g_hChildStd_IN_Wr ) ) ); - outPipe = std::move( pipe ); - - AutoHandle hProcess{ piProcInfo.hProcess }; - AutoHandle hThread{ piProcInfo.hThread }; - - return std::unique_ptr( new Process( std::move( hProcess ), NULL ) ); - } - - PrintWinApiError( GetLastError() ); - - return nullptr; -} - -std::unique_ptr -Process::CreateWithPseudoTerminal( const std::string& program, const std::vector& args, - const std::string& workingDirectory, - Terminal::PseudoTerminal& pseudoTerminal ) { - HRESULT hr{ E_UNEXPECTED }; - - AutoHandle hProcess{}; - AutoHandle hThread{}; - - STARTUPINFOEXW startupInfo{}; - PROCESS_INFORMATION piClient{}; - - std::ostringstream oss; - oss << std::quoted( program ); - for ( auto& arg : args ) { - oss << ' ' << arg; - } - std::wstring commandLine = stringToWideString( oss.str() ); - // TODO: If workingDirectory is relative, make it absolute (relative to the - // current process working directory) - std::wstring workingDir = stringToWideString( workingDirectory ); - - if ( ( hr = InitializeStartupInfoAttachedToPseudoConsole( &startupInfo, - pseudoTerminal.m_phPC ) ) != S_OK ) { - PrintErrorResult( hr ); - goto fail; - } - - hr = CreateProcessW( NULL, // No module name - use Command Line - (wchar_t*)commandLine.c_str(), // Command Line - NULL, // Process handle not inheritable - NULL, // Thread handle not inheritable - FALSE, // Inherit handles - EXTENDED_STARTUPINFO_PRESENT, // Creation flags - NULL, // Use parent's environment block - workingDir.empty() ? NULL - : workingDir.c_str(), // Use parent's starting directory - &startupInfo.StartupInfo, // Pointer to STARTUPINFO - &piClient ) // Pointer to PROCESS_INFORMATION - ? S_OK - : HANDLE_WIN_ERR( GetLastError() ); - - if ( hr != S_OK ) { - goto fail; - } - - hProcess = AutoHandle( piClient.hProcess ); - hThread = AutoHandle( piClient.hThread ); - - pseudoTerminal.m_attached = true; - - return std::unique_ptr( - new Process( std::move( hProcess ), startupInfo.lpAttributeList ) ); -fail: - return std::unique_ptr(); -} - -#endif diff --git a/src/tools/eterm/system/processfactory.cpp b/src/tools/eterm/system/processfactory.cpp index 2b9b36047..d344c3059 100644 --- a/src/tools/eterm/system/processfactory.cpp +++ b/src/tools/eterm/system/processfactory.cpp @@ -15,7 +15,7 @@ std::unique_ptr ProcessFactory::CreateWithStdioPipe( const std::string std::unique_ptr ProcessFactory::CreateWithPseudoTerminal( const std::string& program, const std::vector& args, const std::string& workingDirectory, int numColumns, int numRows, - std::unique_ptr& outPseudoTerminal ) { + std::unique_ptr& outPseudoTerminal ) { outPseudoTerminal = nullptr; auto pseudoTerminal = Terminal::PseudoTerminal::Create( numColumns, numRows ); diff --git a/src/tools/eterm/system/processfactory.hpp b/src/tools/eterm/system/processfactory.hpp index fe720d59f..9d2b93dbf 100644 --- a/src/tools/eterm/system/processfactory.hpp +++ b/src/tools/eterm/system/processfactory.hpp @@ -23,14 +23,22 @@ #include "iprocessfactory.hpp" +using namespace Hexe::Terminal; + namespace Hexe { namespace System { + class ProcessFactory : public IProcessFactory { public: ProcessFactory() = default; + virtual ~ProcessFactory() = default; + ProcessFactory( const ProcessFactory& ) = delete; + ProcessFactory( ProcessFactory&& ) = delete; + ProcessFactory& operator=( const ProcessFactory& ) = delete; + ProcessFactory& operator=( ProcessFactory&& ) = delete; virtual std::unique_ptr CreateWithStdioPipe( const std::string& program, @@ -39,9 +47,10 @@ class ProcessFactory : public IProcessFactory { std::unique_ptr& outPipe, bool withStderr = true ) override; - virtual std::unique_ptr CreateWithPseudoTerminal( - const std::string& program, const std::vector& args, - const std::string& workingDirectory, int numColumns, int numRows, - std::unique_ptr& outPseudoTerminal ) override; + virtual std::unique_ptr + CreateWithPseudoTerminal( const std::string& program, const std::vector& args, + const std::string& workingDirectory, int numColumns, int numRows, + std::unique_ptr& outPseudoTerminal ) override; }; + }} // namespace Hexe::System diff --git a/src/tools/eterm/terminal/config.def.hpp b/src/tools/eterm/terminal/config.def.hpp deleted file mode 100644 index 8536d783a..000000000 --- a/src/tools/eterm/terminal/config.def.hpp +++ /dev/null @@ -1,97 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -/* identification sequence returned in DA and DECID */ -const char* vtiden = "\033[?6c"; - -/* - * word delimiter string - * - * More advanced example: L" `'\"()[]{}" - */ -const unsigned int worddelimiters[] = { ' ', 0 }; - -/* selection timeouts (in milliseconds) */ -static unsigned int doubleclicktimeout = 300; -static unsigned int tripleclicktimeout = 600; - -/* - * bell volume. It must be a value between -100 and 100. Use 0 for disabling - * it - */ -static int bellvolume = 0; - -/* default TERM value */ -const char* termname = "st-256color"; - -/* - * Default shape of cursor - * 2: Block ("█") - * 4: Underline ("_") - * 6: Bar ("|") - * 7: Snowman ("☃") - */ -static unsigned int cursorshape = 2; - -/* - * spaces per tab - * - * When you are changing this value, don't forget to adapt the »it« value in - * the st.info and appropriately install the st.info in the environment where - * you use this st version. - * - * it#$tabspaces, - * - * Secondly make sure your kernel is not expanding tabs. When running `stty - * -a` »tab0« should appear. You can tell the terminal to not expand tabs by - * running following command: - * - * stty tabs - */ -const unsigned int tabspaces = 8; - -/* Terminal colors (16 first used in escape sequence) */ -static const char* colorname[258] = { - // /* 8 normal colors */ - // "black", - // "red3", - // "green3", - // "yellow3", - // "blue2", - // "magenta3", - // "cyan3", - // "gray90", - - // /* 8 bright colors */ - // "gray50", - // "red", - // "green", - // "yellow", - // "#5c5cff", - // "magenta", - // "cyan", - // "white" - - "#1e2127", "#e06c75", "#98c379", "#d19a66", "#61afef", "#c678dd", "#56b6c2", - "#abb2bf", "#5c6370", "#e06c75", "#98c379", "#d19a66", "#61afef", "#c678dd", - "#56b6c2", "#ffffff", "#1e2127", "#abb2bf" - -}; - -/* - * Printable characters in ASCII, used to estimate the advance width - * of single wide characters. - */ -static const char ascii_printable[] = " !\"#$%&'()*+,-./0123456789:;<=>?" - "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" - "`abcdefghijklmnopqrstuvwxyz{|}~"; - -// /* -// * Selection types' masks. -// * Use the same masks as usual. -// * Button1Mask is always unset, to make masks match between ButtonPress. -// * ButtonRelease and MotionNotify. -// * If no match is found, regular selection is used. -// */ -// static unsigned int selmasks[] = { -// [SEL_RECTANGULAR] = Mod1Mask, -// }; diff --git a/src/tools/eterm/terminal/ipseudoterminal.hpp b/src/tools/eterm/terminal/ipseudoterminal.hpp index 3460fef13..0cb2d99fa 100644 --- a/src/tools/eterm/terminal/ipseudoterminal.hpp +++ b/src/tools/eterm/terminal/ipseudoterminal.hpp @@ -25,18 +25,26 @@ #include namespace Hexe { namespace Terminal { + class IPseudoTerminal : public Hexe::System::IPipe { public: IPseudoTerminal() = default; + IPseudoTerminal( IPseudoTerminal&& ) = delete; + IPseudoTerminal( const IPseudoTerminal& ) = delete; + IPseudoTerminal& operator=( IPseudoTerminal&& ) = delete; + IPseudoTerminal& operator=( const IPseudoTerminal& ) = delete; + virtual ~IPseudoTerminal() = default; virtual int GetNumColumns() const = 0; + virtual int GetNumRows() const = 0; virtual bool Resize( int columns, int rows ) = 0; }; + }} // namespace Hexe::Terminal diff --git a/src/tools/eterm/terminal/pseudoterminal.cpp b/src/tools/eterm/terminal/pseudoterminal.cpp index 9f83f42b5..95b310f2a 100644 --- a/src/tools/eterm/terminal/pseudoterminal.cpp +++ b/src/tools/eterm/terminal/pseudoterminal.cpp @@ -20,9 +20,9 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#ifndef WIN32 +#ifndef _WIN32 // Windows has its own source file -#include "../terminal/pseudoterminal.hpp" +#include "pseudoterminal.hpp" #include #include #include @@ -34,6 +34,7 @@ using namespace Hexe::Terminal; PseudoTerminal::~PseudoTerminal() {} + PseudoTerminal::PseudoTerminal( int columns, int rows, AutoHandle&& master, AutoHandle&& slave ) : m_columns( columns ), m_rows( rows ), @@ -121,4 +122,118 @@ std::unique_ptr PseudoTerminal::Create( int columns, int rows ) new PseudoTerminal( columns, rows, std::move( master ), std::move( slave ) ) ); } +#else +#include "pseudoterminal.hpp" +#include "windowserrors.hpp" +#include +#define NTDDI_VERSION NTDDI_WIN10_RS5 +#include + +using namespace Hexe::Terminal; + +PseudoTerminal::~PseudoTerminal() { + ClosePseudoConsole( m_phPC ); +} + +PseudoTerminal::PseudoTerminal( int columns, int rows, AutoHandle&& hInput, AutoHandle&& hOutput, + void* hPC ) : + m_hInput( std::move( hInput ) ), + m_hOutput( std::move( hOutput ) ), + m_phPC( hPC ), + m_attached( false ) { + m_size.x = (SHORT)columns; + m_size.y = (SHORT)rows; +} + +bool PseudoTerminal::IsTTY() const { + return true; +} + +bool PseudoTerminal::Resize( int columns, int rows ) { + HRESULT hr = ResizePseudoConsole( m_phPC, { (SHORT)columns, (SHORT)rows } ); + if ( hr != S_OK ) { + PrintErrorResult( hr ); + return false; + } + m_size.x = (SHORT)columns; + m_size.y = (SHORT)rows; + return true; +} + +int PseudoTerminal::Write( const char* s, size_t n ) { + constexpr size_t lim = 256; + + DWORD c = (DWORD)n; + DWORD r = 0; + while ( n > 0 ) { + if ( !WriteFile( (HANDLE)m_hOutput.Get(), s, (DWORD)( n > lim ? lim : n ), &r, nullptr ) ) { + PrintLastWinApiError(); + return -1; + } + + s += r; + n -= r; + } + return (int)c; +} + +int PseudoTerminal::Read( char* buf, size_t n, bool block ) { + DWORD available; + DWORD read; + + if ( !block ) { + if ( !PeekNamedPipe( (HANDLE)m_hInput.Get(), nullptr, 0, nullptr, &available, nullptr ) ) { + PrintLastWinApiError(); + return -1; + } + if ( available == 0 ) + return 0; + } + + if ( !ReadFile( (HANDLE)m_hInput.Get(), buf, (DWORD)n, &read, nullptr ) ) { + PrintLastWinApiError(); + return -1; + } + return (int)read; +} + +int PseudoTerminal::GetNumColumns() const { + return m_size.x; +} + +int PseudoTerminal::GetNumRows() const { + return m_size.y; +} + +std::unique_ptr PseudoTerminal::Create( int columns, int rows ) { + using Pointer = std::unique_ptr; + + HRESULT hr{ E_UNEXPECTED }; + void* hPC{ INVALID_HANDLE_VALUE }; + AutoHandle hPipeIn{}; + AutoHandle hPipeOut{}; + + AutoHandle hPipePTYIn{}; + AutoHandle hPipePTYOut{}; + + if ( CreatePipe( hPipePTYIn.Get(), hPipeOut.Get(), NULL, 0 ) && + CreatePipe( hPipeIn.Get(), hPipePTYOut.Get(), NULL, 0 ) ) { + COORD ptySize; + ptySize.X = (SHORT)columns; + ptySize.Y = (SHORT)rows; + + assert( ptySize.X > 0 ); + assert( ptySize.Y > 0 ); + + hr = CreatePseudoConsole( ptySize, (HANDLE)hPipePTYIn.Get(), (HANDLE)hPipePTYOut.Get(), 0, &hPC ); + } + + if ( hr != S_OK ) { + PrintErrorResult( hr ); + return Pointer(); + } + + return Pointer( + new PseudoTerminal( columns, rows, std::move( hPipeIn ), std::move( hPipeOut ), hPC ) ); +} #endif diff --git a/src/tools/eterm/terminal/pseudoterminal.hpp b/src/tools/eterm/terminal/pseudoterminal.hpp index 07e88b9e2..31d25d77b 100644 --- a/src/tools/eterm/terminal/pseudoterminal.hpp +++ b/src/tools/eterm/terminal/pseudoterminal.hpp @@ -23,8 +23,11 @@ #include "../system/autohandle.hpp" #include "ipseudoterminal.hpp" +#include #include +using namespace EE::Math; + namespace Hexe { namespace System { class Process; @@ -33,16 +36,16 @@ namespace Terminal { class PseudoTerminal final : public IPseudoTerminal { private: friend class ::Hexe::System::Process; -#ifdef WIN32 - COORD m_size; +#ifdef _WIN32 + Vector2i m_size; AutoHandle m_hInput; AutoHandle m_hOutput; - HPCON m_phPC; + void* m_phPC; bool m_attached; - PseudoTerminal( int columns, int rows, AutoHandle&& hInput, AutoHandle&& hOutput, HPCON hPC ); + PseudoTerminal( int columns, int rows, AutoHandle&& hInput, AutoHandle&& hOutput, void* hPC ); #else int m_columns; int m_rows; diff --git a/src/tools/eterm/terminal/pseudoterminal.win32.cpp b/src/tools/eterm/terminal/pseudoterminal.win32.cpp deleted file mode 100644 index 55d6cc57e..000000000 --- a/src/tools/eterm/terminal/pseudoterminal.win32.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// The MIT License (MIT) - -// Copyright (c) 2020 Fredrik A. Kristiansen - -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -#ifdef WIN32 -#include "../terminal/pseudoterminal.h" -#include "windowserrors.h" -#include - -using namespace Hexe::Terminal; - -PseudoTerminal::~PseudoTerminal() { - ClosePseudoConsole( m_phPC ); -} - -PseudoTerminal::PseudoTerminal( int columns, int rows, AutoHandle&& hInput, AutoHandle&& hOutput, - HPCON hPC ) : - m_hInput( std::move( hInput ) ), - m_hOutput( std::move( hOutput ) ), - m_phPC( hPC ), - m_attached( false ) { - m_size.X = (SHORT)columns; - m_size.Y = (SHORT)rows; -} - -bool PseudoTerminal::IsTTY() const { - return true; -} - -bool PseudoTerminal::Resize( int columns, int rows ) { - HRESULT hr = ResizePseudoConsole( m_phPC, { (SHORT)columns, (SHORT)rows } ); - if ( hr != S_OK ) { - PrintErrorResult( hr ); - return false; - } - m_size.X = (SHORT)columns; - m_size.Y = (SHORT)rows; - return true; -} - -int PseudoTerminal::Write( const char* s, size_t n ) { - constexpr size_t lim = 256; - - DWORD c = (DWORD)n; - DWORD r = 0; - while ( n > 0 ) { - if ( !WriteFile( (HANDLE)m_hOutput, s, (DWORD)( n > lim ? lim : n ), &r, nullptr ) ) { - PrintLastWinApiError(); - return -1; - } - - s += r; - n -= r; - } - return (int)c; -} - -int PseudoTerminal::Read( char* buf, size_t n, bool block ) { - DWORD available; - DWORD read; - - if ( !block ) { - if ( !PeekNamedPipe( (HANDLE)m_hInput, nullptr, 0, nullptr, &available, nullptr ) ) { - PrintLastWinApiError(); - return -1; - } - if ( available == 0 ) - return 0; - } - - if ( !ReadFile( (HANDLE)m_hInput, buf, (DWORD)n, &read, nullptr ) ) { - PrintLastWinApiError(); - return -1; - } - return (int)read; -} - -int PseudoTerminal::GetNumColumns() const { - return m_size.X; -} - -int PseudoTerminal::GetNumRows() const { - return m_size.Y; -} - -std::unique_ptr PseudoTerminal::Create( int columns, int rows ) { - using Pointer = std::unique_ptr; - - HRESULT hr{ E_UNEXPECTED }; - HPCON hPC{ INVALID_HANDLE_VALUE }; - AutoHandle hPipeIn{}; - AutoHandle hPipeOut{}; - - AutoHandle hPipePTYIn{}; - AutoHandle hPipePTYOut{}; - - if ( CreatePipe( hPipePTYIn.Get(), hPipeOut.Get(), NULL, 0 ) && - CreatePipe( hPipeIn.Get(), hPipePTYOut.Get(), NULL, 0 ) ) { - COORD ptySize; - ptySize.X = (SHORT)columns; - ptySize.Y = (SHORT)rows; - - assert( ptySize.X > 0 ); - assert( ptySize.Y > 0 ); - - hr = CreatePseudoConsole( ptySize, (HANDLE)hPipePTYIn, (HANDLE)hPipePTYOut, 0, &hPC ); - } - - if ( hr != S_OK ) { - PrintErrorResult( hr ); - return Pointer(); - } - - return Pointer( - new PseudoTerminal( columns, rows, std::move( hPipeIn ), std::move( hPipeOut ), hPC ) ); -} -#endif diff --git a/src/tools/eterm/terminal/terminaldisplay.cpp b/src/tools/eterm/terminal/terminaldisplay.cpp index 30546258a..4efcf9d5c 100644 --- a/src/tools/eterm/terminal/terminaldisplay.cpp +++ b/src/tools/eterm/terminal/terminaldisplay.cpp @@ -29,17 +29,22 @@ using namespace Hexe::Terminal; TerminalDisplay::TerminalDisplay() : mMode( MODE_VISIBLE ), mCursorMode( SteadyBar ), mEmulator( nullptr ) {} + void TerminalDisplay::Attach( TerminalEmulator* terminal ) { assert( mEmulator == nullptr ); mEmulator = terminal; } + void TerminalDisplay::Detach( TerminalEmulator* terminal ) { assert( mEmulator == terminal ); mEmulator = nullptr; } + void TerminalDisplay::Bell() {} + void TerminalDisplay::ResetColors() {} -int TerminalDisplay::ResetColor( int index, const char* name ) { + +int TerminalDisplay::ResetColor( int /*index*/, const char* /*name*/ ) { return 0; } @@ -58,9 +63,13 @@ void TerminalDisplay::SetCursorMode( cursor_mode cursor ) { cursor_mode TerminalDisplay::GetCursorMode() const { return mCursorMode; } -void TerminalDisplay::SetTitle( const char* title ) {} -void TerminalDisplay::SetIconTitle( const char* title ) {} -void TerminalDisplay::SetClipboard( const char* text ) {} + +void TerminalDisplay::SetTitle( const char* ) {} + +void TerminalDisplay::SetIconTitle( const char* ) {} + +void TerminalDisplay::SetClipboard( const char* ) {} + const char* TerminalDisplay::GetClipboard() const { return ""; } diff --git a/src/tools/eterm/terminal/terminaldisplay.hpp b/src/tools/eterm/terminal/terminaldisplay.hpp index 5e2e1b48a..a24bef464 100644 --- a/src/tools/eterm/terminal/terminaldisplay.hpp +++ b/src/tools/eterm/terminal/terminaldisplay.hpp @@ -24,6 +24,7 @@ #include "types.hpp" namespace Hexe { namespace Terminal { + class TerminalEmulator; class TerminalDisplay { protected: @@ -61,7 +62,8 @@ class TerminalDisplay { virtual bool DrawBegin( int columns, int rows ) = 0; virtual void DrawLine( Line line, int x1, int y, int x2 ) = 0; - virtual void DrawCursor( int cx, int cy, Glyph g, int ox, int oy, Glyph og ) = 0; + virtual void DrawCursor( int cx, int cy, TerminalGlyph g, int ox, int oy, TerminalGlyph og ) = 0; virtual void DrawEnd() = 0; }; + }} // namespace Hexe::Terminal diff --git a/src/tools/eterm/terminal/terminalemulator.cpp b/src/tools/eterm/terminal/terminalemulator.cpp index a82705242..368d09f85 100644 --- a/src/tools/eterm/terminal/terminalemulator.cpp +++ b/src/tools/eterm/terminal/terminalemulator.cpp @@ -36,7 +36,6 @@ #include "../terminal/terminalemulator.hpp" #include "boxdrawdata.hpp" -#include "config.def.hpp" #include #include #include @@ -50,6 +49,37 @@ #include #include +/* identification sequence returned in DA and DECID */ +static const char* vtiden = "\033[?6c"; + +/* + * word delimiter string + * + * More advanced example: L" `'\"()[]{}" + */ +const static unsigned int worddelimiters[] = { ' ', 0 }; + +/* + * spaces per tab + * + * When you are changing this value, don't forget to adapt the »it« value in + * the st.info and appropriately install the st.info in the environment where + * you use this st version. + * + * it#$tabspaces, + * + * Secondly make sure your kernel is not expanding tabs. When running `stty + * -a` »tab0« should appear. You can tell the terminal to not expand tabs by + * running following command: + * + * stty tabs + */ +static const unsigned int tabspaces = 8; + +#ifdef WIN32 +#include +#endif + #define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) #define MAX( a, b ) ( ( a ) < ( b ) ? ( b ) : ( a ) ) #define LEN( a ) ( sizeof( a ) / sizeof( a )[0] ) @@ -73,11 +103,11 @@ #define ISCONTROLC0( c ) ( BETWEEN( c, 0, 0x1f ) || ( c ) == 0x7f ) #define ISCONTROLC1( c ) ( BETWEEN( c, 0x80, 0x9f ) ) #define ISCONTROL( c ) ( ISCONTROLC0( c ) || ISCONTROLC1( c ) ) -#define ISDELIM( u ) ( u && Hexe_wcschr( worddelimiters, u ) ) +#define ISDELIM( u ) ( u && _wcschr( worddelimiters, u ) ) using namespace Hexe::Terminal; -static inline bool is_emoji_presentation( int32_t code ) { +static inline bool is_emoji( int32_t code ) { const int32_t rangeMin = 127744; const int32_t rangeMax = 131069; const int32_t rangeMin2 = 126980; @@ -89,22 +119,16 @@ static inline bool is_emoji_presentation( int32_t code ) { ( rangeMin3 <= code && code <= rangeMax3 ) ); } -static inline bool is_emoji( int32_t code ) { - if ( is_emoji_presentation( code ) ) - return true; - return false; -} - -static size_t Hexe_wcslen( const Rune* s ) { +static size_t _wcslen( const Rune* s ) { const Rune* a; for ( a = s; *s; s++ ) ; return s - a; } -static Rune* Hexe_wcschr( const Rune* s, Rune c ) { +static Rune* _wcschr( const Rune* s, const Rune& c ) { if ( !c ) - return (Rune*)s + Hexe_wcslen( s ); + return (Rune*)s + _wcslen( s ); for ( ; *s && *s != c; s++ ) ; return *s ? (Rune*)s : 0; @@ -115,13 +139,13 @@ static const uchar utfmask[UTF_SIZ + 1] = { 0xC0, 0x80, 0xE0, 0xF0, 0xF8 }; static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000 }; static const Rune utfmax[UTF_SIZ + 1] = { 0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF }; -int Hexe::Terminal::isboxdraw( Rune u ) { +static int isboxdraw( const Rune& u ) { Rune block = u & ~0xff; return ( block == 0x2500 && boxdata[u & 0xFF] ) || ( block == 0x2800 ); } /* the "index" is actually the entire shape data encoded as ushort */ -ushort Hexe::Terminal::boxdrawindex( const Glyph* g ) { +ushort TerminalEmulator::boxdrawindex( const TerminalGlyph* g ) { if ( ( g->u & ~0xff ) == 0x2800 ) return BRL | ( g->u & 0xFF ); if ( ( g->mode & ATTR_BOLD ) ) @@ -145,16 +169,6 @@ static void* xrealloc( void* p, size_t len ) { return p; } -static char* xstrdup( char* s ) { -#ifdef WIN32 - s = _strdup( s ); -#else - s = strdup( s ); -#endif - assert( s != NULL ); - return s; -} - static size_t utf8decode( const char*, Rune*, size_t ); static Rune utf8decodebyte( char, size_t* ); static char utf8encodebyte( Rune, size_t ); @@ -164,7 +178,7 @@ static size_t utf8encode( Rune, char* ); static char* base64dec( const char* ); static char base64dec_getc( const char** ); -static intmax_t xwrite( int, const char*, size_t ); +// static intmax_t xwrite( int, const char*, size_t ); size_t utf8decode( const char* c, Rune* u, size_t clen ) { size_t i, j, len, type; @@ -374,7 +388,7 @@ int TerminalEmulator::selected( int x, int y ) { void TerminalEmulator::selsnap( int* x, int* y, int direction ) { int newx, newy, xt, yt; int delim, prevdelim; - Glyph *gp, *prevgp; + TerminalGlyph *gp, *prevgp; switch ( sel.snap ) { case SNAP_WORD: @@ -443,7 +457,7 @@ void TerminalEmulator::selsnap( int* x, int* y, int direction ) { char* TerminalEmulator::getsel( void ) { char *str, *ptr; int y, bufsize, lastx, linelen; - Glyph *gp, *last; + TerminalGlyph *gp, *last; if ( sel.ob.x == -1 ) return NULL; @@ -565,7 +579,7 @@ void TerminalEmulator::ttywrite( const char* s, size_t n, int may_echo ) { } void TerminalEmulator::ttywriteraw( const char* s, size_t n ) { - if ( m_pty->Write( s, n ) < n ) { + if ( m_pty->Write( s, n ) < (int)n ) { _die( "Failed to write to TTY" ); } } @@ -631,7 +645,7 @@ void TerminalEmulator::treset( void ) { uint i; term.c = TCursor{}; - term.c.attr = Glyph{}; + term.c.attr = TerminalGlyph{}; term.c.attr.mode = ATTR_NULL; term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; @@ -659,7 +673,7 @@ void TerminalEmulator::treset( void ) { void TerminalEmulator::tnew( int col, int row ) { term = Term{}; term.c = TCursor{}; - term.c.attr = Glyph{}; + term.c.attr = TerminalGlyph{}; term.c.attr.fg = defaultfg; term.c.attr.bg = defaultbg; @@ -789,7 +803,7 @@ void TerminalEmulator::tmoveto( int x, int y ) { term.c.y = LIMIT( y, miny, maxy ); } -void TerminalEmulator::tsetchar( Rune u, Glyph* attr, int x, int y ) { +void TerminalEmulator::tsetchar( Rune u, TerminalGlyph* attr, int x, int y ) { static const char* vt100_0[62] = { /* 0x41 - 0x7e */ "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ @@ -829,7 +843,7 @@ void TerminalEmulator::tsetchar( Rune u, Glyph* attr, int x, int y ) { void TerminalEmulator::tclearregion( int x1, int y1, int x2, int y2 ) { int x, y, temp; - Glyph* gp; + TerminalGlyph* gp; if ( x1 > x2 ) temp = x1, x1 = x2, x2 = temp; @@ -857,7 +871,7 @@ void TerminalEmulator::tclearregion( int x1, int y1, int x2, int y2 ) { void TerminalEmulator::tdeletechar( int n ) { int dst, src, size; - Glyph* line; + TerminalGlyph* line; LIMIT( n, 0, term.col - term.c.x ); @@ -866,13 +880,13 @@ void TerminalEmulator::tdeletechar( int n ) { size = term.col - src; line = term.line[term.c.y]; - memmove( &line[dst], &line[src], size * sizeof( Glyph ) ); + memmove( &line[dst], &line[src], size * sizeof( TerminalGlyph ) ); tclearregion( term.col - n, term.c.y, term.col - 1, term.c.y ); } void TerminalEmulator::tinsertblank( int n ) { int dst, src, size; - Glyph* line; + TerminalGlyph* line; LIMIT( n, 0, term.col - term.c.x ); @@ -881,7 +895,7 @@ void TerminalEmulator::tinsertblank( int n ) { size = term.col - dst; line = term.line[term.c.y]; - memmove( &line[dst], &line[src], size * sizeof( Glyph ) ); + memmove( &line[dst], &line[src], size * sizeof( TerminalGlyph ) ); tclearregion( src, term.c.y, dst - 1, term.c.y ); } @@ -1434,6 +1448,7 @@ void TerminalEmulator::strhandle( void ) { dec = base64dec( strescseq.args[2] ); if ( dec ) { SetClipboard( dec ); + free( dec ); } else { fprintf( stderr, "erresc: invalid base64\n" ); } @@ -1526,13 +1541,13 @@ void TerminalEmulator::strreset( void ) { strescseq.siz = STR_BUF_SIZ; } -void TerminalEmulator::sendbreak( const Arg* arg ) { +void TerminalEmulator::sendbreak( const Arg* ) { // TODO: Do something about this // if (tcsendbreak(cmdfd, 0)) // perror("Error sending break"); } -void TerminalEmulator::tprinter( const char* s, size_t len ) { +void TerminalEmulator::tprinter( const char* /*s*/, size_t /*len*/ ) { // TODO: Do something about this // if (iofd != -1 && xwrite(iofd, s, len) < 0) // { @@ -1542,15 +1557,15 @@ void TerminalEmulator::tprinter( const char* s, size_t len ) { // } } -void TerminalEmulator::toggleprinter( const Arg* arg ) { +void TerminalEmulator::toggleprinter( const Arg* ) { term.mode ^= MODE_PRINT; } -void TerminalEmulator::printscreen( const Arg* arg ) { +void TerminalEmulator::printscreen( const Arg* ) { tdump(); } -void TerminalEmulator::printsel( const Arg* arg ) { +void TerminalEmulator::printsel( const Arg* ) { tdumpsel(); } @@ -1565,7 +1580,7 @@ void TerminalEmulator::tdumpsel( void ) { void TerminalEmulator::tdumpline( int n ) { char buf[UTF_SIZ]; - Glyph *bp, *end; + TerminalGlyph *bp, *end; bp = &term.line[n][0]; end = &bp[MIN( tlinelen( n ), term.col ) - 1]; @@ -1837,9 +1852,9 @@ static const unsigned char wtable[] = { #include "wide.hpp" }; -static int Hexe_wcwidth( Rune wc ) { +static int wcwidth( Rune wc ) { if ( wc < 0xffU ) - return ( wc + 1 & 0x7f ) >= 0x21 ? 1 : wc ? -1 : 0; + return ( ( wc + 1 ) & 0x7f ) >= 0x21 ? 1 : wc ? -1 : 0; if ( is_emoji( wc ) ) return 2; @@ -1867,7 +1882,7 @@ void TerminalEmulator::tputc( Rune u ) { int control; int width; size_t len; - Glyph* gp; + TerminalGlyph* gp; control = ISCONTROL( u ); if ( u < 127 || !IS_SET( MODE_UTF8 ) ) { @@ -1876,7 +1891,7 @@ void TerminalEmulator::tputc( Rune u ) { } else { len = utf8encode( u, c ); // if (!control && (width = wcwidth9(u)) < 0) { - if ( !control && ( width = Hexe_wcwidth( u ) ) < 0 ) { + if ( !control && ( width = wcwidth( u ) ) < 0 ) { if ( width == -1 ) { width = 0; } else { @@ -1978,7 +1993,7 @@ check_control_code: } if ( IS_SET( MODE_INSERT ) && term.c.x + width < term.col ) - memmove( gp + width, gp, ( term.col - term.c.x - width ) * sizeof( Glyph ) ); + memmove( gp + width, gp, ( term.col - term.c.x - width ) * sizeof( TerminalGlyph ) ); if ( term.c.x + width > term.col ) { tnewline( 1 ); @@ -2009,7 +2024,7 @@ check_control_code: int TerminalEmulator::twrite( const char* buf, int buflen, int show_ctrl ) { size_t charsize; Rune u; - size_t n; + int n; for ( n = 0; n < buflen; n += charsize ) { if ( IS_SET( MODE_UTF8 ) ) { @@ -2075,14 +2090,14 @@ void TerminalEmulator::tresize( int col, int row ) { /* resize each row to new width, zero-pad if needed */ for ( i = 0; i < minrow; i++ ) { - term.line[i] = (Line)xrealloc( term.line[i], col * sizeof( Glyph ) ); - term.alt[i] = (Line)xrealloc( term.alt[i], col * sizeof( Glyph ) ); + term.line[i] = (Line)xrealloc( term.line[i], col * sizeof( TerminalGlyph ) ); + term.alt[i] = (Line)xrealloc( term.alt[i], col * sizeof( TerminalGlyph ) ); } /* allocate any new rows */ for ( /* i = minrow */; i < row; i++ ) { - term.line[i] = (Line)xmalloc( col * sizeof( Glyph ) ); - term.alt[i] = (Line)xmalloc( col * sizeof( Glyph ) ); + term.line[i] = (Line)xmalloc( col * sizeof( TerminalGlyph ) ); + term.alt[i] = (Line)xmalloc( col * sizeof( TerminalGlyph ) ); } if ( col > term.col ) { bp = term.tabs + term.col; @@ -2135,7 +2150,7 @@ void TerminalEmulator::drawregion( TerminalDisplay& dpy, int x1, int y1, int x2, } void TerminalEmulator::draw( void ) { - int cx = term.c.x, ocx = term.ocx, ocy = term.ocy; + int cx = term.c.x /*, ocx = term.ocx, ocy = term.ocy*/; { auto dpy = m_dpy.lock(); @@ -2153,13 +2168,16 @@ void TerminalEmulator::draw( void ) { cx--; drawregion( *dpy, 0, 0, term.col, term.row ); + dpy->DrawCursor( cx, term.c.y, term.line[term.c.y][cx], term.ocx, term.ocy, term.line[term.ocy][term.ocx] ); + term.ocx = cx; term.ocy = term.c.y; dpy->DrawEnd(); } + // if (ocx != term.ocx || ocy != term.ocy) // xximspot(term.ocx, term.ocy); } @@ -2216,7 +2234,7 @@ TerminalEmulator::TerminalEmulator( PtyPtr&& pty, ProcPtr&& process, tnew( col, row ); if ( display ) { - display->SetCursorMode( (Hexe::Terminal::cursor_mode)cursorshape ); + display->SetCursorMode( SteadyUnderline ); display->Attach( this ); LoadColors(); } @@ -2245,7 +2263,7 @@ void TerminalEmulator::Terminate() { } } -void TerminalEmulator::OnProcessExit( int exitCode ) {} +void TerminalEmulator::OnProcessExit( int /*exitCode*/ ) {} void TerminalEmulator::SetClipboard( const char* str ) { auto dpy = m_dpy.lock(); @@ -2294,7 +2312,7 @@ void TerminalEmulator::Update() { return; } - int n = 10; + int n = 1024; while ( ttyread() > 0 && n > 0 ) { --n; } @@ -2305,6 +2323,7 @@ void TerminalEmulator::Update() { draw(); m_process->CheckExitStatus(); + if ( m_process->HasExited() ) { m_exitCode = m_process->GetExitCode(); m_status = TERMINATED; diff --git a/src/tools/eterm/terminal/terminalemulator.hpp b/src/tools/eterm/terminal/terminalemulator.hpp index fb377bee6..ba697e110 100644 --- a/src/tools/eterm/terminal/terminalemulator.hpp +++ b/src/tools/eterm/terminal/terminalemulator.hpp @@ -42,9 +42,6 @@ #include #include #include -#ifdef WIN32 -#include -#endif namespace Hexe { namespace Terminal { constexpr int ESC_BUF_SIZ = 512; @@ -95,15 +92,14 @@ typedef struct { int narg; /* nb of args */ } STREscape; -int isboxdraw( Rune ); -ushort boxdrawindex( const Glyph* ); - class TerminalEmulator final { public: using DpyPtr = std::weak_ptr; using PtyPtr = std::unique_ptr; using ProcPtr = std::unique_ptr; + static ushort boxdrawindex( const TerminalGlyph* g ) ; + private: DpyPtr m_dpy; PtyPtr m_pty; @@ -131,16 +127,13 @@ class TerminalEmulator final { int allowaltscreen; int allowwindowops; - private: void SetClipboard( const char* str ); void LoadColors(); int ResetColor( int x, const char* name ); - private: void OnProcessExit( int exitCode ); - private: void csidump(); void csihandle(); void csiparse(); @@ -173,7 +166,7 @@ class TerminalEmulator final { void tscrollup( int, int ); void tscrolldown( int, int ); void tsetattr( int*, int ); - void tsetchar( Rune, Glyph*, int, int ); + void tsetchar( Rune, TerminalGlyph*, int, int ); void tsetdirt( int, int ); void tsetscroll( int, int ); void tswapscreen(); @@ -191,7 +184,6 @@ class TerminalEmulator final { void selscroll( int, int ); void selsnap( int*, int*, int ); - private: void _die( const char*, ... ); void drawregion( TerminalDisplay& dpy, int, int, int, int ); void draw(); diff --git a/src/tools/eterm/terminal/types.hpp b/src/tools/eterm/terminal/types.hpp index 68df8787e..68109ced0 100644 --- a/src/tools/eterm/terminal/types.hpp +++ b/src/tools/eterm/terminal/types.hpp @@ -96,9 +96,9 @@ typedef struct { ushort mode; /* attribute flags */ uint32_t fg; /* foreground */ uint32_t bg; /* background */ -} Glyph; +} TerminalGlyph; -typedef Glyph* Line; +typedef TerminalGlyph* Line; union Arg { int i; @@ -142,7 +142,7 @@ enum escape_state { }; typedef struct { - Glyph attr; /* current char attributes */ + TerminalGlyph attr; /* current char attributes */ int x; int y; char state; diff --git a/src/tools/eterm/terminal/windowserrors.hpp b/src/tools/eterm/terminal/windowserrors.hpp index f7f8923aa..91f071030 100644 --- a/src/tools/eterm/terminal/windowserrors.hpp +++ b/src/tools/eterm/terminal/windowserrors.hpp @@ -20,7 +20,8 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #pragma once -#ifdef WIN32 +#ifdef _WIN32 +#define NTDDI_VERSION NTDDI_WIN10_RS5 #include #include #include @@ -52,4 +53,4 @@ static void PrintLastWinApiError( void ) { PrintWinApiError( GetLastError() ); } -#endif \ No newline at end of file +#endif