You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1076 lines
28 KiB

/*
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>
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>
#include <signal.h>
// Qt
#include <QtGui/QApplication>
#include <QtCore/QByteRef>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QRegExp>
#include <QtCore/QStringList>
#include <QtDBus/QtDBus>
#include <QtCore/QDate>
// KDE
#include <KDebug>
#include <KLocale>
#include <KMessageBox>
#include <KNotification>
#include <KProcess>
#include <KRun>
#include <kshell.h>
#include <KStandardDirs>
// Konsole
#include <config-konsole.h>
#include <sessionadaptor.h>
#include "Pty.h"
#include "TerminalDisplay.h"
#include "ShellCommand.h"
#include "Vt102Emulation.h"
#include "ZModemDialog.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(finished(int,QProcess::ExitStatus)), 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->state() == QProcess::Running;
}
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*)) );
}
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();
}
}
QString Session::checkProgram(const QString& program) const
{
// 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.isEmpty())
return QString();
// 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";
exec = KRun::binaryName(exec, false);
exec = KShell::tildeExpand(exec);
QString pexec = KGlobal::dirs()->findExe(exec);
if ( pexec.isEmpty() )
{
kError() << i18n("Could not find binary: ") << exec;
return QString();
}
return exec;
}
void Session::terminalWarning(const QString& message)
{
static const QByteArray warningText = i18n("Warning: ").toLocal8Bit();
QByteArray messageText = message.toLocal8Bit();
static const char* redPenOn = "\E[1m\E[31m";
static const char* redPenOff = "\E[0m";
_emulation->receiveData(redPenOn,strlen(redPenOn));
_emulation->receiveData("\n\r\n\r",4);
_emulation->receiveData(warningText.constData(),strlen(warningText.constData()));
_emulation->receiveData(messageText.constData(),strlen(messageText.constData()));
_emulation->receiveData("\n\r\n\r",4);
_emulation->receiveData(redPenOff,strlen(redPenOff));
}
void Session::run()
{
//check that everything is in place to run the session
if (_program.isEmpty())
kDebug() << "Session::run() - program to run not set.";
if (_arguments.isEmpty())
kDebug() << "Session::run() - no command line arguments specified.";
const int CHOICE_COUNT = 3;
QString programs[CHOICE_COUNT] = {_program,getenv("SHELL"),"/bin/sh"};
QString exec;
int choice = 0;
while (choice < CHOICE_COUNT)
{
exec = checkProgram(programs[choice]);
if (exec.isEmpty())
choice++;
else
break;
}
// if a program was specified via setProgram(), but it couldn't be found, print a warning
if (choice != 0 && choice < CHOICE_COUNT && !_program.isEmpty())
{
terminalWarning(i18n("Could not find '%1', starting '%2' instead. Please check your profile settings.",_program,exec));
}
// if none of the choices are available, print a warning
else if (choice == CHOICE_COUNT)
{
terminalWarning(i18n("Could not find an interactive shell to start."));
return;
}
// if no arguments are specified, fall back to program name
QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ?
QStringList() << exec : _arguments;
QString dbusService = QDBusConnection::sessionBus().baseService();
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(exec,
arguments,
_environment << backgroundColorHint,
windowId(),
_addToUtmp,
dbusService,
(QLatin1String("/Sessions/") +
QString::number(_sessionId)));
if (result < 0)
{
terminalWarning(i18n("Could not start program '%1' with arguments '%2'."));
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;
if ((what == IconNameAndWindowTitle) || (what == WindowTitle))
{
if ( _userTitle != caption ) {
_userTitle = caption;
modified = true;
}
}
if ((what == IconNameAndWindowTitle) || (what == IconName))
{
if ( _iconText != caption ) {
_iconText = caption;
modified = true;
}
}
if (what == TextColor || what == BackgroundColor)
{
QString colorString = caption.section(';',0,0);
QColor color = QColor(colorString);
if (color.isValid())
{
if (what == TextColor)
emit changeForegroundColorRequest(color);
else
emit changeBackgroundColorRequest(color);
}
}
if (what == SessionName)
{
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 == ProfileChange)
{
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", i18n("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)
{
emit bellRequest( i18n("Bell in session '%1'",_nameTitle) );
}
else if (state==NOTIFYACTIVITY)
{
if (_monitorSilence) {
_monitorTimer->start(_silenceSeconds*1000);
}
if ( _monitorActivity ) {
//FIXME: See comments in Session::monitorTimerDone()
if (!_notifiedActivity) {
KNotification::event("Activity", i18n("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::kill(int signal)
{
int result = ::kill(_shellProcess->pid(),signal);
if ( result == 0 )
{
_shellProcess->waitForFinished();
return true;
}
else
return false;
}
void Session::close()
{
_autoClose = true;
_wantedClose = true;
if (!isRunning() || !kill(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 = i18n("Finished");
emit titleChanged();
return;
}
QString message;
if (!_wantedClose || exitStatus != 0)
{
if (_shellProcess->exitStatus() == QProcess::NormalExit)
message = i18n("Program '%1' exited with status %2.", _shellProcess->program().first(), exitStatus);
else
message = i18n("Program '%1' crashed.", _shellProcess->program().first());
//FIXME: See comments in Session::monitorTimerDone()
KNotification::event("Finished", message , QPixmap(),
QApplication::activeWindow(),
KNotification::CloseWhenWidgetActivated);
}
if ( exitStatus != 0 || _shellProcess->exitStatus() != QProcess::NormalExit )
terminalWarning(message);
else
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)
{
kDebug() << k_funcinfo;
if ( _masterMode & CopyInputToAll )
{
kDebug() << "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)
{
kDebug() << k_funcinfo;
if ( _masterMode & CopyInputToAll )
{
kDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle();
disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
SLOT(sendString(const char*,int)) );
}
}
#include "Session.moc"