mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +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:
@@ -21,6 +21,12 @@ class EE_API Clipboard {
|
||||
/** @return The parent window of the clipboard */
|
||||
EE::Window::Window* getWindow() const;
|
||||
|
||||
/** @return The Clipboard Primary Selection Text if available */
|
||||
virtual std::string getPrimarySelectionText() { return ""; }
|
||||
|
||||
/** Set the current clipboard primary selection text */
|
||||
virtual void setPrimarySelectionText( const std::string& text ) {}
|
||||
|
||||
protected:
|
||||
friend class Window;
|
||||
|
||||
|
||||
@@ -172,9 +172,12 @@ class EE_API Input {
|
||||
/** Pop the callback id indicated. */
|
||||
void popCallback( const Uint32& CallbackId );
|
||||
|
||||
/** @return The Mouse position vector */
|
||||
/** @return The Mouse position */
|
||||
Vector2i getMousePos() const;
|
||||
|
||||
/** @return The Mouse position with no clamping */
|
||||
Vector2i getRelativeMousePos() const;
|
||||
|
||||
/** This will change the value of the mouse pos, will not REALLY move the mouse ( for that is
|
||||
* InjectMousePos ). */
|
||||
void setMousePos( const Vector2i& Pos );
|
||||
@@ -287,6 +290,7 @@ class EE_API Input {
|
||||
Uint32 mLastButtonMiddleClick;
|
||||
Uint32 mTClick;
|
||||
Vector2i mMousePos;
|
||||
Vector2i mRealMousePos;
|
||||
Uint32 mNumCallBacks;
|
||||
Float mMouseSpeed;
|
||||
bool mInputGrabbed;
|
||||
|
||||
@@ -49,6 +49,26 @@ std::string ClipboardSDL::getText() {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string ClipboardSDL::getPrimarySelectionText() {
|
||||
#if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
|
||||
return "";
|
||||
#else
|
||||
if ( SDL_HasPrimarySelectionText() ) {
|
||||
char* text = SDL_GetPrimarySelectionText();
|
||||
std::string str( text );
|
||||
SDL_free( text );
|
||||
return str;
|
||||
}
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClipboardSDL::setPrimarySelectionText( const std::string& text ) {
|
||||
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
|
||||
SDL_SetPrimarySelectionText( text.c_str() );
|
||||
#endif
|
||||
}
|
||||
|
||||
String ClipboardSDL::getWideText() {
|
||||
return String::fromUtf8( getText() );
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ class EE_API ClipboardSDL : public Clipboard {
|
||||
|
||||
void setText( const std::string& text );
|
||||
|
||||
std::string getPrimarySelectionText();
|
||||
|
||||
void setPrimarySelectionText( const std::string& text );
|
||||
|
||||
protected:
|
||||
friend class WindowSDL;
|
||||
|
||||
|
||||
@@ -95,6 +95,8 @@ void Input::processEvent( InputEvent* Event ) {
|
||||
mMousePos.y += static_cast<Int32>( (Float)Event->motion.yrel * mMouseSpeed );
|
||||
}
|
||||
|
||||
mRealMousePos = mMousePos;
|
||||
|
||||
if ( mMousePos.x >= (int)mWindow->getWidth() ) {
|
||||
mMousePos.x = mWindow->getWidth();
|
||||
} else if ( mMousePos.x < 0 ) {
|
||||
@@ -308,6 +310,10 @@ Vector2i Input::getMousePos() const {
|
||||
return mMousePos;
|
||||
}
|
||||
|
||||
Vector2i Input::getRelativeMousePos() const {
|
||||
return mRealMousePos;
|
||||
}
|
||||
|
||||
void Input::setMousePos( const Vector2i& Pos ) {
|
||||
mMousePos = Pos;
|
||||
}
|
||||
|
||||
@@ -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() ); } );
|
||||
|
||||
@@ -196,6 +196,12 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
|
||||
"Maximum rendering frames per second of the terminal. Default "
|
||||
"value will be the refresh rate of the screen.",
|
||||
{ "max-fps" }, 0 );
|
||||
args::MapFlag<std::string, TerminalCursorMode> cursorStyle(
|
||||
parser, "cursor-style",
|
||||
"Sets the cursor-style (accepted values: blinking_block, steady_block, blink_underline, "
|
||||
"steady_underline, blink_bar, steady_bar)",
|
||||
{ "cursor-style" }, TerminalCursorHelper::getTerminalCursorModeMap(),
|
||||
TerminalCursorMode::SteadyUnderline );
|
||||
args::Flag benchmarkModeFlag(
|
||||
parser, "benchmark-mode",
|
||||
"Render as much as possible to measure the rendering performance.", { "benchmark-mode" } );
|
||||
@@ -322,6 +328,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
|
||||
}
|
||||
|
||||
terminal->getTerminal()->setAllowMemoryTrimnming( true );
|
||||
terminal->setCursorMode( cursorStyle.Get() );
|
||||
terminal->pushEventCallback( [&closeOnExit]( const TerminalDisplay::Event& event ) {
|
||||
if ( event.type == TerminalDisplay::EventType::TITLE ) {
|
||||
windowStringData = event.eventData;
|
||||
@@ -353,9 +360,11 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) {
|
||||
win->runMainLoop( [fontMono] {
|
||||
bool termNeedsUpdate = false;
|
||||
win->getInput()->update();
|
||||
auto mousePos = win->getInput()->getRelativeMousePos();
|
||||
bool mouseOutsideBounds = mousePos.y < 0 || mousePos.y > win->getSize().getHeight();
|
||||
|
||||
if ( terminal )
|
||||
termNeedsUpdate = !terminal->update();
|
||||
termNeedsUpdate = !terminal->update( !mouseOutsideBounds );
|
||||
|
||||
if ( ( terminal && ( benchmarkMode || terminal->isDirty() ) &&
|
||||
( !termNeedsUpdate || lastRender.getElapsedTime() >= frameTime ) ) ||
|
||||
|
||||
Reference in New Issue
Block a user