Files
konsole/src/Emulation.cpp
Mariusz Glebocki 9855b2c125 Use flags for each session notification type
Replaces single-value session state with separate flags for each
possible notification (silence, activity, bell). This will allow for
more flexible control over how specific notifications are displayed.
2020-04-10 13:51:07 -04:00

343 lines
8.7 KiB
C++

/*
Copyright 2007-2008 Robert Knight <robertknight@gmail.com>
Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
Copyright 1996 by Matthias Ettrich <ettrich@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.
*/
// Own
#include "Emulation.h"
// Qt
#include <QKeyEvent>
// Konsole
#include "KeyboardTranslator.h"
#include "KeyboardTranslatorManager.h"
#include "Screen.h"
#include "ScreenWindow.h"
using namespace Konsole;
Emulation::Emulation() :
_windows(QList<ScreenWindow *>()),
_currentScreen(nullptr),
_codec(nullptr),
_decoder(nullptr),
_keyTranslator(nullptr),
_usesMouseTracking(false),
_bracketedPasteMode(false),
_bulkTimer1(new QTimer(this)),
_bulkTimer2(new QTimer(this)),
_imageSizeInitialized(false)
{
// 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::checkScreenInUse()
{
emit primaryScreenInUse(_currentScreen == _screen[0]);
}
void Emulation::checkSelectedText()
{
QString text = _currentScreen->selectedText(Screen::PreserveLineBreaks);
emit selectionChanged(text);
}
Emulation::~Emulation()
{
for (ScreenWindow *window : qAsConst(_windows)) {
delete window;
}
delete _screen[0];
delete _screen[1];
delete _decoder;
}
void Emulation::setScreen(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;
delete _decoder;
_decoder = _codec->makeDecoder();
emit useUtf8Request(utf8());
} else {
setCodec(LocaleCodec);
}
}
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::receiveChar(uint c)
{
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:
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
emit sendData(ev->text().toLocal8Bit());
}
}
void Emulation::sendMouseEvent(int /*buttons*/, int /*column*/, int /*row*/, int /*eventType*/)
{
// default implementation does nothing
}
/*
We are doing code conversion from locale to unicode first.
*/
void Emulation::receiveData(const char *text, int length)
{
bufferedUpdate();
QVector<uint> unicodeText = _decoder->toUnicode(text, length).toUcs4();
//send characters to terminal emulator
for (auto &&i : unicodeText) {
receiveChar(i);
}
//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?
for (int i = 0; i < length; i++) {
if (text[i] == '\030') {
if (length - i - 1 > 3) {
if (qstrncmp(text + i + 1, "B00", 3) == 0) {
emit zmodemDownloadDetected();
} else if (qstrncmp(text + i + 1, "B01", 3) == 0) {
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();
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) {
emit imageSizeChanged(lines, columns);
}
} else {
_screen[0]->resizeImage(lines, columns);
_screen[1]->resizeImage(lines, columns);
emit imageSizeChanged(lines, columns);
bufferedUpdate();
}
if (!_imageSizeInitialized) {
_imageSizeInitialized = true;
emit imageSizeInitialized();
}
}
QSize Emulation::imageSize() const
{
return {_currentScreen->getColumns(), _currentScreen->getLines()};
}