Mercurial > octave-nkf
view gui/src/terminal/Session.cpp @ 13552:324adf6109e7
Isolated and eliminated class ProcessInfo.
author | Jacob Dawid <jacob.dawid@googlemail.com> |
---|---|
date | Fri, 29 Jul 2011 18:53:35 +0200 |
parents | c70511cf64ee |
children | dfbc6a12c6fb |
line wrap: on
line source
/* This file is part of Konsole Copyright 2006-2008 by Robert Knight <robertknight@gmail.com> Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de> Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.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 <QtCore/QDate> #include "kprocess.h" #include "kptydevice.h" //#include "ProcessInfo.h" #include "Pty.h" #include "TerminalDisplay.h" #include "ShellCommand.h" #include "Vt102Emulation.h" int Session::lastSessionId = 0; // HACK This is copied out of QUuid::createUuid with reseeding forced. // Required because color schemes repeatedly seed the RNG... // ...with a constant. QUuid createUuid () { static const int intbits = sizeof (int) * 8; static int randbits = 0; if (!randbits) { int max = RAND_MAX; do { ++randbits; } while ((max = max >> 1)); } qsrand (uint (QDateTime::currentDateTime ().toTime_t ())); qrand (); // Skip first QUuid result; uint * data = &(result.data1); int chunks = 16 / sizeof (uint); while (chunks--) { uint randNumber = 0; for (int filled = 0; filled < intbits; filled += randbits) randNumber |= qrand () << filled; *(data + chunks) = randNumber; } result.data4[0] = (result.data4[0] & 0x3F) | 0x80; // UV_DCE result.data3 = (result.data3 & 0x0FFF) | 0x4000; // UV_Random return result; } Session::Session (QObject * parent): QObject (parent), _shellProcess (0), _emulation (0), _monitorActivity (false), _monitorSilence (false), _notifiedActivity (false), _autoClose (true), _wantedClose (false), _silenceSeconds (10), _addToUtmp (true), _flowControl (true), _fullScripting (false), _sessionId (0), /*_sessionProcessInfo (0), _foregroundProcessInfo (0),*/ _foregroundPid (0) //, _zmodemBusy(false) //, _zmodemProc(0) //, _zmodemProgress(0) , _hasDarkBackground (false) { _uniqueIdentifier = createUuid (); //prepare DBus communication //new SessionAdaptor(this); _sessionId = ++lastSessionId; // JPS: commented out for lack of DBUS support by default on OSX //QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this); //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 (changeTabTextColorRequest (int)), this, SIGNAL (changeTabTextColorRequest (int))); connect (_emulation, SIGNAL (profileChangeCommandReceived (const QString &)), this, SIGNAL (profileChangeCommandReceived (const QString &))); connect (_emulation, SIGNAL (flowControlKeyPressed (bool)), this, SLOT (updateFlowControlState (bool))); //create new teletype for I/O with shell process openTeletype (-1); //setup timer for monitoring session activity _monitorTimer = new QTimer (this); _monitorTimer->setSingleShot (true); connect (_monitorTimer, SIGNAL (timeout ()), this, SLOT (monitorTimerDone ())); } void Session::openTeletype (int fd) { if (_shellProcess && isRunning ()) { //kWarning() << "Attempted to open teletype in a running session."; return; } delete _shellProcess; if (fd < 0) _shellProcess = new Pty (); else _shellProcess = new Pty (fd); _shellProcess->setUtf8Mode (_emulation->utf8 ()); //connect teletype to emulation backend 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 (useUtf8Request (bool)), _shellProcess, SLOT (setUtf8Mode (bool))); connect (_shellProcess, SIGNAL (finished (int, QProcess::ExitStatus)), this, SLOT (done (int))); connect (_emulation, SIGNAL (imageSizeChanged (int, int)), this, SLOT (updateWindowSize (int, int))); } 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); } bool Session::setCodec (QByteArray name) { QTextCodec *codec = QTextCodec::codecForName (name); if (codec) { setCodec (codec); return true; } return false; } QByteArray Session::codec () { return _emulation->codec ()->name (); } void Session::setProgram (const QString & program) { _program = ShellCommand::expand (program); } void Session::setInitialWorkingDirectory (const QString & dir) { //_initialWorkingDir = KShell::tildeExpand(ShellCommand::expand(dir)); _initialWorkingDir = ShellCommand::expand (dir); } void Session::setArguments (const QStringList & arguments) { _arguments = ShellCommand::expand (arguments); } /* QString Session::currentWorkingDirectory () { // only returned cached value //if (_currentWorkingDir.isEmpty ()) // updateWorkingDirectory (); return _currentWorkingDir; } */ /* ProcessInfo * Session::updateWorkingDirectory () { ProcessInfo *process = getProcessInfo (); _currentWorkingDir = process->validCurrentDir (); return process; }*/ 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 = qgetenv ("SHELL"); if (exec.isEmpty ()) exec = "/bin/sh"; return program; } void Session::terminalWarning (const QString & message) { static const QByteArray warningText = QByteArray ("@info:shell Alert the user with red color text"); QByteArray messageText = message.toLocal8Bit (); static const char redPenOn[] = "\033[1m\033[31m"; static const char redPenOff[] = "\033[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)); } QString Session::shellSessionId () const { QString friendlyUuid (_uniqueIdentifier.toString ()); friendlyUuid.remove ('-').remove ('{').remove ('}'); return friendlyUuid; } void Session::run () { //check that everything is in place to run the session if (_program.isEmpty ()) { //kWarning() << "Session::run() - program to run not set."; } if (_arguments.isEmpty ()) { //kWarning() << "Session::run() - no command line arguments specified."; } if (_uniqueIdentifier.isNull ()) { _uniqueIdentifier = createUuid (); } const int CHOICE_COUNT = 3; QString programs[CHOICE_COUNT] = { _program, qgetenv ("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 ()) { QString msg; QTextStream msgStream (&msg); msgStream << "Could not find '" << _program << "', starting '" << exec << "' instead. Please check your profile settings."; terminalWarning (msg); //terminalWarning(i18n("Could not find '%1', starting '%2' instead. Please check your profile settings.",_program.toLatin1().data(),exec.toLatin1().data())); } // if none of the choices are available, print a warning else if (choice == CHOICE_COUNT) { terminalWarning (QString ("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; // JPS: commented out for lack of DBUS support by default on OSX QString dbusService = ""; //QDBusConnection::sessionBus().baseService(); if (!_initialWorkingDir.isEmpty ()) _shellProcess->setWorkingDirectory (_initialWorkingDir); else _shellProcess->setWorkingDirectory (QDir::homePath ()); _shellProcess->setFlowControlEnabled (_flowControl); _shellProcess->setErase (_emulation->eraseChar ()); // 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"; _environment << backgroundColorHint; _environment << QString ("SHELL_SESSION_ID=%1").arg (shellSessionId ()); int result = _shellProcess->start (exec, arguments, _environment, windowId (), _addToUtmp, dbusService, (QLatin1String ("/Sessions/") + QString::number (_sessionId))); if (result < 0) { QString msg; QTextStream msgStream (&msg); msgStream << QString ("Could not start program '") << exec << QString ("' with arguments '") << arguments. join (" ") << QString ("'."); terminalWarning (msg); // terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec.toLatin1().data(), arguments.join(" ").toLatin1().data())); 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)propagateSize, QPixmap(), // QApplication::activeWindow(), // KNotification::CloseWhenWidgetActivated); emit stateChanged (NOTIFYSILENCE); } else { emit stateChanged (NOTIFYNORMAL); } _notifiedActivity = false; } void Session::updateFlowControlState (bool suspended) { if (suspended) { if (flowControlEnabled ()) { foreach (TerminalDisplay * display, _views) { if (display->flowControlWarningEnabled ()) display->outputSuspended (true); } } } else { foreach (TerminalDisplay * display, _views) display->outputSuspended (false); } } void Session::activityStateSet (int state) { if (state == NOTIFYBELL) { emit bellRequest (QString ("Bell in session '%1'"). arg (_nameTitle.toLatin1 ().data ())); } 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::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 ()); view->processFilters (); } } // 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); } } void Session::updateWindowSize (int lines, int columns) { Q_ASSERT (lines > 0 && columns > 0); _shellProcess->setWindowSize (lines, columns); } 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)) { if (isRunning ()) { //kWarning() << "Process" << _shellProcess->pid() << "did not respond to SIGHUP"; // close the pty and wait to see if the process finishes. If it does, // the done() slot will have been called so we can return. Otherwise, // emit the finished() signal regardless _shellProcess->pty ()->close (); if (_shellProcess->waitForFinished (3000)) return; //kWarning() << "Unable to kill process" << _shellProcess->pid(); } // Forced close. QTimer::singleShot (1, this, SIGNAL (finished ())); } } void Session::sendText (const QString & text) const { _emulation->sendText (text); } void Session::sendMouseEvent (int buttons, int column, int line, int eventType) { _emulation->sendMouseEvent (buttons, column, line, eventType); } Session::~Session () { //if (_foregroundProcessInfo) // delete _foregroundProcessInfo; //if (_sessionProcessInfo) // delete _sessionProcessInfo; delete _emulation; delete _shellProcess; //delete _zmodemProc; } void Session::done (int exitStatus) { if (!_autoClose) { _userTitle = QString ("@info:shell This session is done"); emit titleChanged (); return; } QString message; QTextStream msgStream (&message); if (!_wantedClose || exitStatus != 0) { if (_shellProcess->exitStatus () == QProcess::NormalExit) { msgStream << "Program '" << _program << "' exited with statis " << exitStatus << "."; //message = i18n("Program '%1' exited with status %2.", _program.toLatin1().data(), exitStatus); } else { msgStream << "Program '" << _program << "' crashed."; //message = i18n("Program '%1' crashed.", _program.toLatin1().data()); } //FIXME: See comments in Session::monitorTimerDone() //KNotification::event("Finished", message , QPixmap(), // QApplication::activeWindow(), // KNotification::CloseWhenWidgetActivated); } if (!_wantedClose && _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 (); } /* ProcessInfo * Session::getProcessInfo () { ProcessInfo *process; if (isForegroundProcessActive ()) process = _foregroundProcessInfo; else { updateSessionProcessInfo (); process = _sessionProcessInfo; } return process; }*/ void Session::updateSessionProcessInfo () { /* Q_ASSERT (_shellProcess); if (!_sessionProcessInfo) { _sessionProcessInfo = ProcessInfo::newInstance (processId ()); _sessionProcessInfo->setUserHomeDir (); } _sessionProcessInfo->update ();*/ } bool Session::updateForegroundProcessInfo () { /* bool valid = (_foregroundProcessInfo != 0); // has foreground process changed? Q_ASSERT (_shellProcess); int pid = _shellProcess->foregroundProcessGroup (); if (pid != _foregroundPid) { if (valid) delete _foregroundProcessInfo; _foregroundProcessInfo = ProcessInfo::newInstance (pid); _foregroundPid = pid; valid = true; } if (valid) { _foregroundProcessInfo->update (); valid = _foregroundProcessInfo->isValid (); } return valid;*/ return true; } bool Session::isRemote () {/* ProcessInfo *process = getProcessInfo (); bool ok = false; return (process->name (&ok) == "ssh" && ok); */ return false; } QString Session::getDynamicTitle () {/* // update current directory from process ProcessInfo *process = updateWorkingDirectory (); // format tab titles using process info bool ok = false; QString title; if (process->name (&ok) == "ssh" && ok) { SSHProcessInfo sshInfo (*process); title = sshInfo.format (tabTitleFormat (Session::RemoteTabTitle)); } else title = process->format (tabTitleFormat (Session::LocalTabTitle)); return title;*/ return ""; } void Session::setIconName (const QString & iconName) { if (iconName != _iconName) { _iconName = iconName; emit titleChanged (); } } void Session::setIconText (const QString & iconText) { _iconText = 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) { _flowControl = enabled; if (_shellProcess) _shellProcess->setFlowControlEnabled (_flowControl); emit flowControlEnabledChanged (enabled); } bool Session::flowControlEnabled () const { if (_shellProcess) return _shellProcess->flowControlEnabled (); else return _flowControl; } 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::processId () const { return _shellProcess->pid (); } void Session::setTitle (int role, const QString & title) { switch (role) { case (0): this->setTitle (Session::NameRole, title); break; case (1): this->setTitle (Session::DisplayedTitleRole, title); break; } } QString Session::title (int role) const { switch (role) { case (0): return this->title (Session::NameRole); case (1): return this->title (Session::DisplayedTitleRole); default: return QString (); } } void Session::setTabTitleFormat (int context, const QString & format) { switch (context) { case (0): this->setTabTitleFormat (Session::LocalTabTitle, format); break; case (1): this->setTabTitleFormat (Session::RemoteTabTitle, format); break; } } QString Session::tabTitleFormat (int context) const { switch (context) { case (0): return this->tabTitleFormat (Session::LocalTabTitle); case (1): return this->tabTitleFormat (Session::RemoteTabTitle); default: return QString (); } } /* int Session::foregroundProcessId () { int pid; bool ok = false; pid = getProcessInfo ()->pid (&ok); if (!ok) pid = -1; return pid; }*/ bool Session::isForegroundProcessActive () { // foreground process info is always updated after this return updateForegroundProcessInfo () && (processId () != _foregroundPid); } /* QString Session::foregroundProcessName () { QString name; if (updateForegroundProcessInfo ()) { bool ok = false; name = _foregroundProcessInfo->name (&ok); if (!ok) name.clear (); } return name; }*/ SessionGroup::SessionGroup (QObject * parent):QObject (parent), _masterMode (0) { } SessionGroup::~SessionGroup () { } 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) { connect (session, SIGNAL (finished ()), this, SLOT (sessionFinished ())); _sessions.insert (session, false); } void SessionGroup::removeSession (Session * session) { disconnect (session, SIGNAL (finished ()), this, SLOT (sessionFinished ())); setMasterStatus (session, false); _sessions.remove (session); } void SessionGroup::sessionFinished () { Session *session = qobject_cast < Session * >(sender ()); Q_ASSERT (session); removeSession (session); } void SessionGroup::setMasterMode (int mode) { _masterMode = mode; } QList < Session * >SessionGroup::masters () const { return _sessions.keys (true); } void SessionGroup::setMasterStatus (Session * session, bool master) { const bool wasMaster = _sessions[session]; if (wasMaster == master) { // No status change -> nothing to do. return; } _sessions[session] = master; if (master) { connect (session->emulation (), SIGNAL (sendData (const char *, int)), this, SLOT (forwardData (const char *, int))); } else { disconnect (session->emulation (), SIGNAL (sendData (const char *, int)), this, SLOT (forwardData (const char *, int))); } } void SessionGroup::forwardData (const char *data, int size) { static bool _inForwardData = false; if (_inForwardData) { // Avoid recursive calls among session groups! // A recursive call happens when a master in group A calls forwardData() // in group B. If one of the destination sessions in group B is also a // master of a group including the master session of group A, this would // again call forwardData() in group A, and so on. return; } _inForwardData = true; QListIterator < Session * >iter (_sessions.keys ()); while (iter.hasNext ()) { Session *other = iter.next (); if (!_sessions[other]) { other->emulation ()->sendString (data, size); } } _inForwardData = false; }