mirror of
https://github.com/KDE/konsole.git
synced 2026-01-13 01:28:14 -05:00
* speeds up incremental builds as changes to a header will not always need the full mocs_compilation.cpp for all the target's headers rebuild, while having a moc file sourced into a source file only adds minor extra costs, due to small own code and the used headers usually already covered by the source file, being for the same class/struct * seems to not slow down clean builds, due to empty mocs_compilation.cpp resulting in those quickly processed, while the minor extra cost of the sourced moc files does not outweigh that in summary. Measured times actually improved by some percent points. (ideally CMake would just skip empty mocs_compilation.cpp & its object file one day) * enables compiler to see all methods of a class in same compilation unit to do some sanity checks * potentially more inlining in general, due to more in the compilation unit * allows to keep using more forward declarations in the header, as with the moc code being sourced into the cpp file there definitions can be ensured and often are already for the needs of the normal class methods
335 lines
8.4 KiB
C++
335 lines
8.4 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
|
|
SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
|
|
SPDX-FileCopyrightText: 1996 Matthias Ettrich <ettrich@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
// Own
|
|
#include "Emulation.h"
|
|
|
|
// Qt
|
|
#include <QKeyEvent>
|
|
|
|
// Konsole
|
|
#include "Screen.h"
|
|
#include "ScreenWindow.h"
|
|
#include "keyboardtranslator/KeyboardTranslator.h"
|
|
#include "keyboardtranslator/KeyboardTranslatorManager.h"
|
|
|
|
using namespace Konsole;
|
|
|
|
Emulation::Emulation()
|
|
{
|
|
// create screens with a default size
|
|
_screen[0] = new Screen(40, 80);
|
|
_screen[1] = new Screen(40, 80);
|
|
_currentScreen = _screen[0];
|
|
|
|
QObject::connect(&_bulkTimer1, &QTimer::timeout, this, &Konsole::Emulation::showBulk);
|
|
QObject::connect(&_bulkTimer2, &QTimer::timeout, this, &Konsole::Emulation::showBulk);
|
|
|
|
// listen for mouse status changes
|
|
connect(this, &Konsole::Emulation::programRequestsMouseTracking, this, &Konsole::Emulation::setUsesMouseTracking);
|
|
connect(this, &Konsole::Emulation::programBracketedPasteModeChanged, this, &Konsole::Emulation::bracketedPasteModeChanged);
|
|
}
|
|
|
|
bool Emulation::programUsesMouseTracking() const
|
|
{
|
|
return _usesMouseTracking;
|
|
}
|
|
|
|
void Emulation::setUsesMouseTracking(bool usesMouseTracking)
|
|
{
|
|
_usesMouseTracking = usesMouseTracking;
|
|
}
|
|
|
|
bool Emulation::programBracketedPasteMode() const
|
|
{
|
|
return _bracketedPasteMode;
|
|
}
|
|
|
|
void Emulation::bracketedPasteModeChanged(bool bracketedPasteMode)
|
|
{
|
|
_bracketedPasteMode = bracketedPasteMode;
|
|
}
|
|
|
|
ScreenWindow *Emulation::createWindow()
|
|
{
|
|
auto window = new ScreenWindow(_currentScreen);
|
|
_windows << window;
|
|
|
|
connect(window, &Konsole::ScreenWindow::selectionChanged, this, &Konsole::Emulation::bufferedUpdate);
|
|
connect(window, &Konsole::ScreenWindow::selectionChanged, this, &Konsole::Emulation::checkSelectedText);
|
|
|
|
connect(this, &Konsole::Emulation::outputChanged, window, &Konsole::ScreenWindow::notifyOutputChanged);
|
|
|
|
return window;
|
|
}
|
|
|
|
void Emulation::setCurrentTerminalDisplay(TerminalDisplay *display)
|
|
{
|
|
_screen[0]->setCurrentTerminalDisplay(display);
|
|
_screen[1]->setCurrentTerminalDisplay(display);
|
|
}
|
|
|
|
void Emulation::checkScreenInUse()
|
|
{
|
|
Q_EMIT primaryScreenInUse(_currentScreen == _screen[0]);
|
|
}
|
|
|
|
void Emulation::checkSelectedText()
|
|
{
|
|
bool isEmpty = !_currentScreen->hasSelection();
|
|
Q_EMIT selectionChanged(isEmpty);
|
|
}
|
|
|
|
Emulation::~Emulation()
|
|
{
|
|
for (ScreenWindow *window : qAsConst(_windows)) {
|
|
delete window;
|
|
}
|
|
|
|
delete _screen[0];
|
|
delete _screen[1];
|
|
}
|
|
|
|
void Emulation::setPeekPrimary(const bool doPeek)
|
|
{
|
|
if (doPeek == _peekingPrimary) {
|
|
return;
|
|
}
|
|
_peekingPrimary = doPeek;
|
|
setScreenInternal(doPeek ? 0 : _activeScreenIndex);
|
|
Q_EMIT outputChanged();
|
|
}
|
|
|
|
void Emulation::setScreen(int index)
|
|
{
|
|
_activeScreenIndex = index;
|
|
_peekingPrimary = false;
|
|
setScreenInternal(_activeScreenIndex);
|
|
}
|
|
|
|
void Emulation::setScreenInternal(int index)
|
|
{
|
|
Screen *oldScreen = _currentScreen;
|
|
_currentScreen = _screen[index & 1];
|
|
if (_currentScreen != oldScreen) {
|
|
// tell all windows onto this emulation to switch to the newly active screen
|
|
for (ScreenWindow *window : qAsConst(_windows)) {
|
|
window->setScreen(_currentScreen);
|
|
}
|
|
|
|
checkScreenInUse();
|
|
checkSelectedText();
|
|
}
|
|
}
|
|
|
|
void Emulation::clearHistory()
|
|
{
|
|
_screen[0]->setScroll(_screen[0]->getScroll(), false);
|
|
}
|
|
|
|
void Emulation::setHistory(const HistoryType &history)
|
|
{
|
|
_screen[0]->setScroll(history);
|
|
|
|
showBulk();
|
|
}
|
|
|
|
const HistoryType &Emulation::history() const
|
|
{
|
|
return _screen[0]->getScroll();
|
|
}
|
|
|
|
void Emulation::setCodec(const QTextCodec *codec)
|
|
{
|
|
if (codec != nullptr) {
|
|
_codec = codec;
|
|
|
|
_decoder.reset(_codec->makeDecoder());
|
|
|
|
Q_EMIT useUtf8Request(utf8());
|
|
} else {
|
|
#if defined(Q_OS_WIN)
|
|
setCodec(Utf8Codec);
|
|
#else
|
|
setCodec(LocaleCodec);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Emulation::setCodec(EmulationCodec codec)
|
|
{
|
|
if (codec == Utf8Codec) {
|
|
setCodec(QTextCodec::codecForName("utf8"));
|
|
} else if (codec == LocaleCodec) {
|
|
setCodec(QTextCodec::codecForLocale());
|
|
}
|
|
}
|
|
|
|
void Emulation::setKeyBindings(const QString &name)
|
|
{
|
|
_keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
|
|
if (_keyTranslator == nullptr) {
|
|
_keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator();
|
|
}
|
|
}
|
|
|
|
QString Emulation::keyBindings() const
|
|
{
|
|
return _keyTranslator->name();
|
|
}
|
|
|
|
// process application unicode input to terminal
|
|
// this is a trivial scanner
|
|
void Emulation::receiveChars(const QVector<uint> &chars)
|
|
{
|
|
for (uint c : chars) {
|
|
c &= 0xff;
|
|
switch (c) {
|
|
case '\b':
|
|
_currentScreen->backspace();
|
|
break;
|
|
case '\t':
|
|
_currentScreen->tab();
|
|
break;
|
|
case '\n':
|
|
_currentScreen->newLine();
|
|
break;
|
|
case '\r':
|
|
_currentScreen->toStartOfLine();
|
|
break;
|
|
case 0x07:
|
|
Q_EMIT bell();
|
|
break;
|
|
default:
|
|
_currentScreen->displayCharacter(c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Emulation::sendKeyEvent(QKeyEvent *ev)
|
|
{
|
|
if (!ev->text().isEmpty()) {
|
|
// A block of text
|
|
// Note that the text is proper unicode.
|
|
// We should do a conversion here
|
|
Q_EMIT sendData(ev->text().toLocal8Bit());
|
|
}
|
|
}
|
|
|
|
void Emulation::receiveData(const char *text, int length)
|
|
{
|
|
Q_ASSERT(_decoder);
|
|
|
|
bufferedUpdate();
|
|
|
|
// send characters to terminal emulator
|
|
const QVector<uint> chars = _decoder->toUnicode(text, length).toUcs4();
|
|
receiveChars(chars);
|
|
|
|
// look for z-modem indicator
|
|
//-- someone who understands more about z-modems that I do may be able to move
|
|
// this check into the above for loop?
|
|
auto *found = static_cast<const char *>(memchr(text, '\030', length));
|
|
if (found) {
|
|
auto startPos = found - text;
|
|
if (startPos < 0) {
|
|
return;
|
|
}
|
|
for (int i = startPos; i < length - 4; i++) {
|
|
if (text[i] == '\030') {
|
|
if (qstrncmp(text + i + 1, "B00", 3) == 0) {
|
|
Q_EMIT zmodemDownloadDetected();
|
|
} else if (qstrncmp(text + i + 1, "B01", 3) == 0) {
|
|
Q_EMIT zmodemUploadDetected();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Emulation::writeToStream(TerminalCharacterDecoder *decoder, int startLine, int endLine)
|
|
{
|
|
_currentScreen->writeLinesToStream(decoder, startLine, endLine);
|
|
}
|
|
|
|
int Emulation::lineCount() const
|
|
{
|
|
// sum number of lines currently on _screen plus number of lines in history
|
|
return _currentScreen->getLines() + _currentScreen->getHistLines();
|
|
}
|
|
|
|
void Emulation::showBulk()
|
|
{
|
|
_bulkTimer1.stop();
|
|
_bulkTimer2.stop();
|
|
|
|
Q_EMIT outputChanged();
|
|
|
|
_currentScreen->resetScrolledLines();
|
|
_currentScreen->resetDroppedLines();
|
|
}
|
|
|
|
void Emulation::bufferedUpdate()
|
|
{
|
|
static const int BULK_TIMEOUT1 = 10;
|
|
static const int BULK_TIMEOUT2 = 40;
|
|
|
|
_bulkTimer1.setSingleShot(true);
|
|
_bulkTimer1.start(BULK_TIMEOUT1);
|
|
if (!_bulkTimer2.isActive()) {
|
|
_bulkTimer2.setSingleShot(true);
|
|
_bulkTimer2.start(BULK_TIMEOUT2);
|
|
}
|
|
}
|
|
|
|
char Emulation::eraseChar() const
|
|
{
|
|
return '\b';
|
|
}
|
|
|
|
void Emulation::setImageSize(int lines, int columns)
|
|
{
|
|
if ((lines < 1) || (columns < 1)) {
|
|
return;
|
|
}
|
|
|
|
QSize screenSize[2] = {QSize(_screen[0]->getColumns(), _screen[0]->getLines()), //
|
|
QSize(_screen[1]->getColumns(), _screen[1]->getLines())};
|
|
QSize newSize(columns, lines);
|
|
|
|
if (newSize == screenSize[0] && newSize == screenSize[1]) {
|
|
// If this method is called for the first time, always emit
|
|
// SIGNAL(imageSizeChange()), even if the new size is the same as the
|
|
// current size. See #176902
|
|
if (!_imageSizeInitialized) {
|
|
Q_EMIT imageSizeChanged(lines, columns);
|
|
}
|
|
} else {
|
|
_screen[0]->resizeImage(lines, columns);
|
|
_screen[1]->resizeImage(lines, columns);
|
|
|
|
Q_EMIT imageSizeChanged(lines, columns);
|
|
|
|
bufferedUpdate();
|
|
}
|
|
|
|
if (!_imageSizeInitialized) {
|
|
_imageSizeInitialized = true;
|
|
|
|
Q_EMIT imageSizeInitialized();
|
|
}
|
|
}
|
|
|
|
QSize Emulation::imageSize() const
|
|
{
|
|
return {_currentScreen->getColumns(), _currentScreen->getLines()};
|
|
}
|
|
|
|
#include "moc_Emulation.cpp"
|