diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c8706b4c1..d4c38596a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -168,6 +168,7 @@ set(konsoleprivate_SRCS ${windowadaptors_SRCS} ViewManager.cpp ViewProperties.cpp Vt102Emulation.cpp + DumbEmulation.cpp WindowSystemInfo.cpp ZModemDialog.cpp filterHotSpots/EscapeSequenceUrlFilter.cpp diff --git a/src/DumbEmulation.cpp b/src/DumbEmulation.cpp new file mode 100644 index 000000000..77976f50c --- /dev/null +++ b/src/DumbEmulation.cpp @@ -0,0 +1,199 @@ +/* + SPDX-FileCopyrightText: 2007-2008 Robert Knight + SPDX-FileCopyrightText: 1997, 1998 Lars Doelle + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +// Own +#include "DumbEmulation.h" +#include "config-konsole.h" + +// Standard +#include +#include + +// Qt +#include +#include + +// KDE +#include + +// Konsole +#include "keyboardtranslator/KeyboardTranslator.h" +#include "session/SessionController.h" +#include "terminalDisplay/TerminalDisplay.h" + +using Konsole::DumbEmulation; + +DumbEmulation::DumbEmulation() + : Emulation() +{ + TERM = QStringLiteral("dumb"); +} + +DumbEmulation::~DumbEmulation() +{ +} + +void DumbEmulation::clearEntireScreen() +{ + _currentScreen->clearEntireScreen(); + bufferedUpdate(); +} + +void DumbEmulation::reset([[maybe_unused]] bool softReset, [[maybe_unused]] bool preservePrompt) +{ + + // Save the current codec so we can set it later. + // Ideally we would want to use the profile setting + const QByteArray currentCodec(encoder().name()); + + if (currentCodec != nullptr) { + setCodec(currentCodec); + } else { + setCodec(LocaleCodec); + } + + Q_EMIT resetCursorStyleRequest(); + + bufferedUpdate(); +} + +// process an incoming unicode character +void DumbEmulation::receiveChars(const QVector &chars) +{ + for (uint cc : chars) { + if (cc == '\r') { + _currentScreen->nextLine(); + continue; + } + _currentScreen->displayCharacter(cc); + } +} + +void DumbEmulation::sendString(const QByteArray &s) +{ + Q_EMIT sendData(s); +} + +void DumbEmulation::sendText(const QString &text) +{ + if (!text.isEmpty()) { + QKeyEvent event(QEvent::KeyPress, 0, Qt::NoModifier, text); + sendKeyEvent(&event); // expose as a big fat keypress event + } +} + +void DumbEmulation::sendKeyEvent(QKeyEvent *event) +{ + const Qt::KeyboardModifiers modifiers = event->modifiers(); + KeyboardTranslator::States states = KeyboardTranslator::NoState; + + TerminalDisplay *currentView = _currentScreen->currentTerminalDisplay(); + bool isReadOnly = false; + if (currentView != nullptr) { + isReadOnly = currentView->getReadOnly(); + } + + if (!isReadOnly) { + // check flow control state + if ((modifiers & Qt::ControlModifier) != 0U) { + switch (event->key()) { + case Qt::Key_S: + Q_EMIT flowControlKeyPressed(true); + break; + case Qt::Key_C: + // No Sixel support + case Qt::Key_Q: // cancel flow control + Q_EMIT flowControlKeyPressed(false); + break; + } + } + } + + // look up key binding + if (_keyTranslator != nullptr) { + KeyboardTranslator::Entry entry = _keyTranslator->findEntry(event->key(), modifiers, states); + + // send result to terminal + QByteArray textToSend; + + // special handling for the Alt (aka. Meta) modifier. pressing + // Alt+[Character] results in Esc+[Character] being sent + // (unless there is an entry defined for this particular combination + // in the keyboard modifier) + const bool wantsAltModifier = ((entry.modifiers() & entry.modifierMask() & Qt::AltModifier) != 0U); + const bool wantsMetaModifier = ((entry.modifiers() & entry.modifierMask() & Qt::MetaModifier) != 0U); + const bool wantsAnyModifier = ((entry.state() & entry.stateMask() & KeyboardTranslator::AnyModifierState) != 0); + + if (((modifiers & Qt::AltModifier) != 0U) && !(wantsAltModifier || wantsAnyModifier) && !event->text().isEmpty()) { + textToSend.prepend("\033"); + } + if (((modifiers & Qt::MetaModifier) != 0U) && !(wantsMetaModifier || wantsAnyModifier) && !event->text().isEmpty()) { + textToSend.prepend("\030@s"); + } + + if (entry.command() != KeyboardTranslator::NoCommand) { + if ((entry.command() & KeyboardTranslator::EraseCommand) != 0) { + textToSend += eraseChar(); + } + } else if (!entry.text().isEmpty()) { + textToSend += entry.text(true, modifiers); + } else { + Q_ASSERT(_encoder.isValid()); + textToSend += _encoder.encode(event->text()); + } + + if (!isReadOnly) { + Q_EMIT sendData(textToSend); + } + } else { + if (!isReadOnly) { + // print an error message to the terminal if no key translator has been + // set + QString translatorError = i18n( + "No keyboard translator available. " + "The information needed to convert key presses " + "into characters to send to the terminal " + "is missing."); + reset(); + receiveData(translatorError.toLatin1().constData(), translatorError.count()); + } + } +} + +void DumbEmulation::setMode(int) +{ +} + +void DumbEmulation::resetMode(int) +{ +} + +void DumbEmulation::saveCursor() +{ + _currentScreen->saveCursor(); +} + +void DumbEmulation::restoreCursor() +{ + _currentScreen->restoreCursor(); +} + +void DumbEmulation::updateSessionAttributes() +{ +} + +char DumbEmulation::eraseChar() const +{ + return '\b'; +} + +void DumbEmulation::focusChanged([[maybe_unused]] bool focused) +{ +} + +void DumbEmulation::sendMouseEvent([[maybe_unused]] int cb, [[maybe_unused]] int cx, [[maybe_unused]] int cy, [[maybe_unused]] int eventType) +{ +} diff --git a/src/DumbEmulation.h b/src/DumbEmulation.h new file mode 100644 index 000000000..8bfc118aa --- /dev/null +++ b/src/DumbEmulation.h @@ -0,0 +1,77 @@ +/* + SPDX-FileCopyrightText: 2007-2008 Robert Knight + SPDX-FileCopyrightText: 1997, 1998 Lars Doelle + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#ifndef DUMBEMULATION_H +#define DUMBEMULATION_H + +// Qt +#include + +// Konsole +#include "Emulation.h" +#include "Screen.h" + +class QTimer; +class QKeyEvent; + +namespace Konsole +{ +/** + * Provides a dumb terminal emulation. + * The only non printable character is \n + */ +class KONSOLEPRIVATE_EXPORT DumbEmulation : public Emulation +{ + Q_OBJECT + +public: + /** Constructs a new emulation */ + DumbEmulation(); + ~DumbEmulation() override; + + // reimplemented from Emulation + void clearEntireScreen() override; + void reset(bool softReset = false, bool preservePrompt = false) override; + char eraseChar() const override; + +public Q_SLOTS: + // reimplemented from Emulation + void sendString(const QByteArray &string) override; + void sendText(const QString &text) override; + void sendKeyEvent(QKeyEvent *) override; + void sendMouseEvent(int buttons, int column, int line, int eventType) override; + void focusChanged(bool focused) override; + +protected: + // reimplemented from Emulation + void setMode(int mode) override; + void resetMode(int mode) override; + void receiveChars(const QVector &chars) override; + +private Q_SLOTS: + // Causes sessionAttributeChanged() to be emitted for each (int,QString) + // pair in _pendingSessionAttributesUpdates. + // Used to buffer multiple attribute updates in the current session + void updateSessionAttributes(); + +private: + unsigned int applyCharset(uint c); + void setCharset(int n, int cs); + void useCharset(int n); + void setAndUseCharset(int n, int cs); + void saveCursor(); + void restoreCursor(); + void resetCharset(int scrno); + + // clears the screen and resizes it to the specified + // number of columns + void clearScreenAndSetColumns(int columnCount); +}; + +} + +#endif // DUMBEMULATION_H diff --git a/src/Emulation.h b/src/Emulation.h index 4143908b4..190b25e13 100644 --- a/src/Emulation.h +++ b/src/Emulation.h @@ -89,6 +89,8 @@ public: Emulation(); ~Emulation() override; + QString TERM; + /** * Creates a new window onto the output from this emulation. The contents * of the window are then rendered by views which are set to use this window using the diff --git a/src/Vt102Emulation.cpp b/src/Vt102Emulation.cpp index baf7bc4f8..73ed14bc5 100644 --- a/src/Vt102Emulation.cpp +++ b/src/Vt102Emulation.cpp @@ -78,6 +78,8 @@ Vt102Emulation::Vt102Emulation() _sessionAttributesUpdateTimer->setSingleShot(true); QObject::connect(_sessionAttributesUpdateTimer, &QTimer::timeout, this, &Konsole::Vt102Emulation::updateSessionAttributes); + TERM = QStringLiteral("xterm-256color"); + initTokenizer(); imageId = 0; savedKeys = QMap(); diff --git a/src/profile/Profile.cpp b/src/profile/Profile.cpp index ecafc65a6..be3f9ede2 100644 --- a/src/profile/Profile.cpp +++ b/src/profile/Profile.cpp @@ -144,6 +144,7 @@ const std::vector Profile::DefaultProperties = { {PeekPrimaryKeySequence, "PeekPrimaryKeySequence", TERMINAL_GROUP, QString()}, {LineNumbers, "LineNumbers", TERMINAL_GROUP, 0}, {AutoSaveInterval, "AutoSaveInterval", TERMINAL_GROUP, 10000}, + {Emulation, "Emulation", TERMINAL_GROUP, QVariant::String}, // Cursor {UseCustomCursorColor, "UseCustomCursorColor", CURSOR_GROUP, false}, diff --git a/src/profile/Profile.h b/src/profile/Profile.h index d89a262b5..e2fa43c3c 100644 --- a/src/profile/Profile.h +++ b/src/profile/Profile.h @@ -361,6 +361,8 @@ public: /** Shortcut for peeking primary screen */ PeekPrimaryKeySequence, + Emulation, + /** (bool) If true, text that matches a color in hex format * when hovered by the mouse pointer. */ @@ -903,6 +905,11 @@ public: return property(Profile::SemanticInputClick); } + QString emulation() const + { + return property(Profile::Emulation); + } + /** Return a list of all properties names and their type * (for use with -p option). */ diff --git a/src/session/Session.cpp b/src/session/Session.cpp index 1048b9c6a..44da27974 100644 --- a/src/session/Session.cpp +++ b/src/session/Session.cpp @@ -45,6 +45,7 @@ #include #endif +#include "DumbEmulation.h" #include "Pty.h" #include "SSHProcessInfo.h" #include "SessionController.h" @@ -79,8 +80,12 @@ using namespace Konsole; static bool show_disallow_certain_dbus_methods_message = true; static const int ZMODEM_BUFFER_SIZE = 1048576; // 1 Mb - Session::Session(QObject *parent) +{ + Session(parent, Profile::Ptr()); +} + +Session::Session(QObject *parent, Profile::Ptr profile) : QObject(parent) { _uniqueIdentifier = QUuid::createUuid(); @@ -101,7 +106,11 @@ Session::Session(QObject *parent) #endif // create emulation backend - _emulation = new Vt102Emulation(); + if (profile->emulation() == QStringLiteral("dumb")) { + _emulation = new DumbEmulation(); + } else { + _emulation = new Vt102Emulation(); + } _emulation->reset(); connect(_emulation, &Konsole::Emulation::sessionAttributeChanged, this, &Konsole::Session::setSessionAttribute); @@ -552,6 +561,7 @@ void Session::run() addEnvironmentEntry(QStringLiteral("SHELL_SESSION_ID=%1").arg(shellSessionId())); addEnvironmentEntry(QStringLiteral("WINDOWID=%1").arg(QString::number(windowId()))); + addEnvironmentEntry(QStringLiteral("TERM=%1").arg(_emulation->TERM)); #if HAVE_DBUS const QString dbusService = QDBusConnection::sessionBus().baseService(); diff --git a/src/session/Session.h b/src/session/Session.h index ffc8a32a6..9ad8846d7 100644 --- a/src/session/Session.h +++ b/src/session/Session.h @@ -23,6 +23,7 @@ #include "Shortcut_p.h" #include "config-konsole.h" #include "konsoleprivate_export.h" +#include "profile/Profile.h" class QColor; class QTextCodec; @@ -76,6 +77,7 @@ public: * variable. */ explicit Session(QObject *parent = nullptr); + explicit Session(QObject *parent, Profile::Ptr profile); ~Session() override; /* Returns the process info so the plugins can peek at it's name */ diff --git a/src/session/SessionManager.cpp b/src/session/SessionManager.cpp index 81a0d9683..9054c6754 100644 --- a/src/session/SessionManager.cpp +++ b/src/session/SessionManager.cpp @@ -93,7 +93,7 @@ Session *SessionManager::createSession(Profile::Ptr profile) } // configuration information found, create a new session based on this - auto session = new Session(); + auto session = new Session(nullptr, profile); Q_ASSERT(session); applyProfile(session, profile, false);