diff options
Diffstat (limited to 'qtermwidget/Session.cpp')
-rw-r--r-- | qtermwidget/Session.cpp | 1021 |
1 files changed, 1021 insertions, 0 deletions
diff --git a/qtermwidget/Session.cpp b/qtermwidget/Session.cpp new file mode 100644 index 0000000..48bed7e --- /dev/null +++ b/qtermwidget/Session.cpp @@ -0,0 +1,1021 @@ +/* + This file is part of Konsole + + Copyright (C) 2006-2007 by Robert Knight <robertknight@gmail.com> + Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de> + + Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. +*/ + +// Own +#include "Session.h" + +// Standard +#include <assert.h> +#include <stdlib.h> + +// Qt +#include <QtGui/QApplication> +#include <QtCore/QByteRef> +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QRegExp> +#include <QtCore/QStringList> +#include <QtCore> + +#include "Pty.h" +#include "TerminalDisplay.h" +#include "ShellCommand.h" +#include "Vt102Emulation.h" + +using namespace Konsole; + +int Session::lastSessionId = 0; + +Session::Session() : + _shellProcess(0) + , _emulation(0) + , _monitorActivity(false) + , _monitorSilence(false) + , _notifiedActivity(false) + , _autoClose(true) + , _wantedClose(false) + , _silenceSeconds(10) + , _addToUtmp(false) // disabled by default because of a bug encountered on certain systems + // which caused Konsole to hang when closing a tab and then opening a new + // one. A 'QProcess destroyed while still running' warning was being + // printed to the terminal. Likely a problem in KPty::logout() + // or KPty::login() which uses a QProcess to start /usr/bin/utempter + , _flowControl(true) + , _fullScripting(false) + , _sessionId(0) +// , _zmodemBusy(false) +// , _zmodemProc(0) +// , _zmodemProgress(0) + , _hasDarkBackground(false) +{ + //prepare DBus communication +// new SessionAdaptor(this); + _sessionId = ++lastSessionId; +// QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this); + + //create teletype for I/O with shell process + _shellProcess = new Pty(); + + //create emulation backend + _emulation = new Vt102Emulation(); + + connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ), + this, SLOT( setUserTitle( int, const QString & ) ) ); + connect( _emulation, SIGNAL( stateSet(int) ), + this, SLOT( activityStateSet(int) ) ); +// connect( _emulation, SIGNAL( zmodemDetected() ), this , +// SLOT( fireZModemDetected() ) ); + connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ), + this, SIGNAL( changeTabTextColorRequest( int ) ) ); + connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)), + this, SIGNAL( profileChangeCommandReceived(const QString&)) ); + // TODO + // connect( _emulation,SIGNAL(imageSizeChanged(int,int)) , this , + // SLOT(onEmulationSizeChange(int,int)) ); + + //connect teletype to emulation backend + _shellProcess->setUtf8Mode(_emulation->utf8()); + + connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this, + SLOT(onReceiveBlock(const char*,int)) ); + connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess, + SLOT(sendData(const char*,int)) ); + connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) ); + connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) ); + + + connect( _shellProcess,SIGNAL(done(int)), this, SLOT(done(int)) ); + + //setup timer for monitoring session activity + _monitorTimer = new QTimer(this); + _monitorTimer->setSingleShot(true); + connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone())); +} + +WId Session::windowId() const +{ + // Returns a window ID for this session which is used + // to set the WINDOWID environment variable in the shell + // process. + // + // Sessions can have multiple views or no views, which means + // that a single ID is not always going to be accurate. + // + // If there are no views, the window ID is just 0. If + // there are multiple views, then the window ID for the + // top-level window which contains the first view is + // returned + + if ( _views.count() == 0 ) + return 0; + else + { + QWidget* window = _views.first(); + + Q_ASSERT( window ); + + while ( window->parentWidget() != 0 ) + window = window->parentWidget(); + + return window->winId(); + } +} + +void Session::setDarkBackground(bool darkBackground) +{ + _hasDarkBackground = darkBackground; +} +bool Session::hasDarkBackground() const +{ + return _hasDarkBackground; +} +bool Session::isRunning() const +{ + return _shellProcess->isRunning(); +} + +void Session::setCodec(QTextCodec* codec) +{ + emulation()->setCodec(codec); +} + +void Session::setProgram(const QString& program) +{ + _program = ShellCommand::expand(program); +} +void Session::setInitialWorkingDirectory(const QString& dir) +{ + _initialWorkingDir = ShellCommand::expand(dir); +} +void Session::setArguments(const QStringList& arguments) +{ + _arguments = ShellCommand::expand(arguments); +} + +QList<TerminalDisplay*> Session::views() const +{ + return _views; +} + +void Session::addView(TerminalDisplay* widget) +{ + Q_ASSERT( !_views.contains(widget) ); + + _views.append(widget); + + if ( _emulation != 0 ) + { + // connect emulation - view signals and slots + connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation , + SLOT(sendKeyEvent(QKeyEvent*)) ); + connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation , + SLOT(sendMouseEvent(int,int,int,int)) ); + connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation , + SLOT(sendString(const char*)) ); + + // allow emulation to notify view when the foreground process + // indicates whether or not it is interested in mouse signals + connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget , + SLOT(setUsesMouse(bool)) ); + + widget->setUsesMouse( _emulation->programUsesMouse() ); + + widget->setScreenWindow(_emulation->createWindow()); + } + + //connect view signals and slots + QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this, + SLOT(onViewSizeChange(int,int))); + + QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this , + SLOT(viewDestroyed(QObject*)) ); +//slot for close + QObject::connect(this, SIGNAL(finished()), widget, SLOT(close())); + +} + +void Session::viewDestroyed(QObject* view) +{ + TerminalDisplay* display = (TerminalDisplay*)view; + + Q_ASSERT( _views.contains(display) ); + + removeView(display); +} + +void Session::removeView(TerminalDisplay* widget) +{ + _views.removeAll(widget); + + disconnect(widget,0,this,0); + + if ( _emulation != 0 ) + { + // disconnect + // - key presses signals from widget + // - mouse activity signals from widget + // - string sending signals from widget + // + // ... and any other signals connected in addView() + disconnect( widget, 0, _emulation, 0); + + // disconnect state change signals emitted by emulation + disconnect( _emulation , 0 , widget , 0); + } + + // close the session automatically when the last view is removed + if ( _views.count() == 0 ) + { + close(); + } +} + +void Session::run() +{ + //check that everything is in place to run the session + if (_program.isEmpty()) + qDebug() << "Session::run() - program to run not set."; + if (_arguments.isEmpty()) + qDebug() << "Session::run() - no command line arguments specified."; + + // Upon a KPty error, there is no description on what that error was... + // Check to see if the given program is executable. + QString exec = QFile::encodeName(_program); + + // if 'exec' is not specified, fall back to default shell. if that + // is not set then fall back to /bin/sh + if ( exec.isEmpty() ) + exec = getenv("SHELL"); + if ( exec.isEmpty() ) + exec = "/bin/sh"; + + // if no arguments are specified, fall back to shell + QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ? + QStringList() << exec : _arguments; + QString pexec = exec; + + if ( pexec.isEmpty() ) { + qDebug()<<"can not execute "<<exec<<endl; + QTimer::singleShot(1, this, SIGNAL(finished())); + return; + } + + QString cwd_save = QDir::currentPath(); + if (!_initialWorkingDir.isEmpty()) + _shellProcess->setWorkingDirectory(_initialWorkingDir); + else + _shellProcess->setWorkingDirectory(QDir::homePath()); + + _shellProcess->setXonXoff(_flowControl); + _shellProcess->setErase(_emulation->getErase()); + + // this is not strictly accurate use of the COLORFGBG variable. This does not + // tell the terminal exactly which colors are being used, but instead approximates + // the color scheme as "black on white" or "white on black" depending on whether + // the background color is deemed dark or not + QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15"; + + int result = _shellProcess->start(QFile::encodeName(_program), + arguments, + _environment << backgroundColorHint, + windowId(), + _addToUtmp); + + if (result < 0) + { + return; + } + + _shellProcess->setWriteable(false); // We are reachable via kwrited. + + emit started(); +} + +void Session::setUserTitle( int what, const QString &caption ) +{ + //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle ) + bool modified = false; + + // (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only _nameTitle + if ((what == 0) || (what == 2)) + { + if ( _userTitle != caption ) { + _userTitle = caption; + modified = true; + } + } + + if ((what == 0) || (what == 1)) + { + if ( _iconText != caption ) { + _iconText = caption; + modified = true; + } + } + + if (what == 11) + { + QString colorString = caption.section(';',0,0); + qDebug() << __FILE__ << __LINE__ << ": setting background colour to " << colorString; + QColor backColor = QColor(colorString); + if (backColor.isValid()){// change color via \033]11;Color\007 + if (backColor != _modifiedBackground) + { + _modifiedBackground = backColor; + + // bail out here until the code to connect the terminal display + // to the changeBackgroundColor() signal has been written + // and tested - just so we don't forget to do this. + Q_ASSERT( 0 ); + + emit changeBackgroundColorRequest(backColor); + } + } + } + + if (what == 30) + { + if ( _nameTitle != caption ) { + setTitle(Session::NameRole,caption); + return; + } + } + + if (what == 31) + { + QString cwd=caption; + cwd=cwd.replace( QRegExp("^~"), QDir::homePath() ); + emit openUrlRequest(cwd); + } + + // change icon via \033]32;Icon\007 + if (what == 32) + { + if ( _iconName != caption ) { + _iconName = caption; + + modified = true; + } + } + + if (what == 50) + { + emit profileChangeCommandReceived(caption); + return; + } + + if ( modified ) + emit titleChanged(); +} + +QString Session::userTitle() const +{ + return _userTitle; +} +void Session::setTabTitleFormat(TabTitleContext context , const QString& format) +{ + if ( context == LocalTabTitle ) + _localTabTitleFormat = format; + else if ( context == RemoteTabTitle ) + _remoteTabTitleFormat = format; +} +QString Session::tabTitleFormat(TabTitleContext context) const +{ + if ( context == LocalTabTitle ) + return _localTabTitleFormat; + else if ( context == RemoteTabTitle ) + return _remoteTabTitleFormat; + + return QString(); +} + +void Session::monitorTimerDone() +{ + //FIXME: The idea here is that the notification popup will appear to tell the user than output from + //the terminal has stopped and the popup will disappear when the user activates the session. + // + //This breaks with the addition of multiple views of a session. The popup should disappear + //when any of the views of the session becomes active + + + //FIXME: Make message text for this notification and the activity notification more descriptive. + if (_monitorSilence) { +// KNotification::event("Silence", ("Silence in session '%1'", _nameTitle), QPixmap(), +// QApplication::activeWindow(), +// KNotification::CloseWhenWidgetActivated); + emit stateChanged(NOTIFYSILENCE); + } + else + { + emit stateChanged(NOTIFYNORMAL); + } + + _notifiedActivity=false; +} + +void Session::activityStateSet(int state) +{ + if (state==NOTIFYBELL) + { + QString s; s.sprintf("Bell in session '%s'",_nameTitle.toAscii().data()); + + emit bellRequest( s ); + } + else if (state==NOTIFYACTIVITY) + { + if (_monitorSilence) { + _monitorTimer->start(_silenceSeconds*1000); + } + + if ( _monitorActivity ) { + //FIXME: See comments in Session::monitorTimerDone() + if (!_notifiedActivity) { +// KNotification::event("Activity", ("Activity in session '%1'", _nameTitle), QPixmap(), +// QApplication::activeWindow(), +// KNotification::CloseWhenWidgetActivated); + _notifiedActivity=true; + } + } + } + + if ( state==NOTIFYACTIVITY && !_monitorActivity ) + state = NOTIFYNORMAL; + if ( state==NOTIFYSILENCE && !_monitorSilence ) + state = NOTIFYNORMAL; + + emit stateChanged(state); +} + +void Session::onViewSizeChange(int /*height*/, int /*width*/) +{ + updateTerminalSize(); +} +void Session::onEmulationSizeChange(int lines , int columns) +{ + setSize( QSize(lines,columns) ); +} + +void Session::updateTerminalSize() +{ + QListIterator<TerminalDisplay*> viewIter(_views); + + int minLines = -1; + int minColumns = -1; + + // minimum number of lines and columns that views require for + // their size to be taken into consideration ( to avoid problems + // with new view widgets which haven't yet been set to their correct size ) + const int VIEW_LINES_THRESHOLD = 2; + const int VIEW_COLUMNS_THRESHOLD = 2; + + //select largest number of lines and columns that will fit in all visible views + while ( viewIter.hasNext() ) + { + TerminalDisplay* view = viewIter.next(); + if ( view->isHidden() == false && + view->lines() >= VIEW_LINES_THRESHOLD && + view->columns() >= VIEW_COLUMNS_THRESHOLD ) + { + minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() ); + minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() ); + } + } + + // backend emulation must have a _terminal of at least 1 column x 1 line in size + if ( minLines > 0 && minColumns > 0 ) + { + _emulation->setImageSize( minLines , minColumns ); + _shellProcess->setWindowSize( minLines , minColumns ); + } +} + +void Session::refresh() +{ + // attempt to get the shell process to redraw the display + // + // this requires the program running in the shell + // to cooperate by sending an update in response to + // a window size change + // + // the window size is changed twice, first made slightly larger and then + // resized back to its normal size so that there is actually a change + // in the window size (some shells do nothing if the + // new and old sizes are the same) + // + // if there is a more 'correct' way to do this, please + // send an email with method or patches to konsole-devel@kde.org + + const QSize existingSize = _shellProcess->windowSize(); + _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1); + _shellProcess->setWindowSize(existingSize.height(),existingSize.width()); +} + +bool Session::sendSignal(int signal) +{ + return _shellProcess->kill(signal); +} + +void Session::close() +{ + _autoClose = true; + _wantedClose = true; + if (!_shellProcess->isRunning() || !sendSignal(SIGHUP)) + { + // Forced close. + QTimer::singleShot(1, this, SIGNAL(finished())); + } +} + +void Session::sendText(const QString &text) const +{ + _emulation->sendText(text); +} + +Session::~Session() +{ + delete _emulation; + delete _shellProcess; +// delete _zmodemProc; +} + +void Session::setProfileKey(const QString& key) +{ + _profileKey = key; + emit profileChanged(key); +} +QString Session::profileKey() const { return _profileKey; } + +void Session::done(int exitStatus) +{ + if (!_autoClose) + { + _userTitle = ("<Finished>"); + emit titleChanged(); + return; + } + if (!_wantedClose && (exitStatus || _shellProcess->signalled())) + { + QString message; + + if (_shellProcess->normalExit()) + message.sprintf ("Session '%s' exited with status %d.", _nameTitle.toAscii().data(), exitStatus); + else if (_shellProcess->signalled()) + { + if (_shellProcess->coreDumped()) + { + + message.sprintf("Session '%s' exited with signal %d and dumped core.", _nameTitle.toAscii().data(), _shellProcess->exitSignal()); + } + else { + message.sprintf("Session '%s' exited with signal %d.", _nameTitle.toAscii().data(), _shellProcess->exitSignal()); + } + } + else + message.sprintf ("Session '%s' exited unexpectedly.", _nameTitle.toAscii().data()); + + //FIXME: See comments in Session::monitorTimerDone() +// KNotification::event("Finished", message , QPixmap(), +// QApplication::activeWindow(), +// KNotification::CloseWhenWidgetActivated); + } + emit finished(); +} + +Emulation* Session::emulation() const +{ + return _emulation; +} + +QString Session::keyBindings() const +{ + return _emulation->keyBindings(); +} + +QStringList Session::environment() const +{ + return _environment; +} + +void Session::setEnvironment(const QStringList& environment) +{ + _environment = environment; +} + +int Session::sessionId() const +{ + return _sessionId; +} + +void Session::setKeyBindings(const QString &id) +{ + _emulation->setKeyBindings(id); +} + +void Session::setTitle(TitleRole role , const QString& newTitle) +{ + if ( title(role) != newTitle ) + { + if ( role == NameRole ) + _nameTitle = newTitle; + else if ( role == DisplayedTitleRole ) + _displayTitle = newTitle; + + emit titleChanged(); + } +} + +QString Session::title(TitleRole role) const +{ + if ( role == NameRole ) + return _nameTitle; + else if ( role == DisplayedTitleRole ) + return _displayTitle; + else + return QString(); +} + +void Session::setIconName(const QString& iconName) +{ + if ( iconName != _iconName ) + { + _iconName = iconName; + emit titleChanged(); + } +} + +void Session::setIconText(const QString& iconText) +{ + _iconText = iconText; + //kDebug(1211)<<"Session setIconText " << _iconText; +} + +QString Session::iconName() const +{ + return _iconName; +} + +QString Session::iconText() const +{ + return _iconText; +} + +void Session::setHistoryType(const HistoryType &hType) +{ + _emulation->setHistory(hType); +} + +const HistoryType& Session::historyType() const +{ + return _emulation->history(); +} + +void Session::clearHistory() +{ + _emulation->clearHistory(); +} + +QStringList Session::arguments() const +{ + return _arguments; +} + +QString Session::program() const +{ + return _program; +} + +// unused currently +bool Session::isMonitorActivity() const { return _monitorActivity; } +// unused currently +bool Session::isMonitorSilence() const { return _monitorSilence; } + +void Session::setMonitorActivity(bool _monitor) +{ + _monitorActivity=_monitor; + _notifiedActivity=false; + + activityStateSet(NOTIFYNORMAL); +} + +void Session::setMonitorSilence(bool _monitor) +{ + if (_monitorSilence==_monitor) + return; + + _monitorSilence=_monitor; + if (_monitorSilence) + { + _monitorTimer->start(_silenceSeconds*1000); + } + else + _monitorTimer->stop(); + + activityStateSet(NOTIFYNORMAL); +} + +void Session::setMonitorSilenceSeconds(int seconds) +{ + _silenceSeconds=seconds; + if (_monitorSilence) { + _monitorTimer->start(_silenceSeconds*1000); + } +} + +void Session::setAddToUtmp(bool set) +{ + _addToUtmp = set; +} + +void Session::setFlowControlEnabled(bool enabled) +{ + if (_flowControl == enabled) + return; + + _flowControl = enabled; + + if (_shellProcess) + _shellProcess->setXonXoff(_flowControl); + + emit flowControlEnabledChanged(enabled); +} +bool Session::flowControlEnabled() const +{ + return _flowControl; +} +//void Session::fireZModemDetected() +//{ +// if (!_zmodemBusy) +// { +// QTimer::singleShot(10, this, SIGNAL(zmodemDetected())); +// _zmodemBusy = true; +// } +//} + +//void Session::cancelZModem() +//{ +// _shellProcess->sendData("\030\030\030\030", 4); // Abort +// _zmodemBusy = false; +//} + +//void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list) +//{ +// _zmodemBusy = true; +// _zmodemProc = new KProcess(); +// _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels ); +// +// *_zmodemProc << zmodem << "-v" << list; +// +// if (!dir.isEmpty()) +// _zmodemProc->setWorkingDirectory(dir); +// +// _zmodemProc->start(); +// +// connect(_zmodemProc,SIGNAL (readyReadStandardOutput()), +// this, SLOT(zmodemReadAndSendBlock())); +// connect(_zmodemProc,SIGNAL (readyReadStandardError()), +// this, SLOT(zmodemReadStatus())); +// connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)), +// this, SLOT(zmodemFinished())); +// +// disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); +// connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) ); +// +// _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false, +// i18n("ZModem Progress")); +// +// connect(_zmodemProgress, SIGNAL(user1Clicked()), +// this, SLOT(zmodemDone())); +// +// _zmodemProgress->show(); +//} + +/*void Session::zmodemReadAndSendBlock() +{ + _zmodemProc->setReadChannel( QProcess::StandardOutput ); + QByteArray data = _zmodemProc->readAll(); + + if ( data.count() == 0 ) + return; + + _shellProcess->sendData(data.constData(),data.count()); +} +*/ +/* +void Session::zmodemReadStatus() +{ + _zmodemProc->setReadChannel( QProcess::StandardError ); + QByteArray msg = _zmodemProc->readAll(); + while(!msg.isEmpty()) + { + int i = msg.indexOf('\015'); + int j = msg.indexOf('\012'); + QByteArray txt; + if ((i != -1) && ((j == -1) || (i < j))) + { + msg = msg.mid(i+1); + } + else if (j != -1) + { + txt = msg.left(j); + msg = msg.mid(j+1); + } + else + { + txt = msg; + msg.truncate(0); + } + if (!txt.isEmpty()) + _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt)); + } +} +*/ +/* +void Session::zmodemRcvBlock(const char *data, int len) +{ + QByteArray ba( data, len ); + + _zmodemProc->write( ba ); +} +*/ +/* +void Session::zmodemFinished() +{ + if (_zmodemProc) + { + delete _zmodemProc; + _zmodemProc = 0; + _zmodemBusy = false; + + disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) ); + connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) ); + + _shellProcess->sendData("\030\030\030\030", 4); // Abort + _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back + _zmodemProgress->transferDone(); + } +} +*/ +void Session::onReceiveBlock( const char* buf, int len ) +{ + _emulation->receiveData( buf, len ); + emit receivedData( QString::fromLatin1( buf, len ) ); +} + +QSize Session::size() +{ + return _emulation->imageSize(); +} + +void Session::setSize(const QSize& size) +{ + if ((size.width() <= 1) || (size.height() <= 1)) + return; + + emit resizeRequest(size); +} +int Session::foregroundProcessId() const +{ + return _shellProcess->foregroundProcessGroup(); +} +int Session::processId() const +{ + return _shellProcess->pid(); +} + +SessionGroup::SessionGroup() + : _masterMode(0) +{ +} +SessionGroup::~SessionGroup() +{ + // disconnect all + connectAll(false); +} +int SessionGroup::masterMode() const { return _masterMode; } +QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); } +bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; } + +void SessionGroup::addSession(Session* session) +{ + _sessions.insert(session,false); + + QListIterator<Session*> masterIter(masters()); + + while ( masterIter.hasNext() ) + connectPair(masterIter.next(),session); +} +void SessionGroup::removeSession(Session* session) +{ + setMasterStatus(session,false); + + QListIterator<Session*> masterIter(masters()); + + while ( masterIter.hasNext() ) + disconnectPair(masterIter.next(),session); + + _sessions.remove(session); +} +void SessionGroup::setMasterMode(int mode) +{ + _masterMode = mode; + + connectAll(false); + connectAll(true); +} +QList<Session*> SessionGroup::masters() const +{ + return _sessions.keys(true); +} +void SessionGroup::connectAll(bool connect) +{ + QListIterator<Session*> masterIter(masters()); + + while ( masterIter.hasNext() ) + { + Session* master = masterIter.next(); + + QListIterator<Session*> otherIter(_sessions.keys()); + while ( otherIter.hasNext() ) + { + Session* other = otherIter.next(); + + if ( other != master ) + { + if ( connect ) + connectPair(master,other); + else + disconnectPair(master,other); + } + } + } +} +void SessionGroup::setMasterStatus(Session* session , bool master) +{ + bool wasMaster = _sessions[session]; + _sessions[session] = master; + + if ( !wasMaster && !master + || wasMaster && master ) + return; + + QListIterator<Session*> iter(_sessions.keys()); + while ( iter.hasNext() ) + { + Session* other = iter.next(); + + if ( other != session ) + { + if ( master ) + connectPair(session,other); + else + disconnectPair(session,other); + } + } +} +void SessionGroup::connectPair(Session* master , Session* other) +{ +// qDebug() << k_funcinfo; + + if ( _masterMode & CopyInputToAll ) + { + qDebug() << "Connection session " << master->nameTitle() << "to" << other->nameTitle(); + + connect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() , + SLOT(sendString(const char*,int)) ); + } +} +void SessionGroup::disconnectPair(Session* master , Session* other) +{ +// qDebug() << k_funcinfo; + + if ( _masterMode & CopyInputToAll ) + { + qDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle(); + + disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() , + SLOT(sendString(const char*,int)) ); + } +} + +//#include "moc_Session.cpp" |