00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "Session.h"
00025
00026
00027 #include <assert.h>
00028 #include <stdlib.h>
00029 #include <signal.h>
00030
00031
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
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
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
00091 new SessionAdaptor(this);
00092 _sessionId = ++lastSessionId;
00093 QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
00094
00095
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
00112 openTeletype(-1);
00113
00114
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
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
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
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
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
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
00243
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
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
00278
00279
00280
00281
00282
00283 disconnect( widget, 0, _emulation, 0);
00284
00285
00286 disconnect( _emulation , 0 , widget , 0);
00287 }
00288
00289
00290 if ( _views.count() == 0 )
00291 {
00292 close();
00293 }
00294 }
00295
00296 QString Session::checkProgram(const QString& program) const
00297 {
00298
00299
00300 QString exec = QFile::encodeName(program);
00301
00302 if (exec.isEmpty())
00303 return QString();
00304
00305
00306
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
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
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
00370 else if (choice == CHOICE_COUNT)
00371 {
00372 terminalWarning(i18n("Could not find an interactive shell to start."));
00373 return;
00374 }
00375
00376
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
00390
00391
00392
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);
00411
00412 emit started();
00413 }
00414
00415 void Session::setUserTitle( int what, const QString &caption )
00416 {
00417
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
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
00508
00509
00510
00511
00512
00513
00514
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
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 , int )
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
00591
00592
00593 const int VIEW_LINES_THRESHOLD = 2;
00594 const int VIEW_COLUMNS_THRESHOLD = 2;
00595
00596
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
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
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
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
00666
00667
00668 _shellProcess->pty()->close();
00669 if (_shellProcess->waitForFinished(3000))
00670 return;
00671
00672 kWarning() << "Unable to kill process" << _shellProcess->pid();
00673 }
00674
00675
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
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
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
00829 ProcessInfo* process = updateWorkingDirectory();
00830
00831
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
00855 if (isChildActive())
00856 {
00857
00858
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
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
00932 bool Session::isMonitorActivity() const { return _monitorActivity; }
00933
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);
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);
01094 _shellProcess->sendData("\001\013\n", 3);
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
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
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
01283
01284
01285
01286
01287
01288