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"