File indexing completed on 2025-01-19 04:23:30
0001 /* 0002 This file is part of Konsole 0003 0004 Copyright (C) 2006-2007 by Robert Knight <robertknight@gmail.com> 0005 Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> 0006 0007 Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 0008 0009 This program is free software; you can redistribute it and/or modify 0010 it under the terms of the GNU General Public License as published by 0011 the Free Software Foundation; either version 2 of the License, or 0012 (at your option) any later version. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program; if not, write to the Free Software 0021 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0022 02110-1301 USA. 0023 */ 0024 0025 // Own 0026 #include "Session.h" 0027 0028 // Standard 0029 #include <stdlib.h> 0030 0031 // Qt 0032 #include <QApplication> 0033 #include <QByteRef> 0034 #include <QDir> 0035 #include <QFile> 0036 #include <QRegExp> 0037 #include <QStringList> 0038 #include <QFile> 0039 #include <QtDebug> 0040 0041 #include "Pty.h" 0042 //#include "kptyprocess.h" 0043 #include "TerminalDisplay.h" 0044 #include "ShellCommand.h" 0045 #include "Vt102Emulation.h" 0046 0047 // QMLTermWidget 0048 #include <QQuickWindow> 0049 0050 using namespace Konsole; 0051 0052 int Session::lastSessionId = 0; 0053 0054 Session::Session(QObject* parent) : 0055 QObject(parent), 0056 _shellProcess(0) 0057 , _emulation(0) 0058 , _monitorActivity(false) 0059 , _monitorSilence(false) 0060 , _notifiedActivity(false) 0061 , _autoClose(true) 0062 , _wantedClose(false) 0063 , _silenceSeconds(10) 0064 , _isTitleChanged(false) 0065 , _addToUtmp(false) // disabled by default because of a bug encountered on certain systems 0066 // which caused Konsole to hang when closing a tab and then opening a new 0067 // one. A 'QProcess destroyed while still running' warning was being 0068 // printed to the terminal. Likely a problem in KPty::logout() 0069 // or KPty::login() which uses a QProcess to start /usr/bin/utempter 0070 , _flowControl(true) 0071 , _fullScripting(false) 0072 , _sessionId(0) 0073 // , _zmodemBusy(false) 0074 // , _zmodemProc(0) 0075 // , _zmodemProgress(0) 0076 , _hasDarkBackground(false) 0077 , _foregroundProcessInfo(NULL) 0078 , _foregroundPid(0) 0079 { 0080 //prepare DBus communication 0081 // new SessionAdaptor(this); 0082 _sessionId = ++lastSessionId; 0083 // QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this); 0084 0085 //create teletype for I/O with shell process 0086 _shellProcess = new Pty(); 0087 ptySlaveFd = _shellProcess->pty()->slaveFd(); 0088 0089 //create emulation backend 0090 _emulation = new Vt102Emulation(); 0091 0092 connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ), 0093 this, SLOT( setUserTitle( int, const QString & ) ) ); 0094 connect( _emulation, SIGNAL( stateSet(int) ), 0095 this, SLOT( activityStateSet(int) ) ); 0096 // connect( _emulation, SIGNAL( zmodemDetected() ), this , 0097 // SLOT( fireZModemDetected() ) ); 0098 connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ), 0099 this, SIGNAL( changeTabTextColorRequest( int ) ) ); 0100 connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString &)), 0101 this, SIGNAL( profileChangeCommandReceived(const QString &)) ); 0102 0103 connect(_emulation, SIGNAL(imageResizeRequest(QSize)), 0104 this, SLOT(onEmulationSizeChange(QSize))); 0105 connect(_emulation, SIGNAL(imageSizeChanged(int, int)), 0106 this, SLOT(onViewSizeChange(int, int))); 0107 connect(_emulation, &Vt102Emulation::cursorChanged, 0108 this, &Session::cursorChanged); 0109 0110 //connect teletype to emulation backend 0111 _shellProcess->setUtf8Mode(_emulation->utf8()); 0112 0113 connect( _shellProcess,SIGNAL(receivedData(const char *,int)),this, 0114 SLOT(onReceiveBlock(const char *,int)) ); 0115 connect( _emulation,SIGNAL(sendData(const char *,int)),_shellProcess, 0116 SLOT(sendData(const char *,int)) ); 0117 connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) ); 0118 connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) ); 0119 0120 connect( _shellProcess,SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(done(int)) ); 0121 // not in kprocess anymore connect( _shellProcess,SIGNAL(done(int)), this, SLOT(done(int)) ); 0122 0123 //setup timer for monitoring session activity 0124 _monitorTimer = new QTimer(this); 0125 _monitorTimer->setSingleShot(true); 0126 connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone())); 0127 } 0128 0129 WId Session::windowId() const 0130 { 0131 // On Qt5, requesting window IDs breaks QQuickWidget and the likes, 0132 // for example, see the following bug reports: 0133 // https://bugreports.qt.io/browse/QTBUG-40765 0134 // https://codereview.qt-project.org/#/c/94880/ 0135 return 0; 0136 } 0137 0138 QString Session::validDirectory(const QString &dir) const 0139 { 0140 QString validDir = dir; 0141 if (validDir.isEmpty()) { 0142 validDir = QDir::currentPath(); 0143 } 0144 0145 const QFileInfo fi(validDir); 0146 if (!fi.exists() || !fi.isDir()) { 0147 validDir = QDir::homePath(); 0148 } 0149 0150 return validDir; 0151 } 0152 0153 void Session::setDarkBackground(bool darkBackground) 0154 { 0155 _hasDarkBackground = darkBackground; 0156 } 0157 bool Session::hasDarkBackground() const 0158 { 0159 return _hasDarkBackground; 0160 } 0161 bool Session::isRunning() const 0162 { 0163 return _shellProcess->state() == QProcess::Running; 0164 } 0165 0166 void Session::setCodec(QTextCodec * codec) 0167 { 0168 emulation()->setCodec(codec); 0169 } 0170 0171 void Session::setProgram(const QString & program) 0172 { 0173 _program = ShellCommand::expand(program); 0174 } 0175 0176 void Session::setInitialWorkingDirectory(const QString & dir) 0177 { 0178 _initialWorkingDir = validDirectory(ShellCommand::expand(dir)); 0179 } 0180 0181 void Session::setArguments(const QStringList & arguments) 0182 { 0183 _arguments = ShellCommand::expand(arguments); 0184 } 0185 0186 QList<TerminalDisplay *> Session::views() const 0187 { 0188 return _views; 0189 } 0190 0191 void Session::addView(TerminalDisplay * widget) 0192 { 0193 Q_ASSERT( !_views.contains(widget) ); 0194 0195 _views.append(widget); 0196 0197 if ( _emulation != 0 ) { 0198 // connect emulation - view Q_SIGNALS and Q_SLOTS 0199 connect( widget , SIGNAL(keyPressedSignal(QKeyEvent *)) , _emulation , 0200 SLOT(sendKeyEvent(QKeyEvent *)) ); 0201 connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation , 0202 SLOT(sendMouseEvent(int,int,int,int)) ); 0203 connect( widget , SIGNAL(sendStringToEmu(const char *)) , _emulation , 0204 SLOT(sendString(const char *)) ); 0205 0206 // allow emulation to notify view when the foreground process 0207 // indicates whether or not it is interested in mouse Q_SIGNALS 0208 connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget , 0209 SLOT(setUsesMouse(bool)) ); 0210 0211 widget->setUsesMouse( _emulation->programUsesMouse() ); 0212 0213 connect( _emulation , SIGNAL(programBracketedPasteModeChanged(bool)) , 0214 widget , SLOT(setBracketedPasteMode(bool)) ); 0215 0216 widget->setBracketedPasteMode(_emulation->programBracketedPasteMode()); 0217 0218 widget->setScreenWindow(_emulation->createWindow()); 0219 } 0220 0221 //connect view Q_SIGNALS and Q_SLOTS 0222 QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this, 0223 SLOT(onViewSizeChange(int,int))); 0224 0225 QObject::connect( widget ,SIGNAL(destroyed(QObject *)) , this , 0226 SLOT(viewDestroyed(QObject *)) ); 0227 //slot for close 0228 QObject::connect(this, SIGNAL(finished()), widget, SLOT(close())); 0229 0230 } 0231 0232 void Session::viewDestroyed(QObject * view) 0233 { 0234 TerminalDisplay * display = (TerminalDisplay *)view; 0235 0236 Q_ASSERT( _views.contains(display) ); 0237 0238 removeView(display); 0239 } 0240 0241 void Session::removeView(TerminalDisplay * widget) 0242 { 0243 _views.removeAll(widget); 0244 0245 disconnect(widget,0,this,0); 0246 0247 if ( _emulation != 0 ) { 0248 // disconnect 0249 // - key presses Q_SIGNALS from widget 0250 // - mouse activity Q_SIGNALS from widget 0251 // - string sending Q_SIGNALS from widget 0252 // 0253 // ... and any other Q_SIGNALS connected in addView() 0254 disconnect( widget, 0, _emulation, 0); 0255 0256 // disconnect state change Q_SIGNALS emitted by emulation 0257 disconnect( _emulation , 0 , widget , 0); 0258 } 0259 0260 // close the session automatically when the last view is removed 0261 if ( _views.count() == 0 ) { 0262 close(); 0263 } 0264 } 0265 0266 void Session::run() 0267 { 0268 // Upon a KPty error, there is no description on what that error was... 0269 // Check to see if the given program is executable. 0270 0271 /* ok iam not exactly sure where _program comes from - however it was set to /bin/bash on my system 0272 * Thats bad for BSD as its /usr/local/bin/bash there - its also bad for arch as its /usr/bin/bash there too! 0273 * So i added a check to see if /bin/bash exists - if no then we use $SHELL - if that does not exist either, we fall back to /bin/sh 0274 * As far as i know /bin/sh exists on every unix system.. You could also just put some ifdef __FREEBSD__ here but i think these 2 filechecks are worth 0275 * their computing time on any system - especially with the problem on arch linux beeing there too. 0276 */ 0277 QString exec = QString::fromLocal8Bit(QFile::encodeName(_program)); 0278 // if 'exec' is not specified, fall back to default shell. if that 0279 // is not set then fall back to /bin/sh 0280 0281 // here we expect full path. If there is no fullpath let's expect it's 0282 // a custom shell (eg. python, etc.) available in the PATH. 0283 if (exec.startsWith(QLatin1Char('/')) || exec.isEmpty()) 0284 { 0285 const QString defaultShell{QLatin1String("/bin/sh")}; 0286 0287 QFile excheck(exec); 0288 if ( exec.isEmpty() || !excheck.exists() ) { 0289 exec = QString::fromLocal8Bit(qgetenv("SHELL")); 0290 } 0291 excheck.setFileName(exec); 0292 0293 if ( exec.isEmpty() || !excheck.exists() ) { 0294 qWarning() << "Neither default shell nor $SHELL is set to a correct path. Fallback to" << defaultShell; 0295 exec = defaultShell; 0296 } 0297 } 0298 0299 // _arguments sometimes contain ("") so isEmpty() 0300 // or count() does not work as expected... 0301 QString argsTmp(_arguments.join(QLatin1Char(' ')).trimmed()); 0302 QStringList arguments; 0303 arguments << exec; 0304 if (argsTmp.length()) 0305 arguments << _arguments; 0306 0307 QString cwd = QDir::currentPath(); 0308 if (!_initialWorkingDir.isEmpty()) { 0309 _shellProcess->setWorkingDirectory(_initialWorkingDir); 0310 } else { 0311 _shellProcess->setWorkingDirectory(cwd); 0312 } 0313 0314 _shellProcess->setFlowControlEnabled(_flowControl); 0315 _shellProcess->setErase(_emulation->eraseChar()); 0316 0317 // this is not strictly accurate use of the COLORFGBG variable. This does not 0318 // tell the terminal exactly which colors are being used, but instead approximates 0319 // the color scheme as "black on white" or "white on black" depending on whether 0320 // the background color is deemed dark or not 0321 QString backgroundColorHint = _hasDarkBackground ? QLatin1String("COLORFGBG=15;0") : QLatin1String("COLORFGBG=0;15"); 0322 0323 /* if we do all the checking if this shell exists then we use it ;) 0324 * Dont know about the arguments though.. maybe youll need some more checking im not sure 0325 * However this works on Arch and FreeBSD now. 0326 */ 0327 int result = _shellProcess->start(exec, 0328 arguments, 0329 _environment << backgroundColorHint, 0330 windowId(), 0331 _addToUtmp); 0332 0333 if (result < 0) { 0334 qDebug() << "CRASHED! result: " << result; 0335 return; 0336 } 0337 0338 _shellProcess->setWriteable(false); // We are reachable via kwrited. 0339 Q_EMIT started(); 0340 } 0341 0342 void Session::runEmptyPTY() 0343 { 0344 _shellProcess->setFlowControlEnabled(_flowControl); 0345 _shellProcess->setErase(_emulation->eraseChar()); 0346 _shellProcess->setWriteable(false); 0347 0348 // disconnet send data from emulator to internal terminal process 0349 disconnect( _emulation,SIGNAL(sendData(const char *,int)), 0350 _shellProcess, SLOT(sendData(const char *,int)) ); 0351 0352 _shellProcess->setEmptyPTYProperties(); 0353 Q_EMIT started(); 0354 } 0355 0356 void Session::setUserTitle( int what, const QString & caption ) 0357 { 0358 //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle ) 0359 bool modified = false; 0360 0361 // (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only _nameTitle 0362 if ((what == 0) || (what == 2)) { 0363 _isTitleChanged = true; 0364 if ( _userTitle != caption ) { 0365 _userTitle = caption; 0366 modified = true; 0367 } 0368 } 0369 0370 if ((what == 0) || (what == 1)) { 0371 _isTitleChanged = true; 0372 if ( _iconText != caption ) { 0373 _iconText = caption; 0374 modified = true; 0375 } 0376 } 0377 0378 if (what == 11) { 0379 QString colorString = caption.section(QLatin1Char(';'),0,0); 0380 //qDebug() << __FILE__ << __LINE__ << ": setting background colour to " << colorString; 0381 QColor backColor = QColor(colorString); 0382 if (backColor.isValid()) { // change color via \033]11;Color\007 0383 if (backColor != _modifiedBackground) { 0384 _modifiedBackground = backColor; 0385 0386 // bail out here until the code to connect the terminal display 0387 // to the changeBackgroundColor() signal has been written 0388 // and tested - just so we don't forget to do this. 0389 Q_ASSERT( 0 ); 0390 0391 Q_EMIT changeBackgroundColorRequest(backColor); 0392 } 0393 } 0394 } 0395 0396 if (what == 30) { 0397 _isTitleChanged = true; 0398 if ( _nameTitle != caption ) { 0399 setTitle(Session::NameRole,caption); 0400 return; 0401 } 0402 } 0403 0404 if (what == 31) { 0405 QString cwd=caption; 0406 cwd=cwd.replace( QRegExp(QLatin1String("^~")), QDir::homePath() ); 0407 Q_EMIT openUrlRequest(cwd); 0408 } 0409 0410 // change icon via \033]32;Icon\007 0411 if (what == 32) { 0412 _isTitleChanged = true; 0413 if ( _iconName != caption ) { 0414 _iconName = caption; 0415 0416 modified = true; 0417 } 0418 } 0419 0420 if (what == 50) { 0421 Q_EMIT profileChangeCommandReceived(caption); 0422 return; 0423 } 0424 0425 if ( modified ) { 0426 Q_EMIT titleChanged(); 0427 } 0428 } 0429 0430 QString Session::userTitle() const 0431 { 0432 return _userTitle; 0433 } 0434 void Session::setTabTitleFormat(TabTitleContext context , const QString & format) 0435 { 0436 if ( context == LocalTabTitle ) { 0437 _localTabTitleFormat = format; 0438 } else if ( context == RemoteTabTitle ) { 0439 _remoteTabTitleFormat = format; 0440 } 0441 } 0442 QString Session::tabTitleFormat(TabTitleContext context) const 0443 { 0444 if ( context == LocalTabTitle ) { 0445 return _localTabTitleFormat; 0446 } else if ( context == RemoteTabTitle ) { 0447 return _remoteTabTitleFormat; 0448 } 0449 0450 return QString(); 0451 } 0452 0453 void Session::monitorTimerDone() 0454 { 0455 //FIXME: The idea here is that the notification popup will appear to tell the user than output from 0456 //the terminal has stopped and the popup will disappear when the user activates the session. 0457 // 0458 //This breaks with the addition of multiple views of a session. The popup should disappear 0459 //when any of the views of the session becomes active 0460 0461 0462 //FIXME: Make message text for this notification and the activity notification more descriptive. 0463 if (_monitorSilence) { 0464 Q_EMIT silence(); 0465 Q_EMIT stateChanged(NOTIFYSILENCE); 0466 } else { 0467 Q_EMIT stateChanged(NOTIFYNORMAL); 0468 } 0469 0470 _notifiedActivity=false; 0471 } 0472 0473 void Session::activityStateSet(int state) 0474 { 0475 if (state==NOTIFYBELL) { 0476 QString s; 0477 s.sprintf("Bell in session '%s'",_nameTitle.toUtf8().data()); 0478 0479 Q_EMIT bellRequest( s ); 0480 } else if (state==NOTIFYACTIVITY) { 0481 if (_monitorSilence) { 0482 _monitorTimer->start(_silenceSeconds*1000); 0483 } 0484 0485 if ( _monitorActivity ) { 0486 //FIXME: See comments in Session::monitorTimerDone() 0487 if (!_notifiedActivity) { 0488 _notifiedActivity=true; 0489 Q_EMIT activity(); 0490 } 0491 } 0492 } 0493 0494 if ( state==NOTIFYACTIVITY && !_monitorActivity ) { 0495 state = NOTIFYNORMAL; 0496 } 0497 if ( state==NOTIFYSILENCE && !_monitorSilence ) { 0498 state = NOTIFYNORMAL; 0499 } 0500 0501 Q_EMIT stateChanged(state); 0502 } 0503 0504 void Session::onViewSizeChange(int /*height*/, int /*width*/) 0505 { 0506 updateTerminalSize(); 0507 } 0508 void Session::onEmulationSizeChange(QSize size) 0509 { 0510 setSize(size); 0511 } 0512 0513 void Session::updateTerminalSize() 0514 { 0515 QListIterator<TerminalDisplay *> viewIter(_views); 0516 0517 int minLines = -1; 0518 int minColumns = -1; 0519 0520 // minimum number of lines and columns that views require for 0521 // their size to be taken into consideration ( to avoid problems 0522 // with new view widgets which haven't yet been set to their correct size ) 0523 const int VIEW_LINES_THRESHOLD = 2; 0524 const int VIEW_COLUMNS_THRESHOLD = 2; 0525 0526 //select largest number of lines and columns that will fit in all visible views 0527 while ( viewIter.hasNext() ) { 0528 TerminalDisplay * view = viewIter.next(); 0529 if ( //!view->isVisible() == false && QMLTermWidget: We need to disable this check. It has issues when resizing invisible terminal. 0530 view->lines() >= VIEW_LINES_THRESHOLD && 0531 view->columns() >= VIEW_COLUMNS_THRESHOLD ) { 0532 minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() ); 0533 minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() ); 0534 } 0535 } 0536 0537 // backend emulation must have a _terminal of at least 1 column x 1 line in size 0538 if ( minLines > 0 && minColumns > 0 ) { 0539 _emulation->setImageSize( minLines , minColumns ); 0540 _shellProcess->setWindowSize( minLines , minColumns ); 0541 } 0542 } 0543 0544 void Session::refresh() 0545 { 0546 // attempt to get the shell process to redraw the display 0547 // 0548 // this requires the program running in the shell 0549 // to cooperate by sending an update in response to 0550 // a window size change 0551 // 0552 // the window size is changed twice, first made slightly larger and then 0553 // resized back to its normal size so that there is actually a change 0554 // in the window size (some shells do nothing if the 0555 // new and old sizes are the same) 0556 // 0557 // if there is a more 'correct' way to do this, please 0558 // send an email with method or patches to konsole-devel@kde.org 0559 0560 const QSize existingSize = _shellProcess->windowSize(); 0561 _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1); 0562 _shellProcess->setWindowSize(existingSize.height(),existingSize.width()); 0563 } 0564 0565 bool Session::sendSignal(int signal) 0566 { 0567 int result = ::kill(_shellProcess->pid(),signal); 0568 0569 if ( result == 0 ) 0570 { 0571 _shellProcess->waitForFinished(); 0572 return true; 0573 } 0574 else 0575 return false; 0576 } 0577 0578 void Session::close() 0579 { 0580 _autoClose = true; 0581 _wantedClose = true; 0582 if (!_shellProcess->isRunning() || !sendSignal(SIGHUP)) { 0583 // Forced close. 0584 QTimer::singleShot(1, this, SIGNAL(finished())); 0585 } 0586 } 0587 0588 void Session::sendText(const QString & text) const 0589 { 0590 _emulation->sendText(text); 0591 } 0592 0593 Session::~Session() 0594 { 0595 delete _emulation; 0596 delete _shellProcess; 0597 // delete _zmodemProc; 0598 } 0599 0600 void Session::setProfileKey(const QString & key) 0601 { 0602 _profileKey = key; 0603 Q_EMIT profileChanged(key); 0604 } 0605 QString Session::profileKey() const 0606 { 0607 return _profileKey; 0608 } 0609 0610 void Session::done(int exitStatus) 0611 { 0612 if (!_autoClose) { 0613 _userTitle = QString::fromLatin1("This session is done. Finished"); 0614 Q_EMIT titleChanged(); 0615 return; 0616 } 0617 0618 QString message; 0619 if (!_wantedClose || exitStatus != 0) { 0620 0621 if (_shellProcess->exitStatus() == QProcess::NormalExit) { 0622 message.sprintf("Session '%s' exited with status %d.", 0623 _nameTitle.toUtf8().data(), exitStatus); 0624 } else { 0625 message.sprintf("Session '%s' crashed.", 0626 _nameTitle.toUtf8().data()); 0627 } 0628 } 0629 0630 if ( !_wantedClose && _shellProcess->exitStatus() != QProcess::NormalExit ) { 0631 message.sprintf("Session '%s' exited unexpectedly.", 0632 _nameTitle.toUtf8().data()); 0633 } 0634 0635 Q_EMIT finished(); 0636 } 0637 0638 Emulation * Session::emulation() const 0639 { 0640 return _emulation; 0641 } 0642 0643 QString Session::keyBindings() const 0644 { 0645 return _emulation->keyBindings(); 0646 } 0647 0648 QStringList Session::environment() const 0649 { 0650 return _environment; 0651 } 0652 0653 void Session::setEnvironment(const QStringList & environment) 0654 { 0655 _environment = environment; 0656 } 0657 0658 int Session::sessionId() const 0659 { 0660 return _sessionId; 0661 } 0662 0663 void Session::setKeyBindings(const QString & id) 0664 { 0665 _emulation->setKeyBindings(id); 0666 } 0667 0668 void Session::setTitle(TitleRole role , const QString & newTitle) 0669 { 0670 if ( title(role) != newTitle ) { 0671 if ( role == NameRole ) { 0672 _nameTitle = newTitle; 0673 } else if ( role == DisplayedTitleRole ) { 0674 _displayTitle = newTitle; 0675 } 0676 0677 Q_EMIT titleChanged(); 0678 } 0679 } 0680 0681 QString Session::title(TitleRole role) const 0682 { 0683 if ( role == NameRole ) { 0684 return _nameTitle; 0685 } else if ( role == DisplayedTitleRole ) { 0686 return _displayTitle; 0687 } else { 0688 return QString(); 0689 } 0690 } 0691 0692 void Session::setIconName(const QString & iconName) 0693 { 0694 if ( iconName != _iconName ) { 0695 _iconName = iconName; 0696 Q_EMIT titleChanged(); 0697 } 0698 } 0699 0700 void Session::setIconText(const QString & iconText) 0701 { 0702 _iconText = iconText; 0703 //kDebug(1211)<<"Session setIconText " << _iconText; 0704 } 0705 0706 QString Session::iconName() const 0707 { 0708 return _iconName; 0709 } 0710 0711 QString Session::iconText() const 0712 { 0713 return _iconText; 0714 } 0715 0716 bool Session::isTitleChanged() const 0717 { 0718 return _isTitleChanged; 0719 } 0720 0721 void Session::setHistoryType(const HistoryType & hType) 0722 { 0723 _emulation->setHistory(hType); 0724 } 0725 0726 const HistoryType & Session::historyType() const 0727 { 0728 return _emulation->history(); 0729 } 0730 0731 void Session::clearHistory() 0732 { 0733 _emulation->clearHistory(); 0734 } 0735 0736 QStringList Session::arguments() const 0737 { 0738 return _arguments; 0739 } 0740 0741 QString Session::program() const 0742 { 0743 return _program; 0744 } 0745 0746 // unused currently 0747 bool Session::isMonitorActivity() const 0748 { 0749 return _monitorActivity; 0750 } 0751 // unused currently 0752 bool Session::isMonitorSilence() const 0753 { 0754 return _monitorSilence; 0755 } 0756 0757 void Session::setMonitorActivity(bool _monitor) 0758 { 0759 _monitorActivity=_monitor; 0760 _notifiedActivity=false; 0761 0762 activityStateSet(NOTIFYNORMAL); 0763 } 0764 0765 void Session::setMonitorSilence(bool _monitor) 0766 { 0767 if (_monitorSilence==_monitor) { 0768 return; 0769 } 0770 0771 _monitorSilence=_monitor; 0772 if (_monitorSilence) { 0773 _monitorTimer->start(_silenceSeconds*1000); 0774 } else { 0775 _monitorTimer->stop(); 0776 } 0777 0778 activityStateSet(NOTIFYNORMAL); 0779 } 0780 0781 void Session::setMonitorSilenceSeconds(int seconds) 0782 { 0783 _silenceSeconds=seconds; 0784 if (_monitorSilence) { 0785 _monitorTimer->start(_silenceSeconds*1000); 0786 } 0787 } 0788 0789 void Session::setAddToUtmp(bool set) 0790 { 0791 _addToUtmp = set; 0792 } 0793 0794 void Session::setFlowControlEnabled(bool enabled) 0795 { 0796 if (_flowControl == enabled) { 0797 return; 0798 } 0799 0800 _flowControl = enabled; 0801 0802 if (_shellProcess) { 0803 _shellProcess->setFlowControlEnabled(_flowControl); 0804 } 0805 0806 Q_EMIT flowControlEnabledChanged(enabled); 0807 } 0808 bool Session::flowControlEnabled() const 0809 { 0810 return _flowControl; 0811 } 0812 //void Session::fireZModemDetected() 0813 //{ 0814 // if (!_zmodemBusy) 0815 // { 0816 // QTimer::singleShot(10, this, SIGNAL(zmodemDetected())); 0817 // _zmodemBusy = true; 0818 // } 0819 //} 0820 0821 //void Session::cancelZModem() 0822 //{ 0823 // _shellProcess->sendData("\030\030\030\030", 4); // Abort 0824 // _zmodemBusy = false; 0825 //} 0826 0827 //void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list) 0828 //{ 0829 // _zmodemBusy = true; 0830 // _zmodemProc = new KProcess(); 0831 // _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels ); 0832 // 0833 // *_zmodemProc << zmodem << "-v" << list; 0834 // 0835 // if (!dir.isEmpty()) 0836 // _zmodemProc->setWorkingDirectory(dir); 0837 // 0838 // _zmodemProc->start(); 0839 // 0840 // connect(_zmodemProc,SIGNAL (readyReadStandardOutput()), 0841 // this, SLOT(zmodemReadAndSendBlock())); 0842 // connect(_zmodemProc,SIGNAL (readyReadStandardError()), 0843 // this, SLOT(zmodemReadStatus())); 0844 // connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)), 0845 // this, SLOT(zmodemFinished())); 0846 // 0847 // disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); 0848 // connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) ); 0849 // 0850 // _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false, 0851 // i18n("ZModem Progress")); 0852 // 0853 // connect(_zmodemProgress, SIGNAL(user1Clicked()), 0854 // this, SLOT(zmodemDone())); 0855 // 0856 // _zmodemProgress->show(); 0857 //} 0858 0859 /*void Session::zmodemReadAndSendBlock() 0860 { 0861 _zmodemProc->setReadChannel( QProcess::StandardOutput ); 0862 QByteArray data = _zmodemProc->readAll(); 0863 0864 if ( data.count() == 0 ) 0865 return; 0866 0867 _shellProcess->sendData(data.constData(),data.count()); 0868 } 0869 */ 0870 /* 0871 void Session::zmodemReadStatus() 0872 { 0873 _zmodemProc->setReadChannel( QProcess::StandardError ); 0874 QByteArray msg = _zmodemProc->readAll(); 0875 while(!msg.isEmpty()) 0876 { 0877 int i = msg.indexOf('\015'); 0878 int j = msg.indexOf('\012'); 0879 QByteArray txt; 0880 if ((i != -1) && ((j == -1) || (i < j))) 0881 { 0882 msg = msg.mid(i+1); 0883 } 0884 else if (j != -1) 0885 { 0886 txt = msg.left(j); 0887 msg = msg.mid(j+1); 0888 } 0889 else 0890 { 0891 txt = msg; 0892 msg.truncate(0); 0893 } 0894 if (!txt.isEmpty()) 0895 _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt)); 0896 } 0897 } 0898 */ 0899 /* 0900 void Session::zmodemRcvBlock(const char *data, int len) 0901 { 0902 QByteArray ba( data, len ); 0903 0904 _zmodemProc->write( ba ); 0905 } 0906 */ 0907 /* 0908 void Session::zmodemFinished() 0909 { 0910 if (_zmodemProc) 0911 { 0912 delete _zmodemProc; 0913 _zmodemProc = 0; 0914 _zmodemBusy = false; 0915 0916 disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) ); 0917 connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); 0918 0919 _shellProcess->sendData("\030\030\030\030", 4); // Abort 0920 _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back 0921 _zmodemProgress->transferDone(); 0922 } 0923 } 0924 */ 0925 void Session::onReceiveBlock( const char * buf, int len ) 0926 { 0927 _emulation->receiveData( buf, len ); 0928 Q_EMIT receivedData( QString::fromLatin1( buf, len ) ); 0929 } 0930 0931 QSize Session::size() 0932 { 0933 return _emulation->imageSize(); 0934 } 0935 0936 void Session::setSize(const QSize & size) 0937 { 0938 if ((size.width() <= 1) || (size.height() <= 1)) { 0939 return; 0940 } 0941 0942 Q_EMIT resizeRequest(size); 0943 } 0944 int Session::foregroundProcessId() const 0945 { 0946 return _shellProcess->foregroundProcessGroup(); 0947 } 0948 0949 QString Session::foregroundProcessName() 0950 { 0951 QString name; 0952 0953 if (updateForegroundProcessInfo()) { 0954 bool ok = false; 0955 name = _foregroundProcessInfo->name(&ok); 0956 if (!ok) 0957 name.clear(); 0958 } 0959 0960 return name; 0961 } 0962 0963 QString Session::currentDir() 0964 { 0965 QString path; 0966 if (updateForegroundProcessInfo()) { 0967 bool ok = false; 0968 path= _foregroundProcessInfo->currentDir(&ok); 0969 if (!ok) 0970 path.clear(); 0971 } 0972 return path; 0973 } 0974 0975 bool Session::updateForegroundProcessInfo() 0976 { 0977 Q_ASSERT(_shellProcess); 0978 0979 const int foregroundPid = _shellProcess->foregroundProcessGroup(); 0980 if (foregroundPid != _foregroundPid) { 0981 delete _foregroundProcessInfo; 0982 _foregroundProcessInfo = ProcessInfo::newInstance(foregroundPid); 0983 _foregroundPid = foregroundPid; 0984 } 0985 0986 if (_foregroundProcessInfo) { 0987 _foregroundProcessInfo->update(); 0988 return _foregroundProcessInfo->isValid(); 0989 } else { 0990 return false; 0991 } 0992 } 0993 0994 0995 int Session::processId() const 0996 { 0997 return _shellProcess->pid(); 0998 } 0999 int Session::getPtySlaveFd() const 1000 { 1001 return ptySlaveFd; 1002 } 1003 1004 SessionGroup::SessionGroup() 1005 : _masterMode(0) 1006 { 1007 } 1008 SessionGroup::~SessionGroup() 1009 { 1010 // disconnect all 1011 connectAll(false); 1012 } 1013 int SessionGroup::masterMode() const 1014 { 1015 return _masterMode; 1016 } 1017 QList<Session *> SessionGroup::sessions() const 1018 { 1019 return _sessions.keys(); 1020 } 1021 bool SessionGroup::masterStatus(Session * session) const 1022 { 1023 return _sessions[session]; 1024 } 1025 1026 void SessionGroup::addSession(Session * session) 1027 { 1028 _sessions.insert(session,false); 1029 1030 QListIterator<Session *> masterIter(masters()); 1031 1032 while ( masterIter.hasNext() ) { 1033 connectPair(masterIter.next(),session); 1034 } 1035 } 1036 void SessionGroup::removeSession(Session * session) 1037 { 1038 setMasterStatus(session,false); 1039 1040 QListIterator<Session *> masterIter(masters()); 1041 1042 while ( masterIter.hasNext() ) { 1043 disconnectPair(masterIter.next(),session); 1044 } 1045 1046 _sessions.remove(session); 1047 } 1048 void SessionGroup::setMasterMode(int mode) 1049 { 1050 _masterMode = mode; 1051 1052 connectAll(false); 1053 connectAll(true); 1054 } 1055 QList<Session *> SessionGroup::masters() const 1056 { 1057 return _sessions.keys(true); 1058 } 1059 void SessionGroup::connectAll(bool connect) 1060 { 1061 QListIterator<Session *> masterIter(masters()); 1062 1063 while ( masterIter.hasNext() ) { 1064 Session * master = masterIter.next(); 1065 1066 QListIterator<Session *> otherIter(_sessions.keys()); 1067 while ( otherIter.hasNext() ) { 1068 Session * other = otherIter.next(); 1069 1070 if ( other != master ) { 1071 if ( connect ) { 1072 connectPair(master,other); 1073 } else { 1074 disconnectPair(master,other); 1075 } 1076 } 1077 } 1078 } 1079 } 1080 void SessionGroup::setMasterStatus(Session * session, bool master) 1081 { 1082 bool wasMaster = _sessions[session]; 1083 _sessions[session] = master; 1084 1085 if ((!wasMaster && !master) 1086 || (wasMaster && master)) { 1087 return; 1088 } 1089 1090 QListIterator<Session *> iter(_sessions.keys()); 1091 while (iter.hasNext()) { 1092 Session * other = iter.next(); 1093 1094 if (other != session) { 1095 if (master) { 1096 connectPair(session, other); 1097 } else { 1098 disconnectPair(session, other); 1099 } 1100 } 1101 } 1102 } 1103 1104 void SessionGroup::connectPair(Session * master , Session * other) 1105 { 1106 // qDebug() << k_funcinfo; 1107 1108 if ( _masterMode & CopyInputToAll ) { 1109 qDebug() << "Connection session " << master->nameTitle() << "to" << other->nameTitle(); 1110 1111 connect( master->emulation() , SIGNAL(sendData(const char *,int)) , other->emulation() , 1112 SLOT(sendString(const char *,int)) ); 1113 } 1114 } 1115 void SessionGroup::disconnectPair(Session * master , Session * other) 1116 { 1117 // qDebug() << k_funcinfo; 1118 1119 if ( _masterMode & CopyInputToAll ) { 1120 qDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle(); 1121 1122 disconnect( master->emulation() , SIGNAL(sendData(const char *,int)) , other->emulation() , 1123 SLOT(sendString(const char *,int)) ); 1124 } 1125 } 1126 1127 //#include "moc_Session.cpp"