Add dumb terminal emulation

A dumb terminal has only one control sequence: \r which moves the
cursor to the start of the next line.

Profile option `Emulation` selects which emulation to use. Any value
other than `dumb` selects the standard Vt102 emulation.

Updated version of !629
This commit is contained in:
Matan Ziv-Av
2025-03-08 19:02:47 -05:00
committed by Kurt Hindenburg
parent 8ab289866a
commit 774b655dd1
10 changed files with 304 additions and 3 deletions

View File

@@ -168,6 +168,7 @@ set(konsoleprivate_SRCS ${windowadaptors_SRCS}
ViewManager.cpp
ViewProperties.cpp
Vt102Emulation.cpp
DumbEmulation.cpp
WindowSystemInfo.cpp
ZModemDialog.cpp
filterHotSpots/EscapeSequenceUrlFilter.cpp

199
src/DumbEmulation.cpp Normal file
View File

@@ -0,0 +1,199 @@
/*
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robert.knight@gmail.com>
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
// Own
#include "DumbEmulation.h"
#include "config-konsole.h"
// Standard
#include <cstdio>
#include <unistd.h>
// Qt
#include <QEvent>
#include <QKeyEvent>
// KDE
#include <KLocalizedString>
// 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<uint> &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)
{
}

77
src/DumbEmulation.h Normal file
View File

@@ -0,0 +1,77 @@
/*
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robert.knight@gmail.com>
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef DUMBEMULATION_H
#define DUMBEMULATION_H
// Qt
#include <QVector>
// 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<uint> &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

View File

@@ -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

View File

@@ -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<char, qint64>();

View File

@@ -144,6 +144,7 @@ const std::vector<Profile::PropertyInfo> 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},

View File

@@ -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<bool>(Profile::SemanticInputClick);
}
QString emulation() const
{
return property<QString>(Profile::Emulation);
}
/** Return a list of all properties names and their type
* (for use with -p option).
*/

View File

@@ -45,6 +45,7 @@
#include <sessionadaptor.h>
#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();

View File

@@ -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 */

View File

@@ -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);