• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Konsole

Session.cpp

Go to the documentation of this file.
00001 /*
00002     This file is part of Konsole
00003 
00004     Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
00005     Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00020     02110-1301  USA.
00021 */
00022 
00023 // Own
00024 #include "Session.h"
00025 
00026 // Standard
00027 #include <assert.h>
00028 #include <stdlib.h>
00029 #include <signal.h>
00030 
00031 // Qt
00032 #include <QtGui/QApplication>
00033 #include <QtCore/QByteRef>
00034 #include <QtCore/QDir>
00035 #include <QtCore/QFile>
00036 #include <QtCore/QRegExp>
00037 #include <QtCore/QStringList>
00038 #include <QtDBus/QtDBus>
00039 #include <QtCore/QDate>
00040 
00041 // KDE
00042 #include <KDebug>
00043 #include <KLocale>
00044 #include <KMessageBox>
00045 #include <KNotification>
00046 #include <KProcess>
00047 #include <KRun>
00048 #include <kshell.h>
00049 #include <KStandardDirs>
00050 #include <KPtyDevice>
00051 #include <KUrl>
00052 
00053 // Konsole
00054 #include <config-konsole.h>
00055 #include <sessionadaptor.h>
00056 
00057 #include "ProcessInfo.h"
00058 #include "Pty.h"
00059 #include "TerminalDisplay.h"
00060 #include "ShellCommand.h"
00061 #include "Vt102Emulation.h"
00062 #include "ZModemDialog.h"
00063 
00064 using namespace Konsole;
00065 
00066 int Session::lastSessionId = 0;
00067 
00068 Session::Session(QObject* parent) :
00069    QObject(parent)
00070    , _shellProcess(0)
00071    , _emulation(0)
00072    , _monitorActivity(false)
00073    , _monitorSilence(false)
00074    , _notifiedActivity(false)
00075    , _autoClose(true)
00076    , _wantedClose(false)
00077    , _silenceSeconds(10)
00078    , _addToUtmp(true)  
00079    , _flowControl(true)
00080    , _fullScripting(false)
00081    , _sessionId(0)
00082    , _sessionProcessInfo(0)
00083    , _foregroundProcessInfo(0)
00084    , _foregroundPid(0)
00085    , _zmodemBusy(false)
00086    , _zmodemProc(0)
00087    , _zmodemProgress(0)
00088    , _hasDarkBackground(false)
00089 {
00090     //prepare DBus communication
00091     new SessionAdaptor(this);
00092     _sessionId = ++lastSessionId;
00093     QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
00094 
00095     //create emulation backend
00096     _emulation = new Vt102Emulation();
00097 
00098     connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ),
00099            this, SLOT( setUserTitle( int, const QString & ) ) );
00100     connect( _emulation, SIGNAL( stateSet(int) ),
00101            this, SLOT( activityStateSet(int) ) );
00102     connect( _emulation, SIGNAL( zmodemDetected() ), this ,
00103             SLOT( fireZModemDetected() ) );
00104     connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ),
00105            this, SIGNAL( changeTabTextColorRequest( int ) ) );
00106     connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)),
00107            this, SIGNAL( profileChangeCommandReceived(const QString&)) );
00108     connect( _emulation, SIGNAL(flowControlKeyPressed(bool)) , this, 
00109              SLOT(updateFlowControlState(bool)) );
00110 
00111     //create new teletype for I/O with shell process
00112     openTeletype(-1);
00113 
00114     //setup timer for monitoring session activity
00115     _monitorTimer = new QTimer(this);
00116     _monitorTimer->setSingleShot(true);
00117     connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone()));
00118 }
00119 
00120 void Session::openTeletype(int fd)
00121 {
00122     if (_shellProcess && isRunning())
00123     {
00124         kWarning() << "Attempted to open teletype in a running session.";
00125         return;
00126     }
00127 
00128     delete _shellProcess;
00129 
00130     if (fd < 0)
00131         _shellProcess = new Pty();
00132     else
00133         _shellProcess = new Pty(fd);
00134 
00135     _shellProcess->setUtf8Mode(_emulation->utf8());
00136 
00137     //connect teletype to emulation backend
00138     connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this,
00139             SLOT(onReceiveBlock(const char*,int)) );
00140     connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess,
00141             SLOT(sendData(const char*,int)) );
00142     connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) );
00143     connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) );
00144     connect( _shellProcess,SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(done(int)) );
00145     connect( _emulation,SIGNAL(imageSizeChanged(int,int)),this,SLOT(updateWindowSize(int,int)) );
00146 }
00147 
00148 WId Session::windowId() const
00149 {
00150     // Returns a window ID for this session which is used
00151     // to set the WINDOWID environment variable in the shell
00152     // process.
00153     //
00154     // Sessions can have multiple views or no views, which means
00155     // that a single ID is not always going to be accurate.
00156     //
00157     // If there are no views, the window ID is just 0.  If
00158     // there are multiple views, then the window ID for the
00159     // top-level window which contains the first view is
00160     // returned
00161 
00162     if ( _views.count() == 0 )
00163        return 0;
00164     else
00165     {
00166         QWidget* window = _views.first();
00167 
00168         Q_ASSERT( window );
00169 
00170         while ( window->parentWidget() != 0 )
00171             window = window->parentWidget();
00172 
00173         return window->winId();
00174     }
00175 }
00176 
00177 void Session::setDarkBackground(bool darkBackground)
00178 {
00179     _hasDarkBackground = darkBackground;
00180 }
00181 bool Session::hasDarkBackground() const
00182 {
00183     return _hasDarkBackground;
00184 }
00185 bool Session::isRunning() const
00186 {
00187     return _shellProcess->state() == QProcess::Running;
00188 }
00189 
00190 void Session::setCodec(QTextCodec* codec)
00191 {
00192     emulation()->setCodec(codec);
00193 }
00194 
00195 void Session::setProgram(const QString& program)
00196 {
00197     _program = ShellCommand::expand(program);
00198 }
00199 void Session::setInitialWorkingDirectory(const QString& dir)
00200 {
00201     _initialWorkingDir = ShellCommand::expand(dir);
00202 }
00203 void Session::setArguments(const QStringList& arguments)
00204 {
00205     _arguments = ShellCommand::expand(arguments);
00206 }
00207 
00208 QString Session::currentWorkingDirectory()
00209 {
00210     // only returned cached value
00211     if (_currentWorkingDir.isEmpty()) updateWorkingDirectory();
00212     return _currentWorkingDir;
00213 }
00214 ProcessInfo* Session::updateWorkingDirectory()
00215 {
00216     ProcessInfo *process = getProcessInfo();
00217     _currentWorkingDir = process->validCurrentDir();
00218     return process;
00219 }
00220 
00221 QList<TerminalDisplay*> Session::views() const
00222 {
00223     return _views;
00224 }
00225 
00226 void Session::addView(TerminalDisplay* widget)
00227 {
00228      Q_ASSERT( !_views.contains(widget) );
00229 
00230     _views.append(widget);
00231 
00232     if ( _emulation != 0 )
00233     {
00234         // connect emulation - view signals and slots
00235         connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation ,
00236                SLOT(sendKeyEvent(QKeyEvent*)) );
00237         connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation ,
00238                SLOT(sendMouseEvent(int,int,int,int)) );
00239         connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation ,
00240                SLOT(sendString(const char*)) );
00241 
00242         // allow emulation to notify view when the foreground process
00243         // indicates whether or not it is interested in mouse signals
00244         connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget ,
00245                SLOT(setUsesMouse(bool)) );
00246 
00247         widget->setUsesMouse( _emulation->programUsesMouse() );
00248 
00249         widget->setScreenWindow(_emulation->createWindow());
00250     }
00251 
00252     //connect view signals and slots
00253     QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this,
00254                     SLOT(onViewSizeChange(int,int)));
00255 
00256     QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this ,
00257                     SLOT(viewDestroyed(QObject*)) );
00258 }
00259 
00260 void Session::viewDestroyed(QObject* view)
00261 {
00262     TerminalDisplay* display = (TerminalDisplay*)view;
00263 
00264     Q_ASSERT( _views.contains(display) );
00265 
00266     removeView(display);
00267 }
00268 
00269 void Session::removeView(TerminalDisplay* widget)
00270 {
00271     _views.removeAll(widget);
00272 
00273     disconnect(widget,0,this,0);
00274 
00275     if ( _emulation != 0 )
00276     {
00277         // disconnect
00278         //  - key presses signals from widget
00279         //  - mouse activity signals from widget
00280         //  - string sending signals from widget
00281         //
00282         //  ... and any other signals connected in addView()
00283         disconnect( widget, 0, _emulation, 0);
00284 
00285         // disconnect state change signals emitted by emulation
00286         disconnect( _emulation , 0 , widget , 0);
00287     }
00288 
00289     // close the session automatically when the last view is removed
00290     if ( _views.count() == 0 )
00291     {
00292         close();
00293     }
00294 }
00295 
00296 QString Session::checkProgram(const QString& program) const
00297 {
00298   // Upon a KPty error, there is no description on what that error was...
00299   // Check to see if the given program is executable.
00300   QString exec = QFile::encodeName(program);
00301 
00302   if (exec.isEmpty())
00303       return QString();
00304 
00305   // if 'exec' is not specified, fall back to default shell.  if that 
00306   // is not set then fall back to /bin/sh
00307   if ( exec.isEmpty() )
00308       exec = qgetenv("SHELL");
00309   if ( exec.isEmpty() )
00310         exec = "/bin/sh";
00311 
00312   exec = KRun::binaryName(exec, false);
00313   exec = KShell::tildeExpand(exec);
00314   QString pexec = KGlobal::dirs()->findExe(exec);
00315   if ( pexec.isEmpty() ) 
00316   {
00317       kError() << i18n("Could not find binary: ") << exec;
00318     return QString();
00319   }
00320 
00321   return exec;
00322 }
00323 
00324 void Session::terminalWarning(const QString& message)
00325 {
00326     static const QByteArray warningText = i18n("Warning: ").toLocal8Bit(); 
00327     QByteArray messageText = message.toLocal8Bit();
00328 
00329     static const char* redPenOn = "\033[1m\033[31m";
00330     static const char* redPenOff = "\033[0m";
00331 
00332     _emulation->receiveData(redPenOn,strlen(redPenOn));
00333     _emulation->receiveData("\n\r\n\r",4);
00334     _emulation->receiveData(warningText.constData(),strlen(warningText.constData()));
00335     _emulation->receiveData(messageText.constData(),strlen(messageText.constData()));
00336     _emulation->receiveData("\n\r\n\r",4);
00337     _emulation->receiveData(redPenOff,strlen(redPenOff));
00338 }
00339 void Session::run()
00340 {
00341   //check that everything is in place to run the session
00342   if (_program.isEmpty())
00343   {
00344       kDebug() << "Session::run() - program to run not set.";
00345   }
00346   if (_arguments.isEmpty())
00347   {
00348       kDebug() << "Session::run() - no command line arguments specified.";
00349   }
00350 
00351   const int CHOICE_COUNT = 3;
00352   QString programs[CHOICE_COUNT] = {_program,qgetenv("SHELL"),"/bin/sh"};
00353   QString exec;
00354   int choice = 0;
00355   while (choice < CHOICE_COUNT)
00356   {
00357     exec = checkProgram(programs[choice]);
00358     if (exec.isEmpty())
00359         choice++;
00360     else
00361         break;
00362   }
00363 
00364   // if a program was specified via setProgram(), but it couldn't be found, print a warning
00365   if (choice != 0 && choice < CHOICE_COUNT && !_program.isEmpty())
00366   {
00367       terminalWarning(i18n("Could not find '%1', starting '%2' instead.  Please check your profile settings.",_program,exec)); 
00368   }
00369   // if none of the choices are available, print a warning
00370   else if (choice == CHOICE_COUNT)
00371   {
00372       terminalWarning(i18n("Could not find an interactive shell to start."));
00373       return;
00374   }
00375   
00376   // if no arguments are specified, fall back to program name
00377   QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ?
00378                                                    QStringList() << exec : _arguments;
00379 
00380   QString dbusService = QDBusConnection::sessionBus().baseService();
00381   if (!_initialWorkingDir.isEmpty())
00382     _shellProcess->setWorkingDirectory(_initialWorkingDir);
00383   else
00384     _shellProcess->setWorkingDirectory(QDir::homePath());
00385 
00386   _shellProcess->setFlowControlEnabled(_flowControl);
00387   _shellProcess->setErase(_emulation->eraseChar());
00388 
00389   // this is not strictly accurate use of the COLORFGBG variable.  This does not
00390   // tell the terminal exactly which colors are being used, but instead approximates
00391   // the color scheme as "black on white" or "white on black" depending on whether
00392   // the background color is deemed dark or not
00393   QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15";
00394 
00395   int result = _shellProcess->start(exec,
00396                                   arguments,
00397                                   _environment << backgroundColorHint,
00398                                   windowId(),
00399                                   _addToUtmp,
00400                                   dbusService,
00401                                   (QLatin1String("/Sessions/") +
00402                                    QString::number(_sessionId)));
00403 
00404   if (result < 0)
00405   {
00406       terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec, arguments.join(" ")));
00407     return;
00408   }
00409 
00410   _shellProcess->setWriteable(false);  // We are reachable via kwrited.
00411 
00412   emit started();
00413 }
00414 
00415 void Session::setUserTitle( int what, const QString &caption )
00416 {
00417     //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle )
00418     bool modified = false;
00419 
00420     if ((what == IconNameAndWindowTitle) || (what == WindowTitle)) 
00421     {
00422            if ( _userTitle != caption ) {
00423             _userTitle = caption;
00424             modified = true;
00425         }
00426     }
00427 
00428     if ((what == IconNameAndWindowTitle) || (what == IconName))
00429     {
00430         if ( _iconText != caption ) {
00431                _iconText = caption;
00432             modified = true;
00433         }
00434     }
00435 
00436     if (what == TextColor || what == BackgroundColor) 
00437     {
00438       QString colorString = caption.section(';',0,0);
00439       QColor color = QColor(colorString);
00440       if (color.isValid())
00441       {
00442           if (what == TextColor)
00443                   emit changeForegroundColorRequest(color);
00444           else
00445                   emit changeBackgroundColorRequest(color);
00446       }
00447     }
00448 
00449     if (what == SessionName) 
00450     {
00451         if ( _nameTitle != caption ) {
00452                setTitle(Session::NameRole,caption);
00453             return;
00454         }
00455     }
00456 
00457     if (what == 31) 
00458     {
00459        QString cwd=caption;
00460        cwd=cwd.replace( QRegExp("^~"), QDir::homePath() );
00461        emit openUrlRequest(cwd);
00462     }
00463 
00464     // change icon via \033]32;Icon\007
00465     if (what == 32) 
00466     { 
00467         if ( _iconName != caption ) {
00468                _iconName = caption;
00469 
00470             modified = true;
00471         }
00472     }
00473 
00474     if (what == ProfileChange) 
00475     {
00476         emit profileChangeCommandReceived(caption);
00477         return;
00478     }
00479 
00480     if ( modified )
00481         emit titleChanged();
00482 }
00483 
00484 QString Session::userTitle() const
00485 {
00486     return _userTitle;
00487 }
00488 void Session::setTabTitleFormat(TabTitleContext context , const QString& format)
00489 {
00490     if ( context == LocalTabTitle )
00491         _localTabTitleFormat = format;
00492     else if ( context == RemoteTabTitle )
00493         _remoteTabTitleFormat = format;
00494 }
00495 QString Session::tabTitleFormat(TabTitleContext context) const
00496 {
00497     if ( context == LocalTabTitle )
00498         return _localTabTitleFormat;
00499     else if ( context == RemoteTabTitle )
00500         return _remoteTabTitleFormat;
00501 
00502     return QString();
00503 }
00504 
00505 void Session::monitorTimerDone()
00506 {
00507   //FIXME: The idea here is that the notification popup will appear to tell the user than output from
00508   //the terminal has stopped and the popup will disappear when the user activates the session.
00509   //
00510   //This breaks with the addition of multiple views of a session.  The popup should disappear
00511   //when any of the views of the session becomes active
00512   
00513 
00514   //FIXME: Make message text for this notification and the activity notification more descriptive.    
00515   if (_monitorSilence) {
00516     KNotification::event("Silence", i18n("Silence in session '%1'", _nameTitle), QPixmap(),
00517                     QApplication::activeWindow(),
00518                     KNotification::CloseWhenWidgetActivated);
00519     emit stateChanged(NOTIFYSILENCE);
00520   }
00521   else
00522   {
00523     emit stateChanged(NOTIFYNORMAL);
00524   }
00525 
00526   _notifiedActivity=false;
00527 }
00528 void Session::updateFlowControlState(bool suspended)
00529 {
00530     if (suspended)
00531     {
00532         if (flowControlEnabled())
00533         {
00534             foreach(TerminalDisplay* display,_views)
00535             {
00536                 if (display->flowControlWarningEnabled())
00537                     display->outputSuspended(true);
00538             }
00539         }
00540     } 
00541     else
00542     {
00543         foreach(TerminalDisplay* display,_views)
00544             display->outputSuspended(false);
00545     }   
00546 }
00547 void Session::activityStateSet(int state)
00548 {
00549   if (state==NOTIFYBELL)
00550   {
00551       emit bellRequest( i18n("Bell in session '%1'",_nameTitle) );
00552   }
00553   else if (state==NOTIFYACTIVITY)
00554   {
00555     if (_monitorSilence) {
00556       _monitorTimer->start(_silenceSeconds*1000);
00557     }
00558 
00559     if ( _monitorActivity ) {
00560       //FIXME:  See comments in Session::monitorTimerDone()
00561       if (!_notifiedActivity) {
00562         KNotification::event("Activity", i18n("Activity in session '%1'", _nameTitle), QPixmap(),
00563                         QApplication::activeWindow(),
00564         KNotification::CloseWhenWidgetActivated);
00565         _notifiedActivity=true;
00566       }
00567     }
00568   }
00569 
00570   if ( state==NOTIFYACTIVITY && !_monitorActivity )
00571           state = NOTIFYNORMAL;
00572   if ( state==NOTIFYSILENCE && !_monitorSilence )
00573           state = NOTIFYNORMAL;
00574 
00575   emit stateChanged(state);
00576 }
00577 
00578 void Session::onViewSizeChange(int /*height*/, int /*width*/)
00579 {
00580   updateTerminalSize();
00581 }
00582 
00583 void Session::updateTerminalSize()
00584 {
00585     QListIterator<TerminalDisplay*> viewIter(_views);
00586 
00587     int minLines = -1;
00588     int minColumns = -1;
00589 
00590     // minimum number of lines and columns that views require for
00591     // their size to be taken into consideration ( to avoid problems
00592     // with new view widgets which haven't yet been set to their correct size )
00593     const int VIEW_LINES_THRESHOLD = 2;
00594     const int VIEW_COLUMNS_THRESHOLD = 2;
00595 
00596     //select largest number of lines and columns that will fit in all visible views
00597     while ( viewIter.hasNext() )
00598     {
00599         TerminalDisplay* view = viewIter.next();
00600         if ( view->isHidden() == false &&
00601              view->lines() >= VIEW_LINES_THRESHOLD &&
00602              view->columns() >= VIEW_COLUMNS_THRESHOLD )
00603         {
00604             minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() );
00605             minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() );
00606         }
00607     }
00608 
00609     // backend emulation must have a _terminal of at least 1 column x 1 line in size
00610     if ( minLines > 0 && minColumns > 0 )
00611     {
00612         _emulation->setImageSize( minLines , minColumns );
00613     }
00614 }
00615 void Session::updateWindowSize(int lines, int columns)
00616 {
00617     Q_ASSERT(lines > 0 && columns > 0);
00618     _shellProcess->setWindowSize(lines,columns);
00619 }
00620 void Session::refresh()
00621 {
00622     // attempt to get the shell process to redraw the display
00623     //
00624     // this requires the program running in the shell
00625     // to cooperate by sending an update in response to
00626     // a window size change
00627     //
00628     // the window size is changed twice, first made slightly larger and then
00629     // resized back to its normal size so that there is actually a change
00630     // in the window size (some shells do nothing if the
00631     // new and old sizes are the same)
00632     //
00633     // if there is a more 'correct' way to do this, please
00634     // send an email with method or patches to konsole-devel@kde.org
00635 
00636     const QSize existingSize = _shellProcess->windowSize();
00637     _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1);
00638     _shellProcess->setWindowSize(existingSize.height(),existingSize.width());
00639 }
00640 
00641 bool Session::kill(int signal)
00642 {
00643     int result = ::kill(_shellProcess->pid(),signal);    
00644     
00645     if ( result == 0 )
00646     {
00647         _shellProcess->waitForFinished();
00648         return true;
00649     }
00650     else
00651         return false;
00652 }
00653 
00654 void Session::close()
00655 {
00656   _autoClose = true;
00657   _wantedClose = true;
00658 
00659   if (!isRunning() || !kill(SIGHUP))
00660   {
00661      if (isRunning())
00662      {
00663         kDebug() << "Process" << _shellProcess->pid() << "did not respond to SIGHUP";
00664 
00665         // close the pty and wait to see if the process finishes.  If it does,
00666         // the done() slot will have been called so we can return.  Otherwise,
00667         // emit the finished() signal regardless
00668         _shellProcess->pty()->close();
00669         if (_shellProcess->waitForFinished(3000))
00670             return;
00671 
00672         kWarning() << "Unable to kill process" << _shellProcess->pid();
00673      }
00674 
00675      // Forced close.
00676      QTimer::singleShot(1, this, SIGNAL(finished()));
00677   }
00678 }
00679 
00680 void Session::sendText(const QString &text) const
00681 {
00682   _emulation->sendText(text);
00683 }
00684 
00685 Session::~Session()
00686 {
00687   if (_foregroundProcessInfo)
00688       delete _foregroundProcessInfo;
00689   if (_sessionProcessInfo)
00690       delete _sessionProcessInfo;
00691   delete _emulation;
00692   delete _shellProcess;
00693   delete _zmodemProc;
00694 }
00695 
00696 void Session::done(int exitStatus)
00697 {
00698   if (!_autoClose)
00699   {
00700     _userTitle = i18n("Finished");
00701     emit titleChanged();
00702     return;
00703   }
00704 
00705   QString message;
00706   if (!_wantedClose || exitStatus != 0)
00707   {
00708     if (_shellProcess->exitStatus() == QProcess::NormalExit)
00709         message = i18n("Program '%1' exited with status %2.", _program, exitStatus);
00710     else
00711         message = i18n("Program '%1' crashed.", _program);
00712 
00713     //FIXME: See comments in Session::monitorTimerDone()
00714     KNotification::event("Finished", message , QPixmap(),
00715                          QApplication::activeWindow(),
00716                          KNotification::CloseWhenWidgetActivated);
00717   }
00718 
00719   if ( !_wantedClose && _shellProcess->exitStatus() != QProcess::NormalExit )
00720       terminalWarning(message);
00721   else
00722         emit finished();
00723 }
00724 
00725 Emulation* Session::emulation() const
00726 {
00727   return _emulation;
00728 }
00729 
00730 QString Session::keyBindings() const
00731 {
00732   return _emulation->keyBindings();
00733 }
00734 
00735 QStringList Session::environment() const
00736 {
00737   return _environment;
00738 }
00739 
00740 void Session::setEnvironment(const QStringList& environment)
00741 {
00742     _environment = environment;
00743 }
00744 
00745 int Session::sessionId() const
00746 {
00747   return _sessionId;
00748 }
00749 
00750 void Session::setKeyBindings(const QString &id)
00751 {
00752   _emulation->setKeyBindings(id);
00753 }
00754 
00755 void Session::setTitle(TitleRole role , const QString& newTitle)
00756 {
00757     if ( title(role) != newTitle )
00758     {
00759         if ( role == NameRole )
00760             _nameTitle = newTitle;
00761         else if ( role == DisplayedTitleRole )
00762             _displayTitle = newTitle;
00763 
00764         emit titleChanged();
00765     }
00766 }
00767 
00768 QString Session::title(TitleRole role) const
00769 {
00770     if ( role == NameRole )
00771         return _nameTitle;
00772     else if ( role == DisplayedTitleRole )
00773         return _displayTitle;
00774     else
00775         return QString();
00776 }
00777 
00778 ProcessInfo* Session::getProcessInfo()
00779 {
00780     ProcessInfo* process;
00781 
00782     if (isChildActive())
00783         process = _foregroundProcessInfo;
00784     else
00785     {
00786         updateSessionProcessInfo();
00787         process = _sessionProcessInfo;
00788     }
00789 
00790     return process;
00791 }
00792 
00793 void Session::updateSessionProcessInfo()
00794 {
00795     Q_ASSERT(_shellProcess);
00796     if (!_sessionProcessInfo)
00797         _sessionProcessInfo = ProcessInfo::newInstance(processId());
00798     _sessionProcessInfo->update();
00799 }
00800 
00801 bool Session::updateForegroundProcessInfo()
00802 {
00803     bool valid = (_foregroundProcessInfo != 0);
00804 
00805     // has foreground process changed?
00806     Q_ASSERT(_shellProcess);
00807     int pid = _shellProcess->foregroundProcessGroup();
00808     if (pid != _foregroundPid)
00809     {
00810         if (valid)
00811             delete _foregroundProcessInfo;
00812         _foregroundProcessInfo = ProcessInfo::newInstance(pid);
00813         _foregroundPid = pid;
00814         valid = true;
00815     }
00816 
00817     if (valid)
00818     {
00819         _foregroundProcessInfo->update();
00820         valid = _foregroundProcessInfo->isValid();
00821     }
00822 
00823     return valid;
00824 }
00825 
00826 QString Session::getDynamicTitle()
00827 {
00828     // update current directory from process
00829     ProcessInfo* process = updateWorkingDirectory();
00830 
00831     // format tab titles using process info
00832     bool ok = false;
00833     QString title;
00834     if ( process->name(&ok) == "ssh" && ok )
00835     {
00836         SSHProcessInfo sshInfo(*process);
00837         title = sshInfo.format(tabTitleFormat(Session::RemoteTabTitle));
00838     }
00839     else
00840         title = process->format(tabTitleFormat(Session::LocalTabTitle));
00841 
00842     return title;
00843 }
00844 
00845 KUrl Session::getUrl()
00846 {
00847     QString path;
00848     
00849     updateSessionProcessInfo();
00850     if (_sessionProcessInfo->isValid())
00851     {
00852         bool ok = false;
00853 
00854         // check if foreground process is bookmark-able
00855         if (isChildActive())
00856         {
00857             // for remote connections, save the user and host
00858             // bright ideas to get the directory at the other end are welcome :)
00859             if (_foregroundProcessInfo->name(&ok) == "ssh" && ok)
00860             {
00861                 SSHProcessInfo sshInfo(*_foregroundProcessInfo);
00862                 path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host();
00863             }
00864             else
00865             {
00866                 path = _foregroundProcessInfo->currentDir(&ok);
00867                 if (!ok)
00868                     path.clear();
00869             }
00870         }
00871         else // otherwise use the current working directory of the shell process
00872         {
00873             path = _sessionProcessInfo->currentDir(&ok);
00874             if (!ok)
00875                 path.clear();
00876         }
00877     }
00878 
00879     return KUrl(path);
00880 }
00881 
00882 void Session::setIconName(const QString& iconName)
00883 {
00884     if ( iconName != _iconName )
00885     {
00886         _iconName = iconName;
00887         emit titleChanged();
00888     }
00889 }
00890 
00891 void Session::setIconText(const QString& iconText)
00892 {
00893   _iconText = iconText;
00894 }
00895 
00896 QString Session::iconName() const
00897 {
00898   return _iconName;
00899 }
00900 
00901 QString Session::iconText() const
00902 {
00903   return _iconText;
00904 }
00905 
00906 void Session::setHistoryType(const HistoryType &hType)
00907 {
00908   _emulation->setHistory(hType);
00909 }
00910 
00911 const HistoryType& Session::historyType() const
00912 {
00913   return _emulation->history();
00914 }
00915 
00916 void Session::clearHistory()
00917 {
00918     _emulation->clearHistory();
00919 }
00920 
00921 QStringList Session::arguments() const
00922 {
00923   return _arguments;
00924 }
00925 
00926 QString Session::program() const
00927 {
00928   return _program;
00929 }
00930 
00931 // unused currently
00932 bool Session::isMonitorActivity() const { return _monitorActivity; }
00933 // unused currently
00934 bool Session::isMonitorSilence()  const { return _monitorSilence; }
00935 
00936 void Session::setMonitorActivity(bool _monitor)
00937 {
00938   _monitorActivity=_monitor;
00939   _notifiedActivity=false;
00940 
00941   activityStateSet(NOTIFYNORMAL);
00942 }
00943 
00944 void Session::setMonitorSilence(bool _monitor)
00945 {
00946   if (_monitorSilence==_monitor)
00947     return;
00948 
00949   _monitorSilence=_monitor;
00950   if (_monitorSilence)
00951   {
00952     _monitorTimer->start(_silenceSeconds*1000);
00953   }
00954   else
00955     _monitorTimer->stop();
00956 
00957   activityStateSet(NOTIFYNORMAL);
00958 }
00959 
00960 void Session::setMonitorSilenceSeconds(int seconds)
00961 {
00962   _silenceSeconds=seconds;
00963   if (_monitorSilence) {
00964     _monitorTimer->start(_silenceSeconds*1000);
00965   }
00966 }
00967 
00968 void Session::setAddToUtmp(bool set)
00969 {
00970   _addToUtmp = set;
00971 }
00972 
00973 void Session::setFlowControlEnabled(bool enabled)
00974 {
00975   _flowControl = enabled;
00976 
00977   if (_shellProcess)  
00978     _shellProcess->setFlowControlEnabled(_flowControl);
00979   
00980   emit flowControlEnabledChanged(enabled);
00981 }
00982 bool Session::flowControlEnabled() const
00983 {
00984     if (_shellProcess)
00985             return _shellProcess->flowControlEnabled();
00986     else
00987             return _flowControl;
00988 }
00989 void Session::fireZModemDetected()
00990 {
00991   if (!_zmodemBusy)
00992   {
00993     QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
00994     _zmodemBusy = true;
00995   }
00996 }
00997 
00998 void Session::cancelZModem()
00999 {
01000   _shellProcess->sendData("\030\030\030\030", 4); // Abort
01001   _zmodemBusy = false;
01002 }
01003 
01004 void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list)
01005 {
01006   _zmodemBusy = true;
01007   _zmodemProc = new KProcess();
01008   _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels );
01009 
01010   *_zmodemProc << zmodem << "-v" << list;
01011 
01012   if (!dir.isEmpty())
01013      _zmodemProc->setWorkingDirectory(dir);
01014 
01015   _zmodemProc->start();
01016 
01017   connect(_zmodemProc,SIGNAL (readyReadStandardOutput()),
01018           this, SLOT(zmodemReadAndSendBlock()));
01019   connect(_zmodemProc,SIGNAL (readyReadStandardError()),
01020           this, SLOT(zmodemReadStatus()));
01021   connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)),
01022           this, SLOT(zmodemFinished()));
01023 
01024   disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
01025   connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) );
01026 
01027   _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false,
01028                                     i18n("ZModem Progress"));
01029 
01030   connect(_zmodemProgress, SIGNAL(user1Clicked()),
01031           this, SLOT(zmodemDone()));
01032 
01033   _zmodemProgress->show();
01034 }
01035 
01036 void Session::zmodemReadAndSendBlock()
01037 {
01038   _zmodemProc->setReadChannel( QProcess::StandardOutput );
01039   QByteArray data = _zmodemProc->readAll();
01040 
01041   if ( data.count() == 0 )
01042       return;
01043 
01044   _shellProcess->sendData(data.constData(),data.count());
01045 }
01046 
01047 void Session::zmodemReadStatus()
01048 {
01049   _zmodemProc->setReadChannel( QProcess::StandardError );
01050   QByteArray msg = _zmodemProc->readAll();
01051   while(!msg.isEmpty())
01052   {
01053      int i = msg.indexOf('\015');
01054      int j = msg.indexOf('\012');
01055      QByteArray txt;
01056      if ((i != -1) && ((j == -1) || (i < j)))
01057      {
01058        msg = msg.mid(i+1);
01059      }
01060      else if (j != -1)
01061      {
01062        txt = msg.left(j);
01063        msg = msg.mid(j+1);
01064      }
01065      else
01066      {
01067        txt = msg;
01068        msg.truncate(0);
01069      }
01070      if (!txt.isEmpty())
01071        _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt));
01072   }
01073 }
01074 
01075 void Session::zmodemRcvBlock(const char *data, int len)
01076 {
01077   QByteArray ba( data, len );
01078 
01079   _zmodemProc->write( ba );
01080 }
01081 
01082 void Session::zmodemFinished()
01083 {
01084   if (_zmodemProc)
01085   {
01086     delete _zmodemProc;
01087     _zmodemProc = 0;
01088     _zmodemBusy = false;
01089 
01090     disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) );
01091     connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
01092 
01093     _shellProcess->sendData("\030\030\030\030", 4); // Abort
01094     _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back
01095     _zmodemProgress->transferDone();
01096   }
01097 }
01098 
01099 void Session::onReceiveBlock( const char* buf, int len )
01100 {
01101     _emulation->receiveData( buf, len );
01102     emit receivedData( QString::fromLatin1( buf, len ) );
01103 }
01104 
01105 QSize Session::size()
01106 {
01107   return _emulation->imageSize();
01108 }
01109 
01110 void Session::setSize(const QSize& size)
01111 {
01112   if ((size.width() <= 1) || (size.height() <= 1))
01113      return;
01114 
01115   emit resizeRequest(size);
01116 }
01117 int Session::processId() const
01118 {
01119     return _shellProcess->pid();
01120 }
01121 
01122 bool Session::isChildActive()
01123 {
01124     // foreground process info is always updated after this
01125     return updateForegroundProcessInfo() && (processId() != _foregroundPid);
01126 }
01127 
01128 QString Session::childName()
01129 {
01130     QString name;
01131 
01132     if (updateForegroundProcessInfo()) 
01133     {
01134         bool ok = false;
01135         name = _foregroundProcessInfo->name(&ok);
01136         if (!ok)
01137             name.clear();
01138     }
01139 
01140     return name;
01141 }
01142 
01143 void Session::saveSession(KConfigGroup& group)
01144 {
01145     group.writePathEntry("WorkingDir", currentWorkingDirectory());
01146     group.writeEntry("LocalTab",       tabTitleFormat(LocalTabTitle));
01147     group.writeEntry("RemoteTab",      tabTitleFormat(RemoteTabTitle));
01148 }
01149 
01150 void Session::restoreSession(KConfigGroup& group)
01151 {
01152     QString value;
01153 
01154     value = group.readPathEntry("WorkingDir", QString());
01155     if (!value.isEmpty()) setInitialWorkingDirectory(value);
01156     value = group.readEntry("LocalTab");
01157     if (!value.isEmpty()) setTabTitleFormat(LocalTabTitle, value);
01158     value = group.readEntry("RemoteTab");
01159     if (!value.isEmpty()) setTabTitleFormat(RemoteTabTitle, value);
01160 }
01161 
01162 SessionGroup::SessionGroup(QObject* parent)
01163     : QObject(parent), _masterMode(0)
01164 {
01165 }
01166 SessionGroup::~SessionGroup()
01167 {
01168     // disconnect all
01169     connectAll(false);
01170 }
01171 int SessionGroup::masterMode() const { return _masterMode; }
01172 QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); }
01173 bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; }
01174 
01175 void SessionGroup::addSession(Session* session)
01176 {
01177     connect(session,SIGNAL(finished()),this,SLOT(sessionFinished()));
01178 
01179     _sessions.insert(session,false);
01180 
01181     QListIterator<Session*> masterIter(masters());
01182 
01183     while ( masterIter.hasNext() )
01184         connectPair(masterIter.next(),session);
01185 }
01186 void SessionGroup::removeSession(Session* session)
01187 {
01188     disconnect(session,SIGNAL(finished()),this,SLOT(sessionFinished()));
01189 
01190     setMasterStatus(session,false);
01191 
01192     QListIterator<Session*> masterIter(masters());
01193 
01194     while ( masterIter.hasNext() )
01195         disconnectPair(masterIter.next(),session);
01196 
01197     _sessions.remove(session);
01198 }
01199 void SessionGroup::sessionFinished()
01200 {
01201     Session* session = qobject_cast<Session*>(sender());
01202     Q_ASSERT(session);
01203     removeSession(session);
01204 }
01205 void SessionGroup::setMasterMode(int mode)
01206 {
01207    _masterMode = mode;
01208 
01209    connectAll(false);
01210    connectAll(true);
01211 }
01212 QList<Session*> SessionGroup::masters() const
01213 {
01214     return _sessions.keys(true);
01215 }
01216 void SessionGroup::connectAll(bool connect)
01217 {
01218     QListIterator<Session*> masterIter(masters());
01219 
01220     while ( masterIter.hasNext() )
01221     {
01222         Session* master = masterIter.next();
01223 
01224         QListIterator<Session*> otherIter(_sessions.keys());
01225         while ( otherIter.hasNext() )
01226         {
01227             Session* other = otherIter.next();
01228 
01229             if ( other != master )
01230             {
01231                 if ( connect )
01232                     connectPair(master,other);
01233                 else
01234                     disconnectPair(master,other);
01235             }
01236         }
01237     }
01238 }
01239 void SessionGroup::setMasterStatus(Session* session , bool master)
01240 {
01241     bool wasMaster = _sessions[session];
01242     _sessions[session] = master;
01243 
01244     if (    ( !wasMaster && !master )
01245          || ( wasMaster && master ) )
01246       return;
01247 
01248     QListIterator<Session*> iter(_sessions.keys());
01249     while ( iter.hasNext() )
01250     {
01251         Session* other = iter.next();
01252 
01253         if ( other != session )
01254         {
01255             if ( master )
01256                 connectPair(session,other);
01257             else
01258                 disconnectPair(session,other);
01259         }
01260     }
01261 }
01262 void SessionGroup::connectPair(Session* master , Session* other)
01263 {
01264     if ( _masterMode & CopyInputToAll )
01265     {
01266         connect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
01267                  SLOT(sendString(const char*,int)) );
01268     }
01269 }
01270 void SessionGroup::disconnectPair(Session* master , Session* other)
01271 {
01272     if ( _masterMode & CopyInputToAll )
01273     {
01274         disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
01275                 SLOT(sendString(const char*,int)) );
01276     }
01277 }
01278 
01279 #include "Session.moc"
01280 
01281 /*
01282   Local Variables:
01283   mode: c++
01284   c-file-style: "stroustrup"
01285   indent-tabs-mode: nil
01286   tab-width: 4
01287   End:
01288 */

Konsole

Skip menu "Konsole"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • Konsole
  • Libraries
  •   libkonq
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal