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
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>
44 #include <KMessageBox>
45 #include <KNotification>
49 #include <KStandardDirs>
54 #include <config-konsole.h>
55 #include <sessionadaptor.h>
57 #include "ProcessInfo.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
) :
72 , _monitorActivity(false)
73 , _monitorSilence(false)
74 , _notifiedActivity(false)
80 , _fullScripting(false)
82 , _sessionProcessInfo(0)
83 , _foregroundProcessInfo(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
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.";
128 delete _shellProcess
;
131 _shellProcess
= new Pty();
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
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
162 if ( _views
.count() == 0 )
166 QWidget
* window
= _views
.first();
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();
221 QList
<TerminalDisplay
*> Session::views() const
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
) );
269 void Session::removeView(TerminalDisplay
* widget
)
271 _views
.removeAll(widget
);
273 disconnect(widget
,0,this,0);
275 if ( _emulation
!= 0 )
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 )
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
);
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() )
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
;
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
));
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"};
355 while (choice
< CHOICE_COUNT
)
357 exec
= checkProgram(programs
[choice
]);
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."));
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
);
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
,
397 _environment
<< backgroundColorHint
,
401 (QLatin1String("/Sessions/") +
402 QString::number(_sessionId
)));
406 terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec
, arguments
.join(" ")));
410 _shellProcess
->setWriteable(false); // We are reachable via kwrited.
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
;
428 if ((what
== IconNameAndWindowTitle
) || (what
== IconName
))
430 if ( _iconText
!= caption
) {
436 if (what
== TextColor
|| what
== BackgroundColor
)
438 QString colorString
= caption
.section(';',0,0);
439 QColor color
= QColor(colorString
);
442 if (what
== TextColor
)
443 emit
changeForegroundColorRequest(color
);
445 emit
changeBackgroundColorRequest(color
);
449 if (what
== SessionName
)
451 if ( _nameTitle
!= caption
) {
452 setTitle(Session::NameRole
,caption
);
460 cwd
=cwd
.replace( QRegExp("^~"), QDir::homePath() );
461 emit
openUrlRequest(cwd
);
464 // change icon via \033]32;Icon\007
467 if ( _iconName
!= caption
) {
474 if (what
== ProfileChange
)
476 emit
profileChangeCommandReceived(caption
);
484 QString
Session::userTitle() const
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
;
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
);
523 emit
stateChanged(NOTIFYNORMAL
);
526 _notifiedActivity
=false;
528 void Session::updateFlowControlState(bool suspended
)
532 if (flowControlEnabled())
534 foreach(TerminalDisplay
* display
,_views
)
536 if (display
->flowControlWarningEnabled())
537 display
->outputSuspended(true);
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
);
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
);
647 _shellProcess
->waitForFinished();
654 void Session::close()
659 if (!isRunning() || !kill(SIGHUP
))
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))
672 kWarning() << "Unable to kill process" << _shellProcess
->pid();
676 QTimer::singleShot(1, this, SIGNAL(finished()));
680 void Session::sendText(const QString
&text
) const
682 _emulation
->sendText(text
);
687 if (_foregroundProcessInfo
)
688 delete _foregroundProcessInfo
;
689 if (_sessionProcessInfo
)
690 delete _sessionProcessInfo
;
692 delete _shellProcess
;
696 void Session::done(int exitStatus
)
700 _userTitle
= i18n("Finished");
706 if (!_wantedClose
|| exitStatus
!= 0)
708 if (_shellProcess
->exitStatus() == QProcess::NormalExit
)
709 message
= i18n("Program '%1' exited with status %2.", _program
, exitStatus
);
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
);
725 Emulation
* Session::emulation() const
730 QString
Session::keyBindings() const
732 return _emulation
->keyBindings();
735 QStringList
Session::environment() const
740 void Session::setEnvironment(const QStringList
& environment
)
742 _environment
= environment
;
745 int Session::sessionId() const
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
;
768 QString
Session::title(TitleRole role
) const
770 if ( role
== NameRole
)
772 else if ( role
== DisplayedTitleRole
)
773 return _displayTitle
;
778 ProcessInfo
* Session::getProcessInfo()
780 ProcessInfo
* process
;
783 process
= _foregroundProcessInfo
;
786 updateSessionProcessInfo();
787 process
= _sessionProcessInfo
;
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
)
811 delete _foregroundProcessInfo
;
812 _foregroundProcessInfo
= ProcessInfo::newInstance(pid
);
813 _foregroundPid
= pid
;
819 _foregroundProcessInfo
->update();
820 valid
= _foregroundProcessInfo
->isValid();
826 QString
Session::getDynamicTitle()
828 // update current directory from process
829 ProcessInfo
* process
= updateWorkingDirectory();
831 // format tab titles using process info
834 if ( process
->name(&ok
) == "ssh" && ok
)
836 SSHProcessInfo
sshInfo(*process
);
837 title
= sshInfo
.format(tabTitleFormat(Session::RemoteTabTitle
));
840 title
= process
->format(tabTitleFormat(Session::LocalTabTitle
));
845 KUrl
Session::getUrl()
849 updateSessionProcessInfo();
850 if (_sessionProcessInfo
->isValid())
854 // check if foreground process is bookmark-able
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();
866 path
= _foregroundProcessInfo
->currentDir(&ok
);
871 else // otherwise use the current working directory of the shell process
873 path
= _sessionProcessInfo
->currentDir(&ok
);
882 void Session::setIconName(const QString
& iconName
)
884 if ( iconName
!= _iconName
)
886 _iconName
= iconName
;
891 void Session::setIconText(const QString
& iconText
)
893 _iconText
= iconText
;
896 QString
Session::iconName() const
901 QString
Session::iconText() const
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
926 QString
Session::program() const
932 bool Session::isMonitorActivity() const { return _monitorActivity
; }
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
)
949 _monitorSilence
=_monitor
;
952 _monitorTimer
->start(_silenceSeconds
*1000);
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
)
973 void Session::setFlowControlEnabled(bool enabled
)
975 _flowControl
= enabled
;
978 _shellProcess
->setFlowControlEnabled(_flowControl
);
980 emit
flowControlEnabledChanged(enabled
);
982 bool Session::flowControlEnabled() const
985 return _shellProcess
->flowControlEnabled();
989 void Session::fireZModemDetected()
993 QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
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
)
1007 _zmodemProc
= new KProcess();
1008 _zmodemProc
->setOutputChannelMode( KProcess::SeparateChannels
);
1010 *_zmodemProc
<< zmodem
<< "-v" << list
;
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 )
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');
1056 if ((i
!= -1) && ((j
== -1) || (i
< j
)))
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()
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))
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()
1132 if (updateForegroundProcessInfo())
1135 name
= _foregroundProcessInfo
->name(&ok
);
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
)
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()
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());
1203 removeSession(session
);
1205 void SessionGroup::setMasterMode(int mode
)
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
)
1232 connectPair(master
,other
);
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
) )
1248 QListIterator
<Session
*> iter(_sessions
.keys());
1249 while ( iter
.hasNext() )
1251 Session
* other
= iter
.next();
1253 if ( other
!= session
)
1256 connectPair(session
,other
);
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"
1284 c-file-style: "stroustrup"
1285 indent-tabs-mode: nil