summaryrefslogtreecommitdiff
path: root/qtermwidget/Session.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qtermwidget/Session.cpp')
-rw-r--r--qtermwidget/Session.cpp1021
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"