File indexing completed on 2024-04-21 05:51:22

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
0003     SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
0004     SPDX-FileCopyrightText: 1996 Matthias Ettrich <ettrich@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 // Own
0010 #include "Emulation.h"
0011 
0012 // Qt
0013 #include <QKeyEvent>
0014 
0015 // Konsole
0016 #include "Screen.h"
0017 #include "ScreenWindow.h"
0018 #include "keyboardtranslator/KeyboardTranslator.h"
0019 #include "keyboardtranslator/KeyboardTranslatorManager.h"
0020 
0021 using namespace Konsole;
0022 
0023 Emulation::Emulation()
0024 {
0025     // create screens with a default size
0026     _screen[0] = new Screen(40, 80);
0027     _screen[1] = new Screen(40, 80);
0028     _currentScreen = _screen[0];
0029 
0030     QObject::connect(&_bulkTimer1, &QTimer::timeout, this, &Konsole::Emulation::showBulk);
0031     QObject::connect(&_bulkTimer2, &QTimer::timeout, this, &Konsole::Emulation::showBulk);
0032 
0033     // listen for mouse status changes
0034     connect(this, &Konsole::Emulation::programRequestsMouseTracking, this, &Konsole::Emulation::setUsesMouseTracking);
0035     connect(this, &Konsole::Emulation::programBracketedPasteModeChanged, this, &Konsole::Emulation::bracketedPasteModeChanged);
0036 }
0037 
0038 bool Emulation::programUsesMouseTracking() const
0039 {
0040     return _usesMouseTracking;
0041 }
0042 
0043 void Emulation::setUsesMouseTracking(bool usesMouseTracking)
0044 {
0045     _usesMouseTracking = usesMouseTracking;
0046 }
0047 
0048 bool Emulation::programBracketedPasteMode() const
0049 {
0050     return _bracketedPasteMode;
0051 }
0052 
0053 void Emulation::bracketedPasteModeChanged(bool bracketedPasteMode)
0054 {
0055     _bracketedPasteMode = bracketedPasteMode;
0056 }
0057 
0058 ScreenWindow *Emulation::createWindow()
0059 {
0060     auto window = new ScreenWindow(_currentScreen);
0061     _windows << window;
0062 
0063     connect(window, &Konsole::ScreenWindow::selectionChanged, this, &Konsole::Emulation::bufferedUpdate);
0064     connect(window, &Konsole::ScreenWindow::selectionChanged, this, &Konsole::Emulation::checkSelectedText);
0065 
0066     connect(this, &Konsole::Emulation::outputChanged, window, &Konsole::ScreenWindow::notifyOutputChanged);
0067 
0068     return window;
0069 }
0070 
0071 void Emulation::setCurrentTerminalDisplay(TerminalDisplay *display)
0072 {
0073     _screen[0]->setCurrentTerminalDisplay(display);
0074     _screen[1]->setCurrentTerminalDisplay(display);
0075 }
0076 
0077 void Emulation::checkScreenInUse()
0078 {
0079     Q_EMIT primaryScreenInUse(_currentScreen == _screen[0]);
0080 }
0081 
0082 void Emulation::checkSelectedText()
0083 {
0084     bool isEmpty = !_currentScreen->hasSelection();
0085     Q_EMIT selectionChanged(isEmpty);
0086 }
0087 
0088 Emulation::~Emulation()
0089 {
0090     for (ScreenWindow *window : std::as_const(_windows)) {
0091         delete window;
0092     }
0093 
0094     delete _screen[0];
0095     delete _screen[1];
0096 }
0097 
0098 void Emulation::setPeekPrimary(const bool doPeek)
0099 {
0100     if (doPeek == _peekingPrimary) {
0101         return;
0102     }
0103     _peekingPrimary = doPeek;
0104     setScreenInternal(doPeek ? 0 : _activeScreenIndex);
0105     Q_EMIT outputChanged();
0106 }
0107 
0108 void Emulation::setScreen(int index)
0109 {
0110     _activeScreenIndex = index;
0111     _peekingPrimary = false;
0112     setScreenInternal(_activeScreenIndex);
0113 }
0114 
0115 void Emulation::setScreenInternal(int index)
0116 {
0117     Screen *oldScreen = _currentScreen;
0118     _currentScreen = _screen[index & 1];
0119     if (_currentScreen != oldScreen) {
0120         // tell all windows onto this emulation to switch to the newly active screen
0121         for (ScreenWindow *window : std::as_const(_windows)) {
0122             window->setScreen(_currentScreen);
0123         }
0124 
0125         checkScreenInUse();
0126         checkSelectedText();
0127     }
0128 }
0129 
0130 void Emulation::clearHistory()
0131 {
0132     _screen[0]->setScroll(_screen[0]->getScroll(), false);
0133 }
0134 
0135 void Emulation::setHistory(const HistoryType &history)
0136 {
0137     _screen[0]->setScroll(history);
0138 
0139     showBulk();
0140 }
0141 
0142 const HistoryType &Emulation::history() const
0143 {
0144     return _screen[0]->getScroll();
0145 }
0146 
0147 void Emulation::setCodec(const QTextCodec *codec)
0148 {
0149     if (codec != nullptr) {
0150         _codec = codec;
0151 
0152         _decoder.reset(_codec->makeDecoder());
0153 
0154         Q_EMIT useUtf8Request(utf8());
0155     } else {
0156 #if defined(Q_OS_WIN)
0157         setCodec(Utf8Codec);
0158 #else
0159         setCodec(LocaleCodec);
0160 #endif
0161     }
0162 }
0163 
0164 void Emulation::setCodec(EmulationCodec codec)
0165 {
0166     if (codec == Utf8Codec) {
0167         setCodec(QTextCodec::codecForName("utf8"));
0168     } else if (codec == LocaleCodec) {
0169         setCodec(QTextCodec::codecForLocale());
0170     }
0171 }
0172 
0173 void Emulation::setKeyBindings(const QString &name)
0174 {
0175     _keyTranslator = KeyboardTranslatorManager::instance()->findTranslator(name);
0176     if (_keyTranslator == nullptr) {
0177         _keyTranslator = KeyboardTranslatorManager::instance()->defaultTranslator();
0178     }
0179 }
0180 
0181 QString Emulation::keyBindings() const
0182 {
0183     return _keyTranslator->name();
0184 }
0185 
0186 // process application unicode input to terminal
0187 // this is a trivial scanner
0188 void Emulation::receiveChars(const QVector<uint> &chars)
0189 {
0190     for (uint c : chars) {
0191         c &= 0xff;
0192         switch (c) {
0193         case '\b':
0194             _currentScreen->backspace();
0195             break;
0196         case '\t':
0197             _currentScreen->tab();
0198             break;
0199         case '\n':
0200             _currentScreen->newLine();
0201             break;
0202         case '\r':
0203             _currentScreen->toStartOfLine();
0204             break;
0205         case 0x07:
0206             Q_EMIT bell();
0207             break;
0208         default:
0209             _currentScreen->displayCharacter(c);
0210             break;
0211         }
0212     }
0213 }
0214 
0215 void Emulation::sendKeyEvent(QKeyEvent *ev)
0216 {
0217     if (!ev->text().isEmpty()) {
0218         // A block of text
0219         // Note that the text is proper unicode.
0220         // We should do a conversion here
0221         Q_EMIT sendData(ev->text().toLocal8Bit());
0222     }
0223 }
0224 
0225 void Emulation::receiveData(const char *text, int length)
0226 {
0227     Q_ASSERT(_decoder);
0228 
0229     bufferedUpdate();
0230 
0231     // send characters to terminal emulator
0232     const QVector<uint> chars = _decoder->toUnicode(text, length).toUcs4();
0233     receiveChars(chars);
0234 
0235     // look for z-modem indicator
0236     //-- someone who understands more about z-modems that I do may be able to move
0237     // this check into the above for loop?
0238     auto *found = static_cast<const char *>(memchr(text, '\030', length));
0239     if (found) {
0240         auto startPos = found - text;
0241         if (startPos < 0) {
0242             return;
0243         }
0244         for (int i = startPos; i < length - 4; i++) {
0245             if (text[i] == '\030') {
0246                 if (qstrncmp(text + i + 1, "B00", 3) == 0) {
0247                     Q_EMIT zmodemDownloadDetected();
0248                 } else if (qstrncmp(text + i + 1, "B01", 3) == 0) {
0249                     Q_EMIT zmodemUploadDetected();
0250                 }
0251             }
0252         }
0253     }
0254 }
0255 
0256 void Emulation::writeToStream(TerminalCharacterDecoder *decoder, int startLine, int endLine)
0257 {
0258     _currentScreen->writeLinesToStream(decoder, startLine, endLine);
0259 }
0260 
0261 int Emulation::lineCount() const
0262 {
0263     // sum number of lines currently on _screen plus number of lines in history
0264     return _currentScreen->getLines() + _currentScreen->getHistLines();
0265 }
0266 
0267 void Emulation::showBulk()
0268 {
0269     _bulkTimer1.stop();
0270     _bulkTimer2.stop();
0271 
0272     Q_EMIT outputChanged();
0273 
0274     _currentScreen->resetScrolledLines();
0275     _currentScreen->resetDroppedLines();
0276 }
0277 
0278 void Emulation::bufferedUpdate()
0279 {
0280     static const int BULK_TIMEOUT1 = 10;
0281     static const int BULK_TIMEOUT2 = 40;
0282 
0283     _bulkTimer1.setSingleShot(true);
0284     _bulkTimer1.start(BULK_TIMEOUT1);
0285     if (!_bulkTimer2.isActive()) {
0286         _bulkTimer2.setSingleShot(true);
0287         _bulkTimer2.start(BULK_TIMEOUT2);
0288     }
0289 }
0290 
0291 char Emulation::eraseChar() const
0292 {
0293     return '\b';
0294 }
0295 
0296 void Emulation::setImageSize(int lines, int columns)
0297 {
0298     if ((lines < 1) || (columns < 1)) {
0299         return;
0300     }
0301 
0302     QSize screenSize[2] = {QSize(_screen[0]->getColumns(), _screen[0]->getLines()), //
0303                            QSize(_screen[1]->getColumns(), _screen[1]->getLines())};
0304     QSize newSize(columns, lines);
0305 
0306     if (newSize == screenSize[0] && newSize == screenSize[1]) {
0307         // If this method is called for the first time, always emit
0308         // SIGNAL(imageSizeChange()), even if the new size is the same as the
0309         // current size.  See #176902
0310         if (!_imageSizeInitialized) {
0311             Q_EMIT imageSizeChanged(lines, columns);
0312         }
0313     } else {
0314         _screen[0]->resizeImage(lines, columns);
0315         _screen[1]->resizeImage(lines, columns);
0316 
0317         Q_EMIT imageSizeChanged(lines, columns);
0318 
0319         bufferedUpdate();
0320     }
0321 
0322     if (!_imageSizeInitialized) {
0323         _imageSizeInitialized = true;
0324 
0325         Q_EMIT imageSizeInitialized();
0326     }
0327 }
0328 
0329 QSize Emulation::imageSize() const
0330 {
0331     return {_currentScreen->getColumns(), _currentScreen->getLines()};
0332 }
0333 
0334 #include "moc_Emulation.cpp"