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>
6
Copyright 2009 by Thomas Dreibholz <dreibh@iem.uni-due.de>
8
This program is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2 of the License, or
11
(at your option) any later version.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program; if not, write to the Free Software
20
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
33
#include <QtGui/QApplication>
34
#include <QtCore/QByteRef>
35
#include <QtCore/QDir>
36
#include <QtCore/QFile>
37
#include <QtCore/QRegExp>
38
#include <QtCore/QStringList>
39
#include <QtDBus/QtDBus>
40
#include <QtCore/QDate>
45
#include <KMessageBox>
46
#include <KNotification>
50
#include <KStandardDirs>
55
#include <config-konsole.h>
56
#include <sessionadaptor.h>
58
#include "ProcessInfo.h"
60
#include "TerminalDisplay.h"
61
#include "ShellCommand.h"
62
#include "Vt102Emulation.h"
63
#include "ZModemDialog.h"
65
using namespace Konsole;
67
int Session::lastSessionId = 0;
69
// HACK This is copied out of QUuid::createUuid with reseeding forced.
70
// Required because color schemes repeatedly seed the RNG...
71
// ...with a constant.
74
static const int intbits = sizeof(int)*8;
75
static int randbits = 0;
79
do { ++randbits; } while ((max=max>>1));
82
qsrand(uint(QDateTime::currentDateTime().toTime_t()));
83
qrand(); // Skip first
86
uint *data = &(result.data1);
87
int chunks = 16 / sizeof(uint);
90
for (int filled = 0; filled < intbits; filled += randbits)
91
randNumber |= qrand()<<filled;
92
*(data+chunks) = randNumber;
95
result.data4[0] = (result.data4[0] & 0x3F) | 0x80; // UV_DCE
96
result.data3 = (result.data3 & 0x0FFF) | 0x4000; // UV_Random
101
Session::Session(QObject* parent) :
105
, _monitorActivity(false)
106
, _monitorSilence(false)
107
, _notifiedActivity(false)
109
, _wantedClose(false)
110
, _silenceSeconds(10)
113
, _fullScripting(false)
115
, _sessionProcessInfo(0)
116
, _foregroundProcessInfo(0)
121
, _hasDarkBackground(false)
123
_uniqueIdentifier = createUuid();
125
//prepare DBus communication
126
new SessionAdaptor(this);
127
_sessionId = ++lastSessionId;
128
QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
130
//create emulation backend
131
_emulation = new Vt102Emulation();
133
connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ),
134
this, SLOT( setUserTitle( int, const QString & ) ) );
135
connect( _emulation, SIGNAL( stateSet(int) ),
136
this, SLOT( activityStateSet(int) ) );
137
connect( _emulation, SIGNAL( zmodemDetected() ), this ,
138
SLOT( fireZModemDetected() ) );
139
connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ),
140
this, SIGNAL( changeTabTextColorRequest( int ) ) );
141
connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)),
142
this, SIGNAL( profileChangeCommandReceived(const QString&)) );
143
connect( _emulation, SIGNAL(flowControlKeyPressed(bool)) , this,
144
SLOT(updateFlowControlState(bool)) );
146
//create new teletype for I/O with shell process
149
//setup timer for monitoring session activity
150
_monitorTimer = new QTimer(this);
151
_monitorTimer->setSingleShot(true);
152
connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone()));
155
void Session::openTeletype(int fd)
157
if (_shellProcess && isRunning())
159
kWarning() << "Attempted to open teletype in a running session.";
163
delete _shellProcess;
166
_shellProcess = new Pty();
168
_shellProcess = new Pty(fd);
170
_shellProcess->setUtf8Mode(_emulation->utf8());
172
//connect teletype to emulation backend
173
connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this,
174
SLOT(onReceiveBlock(const char*,int)) );
175
connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess,
176
SLOT(sendData(const char*,int)) );
177
connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) );
178
connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) );
179
connect( _shellProcess,SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(done(int)) );
180
connect( _emulation,SIGNAL(imageSizeChanged(int,int)),this,SLOT(updateWindowSize(int,int)) );
183
WId Session::windowId() const
185
// Returns a window ID for this session which is used
186
// to set the WINDOWID environment variable in the shell
189
// Sessions can have multiple views or no views, which means
190
// that a single ID is not always going to be accurate.
192
// If there are no views, the window ID is just 0. If
193
// there are multiple views, then the window ID for the
194
// top-level window which contains the first view is
197
if ( _views.count() == 0 )
201
QWidget* window = _views.first();
205
while ( window->parentWidget() != 0 )
206
window = window->parentWidget();
208
return window->winId();
212
void Session::setDarkBackground(bool darkBackground)
214
_hasDarkBackground = darkBackground;
216
bool Session::hasDarkBackground() const
218
return _hasDarkBackground;
220
bool Session::isRunning() const
222
return _shellProcess->state() == QProcess::Running;
225
void Session::setCodec(QTextCodec* codec)
227
emulation()->setCodec(codec);
230
bool Session::setCodec(QByteArray name)
232
QTextCodec *codec = QTextCodec::codecForName(name);
240
QByteArray Session::codec()
242
return _emulation->codec()->name();
245
void Session::setProgram(const QString& program)
247
_program = ShellCommand::expand(program);
249
void Session::setInitialWorkingDirectory(const QString& dir)
251
_initialWorkingDir = KShell::tildeExpand(ShellCommand::expand(dir));
253
void Session::setArguments(const QStringList& arguments)
255
_arguments = ShellCommand::expand(arguments);
258
QString Session::currentWorkingDirectory()
260
// only returned cached value
261
if (_currentWorkingDir.isEmpty()) updateWorkingDirectory();
262
return _currentWorkingDir;
264
ProcessInfo* Session::updateWorkingDirectory()
266
ProcessInfo *process = getProcessInfo();
267
_currentWorkingDir = process->validCurrentDir();
271
QList<TerminalDisplay*> Session::views() const
276
void Session::addView(TerminalDisplay* widget)
278
Q_ASSERT( !_views.contains(widget) );
280
_views.append(widget);
282
if ( _emulation != 0 )
284
// connect emulation - view signals and slots
285
connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation ,
286
SLOT(sendKeyEvent(QKeyEvent*)) );
287
connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation ,
288
SLOT(sendMouseEvent(int,int,int,int)) );
289
connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation ,
290
SLOT(sendString(const char*)) );
292
// allow emulation to notify view when the foreground process
293
// indicates whether or not it is interested in mouse signals
294
connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget ,
295
SLOT(setUsesMouse(bool)) );
297
widget->setUsesMouse( _emulation->programUsesMouse() );
299
widget->setScreenWindow(_emulation->createWindow());
302
//connect view signals and slots
303
QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this,
304
SLOT(onViewSizeChange(int,int)));
306
QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this ,
307
SLOT(viewDestroyed(QObject*)) );
310
void Session::viewDestroyed(QObject* view)
312
TerminalDisplay* display = (TerminalDisplay*)view;
314
Q_ASSERT( _views.contains(display) );
319
void Session::removeView(TerminalDisplay* widget)
321
_views.removeAll(widget);
323
disconnect(widget,0,this,0);
325
if ( _emulation != 0 )
328
// - key presses signals from widget
329
// - mouse activity signals from widget
330
// - string sending signals from widget
332
// ... and any other signals connected in addView()
333
disconnect( widget, 0, _emulation, 0);
335
// disconnect state change signals emitted by emulation
336
disconnect( _emulation , 0 , widget , 0);
339
// close the session automatically when the last view is removed
340
if ( _views.count() == 0 )
346
QString Session::checkProgram(const QString& program) const
348
// Upon a KPty error, there is no description on what that error was...
349
// Check to see if the given program is executable.
350
QString exec = QFile::encodeName(program);
355
// if 'exec' is not specified, fall back to default shell. if that
356
// is not set then fall back to /bin/sh
357
if ( exec.isEmpty() )
358
exec = qgetenv("SHELL");
359
if ( exec.isEmpty() )
362
exec = KRun::binaryName(exec, false);
363
exec = KShell::tildeExpand(exec);
364
QString pexec = KGlobal::dirs()->findExe(exec);
365
if ( pexec.isEmpty() )
367
kError() << i18n("Could not find binary: ") << exec;
374
void Session::terminalWarning(const QString& message)
376
static const QByteArray warningText = i18nc("@info:shell Alert the user with red color text", "Warning: ").toLocal8Bit();
377
QByteArray messageText = message.toLocal8Bit();
379
static const char redPenOn[] = "\033[1m\033[31m";
380
static const char redPenOff[] = "\033[0m";
382
_emulation->receiveData(redPenOn,strlen(redPenOn));
383
_emulation->receiveData("\n\r\n\r",4);
384
_emulation->receiveData(warningText.constData(),strlen(warningText.constData()));
385
_emulation->receiveData(messageText.constData(),strlen(messageText.constData()));
386
_emulation->receiveData("\n\r\n\r",4);
387
_emulation->receiveData(redPenOff,strlen(redPenOff));
390
QString Session::shellSessionId() const
392
QString friendlyUuid(_uniqueIdentifier.toString());
393
friendlyUuid.remove('-').remove('{').remove('}');
400
//check that everything is in place to run the session
401
if (_program.isEmpty())
403
kWarning() << "Session::run() - program to run not set.";
405
if (_arguments.isEmpty())
407
kWarning() << "Session::run() - no command line arguments specified.";
409
if (_uniqueIdentifier.isNull())
411
_uniqueIdentifier = createUuid();
414
const int CHOICE_COUNT = 3;
415
QString programs[CHOICE_COUNT] = {_program,qgetenv("SHELL"),"/bin/sh"};
418
while (choice < CHOICE_COUNT)
420
exec = checkProgram(programs[choice]);
427
// if a program was specified via setProgram(), but it couldn't be found, print a warning
428
if (choice != 0 && choice < CHOICE_COUNT && !_program.isEmpty())
430
terminalWarning(i18n("Could not find '%1', starting '%2' instead. Please check your profile settings.",_program,exec));
432
// if none of the choices are available, print a warning
433
else if (choice == CHOICE_COUNT)
435
terminalWarning(i18n("Could not find an interactive shell to start."));
439
// if no arguments are specified, fall back to program name
440
QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ?
441
QStringList() << exec : _arguments;
443
QString dbusService = QDBusConnection::sessionBus().baseService();
444
if (!_initialWorkingDir.isEmpty())
445
_shellProcess->setWorkingDirectory(_initialWorkingDir);
447
_shellProcess->setWorkingDirectory(QDir::homePath());
449
_shellProcess->setFlowControlEnabled(_flowControl);
450
_shellProcess->setErase(_emulation->eraseChar());
452
// this is not strictly accurate use of the COLORFGBG variable. This does not
453
// tell the terminal exactly which colors are being used, but instead approximates
454
// the color scheme as "black on white" or "white on black" depending on whether
455
// the background color is deemed dark or not
456
QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15";
457
_environment << backgroundColorHint;
458
_environment << QString("SHELL_SESSION_ID=%1").arg(shellSessionId());
460
int result = _shellProcess->start(exec,
466
(QLatin1String("/Sessions/") +
467
QString::number(_sessionId)));
471
terminalWarning(i18n("Could not start program '%1' with arguments '%2'.", exec, arguments.join(" ")));
475
_shellProcess->setWriteable(false); // We are reachable via kwrited.
480
void Session::setUserTitle( int what, const QString &caption )
482
//set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle )
483
bool modified = false;
485
if ((what == IconNameAndWindowTitle) || (what == WindowTitle))
487
if ( _userTitle != caption ) {
488
_userTitle = caption;
493
if ((what == IconNameAndWindowTitle) || (what == IconName))
495
if ( _iconText != caption ) {
501
if (what == TextColor || what == BackgroundColor)
503
QString colorString = caption.section(';',0,0);
504
QColor color = QColor(colorString);
507
if (what == TextColor)
508
emit changeForegroundColorRequest(color);
510
emit changeBackgroundColorRequest(color);
514
if (what == SessionName)
516
if ( _nameTitle != caption ) {
517
setTitle(Session::NameRole,caption);
525
cwd=cwd.replace( QRegExp("^~"), QDir::homePath() );
526
emit openUrlRequest(cwd);
529
// change icon via \033]32;Icon\007
532
if ( _iconName != caption ) {
539
if (what == ProfileChange)
541
emit profileChangeCommandReceived(caption);
549
QString Session::userTitle() const
553
void Session::setTabTitleFormat(TabTitleContext context , const QString& format)
555
if ( context == LocalTabTitle )
556
_localTabTitleFormat = format;
557
else if ( context == RemoteTabTitle )
558
_remoteTabTitleFormat = format;
560
QString Session::tabTitleFormat(TabTitleContext context) const
562
if ( context == LocalTabTitle )
563
return _localTabTitleFormat;
564
else if ( context == RemoteTabTitle )
565
return _remoteTabTitleFormat;
570
void Session::monitorTimerDone()
572
//FIXME: The idea here is that the notification popup will appear to tell the user than output from
573
//the terminal has stopped and the popup will disappear when the user activates the session.
575
//This breaks with the addition of multiple views of a session. The popup should disappear
576
//when any of the views of the session becomes active
579
//FIXME: Make message text for this notification and the activity notification more descriptive.
580
if (_monitorSilence) {
581
KNotification::event("Silence", i18n("Silence in session '%1'", _nameTitle), QPixmap(),
582
QApplication::activeWindow(),
583
KNotification::CloseWhenWidgetActivated);
584
emit stateChanged(NOTIFYSILENCE);
588
emit stateChanged(NOTIFYNORMAL);
591
_notifiedActivity=false;
593
void Session::updateFlowControlState(bool suspended)
597
if (flowControlEnabled())
599
foreach(TerminalDisplay* display,_views)
601
if (display->flowControlWarningEnabled())
602
display->outputSuspended(true);
608
foreach(TerminalDisplay* display,_views)
609
display->outputSuspended(false);
612
void Session::activityStateSet(int state)
614
if (state==NOTIFYBELL)
616
emit bellRequest( i18n("Bell in session '%1'",_nameTitle) );
618
else if (state==NOTIFYACTIVITY)
620
if (_monitorSilence) {
621
_monitorTimer->start(_silenceSeconds*1000);
624
if ( _monitorActivity ) {
625
//FIXME: See comments in Session::monitorTimerDone()
626
if (!_notifiedActivity) {
627
KNotification::event("Activity", i18n("Activity in session '%1'", _nameTitle), QPixmap(),
628
QApplication::activeWindow(),
629
KNotification::CloseWhenWidgetActivated);
630
_notifiedActivity=true;
635
if ( state==NOTIFYACTIVITY && !_monitorActivity )
636
state = NOTIFYNORMAL;
637
if ( state==NOTIFYSILENCE && !_monitorSilence )
638
state = NOTIFYNORMAL;
640
emit stateChanged(state);
643
void Session::onViewSizeChange(int /*height*/, int /*width*/)
645
updateTerminalSize();
648
void Session::updateTerminalSize()
650
QListIterator<TerminalDisplay*> viewIter(_views);
655
// minimum number of lines and columns that views require for
656
// their size to be taken into consideration ( to avoid problems
657
// with new view widgets which haven't yet been set to their correct size )
658
const int VIEW_LINES_THRESHOLD = 2;
659
const int VIEW_COLUMNS_THRESHOLD = 2;
661
//select largest number of lines and columns that will fit in all visible views
662
while ( viewIter.hasNext() )
664
TerminalDisplay* view = viewIter.next();
665
if ( view->isHidden() == false &&
666
view->lines() >= VIEW_LINES_THRESHOLD &&
667
view->columns() >= VIEW_COLUMNS_THRESHOLD )
669
minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() );
670
minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() );
671
view->processFilters();
675
// backend emulation must have a _terminal of at least 1 column x 1 line in size
676
if ( minLines > 0 && minColumns > 0 )
678
_emulation->setImageSize( minLines , minColumns );
681
void Session::updateWindowSize(int lines, int columns)
683
Q_ASSERT(lines > 0 && columns > 0);
684
_shellProcess->setWindowSize(lines,columns);
686
void Session::refresh()
688
// attempt to get the shell process to redraw the display
690
// this requires the program running in the shell
691
// to cooperate by sending an update in response to
692
// a window size change
694
// the window size is changed twice, first made slightly larger and then
695
// resized back to its normal size so that there is actually a change
696
// in the window size (some shells do nothing if the
697
// new and old sizes are the same)
699
// if there is a more 'correct' way to do this, please
700
// send an email with method or patches to konsole-devel@kde.org
702
const QSize existingSize = _shellProcess->windowSize();
703
_shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1);
704
_shellProcess->setWindowSize(existingSize.height(),existingSize.width());
707
bool Session::kill(int signal)
709
int result = ::kill(_shellProcess->pid(),signal);
713
_shellProcess->waitForFinished();
720
void Session::close()
725
if (!isRunning() || !kill(SIGHUP))
729
kWarning() << "Process" << _shellProcess->pid() << "did not respond to SIGHUP";
731
// close the pty and wait to see if the process finishes. If it does,
732
// the done() slot will have been called so we can return. Otherwise,
733
// emit the finished() signal regardless
734
_shellProcess->pty()->close();
735
if (_shellProcess->waitForFinished(3000))
738
kWarning() << "Unable to kill process" << _shellProcess->pid();
742
QTimer::singleShot(1, this, SIGNAL(finished()));
746
void Session::sendText(const QString &text) const
748
_emulation->sendText(text);
751
void Session::sendMouseEvent(int buttons, int column, int line, int eventType)
753
_emulation->sendMouseEvent(buttons, column, line, eventType);
758
if (_foregroundProcessInfo)
759
delete _foregroundProcessInfo;
760
if (_sessionProcessInfo)
761
delete _sessionProcessInfo;
763
delete _shellProcess;
767
void Session::done(int exitStatus)
771
_userTitle = i18nc("@info:shell This session is done", "Finished");
777
if (!_wantedClose || exitStatus != 0)
779
if (_shellProcess->exitStatus() == QProcess::NormalExit)
780
message = i18n("Program '%1' exited with status %2.", _program, exitStatus);
782
message = i18n("Program '%1' crashed.", _program);
784
//FIXME: See comments in Session::monitorTimerDone()
785
KNotification::event("Finished", message , QPixmap(),
786
QApplication::activeWindow(),
787
KNotification::CloseWhenWidgetActivated);
790
if ( !_wantedClose && _shellProcess->exitStatus() != QProcess::NormalExit )
791
terminalWarning(message);
796
Emulation* Session::emulation() const
801
QString Session::keyBindings() const
803
return _emulation->keyBindings();
806
QStringList Session::environment() const
811
void Session::setEnvironment(const QStringList& environment)
813
_environment = environment;
816
int Session::sessionId() const
821
void Session::setKeyBindings(const QString &id)
823
_emulation->setKeyBindings(id);
826
void Session::setTitle(TitleRole role , const QString& newTitle)
828
if ( title(role) != newTitle )
830
if ( role == NameRole )
831
_nameTitle = newTitle;
832
else if ( role == DisplayedTitleRole )
833
_displayTitle = newTitle;
839
QString Session::title(TitleRole role) const
841
if ( role == NameRole )
843
else if ( role == DisplayedTitleRole )
844
return _displayTitle;
849
ProcessInfo* Session::getProcessInfo()
851
ProcessInfo* process;
853
if (isForegroundProcessActive())
854
process = _foregroundProcessInfo;
857
updateSessionProcessInfo();
858
process = _sessionProcessInfo;
864
void Session::updateSessionProcessInfo()
866
Q_ASSERT(_shellProcess);
867
if (!_sessionProcessInfo)
869
_sessionProcessInfo = ProcessInfo::newInstance(processId());
870
_sessionProcessInfo->setUserHomeDir();
872
_sessionProcessInfo->update();
875
bool Session::updateForegroundProcessInfo()
877
bool valid = (_foregroundProcessInfo != 0);
879
// has foreground process changed?
880
Q_ASSERT(_shellProcess);
881
int pid = _shellProcess->foregroundProcessGroup();
882
if (pid != _foregroundPid)
885
delete _foregroundProcessInfo;
886
_foregroundProcessInfo = ProcessInfo::newInstance(pid);
887
_foregroundPid = pid;
893
_foregroundProcessInfo->update();
894
valid = _foregroundProcessInfo->isValid();
900
bool Session::isRemote()
902
ProcessInfo* process = getProcessInfo();
905
return ( process->name(&ok) == "ssh" && ok );
908
QString Session::getDynamicTitle()
910
// update current directory from process
911
ProcessInfo* process = updateWorkingDirectory();
913
// format tab titles using process info
916
if ( process->name(&ok) == "ssh" && ok )
918
SSHProcessInfo sshInfo(*process);
919
title = sshInfo.format(tabTitleFormat(Session::RemoteTabTitle));
922
title = process->format(tabTitleFormat(Session::LocalTabTitle));
927
KUrl Session::getUrl()
931
updateSessionProcessInfo();
932
if (_sessionProcessInfo->isValid())
936
// check if foreground process is bookmark-able
937
if (isForegroundProcessActive())
939
// for remote connections, save the user and host
940
// bright ideas to get the directory at the other end are welcome :)
941
if (_foregroundProcessInfo->name(&ok) == "ssh" && ok)
943
SSHProcessInfo sshInfo(*_foregroundProcessInfo);
944
path = "ssh://" + sshInfo.userName() + '@' + sshInfo.host();
948
path = _foregroundProcessInfo->currentDir(&ok);
953
else // otherwise use the current working directory of the shell process
955
path = _sessionProcessInfo->currentDir(&ok);
964
void Session::setIconName(const QString& iconName)
966
if ( iconName != _iconName )
968
_iconName = iconName;
973
void Session::setIconText(const QString& iconText)
975
_iconText = iconText;
978
QString Session::iconName() const
983
QString Session::iconText() const
988
void Session::setHistoryType(const HistoryType &hType)
990
_emulation->setHistory(hType);
993
const HistoryType& Session::historyType() const
995
return _emulation->history();
998
void Session::clearHistory()
1000
_emulation->clearHistory();
1003
QStringList Session::arguments() const
1008
QString Session::program() const
1014
bool Session::isMonitorActivity() const { return _monitorActivity; }
1016
bool Session::isMonitorSilence() const { return _monitorSilence; }
1018
void Session::setMonitorActivity(bool _monitor)
1020
_monitorActivity=_monitor;
1021
_notifiedActivity=false;
1023
activityStateSet(NOTIFYNORMAL);
1026
void Session::setMonitorSilence(bool _monitor)
1028
if (_monitorSilence==_monitor)
1031
_monitorSilence=_monitor;
1032
if (_monitorSilence)
1034
_monitorTimer->start(_silenceSeconds*1000);
1037
_monitorTimer->stop();
1039
activityStateSet(NOTIFYNORMAL);
1042
void Session::setMonitorSilenceSeconds(int seconds)
1044
_silenceSeconds=seconds;
1045
if (_monitorSilence) {
1046
_monitorTimer->start(_silenceSeconds*1000);
1050
void Session::setAddToUtmp(bool set)
1055
void Session::setFlowControlEnabled(bool enabled)
1057
_flowControl = enabled;
1060
_shellProcess->setFlowControlEnabled(_flowControl);
1061
emit flowControlEnabledChanged(enabled);
1063
bool Session::flowControlEnabled() const
1066
return _shellProcess->flowControlEnabled();
1068
return _flowControl;
1070
void Session::fireZModemDetected()
1074
QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
1079
void Session::cancelZModem()
1081
_shellProcess->sendData("\030\030\030\030", 4); // Abort
1082
_zmodemBusy = false;
1085
void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list)
1088
_zmodemProc = new KProcess();
1089
_zmodemProc->setOutputChannelMode( KProcess::SeparateChannels );
1091
*_zmodemProc << zmodem << "-v" << list;
1094
_zmodemProc->setWorkingDirectory(dir);
1096
connect(_zmodemProc,SIGNAL (readyReadStandardOutput()),
1097
this, SLOT(zmodemReadAndSendBlock()));
1098
connect(_zmodemProc,SIGNAL (readyReadStandardError()),
1099
this, SLOT(zmodemReadStatus()));
1100
connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)),
1101
this, SLOT(zmodemFinished()));
1103
_zmodemProc->start();
1105
disconnect( _shellProcess,SIGNAL(receivedData(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
1106
connect( _shellProcess,SIGNAL(receivedData(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) );
1108
_zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false,
1109
i18n("ZModem Progress"));
1111
connect(_zmodemProgress, SIGNAL(user1Clicked()),
1112
this, SLOT(zmodemFinished()));
1114
_zmodemProgress->show();
1117
void Session::zmodemReadAndSendBlock()
1119
_zmodemProc->setReadChannel( QProcess::StandardOutput );
1120
QByteArray data = _zmodemProc->readAll();
1122
if ( data.count() == 0 )
1125
_shellProcess->sendData(data.constData(),data.count());
1128
void Session::zmodemReadStatus()
1130
_zmodemProc->setReadChannel( QProcess::StandardError );
1131
QByteArray msg = _zmodemProc->readAll();
1132
while(!msg.isEmpty())
1134
int i = msg.indexOf('\015');
1135
int j = msg.indexOf('\012');
1137
if ((i != -1) && ((j == -1) || (i < j)))
1152
_zmodemProgress->addProgressText(QString::fromLocal8Bit(txt));
1156
void Session::zmodemRcvBlock(const char *data, int len)
1158
QByteArray ba( data, len );
1160
_zmodemProc->write( ba );
1163
void Session::zmodemFinished()
1165
/* zmodemFinished() is called by QProcess's finished() and
1166
ZModemDialog's user1Clicked(). Therefore, an invocation by
1167
user1Clicked() will recursively invoke this function again
1168
when the KProcess is deleted! */
1170
KProcess* process = _zmodemProc;
1171
_zmodemProc = 0; // Set _zmodemProc to 0 avoid recursive invocations!
1172
_zmodemBusy = false;
1173
delete process; // Now, the KProcess may be disposed safely.
1175
disconnect( _shellProcess,SIGNAL(receivedData(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) );
1176
connect( _shellProcess,SIGNAL(receivedData(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
1178
_shellProcess->sendData("\030\030\030\030", 4); // Abort
1179
_shellProcess->sendData("\001\013\n", 3); // Try to get prompt back
1180
_zmodemProgress->transferDone();
1184
void Session::onReceiveBlock( const char* buf, int len )
1186
_emulation->receiveData( buf, len );
1187
emit receivedData( QString::fromLatin1( buf, len ) );
1190
QSize Session::size()
1192
return _emulation->imageSize();
1195
void Session::setSize(const QSize& size)
1197
if ((size.width() <= 1) || (size.height() <= 1))
1200
emit resizeRequest(size);
1202
int Session::processId() const
1204
return _shellProcess->pid();
1207
void Session::setTitle(int role , const QString& title)
1211
this->setTitle(Session::NameRole, title);
1214
this->setTitle(Session::DisplayedTitleRole, title);
1219
QString Session::title(int role) const
1223
return this->title(Session::NameRole);
1225
return this->title(Session::DisplayedTitleRole);
1231
void Session::setTabTitleFormat(int context , const QString& format)
1235
this->setTabTitleFormat(Session::LocalTabTitle, format);
1238
this->setTabTitleFormat(Session::RemoteTabTitle, format);
1243
QString Session::tabTitleFormat(int context) const
1247
return this->tabTitleFormat(Session::LocalTabTitle);
1249
return this->tabTitleFormat(Session::RemoteTabTitle);
1255
int Session::foregroundProcessId()
1260
pid = getProcessInfo()->pid(&ok);
1267
bool Session::isForegroundProcessActive()
1269
// foreground process info is always updated after this
1270
return updateForegroundProcessInfo() && (processId() != _foregroundPid);
1273
QString Session::foregroundProcessName()
1277
if (updateForegroundProcessInfo())
1280
name = _foregroundProcessInfo->name(&ok);
1288
void Session::saveSession(KConfigGroup& group)
1290
group.writePathEntry("WorkingDir", currentWorkingDirectory());
1291
group.writeEntry("LocalTab", tabTitleFormat(LocalTabTitle));
1292
group.writeEntry("RemoteTab", tabTitleFormat(RemoteTabTitle));
1293
group.writeEntry("SessionGuid", _uniqueIdentifier.toString());
1294
group.writeEntry("Encoding", QString(codec()));
1297
void Session::restoreSession(KConfigGroup& group)
1301
value = group.readPathEntry("WorkingDir", QString());
1302
if (!value.isEmpty()) setInitialWorkingDirectory(value);
1303
value = group.readEntry("LocalTab");
1304
if (!value.isEmpty()) setTabTitleFormat(LocalTabTitle, value);
1305
value = group.readEntry("RemoteTab");
1306
if (!value.isEmpty()) setTabTitleFormat(RemoteTabTitle, value);
1307
value = group.readEntry("SessionGuid");
1308
if (!value.isEmpty()) _uniqueIdentifier = QUuid(value);
1309
value = group.readEntry("Encoding");
1310
if (!value.isEmpty()) setCodec(value.toUtf8());
1313
SessionGroup::SessionGroup(QObject* parent)
1314
: QObject(parent), _masterMode(0)
1317
SessionGroup::~SessionGroup()
1320
int SessionGroup::masterMode() const { return _masterMode; }
1321
QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); }
1322
bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; }
1324
void SessionGroup::addSession(Session* session)
1326
connect(session,SIGNAL(finished()),this,SLOT(sessionFinished()));
1327
_sessions.insert(session,false);
1329
void SessionGroup::removeSession(Session* session)
1331
disconnect(session,SIGNAL(finished()),this,SLOT(sessionFinished()));
1332
setMasterStatus(session,false);
1333
_sessions.remove(session);
1335
void SessionGroup::sessionFinished()
1337
Session* session = qobject_cast<Session*>(sender());
1339
removeSession(session);
1341
void SessionGroup::setMasterMode(int mode)
1345
QList<Session*> SessionGroup::masters() const
1347
return _sessions.keys(true);
1349
void SessionGroup::setMasterStatus(Session* session , bool master)
1351
const bool wasMaster = _sessions[session];
1353
if (wasMaster == master) {
1354
// No status change -> nothing to do.
1357
_sessions[session] = master;
1360
connect( session->emulation() , SIGNAL(sendData(const char*,int)) , this,
1361
SLOT(forwardData(const char*,int)) );
1364
disconnect( session->emulation() , SIGNAL(sendData(const char*,int)) , this,
1365
SLOT(forwardData(const char*,int)) );
1368
void SessionGroup::forwardData(const char* data, int size)
1370
static bool _inForwardData = false;
1371
if(_inForwardData) { // Avoid recursive calls among session groups!
1372
// A recursive call happens when a master in group A calls forwardData()
1373
// in group B. If one of the destination sessions in group B is also a
1374
// master of a group including the master session of group A, this would
1375
// again call forwardData() in group A, and so on.
1379
_inForwardData = true;
1380
QListIterator<Session*> iter(_sessions.keys());
1381
while(iter.hasNext()) {
1382
Session* other = iter.next();
1383
if(!_sessions[other]) {
1384
other->emulation()->sendString(data, size);
1387
_inForwardData = false;
1390
#include "Session.moc"