add more spacing
[personal-kdebase.git] / apps / konsole / src / Session.cpp
blob195df0671eed7c7d7f7b2bb1cf9abc5e39f2366c
1 /*
2 This file is part of Konsole
4 Copyright 2006-2008 by Robert Knight <robertknight@gmail.com>
5 Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
23 // Own
24 #include "Session.h"
26 // Standard
27 #include <assert.h>
28 #include <stdlib.h>
29 #include <signal.h>
31 // Qt
32 #include <QtGui/QApplication>
33 #include <QtCore/QByteRef>
34 #include <QtCore/QDir>
35 #include <QtCore/QFile>
36 #include <QtCore/QRegExp>
37 #include <QtCore/QStringList>
38 #include <QtDBus/QtDBus>
39 #include <QtCore/QDate>
41 // KDE
42 #include <KDebug>
43 #include <KLocale>
44 #include <KMessageBox>
45 #include <KNotification>
46 #include <KProcess>
47 #include <KRun>
48 #include <kshell.h>
49 #include <KStandardDirs>
50 #include <KPtyDevice>
51 #include <KUrl>
53 // Konsole
54 #include <config-konsole.h>
55 #include <sessionadaptor.h>
57 #include "ProcessInfo.h"
58 #include "Pty.h"
59 #include "TerminalDisplay.h"
60 #include "ShellCommand.h"
61 #include "Vt102Emulation.h"
62 #include "ZModemDialog.h"
64 using namespace Konsole;
66 int Session::lastSessionId = 0;
68 Session::Session(QObject* parent) :
69 QObject(parent)
70 , _shellProcess(0)
71 , _emulation(0)
72 , _monitorActivity(false)
73 , _monitorSilence(false)
74 , _notifiedActivity(false)
75 , _autoClose(true)
76 , _wantedClose(false)
77 , _silenceSeconds(10)
78 , _addToUtmp(true)
79 , _flowControl(true)
80 , _fullScripting(false)
81 , _sessionId(0)
82 , _sessionProcessInfo(0)
83 , _foregroundProcessInfo(0)
84 , _foregroundPid(0)
85 , _zmodemBusy(false)
86 , _zmodemProc(0)
87 , _zmodemProgress(0)
88 , _hasDarkBackground(false)
90 //prepare DBus communication
91 new SessionAdaptor(this);
92 _sessionId = ++lastSessionId;
93 QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
95 //create emulation backend
96 _emulation = new Vt102Emulation();
98 connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ),
99 this, SLOT( setUserTitle( int, const QString & ) ) );
100 connect( _emulation, SIGNAL( stateSet(int) ),
101 this, SLOT( activityStateSet(int) ) );
102 connect( _emulation, SIGNAL( zmodemDetected() ), this ,
103 SLOT( fireZModemDetected() ) );
104 connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ),
105 this, SIGNAL( changeTabTextColorRequest( int ) ) );
106 connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)),
107 this, SIGNAL( profileChangeCommandReceived(const QString&)) );
108 connect( _emulation, SIGNAL(flowControlKeyPressed(bool)) , this,
109 SLOT(updateFlowControlState(bool)) );
111 //create new teletype for I/O with shell process
112 openTeletype(-1);
114 //setup timer for monitoring session activity
115 _monitorTimer = new QTimer(this);
116 _monitorTimer->setSingleShot(true);
117 connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone()));
120 void Session::openTeletype(int fd)
122 if (_shellProcess && isRunning())
124 kWarning() << "Attempted to open teletype in a running session.";
125 return;
128 delete _shellProcess;
130 if (fd < 0)
131 _shellProcess = new Pty();
132 else
133 _shellProcess = new Pty(fd);
135 _shellProcess->setUtf8Mode(_emulation->utf8());
137 //connect teletype to emulation backend
138 connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this,
139 SLOT(onReceiveBlock(const char*,int)) );
140 connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess,
141 SLOT(sendData(const char*,int)) );
142 connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) );
143 connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) );
144 connect( _shellProcess,SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(done(int)) );
145 connect( _emulation,SIGNAL(imageSizeChanged(int,int)),this,SLOT(updateWindowSize(int,int)) );
148 WId Session::windowId() const
150 // Returns a window ID for this session which is used
151 // to set the WINDOWID environment variable in the shell
152 // process.
154 // Sessions can have multiple views or no views, which means
155 // that a single ID is not always going to be accurate.
157 // If there are no views, the window ID is just 0. If
158 // there are multiple views, then the window ID for the
159 // top-level window which contains the first view is
160 // returned
162 if ( _views.count() == 0 )
163 return 0;
164 else
166 QWidget* window = _views.first();
168 Q_ASSERT( window );
170 while ( window->parentWidget() != 0 )
171 window = window->parentWidget();
173 return window->winId();
177 void Session::setDarkBackground(bool darkBackground)
179 _hasDarkBackground = darkBackground;
181 bool Session::hasDarkBackground() const
183 return _hasDarkBackground;
185 bool Session::isRunning() const
187 return _shellProcess->state() == QProcess::Running;
190 void Session::setCodec(QTextCodec* codec)
192 emulation()->setCodec(codec);
195 void Session::setProgram(const QString& program)
197 _program = ShellCommand::expand(program);
199 void Session::setInitialWorkingDirectory(const QString& dir)
201 _initialWorkingDir = ShellCommand::expand(dir);
203 void Session::setArguments(const QStringList& arguments)
205 _arguments = ShellCommand::expand(arguments);
208 QString Session::currentWorkingDirectory()
210 // only returned cached value
211 if (_currentWorkingDir.isEmpty()) updateWorkingDirectory();
212 return _currentWorkingDir;
214 ProcessInfo* Session::updateWorkingDirectory()
216 ProcessInfo *process = getProcessInfo();
217 _currentWorkingDir = process->validCurrentDir();
218 return process;
221 QList<TerminalDisplay*> Session::views() const
223 return _views;
226 void Session::addView(TerminalDisplay* widget)
228 Q_ASSERT( !_views.contains(widget) );
230 _views.append(widget);
232 if ( _emulation != 0 )
234 // connect emulation - view signals and slots
235 connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation ,
236 SLOT(sendKeyEvent(QKeyEvent*)) );
237 connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation ,
238 SLOT(sendMouseEvent(int,int,int,int)) );
239 connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation ,
240 SLOT(sendString(const char*)) );
242 // allow emulation to notify view when the foreground process
243 // indicates whether or not it is interested in mouse signals
244 connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget ,
245 SLOT(setUsesMouse(bool)) );
247 widget->setUsesMouse( _emulation->programUsesMouse() );
249 widget->setScreenWindow(_emulation->createWindow());
252 //connect view signals and slots
253 QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this,
254 SLOT(onViewSizeChange(int,int)));
256 QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this ,
257 SLOT(viewDestroyed(QObject*)) );
260 void Session::viewDestroyed(QObject* view)
262 TerminalDisplay* display = (TerminalDisplay*)view;
264 Q_ASSERT( _views.contains(display) );
266 removeView(display);
269 void Session::removeView(TerminalDisplay* widget)
271 _views.removeAll(widget);
273 disconnect(widget,0,this,0);
275 if ( _emulation != 0 )
277 // disconnect
278 // - key presses signals from widget
279 // - mouse activity signals from widget
280 // - string sending signals from widget
282 // ... and any other signals connected in addView()
283 disconnect( widget, 0, _emulation, 0);
285 // disconnect state change signals emitted by emulation
286 disconnect( _emulation , 0 , widget , 0);
289 // close the session automatically when the last view is removed
290 if ( _views.count() == 0 )
292 close();
296 QString Session::checkProgram(const QString& program) const
298 // Upon a KPty error, there is no description on what that error was...
299 // Check to see if the given program is executable.
300 QString exec = QFile::encodeName(program);
302 if (exec.isEmpty())
303 return QString();
305 // if 'exec' is not specified, fall back to default shell. if that
306 // is not set then fall back to /bin/sh
307 if ( exec.isEmpty() )
308 exec = qgetenv("SHELL");
309 if ( exec.isEmpty() )
310 exec = "/bin/sh";
312 exec = KRun::binaryName(exec, false);
313 exec = KShell::tildeExpand(exec);
314 QString pexec = KGlobal::dirs()->findExe(exec);
315 if ( pexec.isEmpty() )
317 kError() << i18n("Could not find binary: ") << exec;
318 return QString();
321 return exec;
324 void Session::terminalWarning(const QString& message)
326 static const QByteArray warningText = i18n("Warning: ").toLocal8Bit();
327 QByteArray messageText = message.toLocal8Bit();
329 static const char* redPenOn = "\033[1m\033[31m";
330 static const char* redPenOff = "\033[0m";
332 _emulation->receiveData(redPenOn,strlen(redPenOn));
333 _emulation->receiveData("\n\r\n\r",4);
334 _emulation->receiveData(warningText.constData(),strlen(warningText.constData()));
335 _emulation->receiveData(messageText.constData(),strlen(messageText.constData()));
336 _emulation->receiveData("\n\r\n\r",4);
337 _emulation->receiveData(redPenOff,strlen(redPenOff));
339 void Session::run()
341 //check that everything is in place to run the session
342 if (_program.isEmpty())
344 kDebug() << "Session::run() - program to run not set.";
346 if (_arguments.isEmpty())
348 kDebug() << "Session::run() - no command line arguments specified.";
351 const int CHOICE_COUNT = 3;
352 QString programs[CHOICE_COUNT] = {_program,qgetenv("SHELL"),"/bin/sh"};
353 QString exec;
354 int choice = 0;
355 while (choice < CHOICE_COUNT)
357 exec = checkProgram(programs[choice]);
358 if (exec.isEmpty())
359 choice++;
360 else
361 break;
364 // if a program was specified via setProgram(), but it couldn't be found, print a warning
365 if (choice != 0 && choice < CHOICE_COUNT && !_program.isEmpty())
367 terminalWarning(i18n("Could not find '%1', starting '%2' instead. Please check your profile settings.",_program,exec));
369 // if none of the choices are available, print a warning
370 else if (choice == CHOICE_COUNT)
372 terminalWarning(i18n("Could not find an interactive shell to start."));
373 return;
376 // if no arguments are specified, fall back to program name
377 QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ?
378 QStringList() << exec : _arguments;
380 QString dbusService = QDBusConnection::sessionBus().baseService();
381 if (!_initialWorkingDir.isEmpty())
382 _shellProcess->setWorkingDirectory(_initialWorkingDir);
383 else
384 _shellProcess->setWorkingDirectory(QDir::homePath());
386 _shellProcess->setFlowControlEnabled(_flowControl);
387 _shellProcess->setErase(_emulation->eraseChar());
389 // this is not strictly accurate use of the COLORFGBG variable. This does not
390 // tell the terminal exactly which colors are being used, but instead approximates
391 // the color scheme as "black on white" or "white on black" depending on whether
392 // the background color is deemed dark or not
393 QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15";
395 int result = _shellProcess->start(exec,
396 arguments,
397 _environment << backgroundColorHint,
398 windowId(),
399 _addToUtmp,
400 dbusService,
401 (QLatin1String("/Sessions/") +
402 QString::number(_sessionId)));
404 if (result < 0)
406 terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec, arguments.join(" ")));
407 return;
410 _shellProcess->setWriteable(false); // We are reachable via kwrited.
412 emit started();
415 void Session::setUserTitle( int what, const QString &caption )
417 //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle )
418 bool modified = false;
420 if ((what == IconNameAndWindowTitle) || (what == WindowTitle))
422 if ( _userTitle != caption ) {
423 _userTitle = caption;
424 modified = true;
428 if ((what == IconNameAndWindowTitle) || (what == IconName))
430 if ( _iconText != caption ) {
431 _iconText = caption;
432 modified = true;
436 if (what == TextColor || what == BackgroundColor)
438 QString colorString = caption.section(';',0,0);
439 QColor color = QColor(colorString);
440 if (color.isValid())
442 if (what == TextColor)
443 emit changeForegroundColorRequest(color);
444 else
445 emit changeBackgroundColorRequest(color);
449 if (what == SessionName)
451 if ( _nameTitle != caption ) {
452 setTitle(Session::NameRole,caption);
453 return;
457 if (what == 31)
459 QString cwd=caption;
460 cwd=cwd.replace( QRegExp("^~"), QDir::homePath() );
461 emit openUrlRequest(cwd);
464 // change icon via \033]32;Icon\007
465 if (what == 32)
467 if ( _iconName != caption ) {
468 _iconName = caption;
470 modified = true;
474 if (what == ProfileChange)
476 emit profileChangeCommandReceived(caption);
477 return;
480 if ( modified )
481 emit titleChanged();
484 QString Session::userTitle() const
486 return _userTitle;
488 void Session::setTabTitleFormat(TabTitleContext context , const QString& format)
490 if ( context == LocalTabTitle )
491 _localTabTitleFormat = format;
492 else if ( context == RemoteTabTitle )
493 _remoteTabTitleFormat = format;
495 QString Session::tabTitleFormat(TabTitleContext context) const
497 if ( context == LocalTabTitle )
498 return _localTabTitleFormat;
499 else if ( context == RemoteTabTitle )
500 return _remoteTabTitleFormat;
502 return QString();
505 void Session::monitorTimerDone()
507 //FIXME: The idea here is that the notification popup will appear to tell the user than output from
508 //the terminal has stopped and the popup will disappear when the user activates the session.
510 //This breaks with the addition of multiple views of a session. The popup should disappear
511 //when any of the views of the session becomes active
514 //FIXME: Make message text for this notification and the activity notification more descriptive.
515 if (_monitorSilence) {
516 KNotification::event("Silence", i18n("Silence in session '%1'", _nameTitle), QPixmap(),
517 QApplication::activeWindow(),
518 KNotification::CloseWhenWidgetActivated);
519 emit stateChanged(NOTIFYSILENCE);
521 else
523 emit stateChanged(NOTIFYNORMAL);
526 _notifiedActivity=false;
528 void Session::updateFlowControlState(bool suspended)
530 if (suspended)
532 if (flowControlEnabled())
534 foreach(TerminalDisplay* display,_views)
536 if (display->flowControlWarningEnabled())
537 display->outputSuspended(true);
541 else
543 foreach(TerminalDisplay* display,_views)
544 display->outputSuspended(false);
547 void Session::activityStateSet(int state)
549 if (state==NOTIFYBELL)
551 emit bellRequest( i18n("Bell in session '%1'",_nameTitle) );
553 else if (state==NOTIFYACTIVITY)
555 if (_monitorSilence) {
556 _monitorTimer->start(_silenceSeconds*1000);
559 if ( _monitorActivity ) {
560 //FIXME: See comments in Session::monitorTimerDone()
561 if (!_notifiedActivity) {
562 KNotification::event("Activity", i18n("Activity in session '%1'", _nameTitle), QPixmap(),
563 QApplication::activeWindow(),
564 KNotification::CloseWhenWidgetActivated);
565 _notifiedActivity=true;
570 if ( state==NOTIFYACTIVITY && !_monitorActivity )
571 state = NOTIFYNORMAL;
572 if ( state==NOTIFYSILENCE && !_monitorSilence )
573 state = NOTIFYNORMAL;
575 emit stateChanged(state);
578 void Session::onViewSizeChange(int /*height*/, int /*width*/)
580 updateTerminalSize();
583 void Session::updateTerminalSize()
585 QListIterator<TerminalDisplay*> viewIter(_views);
587 int minLines = -1;
588 int minColumns = -1;
590 // minimum number of lines and columns that views require for
591 // their size to be taken into consideration ( to avoid problems
592 // with new view widgets which haven't yet been set to their correct size )
593 const int VIEW_LINES_THRESHOLD = 2;
594 const int VIEW_COLUMNS_THRESHOLD = 2;
596 //select largest number of lines and columns that will fit in all visible views
597 while ( viewIter.hasNext() )
599 TerminalDisplay* view = viewIter.next();
600 if ( view->isHidden() == false &&
601 view->lines() >= VIEW_LINES_THRESHOLD &&
602 view->columns() >= VIEW_COLUMNS_THRESHOLD )
604 minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() );
605 minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() );
609 // backend emulation must have a _terminal of at least 1 column x 1 line in size
610 if ( minLines > 0 && minColumns > 0 )
612 _emulation->setImageSize( minLines , minColumns );
615 void Session::updateWindowSize(int lines, int columns)
617 Q_ASSERT(lines > 0 && columns > 0);
618 _shellProcess->setWindowSize(lines,columns);
620 void Session::refresh()
622 // attempt to get the shell process to redraw the display
624 // this requires the program running in the shell
625 // to cooperate by sending an update in response to
626 // a window size change
628 // the window size is changed twice, first made slightly larger and then
629 // resized back to its normal size so that there is actually a change
630 // in the window size (some shells do nothing if the
631 // new and old sizes are the same)
633 // if there is a more 'correct' way to do this, please
634 // send an email with method or patches to konsole-devel@kde.org
636 const QSize existingSize = _shellProcess->windowSize();
637 _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1);
638 _shellProcess->setWindowSize(existingSize.height(),existingSize.width());
641 bool Session::kill(int signal)
643 int result = ::kill(_shellProcess->pid(),signal);
645 if ( result == 0 )
647 _shellProcess->waitForFinished();
648 return true;
650 else
651 return false;
654 void Session::close()
656 _autoClose = true;
657 _wantedClose = true;
659 if (!isRunning() || !kill(SIGHUP))
661 if (isRunning())
663 kDebug() << "Process" << _shellProcess->pid() << "did not respond to SIGHUP";
665 // close the pty and wait to see if the process finishes. If it does,
666 // the done() slot will have been called so we can return. Otherwise,
667 // emit the finished() signal regardless
668 _shellProcess->pty()->close();
669 if (_shellProcess->waitForFinished(3000))
670 return;
672 kWarning() << "Unable to kill process" << _shellProcess->pid();
675 // Forced close.
676 QTimer::singleShot(1, this, SIGNAL(finished()));
680 void Session::sendText(const QString &text) const
682 _emulation->sendText(text);
685 Session::~Session()
687 if (_foregroundProcessInfo)
688 delete _foregroundProcessInfo;
689 if (_sessionProcessInfo)
690 delete _sessionProcessInfo;
691 delete _emulation;
692 delete _shellProcess;
693 delete _zmodemProc;
696 void Session::done(int exitStatus)
698 if (!_autoClose)
700 _userTitle = i18n("Finished");
701 emit titleChanged();
702 return;
705 QString message;
706 if (!_wantedClose || exitStatus != 0)
708 if (_shellProcess->exitStatus() == QProcess::NormalExit)
709 message = i18n("Program '%1' exited with status %2.", _program, exitStatus);
710 else
711 message = i18n("Program '%1' crashed.", _program);
713 //FIXME: See comments in Session::monitorTimerDone()
714 KNotification::event("Finished", message , QPixmap(),
715 QApplication::activeWindow(),
716 KNotification::CloseWhenWidgetActivated);
719 if ( !_wantedClose && _shellProcess->exitStatus() != QProcess::NormalExit )
720 terminalWarning(message);
721 else
722 emit finished();
725 Emulation* Session::emulation() const
727 return _emulation;
730 QString Session::keyBindings() const
732 return _emulation->keyBindings();
735 QStringList Session::environment() const
737 return _environment;
740 void Session::setEnvironment(const QStringList& environment)
742 _environment = environment;
745 int Session::sessionId() const
747 return _sessionId;
750 void Session::setKeyBindings(const QString &id)
752 _emulation->setKeyBindings(id);
755 void Session::setTitle(TitleRole role , const QString& newTitle)
757 if ( title(role) != newTitle )
759 if ( role == NameRole )
760 _nameTitle = newTitle;
761 else if ( role == DisplayedTitleRole )
762 _displayTitle = newTitle;
764 emit titleChanged();
768 QString Session::title(TitleRole role) const
770 if ( role == NameRole )
771 return _nameTitle;
772 else if ( role == DisplayedTitleRole )
773 return _displayTitle;
774 else
775 return QString();
778 ProcessInfo* Session::getProcessInfo()
780 ProcessInfo* process;
782 if (isChildActive())
783 process = _foregroundProcessInfo;
784 else
786 updateSessionProcessInfo();
787 process = _sessionProcessInfo;
790 return process;
793 void Session::updateSessionProcessInfo()
795 Q_ASSERT(_shellProcess);
796 if (!_sessionProcessInfo)
797 _sessionProcessInfo = ProcessInfo::newInstance(processId());
798 _sessionProcessInfo->update();
801 bool Session::updateForegroundProcessInfo()
803 bool valid = (_foregroundProcessInfo != 0);
805 // has foreground process changed?
806 Q_ASSERT(_shellProcess);
807 int pid = _shellProcess->foregroundProcessGroup();
808 if (pid != _foregroundPid)
810 if (valid)
811 delete _foregroundProcessInfo;
812 _foregroundProcessInfo = ProcessInfo::newInstance(pid);
813 _foregroundPid = pid;
814 valid = true;
817 if (valid)
819 _foregroundProcessInfo->update();
820 valid = _foregroundProcessInfo->isValid();
823 return valid;
826 QString Session::getDynamicTitle()
828 // update current directory from process
829 ProcessInfo* process = updateWorkingDirectory();
831 // format tab titles using process info
832 bool ok = false;
833 QString title;
834 if ( process->name(&ok) == "ssh" && ok )
836 SSHProcessInfo sshInfo(*process);
837 title = sshInfo.format(tabTitleFormat(Session::RemoteTabTitle));
839 else
840 title = process->format(tabTitleFormat(Session::LocalTabTitle));
842 return title;
845 KUrl Session::getUrl()
847 QString path;
849 updateSessionProcessInfo();
850 if (_sessionProcessInfo->isValid())
852 bool ok = false;
854 // check if foreground process is bookmark-able
855 if (isChildActive())
857 // for remote connections, save the user and host
858 // bright ideas to get the directory at the other end are welcome :)
859 if (_foregroundProcessInfo->name(&ok) == "ssh" && ok)
861 SSHProcessInfo sshInfo(*_foregroundProcessInfo);
862 path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host();
864 else
866 path = _foregroundProcessInfo->currentDir(&ok);
867 if (!ok)
868 path.clear();
871 else // otherwise use the current working directory of the shell process
873 path = _sessionProcessInfo->currentDir(&ok);
874 if (!ok)
875 path.clear();
879 return KUrl(path);
882 void Session::setIconName(const QString& iconName)
884 if ( iconName != _iconName )
886 _iconName = iconName;
887 emit titleChanged();
891 void Session::setIconText(const QString& iconText)
893 _iconText = iconText;
896 QString Session::iconName() const
898 return _iconName;
901 QString Session::iconText() const
903 return _iconText;
906 void Session::setHistoryType(const HistoryType &hType)
908 _emulation->setHistory(hType);
911 const HistoryType& Session::historyType() const
913 return _emulation->history();
916 void Session::clearHistory()
918 _emulation->clearHistory();
921 QStringList Session::arguments() const
923 return _arguments;
926 QString Session::program() const
928 return _program;
931 // unused currently
932 bool Session::isMonitorActivity() const { return _monitorActivity; }
933 // unused currently
934 bool Session::isMonitorSilence() const { return _monitorSilence; }
936 void Session::setMonitorActivity(bool _monitor)
938 _monitorActivity=_monitor;
939 _notifiedActivity=false;
941 activityStateSet(NOTIFYNORMAL);
944 void Session::setMonitorSilence(bool _monitor)
946 if (_monitorSilence==_monitor)
947 return;
949 _monitorSilence=_monitor;
950 if (_monitorSilence)
952 _monitorTimer->start(_silenceSeconds*1000);
954 else
955 _monitorTimer->stop();
957 activityStateSet(NOTIFYNORMAL);
960 void Session::setMonitorSilenceSeconds(int seconds)
962 _silenceSeconds=seconds;
963 if (_monitorSilence) {
964 _monitorTimer->start(_silenceSeconds*1000);
968 void Session::setAddToUtmp(bool set)
970 _addToUtmp = set;
973 void Session::setFlowControlEnabled(bool enabled)
975 _flowControl = enabled;
977 if (_shellProcess)
978 _shellProcess->setFlowControlEnabled(_flowControl);
980 emit flowControlEnabledChanged(enabled);
982 bool Session::flowControlEnabled() const
984 if (_shellProcess)
985 return _shellProcess->flowControlEnabled();
986 else
987 return _flowControl;
989 void Session::fireZModemDetected()
991 if (!_zmodemBusy)
993 QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
994 _zmodemBusy = true;
998 void Session::cancelZModem()
1000 _shellProcess->sendData("\030\030\030\030", 4); // Abort
1001 _zmodemBusy = false;
1004 void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list)
1006 _zmodemBusy = true;
1007 _zmodemProc = new KProcess();
1008 _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels );
1010 *_zmodemProc << zmodem << "-v" << list;
1012 if (!dir.isEmpty())
1013 _zmodemProc->setWorkingDirectory(dir);
1015 _zmodemProc->start();
1017 connect(_zmodemProc,SIGNAL (readyReadStandardOutput()),
1018 this, SLOT(zmodemReadAndSendBlock()));
1019 connect(_zmodemProc,SIGNAL (readyReadStandardError()),
1020 this, SLOT(zmodemReadStatus()));
1021 connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)),
1022 this, SLOT(zmodemFinished()));
1024 disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
1025 connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) );
1027 _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false,
1028 i18n("ZModem Progress"));
1030 connect(_zmodemProgress, SIGNAL(user1Clicked()),
1031 this, SLOT(zmodemDone()));
1033 _zmodemProgress->show();
1036 void Session::zmodemReadAndSendBlock()
1038 _zmodemProc->setReadChannel( QProcess::StandardOutput );
1039 QByteArray data = _zmodemProc->readAll();
1041 if ( data.count() == 0 )
1042 return;
1044 _shellProcess->sendData(data.constData(),data.count());
1047 void Session::zmodemReadStatus()
1049 _zmodemProc->setReadChannel( QProcess::StandardError );
1050 QByteArray msg = _zmodemProc->readAll();
1051 while(!msg.isEmpty())
1053 int i = msg.indexOf('\015');
1054 int j = msg.indexOf('\012');
1055 QByteArray txt;
1056 if ((i != -1) && ((j == -1) || (i < j)))
1058 msg = msg.mid(i+1);
1060 else if (j != -1)
1062 txt = msg.left(j);
1063 msg = msg.mid(j+1);
1065 else
1067 txt = msg;
1068 msg.truncate(0);
1070 if (!txt.isEmpty())
1071 _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt));
1075 void Session::zmodemRcvBlock(const char *data, int len)
1077 QByteArray ba( data, len );
1079 _zmodemProc->write( ba );
1082 void Session::zmodemFinished()
1084 if (_zmodemProc)
1086 delete _zmodemProc;
1087 _zmodemProc = 0;
1088 _zmodemBusy = false;
1090 disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) );
1091 connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
1093 _shellProcess->sendData("\030\030\030\030", 4); // Abort
1094 _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back
1095 _zmodemProgress->transferDone();
1099 void Session::onReceiveBlock( const char* buf, int len )
1101 _emulation->receiveData( buf, len );
1102 emit receivedData( QString::fromLatin1( buf, len ) );
1105 QSize Session::size()
1107 return _emulation->imageSize();
1110 void Session::setSize(const QSize& size)
1112 if ((size.width() <= 1) || (size.height() <= 1))
1113 return;
1115 emit resizeRequest(size);
1117 int Session::processId() const
1119 return _shellProcess->pid();
1122 bool Session::isChildActive()
1124 // foreground process info is always updated after this
1125 return updateForegroundProcessInfo() && (processId() != _foregroundPid);
1128 QString Session::childName()
1130 QString name;
1132 if (updateForegroundProcessInfo())
1134 bool ok = false;
1135 name = _foregroundProcessInfo->name(&ok);
1136 if (!ok)
1137 name.clear();
1140 return name;
1143 void Session::saveSession(KConfigGroup& group)
1145 group.writePathEntry("WorkingDir", currentWorkingDirectory());
1146 group.writeEntry("LocalTab", tabTitleFormat(LocalTabTitle));
1147 group.writeEntry("RemoteTab", tabTitleFormat(RemoteTabTitle));
1150 void Session::restoreSession(KConfigGroup& group)
1152 QString value;
1154 value = group.readPathEntry("WorkingDir", QString());
1155 if (!value.isEmpty()) setInitialWorkingDirectory(value);
1156 value = group.readEntry("LocalTab");
1157 if (!value.isEmpty()) setTabTitleFormat(LocalTabTitle, value);
1158 value = group.readEntry("RemoteTab");
1159 if (!value.isEmpty()) setTabTitleFormat(RemoteTabTitle, value);
1162 SessionGroup::SessionGroup(QObject* parent)
1163 : QObject(parent), _masterMode(0)
1166 SessionGroup::~SessionGroup()
1168 // disconnect all
1169 connectAll(false);
1171 int SessionGroup::masterMode() const { return _masterMode; }
1172 QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); }
1173 bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; }
1175 void SessionGroup::addSession(Session* session)
1177 connect(session,SIGNAL(finished()),this,SLOT(sessionFinished()));
1179 _sessions.insert(session,false);
1181 QListIterator<Session*> masterIter(masters());
1183 while ( masterIter.hasNext() )
1184 connectPair(masterIter.next(),session);
1186 void SessionGroup::removeSession(Session* session)
1188 disconnect(session,SIGNAL(finished()),this,SLOT(sessionFinished()));
1190 setMasterStatus(session,false);
1192 QListIterator<Session*> masterIter(masters());
1194 while ( masterIter.hasNext() )
1195 disconnectPair(masterIter.next(),session);
1197 _sessions.remove(session);
1199 void SessionGroup::sessionFinished()
1201 Session* session = qobject_cast<Session*>(sender());
1202 Q_ASSERT(session);
1203 removeSession(session);
1205 void SessionGroup::setMasterMode(int mode)
1207 _masterMode = mode;
1209 connectAll(false);
1210 connectAll(true);
1212 QList<Session*> SessionGroup::masters() const
1214 return _sessions.keys(true);
1216 void SessionGroup::connectAll(bool connect)
1218 QListIterator<Session*> masterIter(masters());
1220 while ( masterIter.hasNext() )
1222 Session* master = masterIter.next();
1224 QListIterator<Session*> otherIter(_sessions.keys());
1225 while ( otherIter.hasNext() )
1227 Session* other = otherIter.next();
1229 if ( other != master )
1231 if ( connect )
1232 connectPair(master,other);
1233 else
1234 disconnectPair(master,other);
1239 void SessionGroup::setMasterStatus(Session* session , bool master)
1241 bool wasMaster = _sessions[session];
1242 _sessions[session] = master;
1244 if ( ( !wasMaster && !master )
1245 || ( wasMaster && master ) )
1246 return;
1248 QListIterator<Session*> iter(_sessions.keys());
1249 while ( iter.hasNext() )
1251 Session* other = iter.next();
1253 if ( other != session )
1255 if ( master )
1256 connectPair(session,other);
1257 else
1258 disconnectPair(session,other);
1262 void SessionGroup::connectPair(Session* master , Session* other)
1264 if ( _masterMode & CopyInputToAll )
1266 connect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
1267 SLOT(sendString(const char*,int)) );
1270 void SessionGroup::disconnectPair(Session* master , Session* other)
1272 if ( _masterMode & CopyInputToAll )
1274 disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
1275 SLOT(sendString(const char*,int)) );
1279 #include "Session.moc"
1282 Local Variables:
1283 mode: c++
1284 c-file-style: "stroustrup"
1285 indent-tabs-mode: nil
1286 tab-width: 4
1287 End: