mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-18 06:55:48 +03:00
Terminal: multiple issues with the copy/paste operations fixed. Now uses the primary selection when corresponds (middle mouse click, shift+insert). Fixed paste operation, it does not hang the app, pastes instantly contents and behaves exactly the same as other terminals.
Incorporated new features into eterm.
This commit is contained in:
@@ -79,6 +79,8 @@ class PseudoTerminal final : public IPseudoTerminal {
|
||||
AutoHandle mMaster;
|
||||
AutoHandle mSlave;
|
||||
|
||||
std::string mWriteBuffer;
|
||||
|
||||
PseudoTerminal( int columns, int rows, AutoHandle&& master, AutoHandle&& slave );
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <eterm/terminal/terminalcolorscheme.hpp>
|
||||
#include <eterm/terminal/terminalemulator.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@@ -33,6 +32,7 @@ namespace eterm { namespace Terminal {
|
||||
|
||||
enum class TerminalShortcutAction {
|
||||
PASTE,
|
||||
PASTE_SELECTION,
|
||||
COPY,
|
||||
SCROLLUP_ROW,
|
||||
SCROLLDOWN_ROW,
|
||||
@@ -283,7 +283,6 @@ class TerminalDisplay : public ITerminalDisplay {
|
||||
std::vector<TerminalGlyph> mBuffer;
|
||||
std::vector<Color> mColors;
|
||||
std::shared_ptr<TerminalEmulator> mTerminal;
|
||||
mutable String mClipboard;
|
||||
mutable std::string mClipboardUtf8;
|
||||
Uint32 mNumCallBacks;
|
||||
std::map<Uint32, EventFunc> mCallbacks;
|
||||
@@ -371,6 +370,8 @@ class TerminalDisplay : public ITerminalDisplay {
|
||||
|
||||
void drawBg( bool toFBO = false );
|
||||
|
||||
void sanitizeInput( std::string& input );
|
||||
|
||||
};
|
||||
|
||||
}} // namespace eterm::Terminal
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -82,6 +83,17 @@ struct TerminalCursorHelper {
|
||||
}
|
||||
return "steady_underline";
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string, TerminalCursorMode> getTerminalCursorModeMap() {
|
||||
return {
|
||||
{ "blinking_block", TerminalCursorMode::BlinkingBlock },
|
||||
{ "steady_block", TerminalCursorMode::SteadyBlock },
|
||||
{ "blink_underline", TerminalCursorMode::BlinkUnderline },
|
||||
{ "steady_underline", TerminalCursorMode::SteadyUnderline },
|
||||
{ "blink_bar", TerminalCursorMode::BlinkBar },
|
||||
{ "steady_bar", TerminalCursorMode::SteadyBar },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
enum TerminalWinMode {
|
||||
|
||||
@@ -42,11 +42,13 @@
|
||||
#include <unistd.h>
|
||||
using namespace EE::System;
|
||||
|
||||
#if defined( EE_PLATFORM_POSIX )
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
namespace eterm { namespace Terminal {
|
||||
|
||||
#if EE_PLATFORM == EE_PLATFORM_ANDROID
|
||||
#include <fcntl.h>
|
||||
|
||||
int openpty( int* amaster, int* aslave, char* name, struct termios* termp, struct winsize* winp ) {
|
||||
int master, slave;
|
||||
char* name_slave;
|
||||
@@ -129,20 +131,29 @@ int PseudoTerminal::getNumRows() const {
|
||||
}
|
||||
|
||||
int PseudoTerminal::write( const char* s, size_t n ) {
|
||||
size_t c = n;
|
||||
while ( n > 0 ) {
|
||||
ssize_t r = ::write( (int)mMaster, s, n );
|
||||
if ( r < 0 ) {
|
||||
return -1;
|
||||
}
|
||||
n -= r;
|
||||
s += r;
|
||||
if ( !mWriteBuffer.empty() ) {
|
||||
mWriteBuffer.append( s, n );
|
||||
return n;
|
||||
}
|
||||
return c;
|
||||
|
||||
ssize_t r = ::write( (int)mMaster, s, n );
|
||||
if ( r < 0 ) {
|
||||
if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
|
||||
mWriteBuffer.append( s, n );
|
||||
return n;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( (size_t)r < n ) {
|
||||
mWriteBuffer.append( s + r, n - r );
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int PseudoTerminal::read( char* s, size_t n, bool block ) {
|
||||
if ( !block ) {
|
||||
if ( mWriteBuffer.empty() && !block ) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd = mMaster.handle();
|
||||
pfd.events = POLLIN;
|
||||
@@ -155,7 +166,36 @@ int PseudoTerminal::read( char* s, size_t n, bool block ) {
|
||||
perror( "PseudoTerminal::read(poll)" );
|
||||
return -1;
|
||||
}
|
||||
} else if ( !mWriteBuffer.empty() || block ) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd = mMaster.handle();
|
||||
pfd.events = POLLIN;
|
||||
if ( !mWriteBuffer.empty() )
|
||||
pfd.events |= POLLOUT;
|
||||
pfd.revents = 0;
|
||||
auto i = poll( &pfd, 1, block ? -1 : 0 );
|
||||
if ( i < 0 && errno != EAGAIN && errno != EINTR ) {
|
||||
perror( "PseudoTerminal::read(poll)" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ( pfd.revents & POLLOUT ) && !mWriteBuffer.empty() ) {
|
||||
ssize_t r = ::write( (int)mMaster, mWriteBuffer.c_str(), mWriteBuffer.size() );
|
||||
if ( r > 0 ) {
|
||||
if ( (size_t)r == mWriteBuffer.size() ) {
|
||||
mWriteBuffer.clear();
|
||||
} else {
|
||||
mWriteBuffer = mWriteBuffer.substr( r );
|
||||
}
|
||||
} else if ( r < 0 && errno != EAGAIN && errno != EWOULDBLOCK ) {
|
||||
perror( "PseudoTerminal::read(write)" ); // Log error but don't fail read
|
||||
}
|
||||
}
|
||||
|
||||
if ( !( pfd.revents & POLLIN ) )
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t r = ::read( mMaster.handle(), s, n );
|
||||
return (int)r;
|
||||
}
|
||||
@@ -180,6 +220,9 @@ std::unique_ptr<PseudoTerminal> PseudoTerminal::create( int columns, int rows )
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int flags = fcntl( master, F_GETFL, 0 );
|
||||
fcntl( master, F_SETFL, flags | O_NONBLOCK );
|
||||
|
||||
return std::unique_ptr<PseudoTerminal>(
|
||||
new PseudoTerminal( columns, rows, std::move( master ), std::move( slave ) ) );
|
||||
#endif
|
||||
|
||||
@@ -75,7 +75,7 @@ TerminalKeyMap::TerminalKeyMap( const TerminalKey keys[], size_t keysLen,
|
||||
}
|
||||
|
||||
static TerminalShortcut shortcuts[] = {
|
||||
{ KEY_INSERT, KEYMOD_SHIFT, TerminalShortcutAction::PASTE, 0, 0 },
|
||||
{ KEY_INSERT, KEYMOD_SHIFT, TerminalShortcutAction::PASTE_SELECTION, 0, 0 },
|
||||
{ KEY_V, KEYMOD_SHIFT | KEYMOD_CTRL, TerminalShortcutAction::PASTE, 0, 0 },
|
||||
{ KEY_C, KEYMOD_SHIFT | KEYMOD_CTRL, TerminalShortcutAction::COPY, 0, 0 },
|
||||
{ KEY_PAGEUP, KEYMOD_SHIFT, TerminalShortcutAction::SCROLLUP_SCREEN, 0, 0, -1 },
|
||||
@@ -92,7 +92,9 @@ static TerminalMouseShortcut mouseShortcuts[] = {
|
||||
{ EE_BUTTON_WUMASK, 0, TerminalShortcutAction::SCROLLUP_ROW, 0, 0, -1 },
|
||||
{ EE_BUTTON_WDMASK, 0, TerminalShortcutAction::SCROLLDOWN_ROW, 0, 0, -1 },
|
||||
{ EE_BUTTON_WUMASK, KEYMOD_CTRL, TerminalShortcutAction::FONTSIZE_GROW, 0, 0, 0 },
|
||||
{ EE_BUTTON_WDMASK, KEYMOD_CTRL, TerminalShortcutAction::FONTSIZE_SHRINK, 0, 0, 0 } };
|
||||
{ EE_BUTTON_WDMASK, KEYMOD_CTRL, TerminalShortcutAction::FONTSIZE_SHRINK, 0, 0, 0 },
|
||||
{ EE_BUTTON_MMASK, 0, TerminalShortcutAction::PASTE_SELECTION, 0, 0, -1 },
|
||||
};
|
||||
|
||||
static TerminalKey keys[] = {
|
||||
/* keysym mask string appkey appcursor */
|
||||
@@ -678,8 +680,29 @@ void TerminalDisplay::action( TerminalShortcutAction action ) {
|
||||
switch ( action ) {
|
||||
case TerminalShortcutAction::PASTE: {
|
||||
getClipboard();
|
||||
if ( !mClipboard.empty() )
|
||||
mTerminal->ttywrite( mClipboardUtf8.c_str(), mClipboardUtf8.size(), 1 );
|
||||
if ( !mClipboardUtf8.empty() ) {
|
||||
if ( mMode & MODE_BRCKTPASTE ) {
|
||||
mTerminal->write( "\033[200~", 6 );
|
||||
mTerminal->write( mClipboardUtf8.c_str(), mClipboardUtf8.size() );
|
||||
mTerminal->write( "\033[201~", 6 );
|
||||
} else {
|
||||
mTerminal->write( mClipboardUtf8.c_str(), mClipboardUtf8.size() );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TerminalShortcutAction::PASTE_SELECTION: {
|
||||
std::string selection = mWindow->getClipboard()->getPrimarySelectionText();
|
||||
sanitizeInput( selection );
|
||||
if ( !selection.empty() ) {
|
||||
if ( mMode & MODE_BRCKTPASTE ) {
|
||||
mTerminal->write( "\033[200~", 6 );
|
||||
mTerminal->write( selection.c_str(), selection.size() );
|
||||
mTerminal->write( "\033[201~", 6 );
|
||||
} else {
|
||||
mTerminal->write( selection.c_str(), selection.size() );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TerminalShortcutAction::COPY: {
|
||||
@@ -752,24 +775,37 @@ void TerminalDisplay::setIconTitle( const char* title ) {
|
||||
void TerminalDisplay::setClipboard( const char* text ) {
|
||||
if ( text == nullptr )
|
||||
return;
|
||||
mClipboard = text;
|
||||
mClipboardUtf8 = mClipboard.toUtf8();
|
||||
mWindow->getClipboard()->setText( mClipboard );
|
||||
mWindow->getClipboard()->setText( text );
|
||||
}
|
||||
|
||||
const char* TerminalDisplay::getClipboard() const {
|
||||
mClipboard = mWindow->getClipboard()->getText();
|
||||
#ifdef _WIN32
|
||||
if ( mPasteNewlineFix ) {
|
||||
size_t pos;
|
||||
while ( ( pos = mClipboard.find( '\r' ) ) != std::string::npos )
|
||||
mClipboard.erase( pos, 1 );
|
||||
}
|
||||
#endif
|
||||
mClipboardUtf8 = mClipboard.toUtf8();
|
||||
mClipboardUtf8 = mWindow->getClipboard()->getText();
|
||||
const_cast<TerminalDisplay*>( this )->sanitizeInput( mClipboardUtf8 );
|
||||
return mClipboardUtf8.c_str();
|
||||
}
|
||||
|
||||
void TerminalDisplay::sanitizeInput( std::string& input ) {
|
||||
if ( !mPasteNewlineFix )
|
||||
return;
|
||||
|
||||
// Replace isolated \n (not part of \r\n) with \r
|
||||
// This handles Unix-style clipboard text (\n) → simulate Enter as \r
|
||||
// Preserves \r\n (Windows) as a single line ending → \r
|
||||
// Keeps lone \r as-is
|
||||
|
||||
size_t pos = 0;
|
||||
while ( ( pos = input.find( '\n', pos ) ) != std::string::npos ) {
|
||||
if ( pos == 0 || input[pos - 1] != '\r' ) {
|
||||
input.replace( pos, 1, "\r" );
|
||||
} else {
|
||||
// Part of \r\n: remove the \n, keep the preceding \r
|
||||
input.erase( pos, 1 );
|
||||
++pos; // Skip over the kept \r if needed
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
bool TerminalDisplay::drawBegin( Uint32 columns, Uint32 rows ) {
|
||||
if ( columns != mColumns || rows != mRows ) {
|
||||
TerminalGlyph defaultGlyph{};
|
||||
@@ -832,12 +868,13 @@ void TerminalDisplay::onMouseDoubleClick( const Vector2i& pos, const Uint32& fla
|
||||
|
||||
void TerminalDisplay::onMouseMove( const Vector2i& pos, const Uint32& flags ) {
|
||||
bool shiftPressed = ( mWindow->getInput()->getModState() & KEYMOD_SHIFT ) != 0;
|
||||
auto mousePos = mWindow->getInput()->getRelativeMousePos();
|
||||
bool isCapturingMouse = isAppCapturingMouse() && !shiftPressed;
|
||||
|
||||
if ( !isAltScr() && !isCapturingMouse && ( flags & EE_BUTTON_LMASK ) &&
|
||||
mAlreadyClickedLButton ) {
|
||||
Vector2f relPos = { pos.x - mPosition.x - mPadding.Left,
|
||||
pos.y - mPosition.y - mPadding.Top };
|
||||
Vector2f relPos = { mousePos.x - mPosition.x - mPadding.Left,
|
||||
mousePos.y - mPosition.y - mPadding.Top };
|
||||
|
||||
if ( mLastAutoScroll.getElapsedTime() >= Milliseconds( 16 ) ) {
|
||||
if ( relPos.y < 0 ) {
|
||||
@@ -882,22 +919,8 @@ void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) {
|
||||
invalidateLines();
|
||||
mWindow->getInput()->captureMouse( true );
|
||||
}
|
||||
} else if ( flags & EE_BUTTON_MMASK ) {
|
||||
if ( !mAlreadyClickedMButton ) {
|
||||
mAlreadyClickedMButton = true;
|
||||
auto selection = mTerminal->getSelection();
|
||||
if ( !selection.empty() && selection != "\n" ) {
|
||||
for ( auto& chr : selection )
|
||||
onTextInput( chr );
|
||||
} else {
|
||||
getClipboard();
|
||||
if ( !mClipboard.empty() ) {
|
||||
for ( auto& chr : mClipboard )
|
||||
onTextInput( chr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( flags & EE_BUTTON_LMASK ) ) {
|
||||
if ( !mAlreadyClickedLButton ) {
|
||||
mAlreadyClickedLButton = true;
|
||||
@@ -905,6 +928,15 @@ void TerminalDisplay::onMouseDown( const Vector2i& pos, const Uint32& flags ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ( flags & EE_BUTTON_MMASK ) ) {
|
||||
if ( !mAlreadyClickedMButton ) {
|
||||
mAlreadyClickedMButton = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mTerminal->mousereport( TerminalMouseEventType::MouseButtonDown, positionToGrid( pos ), flags,
|
||||
mWindow->getInput()->getModState() );
|
||||
}
|
||||
@@ -914,6 +946,11 @@ void TerminalDisplay::onMouseUp( const Vector2i& pos, const Uint32& flags ) {
|
||||
mDraggingSel = false;
|
||||
}
|
||||
|
||||
if ( flags & EE_BUTTON_LMASK ) {
|
||||
auto selection = mTerminal->getSelection();
|
||||
mWindow->getClipboard()->setPrimarySelectionText( selection );
|
||||
}
|
||||
|
||||
Uint32 smod = sanitizeMod( mWindow->getInput()->getModState() );
|
||||
|
||||
if ( flags & EE_BUTTON_LMASK ) {
|
||||
|
||||
@@ -103,6 +103,8 @@ UITerminal::UITerminal( const std::shared_ptr<TerminalDisplay>& terminalDisplay
|
||||
setCommand( "terminal-font-size-shrink",
|
||||
[this] { mTerm->action( TerminalShortcutAction::FONTSIZE_SHRINK ); } );
|
||||
setCommand( "terminal-paste", [this] { mTerm->action( TerminalShortcutAction::PASTE ); } );
|
||||
setCommand( "terminal-paste-selection",
|
||||
[this] { mTerm->action( TerminalShortcutAction::PASTE_SELECTION ); } );
|
||||
setCommand( "terminal-copy", [this] { mTerm->action( TerminalShortcutAction::COPY ); } );
|
||||
setCommand( "terminal-open-link",
|
||||
[this] { Engine::instance()->openURI( mTerm->getTerminal()->getSelection() ); } );
|
||||
|
||||
Reference in New Issue
Block a user