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"