diff --git a/premake4.lua b/premake4.lua index c3342e94e..c4eef9db7 100644 --- a/premake4.lua +++ b/premake4.lua @@ -174,6 +174,11 @@ newoption { { "SDL2", "SDL2" } } } +newoption { + trigger = "sharedir", + value = "PATH", + description = "Set the shared data directory (default: /usr/share/ecode)", +} newoption { trigger = "with-static-cpp", description = "Builds statically libstdc++" } function explode(div,str) @@ -591,6 +596,10 @@ function build_link_configuration( package_name, use_ee_icon ) linkoptions { "-static-libgcc -static-libstdc++" } end + if _OPTIONS["sharedir"] then + defines { "ECODE_SHAREDIR=\"" .. _OPTIONS["sharedir"] .. "\"" } + end + set_ios_config() set_apple_config() build_arch_configuration() diff --git a/premake5.lua b/premake5.lua index 7a51f935a..2023e4888 100644 --- a/premake5.lua +++ b/premake5.lua @@ -26,6 +26,11 @@ newoption { { "SDL2", "SDL2" }, } } +newoption { + trigger = "sharedir", + value = "PATH", + description = "Set the shared data directory (default: /usr/share/ecode)", +} newoption { trigger = "with-static-cpp", description = "Builds statically libstdc++" } function get_dll_extension() @@ -487,6 +492,10 @@ function build_link_configuration( package_name, use_ee_icon ) filter { "action:export-compile-commands", "system:macosx" } buildoptions { "-std=c++20" } + filter { "options:sharedir" } + if _OPTIONS["sharedir"] then + defines { "ECODE_SHAREDIR=\"" .. _OPTIONS["sharedir"] .. "\"" } + end filter {} end diff --git a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp index 041f0d13a..4b1320970 100644 --- a/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminaldisplay.hpp @@ -274,6 +274,8 @@ class TerminalDisplay : public ITerminalDisplay { const std::string& getWorkingDir() const { return mWorkingDir; } + bool isAppCapturingMouse() const; + protected: EE::Window::Window* mWindow; std::vector mBuffer; @@ -365,6 +367,7 @@ class TerminalDisplay : public ITerminalDisplay { Rectf updateIMELocation(); void drawBg( bool toFBO = false ); + }; }} // namespace eterm::Terminal diff --git a/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp b/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp index 9ccb2d74f..58b11df44 100644 --- a/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminalemulator.hpp @@ -249,6 +249,8 @@ class TerminalEmulator final { mPromptStateChangedCb = promptStateChangedCb; } + int getTerminalMode() const { return mTerm.mode; } + private: DpyPtr mDpy; PtyPtr mPty; diff --git a/src/modules/eterm/include/eterm/terminal/terminaltypes.hpp b/src/modules/eterm/include/eterm/terminal/terminaltypes.hpp index 4269e5d4f..a4ba1e00c 100644 --- a/src/modules/eterm/include/eterm/terminal/terminaltypes.hpp +++ b/src/modules/eterm/include/eterm/terminal/terminaltypes.hpp @@ -25,6 +25,10 @@ #include #include +#include +#include + +using namespace std::literals; namespace eterm { namespace Terminal { @@ -40,6 +44,46 @@ enum TerminalCursorMode { MAX_CURSOR }; +struct TerminalCursorHelper { + static TerminalCursorMode modeFromString( std::string_view str ) { + if ( str == "blinking_block" ) + return TerminalCursorMode::BlinkingBlock; + if ( str == "steady_block" ) + return TerminalCursorMode::SteadyBlock; + if ( str == "blink_underline" ) + return TerminalCursorMode::BlinkUnderline; + if ( str == "steady_underline" ) + return TerminalCursorMode::SteadyUnderline; + if ( str == "blink_bar" ) + return TerminalCursorMode::BlinkBar; + if ( str == "steady_bar" ) + return TerminalCursorMode::SteadyBar; + return TerminalCursorMode::SteadyUnderline; + } + + static std::string modeToString( TerminalCursorMode mode ) { + switch ( mode ) { + case BlinkingBlock: + case BlinkingBlockDefault: + return "blinking_block"; + case SteadyBlock: + return "steady_block"; + case BlinkUnderline: + return "blink_underline"; + case SteadyUnderline: + return "steady_underline"; + case BlinkBar: + return "blink_bar"; + case SteadyBar: + return "steady_bar"; + case StExtension: + case MAX_CURSOR: + break; + } + return "steady_underline"; + } +}; + enum TerminalWinMode { MODE_VISIBLE = 1 << 0, MODE_FOCUSED = 1 << 1, diff --git a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp index 4f58a2f89..dd2ad3764 100644 --- a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp @@ -603,6 +603,11 @@ void TerminalDisplay::setColorScheme( const TerminalColorScheme& colorScheme ) { invalidateLines(); } +bool TerminalDisplay::isAppCapturingMouse() const { + return mTerminal && + ( mMode & ( MODE_MOUSEX10 | MODE_MOUSEBTN | MODE_MOUSEMOTION | MODE_MOUSEMANY ) ); +} + bool TerminalDisplay::isAltScr() const { return mEmulator && mEmulator->tisaltscr(); } @@ -788,6 +793,10 @@ void TerminalDisplay::drawCursor( int cx, int cy, TerminalGlyph g, int, int, Ter if ( mCursor != Vector2i( cx, cy ) || mCursorGlyph != g ) { mCursor.x = cx; mCursor.y = cy; + if ( isBlinkingCursor() ) { + mMode |= MODE_BLINK; + mClock.restart(); + } mCursorGlyph = g; invalidateCursor(); } @@ -804,7 +813,7 @@ void TerminalDisplay::onMouseDoubleClick( const Vector2i& pos, const Uint32& fla if ( flags & EE_BUTTON_LMASK ) mLastDoubleClick.restart(); - if ( !isAltScr() && ( flags & EE_BUTTON_LMASK ) && + if ( !isAppCapturingMouse() && ( flags & EE_BUTTON_LMASK ) && ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_EMPTY || mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_IDLE ) ) { auto gridPos{ positionToGrid( pos ) }; @@ -814,7 +823,10 @@ void TerminalDisplay::onMouseDoubleClick( const Vector2i& pos, const Uint32& fla } void TerminalDisplay::onMouseMove( const Vector2i& pos, const Uint32& flags ) { - if ( !isAltScr() && ( flags & EE_BUTTON_LMASK ) && + bool shiftPressed = ( mWindow->getInput()->getModState() & KEYMOD_SHIFT ) != 0; + bool isCapturingMouse = isAppCapturingMouse() && !shiftPressed; + + if ( !isCapturingMouse && ( flags & EE_BUTTON_LMASK ) && ( mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_EMPTY || mTerminal->getSelectionMode() == TerminalSelectionMode::SEL_READY ) ) { auto gridPos{ positionToGrid( pos ) }; @@ -828,16 +840,18 @@ void TerminalDisplay::onMouseMove( const Vector2i& pos, const Uint32& flags ) { } void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) { - if ( ( flags & EE_BUTTON_LMASK ) && mDraggingSel ) { + bool shiftPressed = ( mWindow->getInput()->getModState() & KEYMOD_SHIFT ) != 0; + bool isCapturingMouse = isAppCapturingMouse() && !shiftPressed; + + if ( ( flags & EE_BUTTON_LMASK ) && mDraggingSel ) return; - } auto gridPos{ positionToGrid( pos ) }; - if ( !isAltScr() && ( flags & EE_BUTTON_LMASK ) && + if ( !isCapturingMouse && ( flags & EE_BUTTON_LMASK ) && mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) { mTerminal->selstart( gridPos.x, gridPos.y, SNAP_LINE ); - } else if ( !isAltScr() && ( flags & EE_BUTTON_LMASK ) ) { + } else if ( !isCapturingMouse && ( flags & EE_BUTTON_LMASK ) ) { if ( !mDraggingSel ) { mTerminal->selstart( gridPos.x, gridPos.y, 0 ); mDraggingSel = true; @@ -1292,6 +1306,85 @@ void TerminalDisplay::drawGrid( const Vector2f& pos ) { invalidateCursor(); } + bool redrawCursor = + !mEmulator->isScrolling() && !IS_SET( MODE_HIDE ) && ( !mUseFrameBuffer || mDirtyCursor ); + bool mustRenderUnderline = false; + + if ( redrawCursor ) { + mDirtyCursor = false; + Color drawcol; + + if ( IS_SET( MODE_REVERSE ) ) { + if ( mEmulator->isSelected( mCursor.x, mCursor.y ) ) { + drawcol = mColorScheme.getCursor(); + } else { + drawcol = mColorScheme.getBackground(); + } + } else { + drawcol = mEmulator->isSelected( mCursor.x, mCursor.y ) ? mColorScheme.getBackground() + : mColorScheme.getCursor(); + } + + mPrimitives.setColor( drawcol ); + + /* draw the new one */ + if ( IS_SET( MODE_FOCUSED ) ) { + switch ( mCursorMode ) { + case StExtension: { + auto* gd = mFont->getGlyphDrawable( + mCursorMode == StExtension ? 0xFB04 /* TUX */ : mCursorGlyph.u, mFontSize ); + gd->setColor( termColor( mCursorGlyph.fg, mColors ) ); + gd->setDrawMode( GlyphDrawable::DrawMode::Text ); + gd->draw( + { pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight } ); + break; + } + case BlinkUnderline: { + if ( !( mMode & MODE_BLINK ) ) + break; + } + case SteadyUnderline: { + mustRenderUnderline = true; + break; + } + case BlinkBar: { + if ( !( mMode & MODE_BLINK ) ) + break; + } + case SteadyBar: { + mPrimitives.drawRectangle( Rectf( + { pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight }, + { std::ceil( PixelDensity::dpToPx( 1 ) ), lineHeight } ) ); + break; + } + case BlinkingBlock: + case BlinkingBlockDefault: { + if ( !( mMode & MODE_BLINK ) ) + break; + } + case SteadyBlock: + case TerminalCursorMode::MAX_CURSOR: { + mPrimitives.drawRectangle( Rectf( + { pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight }, + { spaceCharAdvanceX, lineHeight } ) ); + break; + } + } + } else { + Vector2f cpos{ pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight }; + mPrimitives.setColor( drawcol ); + mPrimitives.drawRectangle( + Rectf( Vector2f( cpos.x, cpos.y ), { spaceCharAdvanceX, cursorThickness } ) ); + mPrimitives.drawRectangle( + Rectf( Vector2f( cpos.x, cpos.y ), { cursorThickness, lineHeight } ) ); + mPrimitives.drawRectangle( Rectf( Vector2f( cpos.x + spaceCharAdvanceX, cpos.y ), + { cursorThickness, lineHeight } ) ); + mPrimitives.drawRectangle( + Rectf( Vector2f( cpos.x, cpos.y + lineHeight - cursorThickness ), + { spaceCharAdvanceX, cursorThickness } ) ); + } + } + if ( mVBForeground ) { mFont->getTexture( mFontSize )->bind(); if ( dirtyFG ) @@ -1317,71 +1410,13 @@ void TerminalDisplay::drawGrid( const Vector2f& pos ) { } } - if ( !mEmulator->isScrolling() && !IS_SET( MODE_HIDE ) && - ( !mUseFrameBuffer || mDirtyCursor ) ) { - mDirtyCursor = false; - Color drawcol; - if ( IS_SET( MODE_REVERSE ) ) { - if ( mEmulator->isSelected( mCursor.x, mCursor.y ) ) { - drawcol = mColorScheme.getCursor(); - } else { - drawcol = mColorScheme.getBackground(); - } - } else { - drawcol = mEmulator->isSelected( mCursor.x, mCursor.y ) ? mColorScheme.getBackground() - : mColorScheme.getCursor(); - } - - mPrimitives.setColor( drawcol ); - /* draw the new one */ - if ( IS_SET( MODE_FOCUSED ) ) { - switch ( mCursorMode ) { - case StExtension: - case SteadyBlock: { - auto* gd = mFont->getGlyphDrawable( - mCursorMode == StExtension ? 0xFB04 /* TUX */ : mCursorGlyph.u, mFontSize ); - gd->setColor( termColor( mCursorGlyph.fg, mColors ) ); - gd->setDrawMode( GlyphDrawable::DrawMode::Text ); - gd->draw( - { pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight } ); - break; - } - case BlinkUnderline: { - if ( !( mMode & MODE_BLINK ) ) - break; - } - case SteadyUnderline: - mPrimitives.drawRectangle( - Rectf( { pos.x + mCursor.x * spaceCharAdvanceX, - pos.y + mCursor.y * lineHeight + lineHeight - cursorThickness }, - { spaceCharAdvanceX, cursorThickness } ) ); - break; - case BlinkingBlock: - case BlinkingBlockDefault: - case BlinkBar: { - if ( !( mMode & MODE_BLINK ) ) - break; - } - case SteadyBar: - case TerminalCursorMode::MAX_CURSOR: - mPrimitives.drawRectangle( Rectf( - { pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight }, - { spaceCharAdvanceX, lineHeight } ) ); - break; - } - } else { - Vector2f cpos{ pos.x + mCursor.x * spaceCharAdvanceX, pos.y + mCursor.y * lineHeight }; - mPrimitives.setColor( drawcol ); - mPrimitives.drawRectangle( - Rectf( Vector2f( cpos.x, cpos.y ), { spaceCharAdvanceX, cursorThickness } ) ); - mPrimitives.drawRectangle( - Rectf( Vector2f( cpos.x, cpos.y ), { cursorThickness, lineHeight } ) ); - mPrimitives.drawRectangle( Rectf( Vector2f( cpos.x + spaceCharAdvanceX, cpos.y ), - { cursorThickness, lineHeight } ) ); - mPrimitives.drawRectangle( - Rectf( Vector2f( cpos.x, cpos.y + lineHeight - cursorThickness ), - { spaceCharAdvanceX, cursorThickness } ) ); - } + // Underline is rendered after foreground render because it usually clashes with the underlines + // decorations and ends up being not visible, I prefer to do this even it it's not standard. + if ( mustRenderUnderline ) { + mPrimitives.drawRectangle( + Rectf( { pos.x + mCursor.x * spaceCharAdvanceX, + pos.y + mCursor.y * lineHeight + lineHeight - cursorThickness }, + { spaceCharAdvanceX, cursorThickness } ) ); } if ( mFrameBuffer && pos.y + lineHeight * mRows < mSize.getHeight() ) { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 39f0b65fc..841a634d4 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -230,6 +230,8 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, ini.getValueB( "terminal", "unsupported_os_warn_disabled", false ); term.closeTerminalTabOnExit = ini.getValueB( "terminal", "close_terminal_tab_on_exit", false ); term.warnBeforeClosingTab = ini.getValueB( "terminal", "warn_before_closing_tab", true ); + term.cursorStyle = TerminalCursorHelper::modeFromString( + ini.getValue( "terminal", "cursor_style", "steady_underline" ) ); workspace.restoreLastSession = ini.getValueB( "workspace", "restore_last_session", false ); workspace.checkForUpdatesAtStartup = @@ -392,6 +394,8 @@ void AppConfig::save( const std::vector& recentFiles, ini.setValueB( "terminal", "unsupported_os_warn_disabled", term.unsupportedOSWarnDisabled ); ini.setValueB( "terminal", "close_terminal_tab_on_exit", term.closeTerminalTabOnExit ); ini.setValueB( "terminal", "warn_before_closing_tab", term.warnBeforeClosingTab ); + ini.setValue( "terminal", "cursor_style", + TerminalCursorHelper::modeToString( term.cursorStyle ) ); ini.setValueB( "window", "vsync", context.VSync ); ini.setValue( "window", "glversion", diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index d6102be40..10c6e65fc 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -9,6 +9,8 @@ #include #include +#include + #include using namespace EE; @@ -18,6 +20,7 @@ using namespace EE::UI::CSS; using namespace EE::UI::Tools; using namespace EE::System; using namespace EE::Window; +using namespace eterm::Terminal; namespace ecode { class App; @@ -180,6 +183,7 @@ struct TerminalConfig { bool unsupportedOSWarnDisabled{ false }; bool closeTerminalTabOnExit{ false }; bool warnBeforeClosingTab{ true }; + TerminalCursorMode cursorStyle{ TerminalCursorMode::SteadyUnderline }; }; struct WorkspaceConfig { diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 00d8cb483..97554341e 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -3761,7 +3761,13 @@ void App::init( InitParameters& params ) { mDisablePlugins = params.disablePlugins; mRedirectToFirstInstance = params.redirectToFirstInstance; - mResPath = Sys::getProcessPath(); +#ifdef ECODE_SHAREDIR + mResPath = ECODE_SHAREDIR; + FileSystem::dirAddSlashAtEnd( mResPath ); +#else + mResPath = Sys::getProcessPath(); +#endif + #if EE_PLATFORM == EE_PLATFORM_LINUX if ( String::contains( mResPath, ".mount_" ) ) FileSystem::dirAddSlashAtEnd( mResPath ); diff --git a/src/tools/ecode/settingsmenu.cpp b/src/tools/ecode/settingsmenu.cpp index 9356ff2b7..871cb44ed 100644 --- a/src/tools/ecode/settingsmenu.cpp +++ b/src/tools/ecode/settingsmenu.cpp @@ -976,6 +976,48 @@ UIMenu* SettingsMenu::createTerminalMenu() { mTerminalMenu->addSubMenu( i18n( "new_terminal_behavior", "New Terminal Behavior" ), findIcon( "terminal" ), newTerminalBehaviorSubMenu ); + UIPopUpMenu* cursorStyleMenu = UIPopUpMenu::New(); + + mTerminalMenu->addSubMenu( i18n( "cursor_style", "Cursor Style" ), findIcon( "terminal" ), + cursorStyleMenu ); + + const auto cursorStyleMenuRefresh = [this, cursorStyleMenu] { + const auto& cfg = mApp->getConfig(); + auto el = cursorStyleMenu->find( + "tcursor_" + TerminalCursorHelper::modeToString( cfg.term.cursorStyle ) ); + if ( el && el->isType( UI_TYPE_MENURADIOBUTTON ) ) + el->asType()->setActive( true ); + }; + + cursorStyleMenu->on( + Event::OnMenuShow, [this, cursorStyleMenu, cursorStyleMenuRefresh]( auto ) { + if ( cursorStyleMenu->getCount() == 0 ) { + cursorStyleMenu->addRadioButton( i18n( "blinking_block", "Blinking Block" ) ) + ->setId( "tcursor_blinking_block" ); + cursorStyleMenu->addRadioButton( i18n( "steady_block", "Steady Block" ) ) + ->setId( "tcursor_steady_block" ); + cursorStyleMenu->addRadioButton( i18n( "blink_underline", "Blink Underline" ) ) + ->setId( "tcursor_blink_underline" ); + cursorStyleMenu->addRadioButton( i18n( "steady_underline", "Steady Underline" ) ) + ->setId( "tcursor_steady_underline" ); + cursorStyleMenu->addRadioButton( i18n( "blink_bar", "Blink Bar" ) ) + ->setId( "tcursor_blink_bar" ); + cursorStyleMenu->addRadioButton( i18n( "steady_bar", "Steady Bar" ) ) + ->setId( "tcursor_steady_bar" ); + } + cursorStyleMenuRefresh(); + } ); + + cursorStyleMenu->on( Event::OnItemClicked, [this]( const Event* event ) { + const std::string& id( event->getNode()->getId() ); + std::string cursor = id.substr( 8 ); + mApp->getConfig().term.cursorStyle = TerminalCursorHelper::modeFromString( cursor ); + mSplitter->forEachWidgetType( UI_TYPE_TERMINAL, [this]( UIWidget* widget ) { + widget->asType()->getTerm()->setCursorMode( + mApp->getConfig().term.cursorStyle ); + } ); + } ); + mTerminalMenu->addSeparator(); mTerminalMenu diff --git a/src/tools/ecode/terminalmanager.cpp b/src/tools/ecode/terminalmanager.cpp index 8a61004e7..f6fa621fd 100644 --- a/src/tools/ecode/terminalmanager.cpp +++ b/src/tools/ecode/terminalmanager.cpp @@ -533,6 +533,8 @@ UITerminal* TerminalManager::createNewTerminal( return nullptr; } + term->getTerm()->setCursorMode( mApp->termConfig().cursorStyle ); + auto ret = mApp->getSplitter()->createWidgetInTabWidget( tabWidget, term, title.empty() ? mApp->i18n( "shell", "Shell" ).toUtf8() : title, true ); ret.first->setIcon( mApp->findIcon( "filetype-bash" ) );