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

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.4
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