diff --git a/include/eepp/graphics/framebuffer.hpp b/include/eepp/graphics/framebuffer.hpp index b3eda89e5..9a38593a1 100644 --- a/include/eepp/graphics/framebuffer.hpp +++ b/include/eepp/graphics/framebuffer.hpp @@ -69,7 +69,7 @@ class EE_API FrameBuffer { Texture* getTexture() const; /** @brief Sets the frame buffer clear color. */ - void setClearColor( ColorAf Color ); + void setClearColor( const ColorAf& color ); /** @return The clear color used for the frame buffer. */ ColorAf getClearColor() const; diff --git a/src/eepp/graphics/framebuffer.cpp b/src/eepp/graphics/framebuffer.cpp index 17d83304b..7eea48c3e 100644 --- a/src/eepp/graphics/framebuffer.cpp +++ b/src/eepp/graphics/framebuffer.cpp @@ -49,8 +49,8 @@ Texture* FrameBuffer::getTexture() const { return mTexture; } -void FrameBuffer::setClearColor( ColorAf Color ) { - mClearColor = Color; +void FrameBuffer::setClearColor( const ColorAf& color ) { + mClearColor = color; } ColorAf FrameBuffer::getClearColor() const { diff --git a/src/eepp/graphics/framebufferfbo.cpp b/src/eepp/graphics/framebufferfbo.cpp index 7b5b1e599..9dfb411fe 100644 --- a/src/eepp/graphics/framebufferfbo.cpp +++ b/src/eepp/graphics/framebufferfbo.cpp @@ -9,7 +9,7 @@ namespace EE { namespace Graphics { bool FrameBufferFBO::isSupported() { - return 0 != GLi->isExtension( EEGL_EXT_framebuffer_object ); + return GLi && 0 != GLi->isExtension( EEGL_EXT_framebuffer_object ); } FrameBufferFBO::FrameBufferFBO( EE::Window::Window* window ) : diff --git a/src/tools/eterm/eterm.cpp b/src/tools/eterm/eterm.cpp index c21b5f424..afc75d98d 100644 --- a/src/tools/eterm/eterm.cpp +++ b/src/tools/eterm/eterm.cpp @@ -137,6 +137,8 @@ EE_MAIN_FUNC int main( int, char*[] ) { win->runMainLoop( &mainLoop ); } + terminal.reset(); + Engine::destroySingleton(); MemoryManager::showResults(); diff --git a/src/tools/eterm/terminal/terminaldisplay.cpp b/src/tools/eterm/terminal/terminaldisplay.cpp index ca98e742a..3477c3624 100644 --- a/src/tools/eterm/terminal/terminaldisplay.cpp +++ b/src/tools/eterm/terminal/terminaldisplay.cpp @@ -1,10 +1,11 @@ #include "terminaldisplay.hpp" #include "../system/processfactory.hpp" #include "boxdrawdata.hpp" -#include "eepp/graphics/fonttruetype.hpp" #include +#include #include #include +#include #include #include #include @@ -360,12 +361,11 @@ static const Color colormapped[256] = { Color( 208, 208, 208 ), Color( 218, 218, 218 ), Color( 228, 228, 228 ), Color( 238, 238, 238 ) }; -std::shared_ptr -TerminalDisplay::create( EE::Window::Window* window, Font* font, const Float& fontSize, - const Sizef& pixelsSize, - std::shared_ptr&& terminalEmulator ) { +std::shared_ptr TerminalDisplay::create( + EE::Window::Window* window, Font* font, const Float& fontSize, const Sizef& pixelsSize, + std::shared_ptr&& terminalEmulator, const bool& useFrameBuffer ) { std::shared_ptr terminal = std::shared_ptr( - new TerminalDisplay( window, font, fontSize, pixelsSize ) ); + new TerminalDisplay( window, font, fontSize, pixelsSize, useFrameBuffer ) ); terminal->mTerminal = std::move( terminalEmulator ); return terminal; } @@ -380,11 +380,10 @@ static Sizei gridSizeFromTermDimensions( Font* font, const Float& fontSize, return { clipColumns, clipRows }; } -std::shared_ptr -TerminalDisplay::create( EE::Window::Window* window, Font* font, const Float& fontSize, - const Sizef& pixelsSize, std::string program, - const std::vector& args, const std::string& workingDir, - const size_t& historySize, IProcessFactory* processFactory ) { +std::shared_ptr TerminalDisplay::create( + EE::Window::Window* window, Font* font, const Float& fontSize, const Sizef& pixelsSize, + std::string program, const std::vector& args, const std::string& workingDir, + const size_t& historySize, IProcessFactory* processFactory, const bool& useFrameBuffer ) { if ( program.empty() ) { #ifdef _WIN32 program = "cmd.exe"; @@ -426,7 +425,7 @@ TerminalDisplay::create( EE::Window::Window* window, Font* font, const Float& fo } std::shared_ptr terminal = std::shared_ptr( - new TerminalDisplay( window, font, fontSize, pixelsSize ) ); + new TerminalDisplay( window, font, fontSize, pixelsSize, useFrameBuffer ) ); terminal->mTerminal = TerminalEmulator::create( std::move( pseudoTerminal ), std::move( process ), terminal, historySize ); @@ -440,13 +439,18 @@ TerminalDisplay::create( EE::Window::Window* window, Font* font, const Float& fo return terminal; } +TerminalDisplay::~TerminalDisplay() { + eeSAFE_DELETE( mFrameBuffer ); +} + TerminalDisplay::TerminalDisplay( EE::Window::Window* window, Font* font, const Float& fontSize, - const Sizef& pixelsSize ) : + const Sizef& pixelsSize, const bool& useFrameBuffer ) : ITerminalDisplay(), mWindow( window ), mFont( font ), mFontSize( fontSize ), - mSize( pixelsSize ) { + mSize( pixelsSize ), + mUseFrameBuffer( useFrameBuffer ) { TerminalGlyph defaultGlyph; defaultGlyph.mode = ATTR_INVISIBLE; auto defaultColor = std::make_pair( 0U, "" ); @@ -454,6 +458,12 @@ TerminalDisplay::TerminalDisplay( EE::Window::Window* window, Font* font, const mColors.resize( eeARRAY_SIZE( colormapped ), defaultColor ); mBuffer.resize( mColumns * mRows, defaultGlyph ); ( (int&)mMode ) |= MODE_FOCUSED; + + Sizei gridSize( gridSizeFromTermDimensions( mFont, mFontSize, mSize - mPadding * 2.f ) ); + mDirtyLines.resize( gridSize.getHeight(), 1 ); + + if ( mUseFrameBuffer ) + createFrameBuffer(); } void TerminalDisplay::resetColors() { @@ -535,7 +545,7 @@ void TerminalDisplay::update() { if ( mFocus && isBlinkingCursor() && mClock.getElapsedTime().asSeconds() > 0.7 ) { mMode ^= MODE_BLINK; mClock.restart(); - invalidate(); + invalidateCursor(); } if ( mTerminal ) mTerminal->update(); @@ -638,7 +648,8 @@ bool TerminalDisplay::drawBegin( int columns, int rows ) { mBuffer.resize( columns * rows, defaultGlyph ); mColumns = columns; mRows = rows; - invalidate(); + invalidateLines(); + invalidateCursor(); } return ( ( mMode & MODE_VISIBLE ) != 0 ); @@ -651,7 +662,7 @@ void TerminalDisplay::drawLine( Line line, int x1, int y, int x2 ) { mBuffer[y * mColumns + i].mode |= ATTR_REVERSE; } } - invalidate(); + invalidateLine( y ); } void TerminalDisplay::drawCursor( int cx, int cy, TerminalGlyph g, int, int, TerminalGlyph ) { @@ -659,14 +670,14 @@ void TerminalDisplay::drawCursor( int cx, int cy, TerminalGlyph g, int, int, Ter mCursor.x = cx; mCursor.y = cy; mCursorGlyph = g; - invalidate(); + invalidateCursor(); } } void TerminalDisplay::drawEnd() {} void TerminalDisplay::draw() { - draw( mPosition.floor() + mPadding ); + draw( nullptr != mFrameBuffer ? mPadding : mPosition.floor() + mPadding ); } void TerminalDisplay::onMouseDoubleClick( const Vector2i& pos, const Uint32& flags ) { @@ -678,7 +689,7 @@ void TerminalDisplay::onMouseDoubleClick( const Vector2i& pos, const Uint32& fla mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_IDLE ) ) { auto gridPos{ positionToGrid( pos ) }; mTerminal->selstart( gridPos.x, gridPos.y, SNAP_WORD ); - invalidate(); + invalidateLines(); } } @@ -690,7 +701,7 @@ void TerminalDisplay::onMouseMotion( const Vector2i& pos, const Uint32& flags ) mTerminal->selextend( gridPos.x, gridPos.y, mWindow->getInput()->getModState() & KEYMOD_SHIFT ? SEL_RECTANGULAR : SEL_REGULAR, 0 ); - invalidate(); + invalidateLines(); } mTerminal->mousereport( TerminalMouseEventType::MouseMotion, positionToGrid( pos ), flags, mWindow->getInput()->getModState() ); @@ -732,7 +743,7 @@ void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) { if ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_IDLE ) { auto gridPos{ positionToGrid( pos ) }; mTerminal->selstart( gridPos.x, gridPos.y, 0 ); - invalidate(); + invalidateLines(); } else if ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_READY ) { mTerminal->selclear(); } @@ -918,11 +929,9 @@ inline static void drawbox( float x, float y, float w, float h, Color fg, Color } } -void TerminalDisplay::draw( const Vector2f& pos ) { - if ( !mEmulator ) - return; - - mDrawing = true; +void TerminalDisplay::drawGrid( const Vector2f& pos ) { + if ( mFrameBuffer ) + mFrameBuffer->bind(); auto fontSize = (Float)mFont->getFontHeight( mFontSize ); auto spaceCharAdvanceX = mFont->getGlyph( 'A', mFontSize, false ).advance; @@ -938,8 +947,6 @@ void TerminalDisplay::draw( const Vector2f& pos ) { Primitives p; p.setForceDraw( false ); - p.setColor( defaultBg ); - p.drawRectangle( Rectf( mPosition.asFloat(), mSize.asFloat() ) ); for ( int j = 0; j < mRows; j++ ) { x = std::floor( pos.x ); @@ -947,6 +954,11 @@ void TerminalDisplay::draw( const Vector2f& pos ) { if ( pos.y + lineHeight * j > pos.y + mSize.getHeight() ) break; + if ( mFrameBuffer && !mDirtyLines[j] ) { + y += lineHeight; + continue; + } + for ( int i = 0; i < mColumns; i++ ) { auto& glyph = mBuffer[j * mColumns + i]; auto fg = termColor( glyph.fg, mColors ); @@ -988,6 +1000,13 @@ void TerminalDisplay::draw( const Vector2f& pos ) { if ( pos.y + lineHeight * j > pos.y + mSize.getHeight() ) break; + if ( mFrameBuffer && !mDirtyLines[j] ) { + y += lineHeight; + continue; + } + + mDirtyLines[j] = false; + for ( int i = 0; i < mColumns; i++ ) { auto& glyph = mBuffer[j * mColumns + i]; auto fg = termColor( glyph.fg, mColors ); @@ -1068,7 +1087,8 @@ void TerminalDisplay::draw( const Vector2f& pos ) { y += lineHeight; } - if ( !mEmulator->isScrolling() && !IS_SET( MODE_HIDE ) ) { + if ( !mEmulator->isScrolling() && !IS_SET( MODE_HIDE ) && mDirtyCursor ) { + mDirtyCursor = false; Color drawcol; if ( IS_SET( MODE_REVERSE ) ) { if ( mEmulator->isSelected( mCursor.x, mCursor.y ) ) { @@ -1136,6 +1156,28 @@ void TerminalDisplay::draw( const Vector2f& pos ) { } } + if ( mFrameBuffer ) + mFrameBuffer->unbind(); +} + +void TerminalDisplay::draw( const Vector2f& pos ) { + if ( !mEmulator ) + return; + + mDrawing = true; + + auto defaultBg = termColor( mEmulator->getDefaultBackground(), mColors ); + Primitives p; + p.setForceDraw( false ); + p.setColor( defaultBg ); + p.drawRectangle( Rectf( mPosition.asFloat(), mSize.asFloat() ) ); + + if ( !mFrameBuffer || mDirty ) + drawGrid( pos ); + + if ( mFrameBuffer ) + drawFrameBuffer(); + mDrawing = false; mDirty = false; } @@ -1168,11 +1210,23 @@ void TerminalDisplay::onSizeChange() { if ( gridSize.getWidth() != mTerminal->getNumColumns() || gridSize.getHeight() != mTerminal->getNumRows() ) { mTerminal->resize( gridSize.getWidth(), gridSize.getHeight() ); + mDirtyLines.resize( gridSize.getHeight(), 1 ); } } else if ( mEmulator ) { if ( gridSize.getWidth() != mEmulator->getNumColumns() || gridSize.getHeight() != mEmulator->getNumRows() ) { mEmulator->resize( gridSize.getWidth(), gridSize.getHeight() ); + mDirtyLines.resize( gridSize.getHeight(), 1 ); + } + } + + if ( mFrameBuffer && ( mFrameBuffer->getWidth() < mSize.getWidth() || + mFrameBuffer->getHeight() < mSize.getHeight() ) ) { + if ( nullptr == mFrameBuffer ) { + createFrameBuffer(); + } else { + Sizei fboSize( getFrameBufferSize() ); + mFrameBuffer->resize( fboSize.getWidth(), fboSize.getHeight() ); } } } @@ -1202,6 +1256,7 @@ void TerminalDisplay::onProcessExit( int exitCode ) { fprintf( stderr, "TerminalDisplay::onProcessExit: Failed to spawn process\n" ); } + mTerminal->clearHistory(); mTerminal->setPtyAndProcess( std::move( pseudoTerminal ), std::move( process ) ); eeSAFE_DELETE( processFactory ); @@ -1340,6 +1395,21 @@ void TerminalDisplay::invalidate() { mDirty = true; } +void TerminalDisplay::invalidateCursor() { + invalidateLine( mCursor.y ); + mDirtyCursor = true; + mDirty = true; +} + +void TerminalDisplay::invalidateLine( const int& line ) { + mDirtyLines[line] = true; + mDirty = true; +} + +void TerminalDisplay::invalidateLines() { + mDirtyLines.assign( mDirtyLines.size(), 1 ); + mDirty = true; +} void TerminalDisplay::setFocus( bool focus ) { if ( focus == mFocus ) return; @@ -1355,5 +1425,33 @@ void TerminalDisplay::setFocus( bool focus ) { } else { mMode ^= MODE_FOCUS; } - invalidate(); + invalidateCursor(); +} + +Sizei TerminalDisplay::getFrameBufferSize() { + return Sizei( Math::nextPowOfTwo( (int)mSize.getWidth() ), + Math::nextPowOfTwo( (int)mSize.getHeight() ) ); +} + +void TerminalDisplay::createFrameBuffer() { + eeSAFE_DELETE( mFrameBuffer ); + Sizei fboSize( getFrameBufferSize() ); + if ( fboSize.getWidth() < 1 ) + fboSize.setWidth( 1 ); + if ( fboSize.getHeight() < 1 ) + fboSize.setHeight( 1 ); + mFrameBuffer = FrameBuffer::New( fboSize.getWidth(), fboSize.getHeight(), true ); + + // Frame buffer failed to create? + if ( !mFrameBuffer->created() ) + eeSAFE_DELETE( mFrameBuffer ); +} + +void TerminalDisplay::drawFrameBuffer() { + if ( mFrameBuffer ) { + Rect r( 0, 0, mSize.getWidth(), mSize.getHeight() ); + TextureRegion textureRegion( mFrameBuffer->getTexture()->getTextureId(), r, + r.getSize().asFloat() ); + textureRegion.draw( mPosition.floor().x, mPosition.floor().y ); + } } diff --git a/src/tools/eterm/terminal/terminaldisplay.hpp b/src/tools/eterm/terminal/terminaldisplay.hpp index abd46503c..e7976379c 100644 --- a/src/tools/eterm/terminal/terminaldisplay.hpp +++ b/src/tools/eterm/terminal/terminaldisplay.hpp @@ -6,6 +6,7 @@ #include "terminalemulator.hpp" #include #include +#include #include #include #include @@ -139,13 +140,16 @@ class TerminalDisplay : public ITerminalDisplay { static std::shared_ptr create( EE::Window::Window* window, Font* font, const Float& fontSize, const Sizef& pixelsSize, - std::shared_ptr&& terminalEmulator ); + std::shared_ptr&& terminalEmulator, + const bool& useFrameBuffer = false ); static std::shared_ptr create( EE::Window::Window* window, Font* font, const Float& fontSize, const Sizef& pixelsSize, std::string program = "", const std::vector& args = {}, const std::string& workingDir = "", const size_t& historySize = 10000, - IProcessFactory* processFactory = nullptr ); + IProcessFactory* processFactory = nullptr, const bool& useFrameBuffer = false ); + + virtual ~TerminalDisplay(); virtual void resetColors(); virtual int resetColor( Uint32 index, const char* name ); @@ -198,6 +202,12 @@ class TerminalDisplay : public ITerminalDisplay { void invalidate(); + void invalidateCursor(); + + void invalidateLine( const int& line ); + + void invalidateLines(); + bool hasFocus() const { return mFocus; } void setFocus( bool focus ); @@ -234,27 +244,33 @@ class TerminalDisplay : public ITerminalDisplay { Sizef mPadding; Vector2f mPosition; Sizef mSize; - std::atomic mDirty{ true }; - std::atomic mDrawing{ false }; + std::vector mDirtyLines; + bool mDirty{ true }; + bool mDirtyCursor{ true }; + bool mDrawing{ false }; Vector2i mCursor; TerminalGlyph mCursorGlyph; bool mUseColorEmoji{ true }; bool mPasteNewlineFix{ true }; bool mFocus{ true }; + bool mUseFrameBuffer{ true }; Clock mClock; Clock mLastDoubleClick; int mColumns{ 0 }; int mRows{ 0 }; + FrameBuffer* mFrameBuffer{ nullptr }; std::string mProgram; std::vector mArgs; std::string mWorkingDir; TerminalDisplay( EE::Window::Window* window, Font* font, const Float& fontSize, - const Sizef& pixelsSize ); + const Sizef& pixelsSize, const bool& useFrameBuffer ); void draw( const Vector2f& pos ); + void drawGrid( const Vector2f& pos ); + Vector2i positionToGrid( const Vector2i& pos ); void onSizeChange(); @@ -262,6 +278,12 @@ class TerminalDisplay : public ITerminalDisplay { virtual void onProcessExit( int exitCode ); void sendEvent( const TerminalDisplay::Event& event ); + + Sizei getFrameBufferSize(); + + void createFrameBuffer(); + + void drawFrameBuffer(); }; #endif // ETERMINALDISPLAY_HPP