diff --git a/lib/Session.cpp b/lib/Session.cpp index 21a265d0..eeb77ec5 100644 --- a/lib/Session.cpp +++ b/lib/Session.cpp @@ -112,8 +112,8 @@ Session::Session(QObject* parent) : connect( _shellProcess,SIGNAL(receivedData(const char *,int)),this, SLOT(onReceiveBlock(const char *,int)) ); - connect( _emulation,SIGNAL(sendData(const char *,int)),_shellProcess, - SLOT(sendData(const char *,int)) ); + connect( _emulation,SIGNAL(sendData(const char *,int)),this, + SLOT(sendToShellProcess(const char *,int)) ); connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) ); connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) ); @@ -124,6 +124,15 @@ Session::Session(QObject* parent) : _monitorTimer = new QTimer(this); _monitorTimer->setSingleShot(true); connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone())); + + //setup baud rate throttle timers + _outputThrottleTimer = new QTimer(this); + _outputThrottleTimer->setSingleShot(true); + connect(_outputThrottleTimer, &QTimer::timeout, this, &Session::_flushOutputBuffer); + + _inputThrottleTimer = new QTimer(this); + _inputThrottleTimer->setSingleShot(true); + connect(_inputThrottleTimer, &QTimer::timeout, this, &Session::_flushInputBuffer); } WId Session::windowId() const @@ -942,8 +951,93 @@ void Session::zmodemFinished() */ void Session::onReceiveBlock( const char * buf, int len ) { - _emulation->receiveData( buf, len ); - emit receivedData( QString::fromLatin1( buf, len ) ); + if (_baudIsOff() || !_baudAffectsDisplay()) { + _emulation->receiveData( buf, len ); + emit receivedData( QString::fromLatin1( buf, len ) ); + return; + } + _outputBuffer.append(buf, len); + if (!_outputThrottleTimer->isActive()) + _outputThrottleTimer->start(_baudCharIntervalMs()); +} + +void Session::_flushOutputBuffer() +{ + if (_outputBuffer.isEmpty()) return; + const char ch = _outputBuffer.at(0); + _outputBuffer.remove(0, 1); + _emulation->receiveData(&ch, 1); + emit receivedData(QString::fromLatin1(&ch, 1)); + if (!_outputBuffer.isEmpty()) + _outputThrottleTimer->start(_baudCharIntervalMs()); +} + +void Session::sendToShellProcess( const char * data, int len ) +{ + if (_baudIsOff() || !_baudAffectsInput() || !_baudIsAuthentic()) { + _shellProcess->sendData( data, len ); + return; + } + _inputBuffer.append(data, len); + if (!_inputThrottleTimer->isActive()) + _inputThrottleTimer->start(_baudCharIntervalMs()); +} + +void Session::_flushInputBuffer() +{ + if (_inputBuffer.isEmpty()) return; + const char ch = _inputBuffer.at(0); + _inputBuffer.remove(0, 1); + _shellProcess->sendData(&ch, 1); + if (!_inputBuffer.isEmpty()) + _inputThrottleTimer->start(_baudCharIntervalMs()); +} + +bool Session::_baudAffectsDisplay() const +{ + return _baudRateMode.startsWith(QLatin1String("display-")) || _baudRateMode.startsWith(QLatin1String("both-")); +} +bool Session::_baudAffectsInput() const +{ + return _baudRateMode.startsWith(QLatin1String("input-")) || _baudRateMode.startsWith(QLatin1String("both-")); +} +bool Session::_baudIsAuthentic() const +{ + return _baudRateMode.endsWith(QLatin1String("-authentic")); +} +bool Session::_baudIsOff() const +{ + return _baudRateMode == QLatin1String("off") || _baudRate <= 0; +} +int Session::_baudCharIntervalMs() const +{ + return qMax(1, 8000 / _baudRate); +} + +void Session::setBaudRate(int bps) +{ + if (_baudRate == bps) return; + _baudRate = bps; + emit baudRateChanged(bps); +} + +void Session::setBaudRateMode(const QString &mode) +{ + if (_baudRateMode == mode) return; + _baudRateMode = mode; + if (_baudIsOff()) { + // Drain buffers immediately when throttling is disabled + if (!_outputBuffer.isEmpty()) { + _emulation->receiveData(_outputBuffer.constData(), _outputBuffer.size()); + emit receivedData(QString::fromLatin1(_outputBuffer)); + _outputBuffer.clear(); + } + if (!_inputBuffer.isEmpty()) { + _shellProcess->sendData(_inputBuffer.constData(), _inputBuffer.size()); + _inputBuffer.clear(); + } + } + emit baudRateModeChanged(mode); } QSize Session::size() diff --git a/lib/Session.h b/lib/Session.h index fd4aa4d5..873621ac 100644 --- a/lib/Session.h +++ b/lib/Session.h @@ -25,8 +25,10 @@ #ifndef SESSION_H #define SESSION_H +#include #include #include +#include #include "Emulation.h" #include "History.h" @@ -60,6 +62,8 @@ class Session : public QObject { Q_PROPERTY(int processId READ processId) Q_PROPERTY(QString keyBindings READ keyBindings WRITE setKeyBindings) Q_PROPERTY(QSize size READ size WRITE setSize) + Q_PROPERTY(int baudRate READ baudRate WRITE setBaudRate NOTIFY baudRateChanged) + Q_PROPERTY(QString baudRateMode READ baudRateMode WRITE setBaudRateMode NOTIFY baudRateModeChanged) /** * Constructs a new session. @@ -365,6 +369,12 @@ class Session : public QObject { */ bool hasDarkBackground() const; + // Baud rate emulation + int baudRate() const { return _baudRate; } + void setBaudRate(int bps); + QString baudRateMode() const { return _baudRateMode; } + void setBaudRateMode(const QString &mode); + /** * Attempts to get the shell program to redraw the current display area. * This can be used after clearing the screen, for example, to get the @@ -428,6 +438,9 @@ public slots: */ void receivedData( const QString & text ); + void baudRateChanged(int newRate); + void baudRateModeChanged(const QString &newMode); + /** Emitted when the session's title has changed. */ void titleChanged(); @@ -501,7 +514,6 @@ private slots: // void fireZModemDetected(); - void onReceiveBlock( const char * buffer, int len ); void monitorTimerDone(); void onViewSizeChange(int height, int width); @@ -512,6 +524,14 @@ private slots: //automatically detach views from sessions when view is destroyed void viewDestroyed(QObject * view); +protected slots: + void onReceiveBlock( const char * buffer, int len ); + void sendToShellProcess( const char * data, int len ); + +private slots: + void _flushOutputBuffer(); + void _flushInputBuffer(); + // void zmodemReadStatus(); // void zmodemReadAndSendBlock(); // void zmodemRcvBlock(const char *data, int len); @@ -575,6 +595,20 @@ private slots: bool _hasDarkBackground; + // Baud rate throttling + int _baudRate = 0; + QString _baudRateMode = QStringLiteral("off"); + QByteArray _outputBuffer; + QByteArray _inputBuffer; + QTimer *_outputThrottleTimer = nullptr; + QTimer *_inputThrottleTimer = nullptr; + + bool _baudAffectsDisplay() const; + bool _baudAffectsInput() const; + bool _baudIsAuthentic() const; + bool _baudIsOff() const; + int _baudCharIntervalMs() const; + ProcessInfo *_foregroundProcessInfo; int _foregroundPid; static int lastSessionId; diff --git a/lib/ksession.cpp b/lib/ksession.cpp index e17ec8ac..1628405b 100644 --- a/lib/ksession.cpp +++ b/lib/ksession.cpp @@ -40,6 +40,8 @@ KSession::KSession(QObject *parent) : connect(m_session, SIGNAL(started()), this, SIGNAL(started())); connect(m_session, SIGNAL(finished()), this, SLOT(sessionFinished())); connect(m_session, SIGNAL(titleChanged()), this, SIGNAL(titleChanged())); + connect(m_session, SIGNAL(baudRateChanged(int)), this, SIGNAL(baudRateChanged(int))); + connect(m_session, SIGNAL(baudRateModeChanged(QString)), this, SIGNAL(baudRateModeChanged(QString))); } KSession::~KSession() @@ -242,6 +244,26 @@ QString KSession::getHistory() const return history; } +int KSession::getBaudRate() const +{ + return m_session->baudRate(); +} + +void KSession::setBaudRate(int bps) +{ + m_session->setBaudRate(bps); +} + +QString KSession::getBaudRateMode() const +{ + return m_session->baudRateMode(); +} + +void KSession::setBaudRateMode(const QString &mode) +{ + m_session->setBaudRateMode(mode); +} + void KSession::sendText(QString text) { m_session->sendText(text); diff --git a/lib/ksession.h b/lib/ksession.h index f4e9d6ef..9bb43dbb 100644 --- a/lib/ksession.h +++ b/lib/ksession.h @@ -30,7 +30,6 @@ namespace Konsole { class TerminalDisplay; -class Session; } class KSession : public QObject @@ -45,6 +44,8 @@ class KSession : public QObject Q_PROPERTY(bool hasActiveProcess READ hasActiveProcess) Q_PROPERTY(QString foregroundProcessName READ foregroundProcessName) Q_PROPERTY(QString currentDir READ currentDir) + Q_PROPERTY(int baudRate READ getBaudRate WRITE setBaudRate NOTIFY baudRateChanged) + Q_PROPERTY(QString baudRateMode READ getBaudRateMode WRITE setBaudRateMode NOTIFY baudRateModeChanged) public: KSession(QObject *parent = 0); @@ -76,6 +77,12 @@ class KSession : public QObject QString getHistory() const; + // Baud rate emulation + int getBaudRate() const; + void setBaudRate(int bps); + QString getBaudRateMode() const; + void setBaudRateMode(const QString &mode); + // Sets whether flow control is enabled void setFlowControlEnabled(bool enabled); @@ -134,6 +141,9 @@ class KSession : public QObject void matchFound(int startColumn, int startLine, int endColumn, int endLine); void noMatchFound(); + void baudRateChanged(int newRate); + void baudRateModeChanged(const QString &newMode); + public slots: /*! Set named key binding for given widget */ @@ -169,10 +179,8 @@ protected slots: private slots: Konsole::Session* createSession(QString name); - //Konsole::KTerminalDisplay* createTerminalDisplay(Konsole::Session *session, QQuickItem* parent); private: - //Konsole::KTerminalDisplay *m_terminalDisplay; QString _initialWorkingDirectory; QString m_shellProgram; QStringList m_shellArgs;