2
This file is part of Konsole
4
Copyright (C) 2006-2007 by Robert Knight <robertknight@gmail.com>
5
Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
7
Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
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>
42
#include "TerminalDisplay.h"
43
#include "ShellCommand.h"
44
#include "Vt102Emulation.h"
46
using namespace Konsole;
48
int Session::lastSessionId = 0;
53
, _monitorActivity(false)
54
, _monitorSilence(false)
55
, _notifiedActivity(false)
59
, _addToUtmp(false) // disabled by default because of a bug encountered on certain systems
60
// which caused Konsole to hang when closing a tab and then opening a new
61
// one. A 'QProcess destroyed while still running' warning was being
62
// printed to the terminal. Likely a problem in KPty::logout()
63
// or KPty::login() which uses a QProcess to start /usr/bin/utempter
65
, _fullScripting(false)
67
// , _zmodemBusy(false)
69
// , _zmodemProgress(0)
70
, _hasDarkBackground(false)
72
//prepare DBus communication
73
// new SessionAdaptor(this);
74
_sessionId = ++lastSessionId;
75
// QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
77
//create teletype for I/O with shell process
78
_shellProcess = new Pty();
80
//create emulation backend
81
_emulation = new Vt102Emulation();
83
connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ),
84
this, SLOT( setUserTitle( int, const QString & ) ) );
85
connect( _emulation, SIGNAL( stateSet(int) ),
86
this, SLOT( activityStateSet(int) ) );
87
// connect( _emulation, SIGNAL( zmodemDetected() ), this ,
88
// SLOT( fireZModemDetected() ) );
89
connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ),
90
this, SIGNAL( changeTabTextColorRequest( int ) ) );
91
connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString&)),
92
this, SIGNAL( profileChangeCommandReceived(const QString&)) );
94
// connect( _emulation,SIGNAL(imageSizeChanged(int,int)) , this ,
95
// SLOT(onEmulationSizeChange(int,int)) );
97
//connect teletype to emulation backend
98
_shellProcess->setUtf8Mode(_emulation->utf8());
100
connect( _shellProcess,SIGNAL(receivedData(const char*,int)),this,
101
SLOT(onReceiveBlock(const char*,int)) );
102
connect( _emulation,SIGNAL(sendData(const char*,int)),_shellProcess,
103
SLOT(sendData(const char*,int)) );
104
connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) );
105
connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) );
108
connect( _shellProcess,SIGNAL(done(int)), this, SLOT(done(int)) );
110
//setup timer for monitoring session activity
111
_monitorTimer = new QTimer(this);
112
_monitorTimer->setSingleShot(true);
113
connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone()));
116
WId Session::windowId() const
118
// Returns a window ID for this session which is used
119
// to set the WINDOWID environment variable in the shell
122
// Sessions can have multiple views or no views, which means
123
// that a single ID is not always going to be accurate.
125
// If there are no views, the window ID is just 0. If
126
// there are multiple views, then the window ID for the
127
// top-level window which contains the first view is
130
if ( _views.count() == 0 )
134
QWidget* window = _views.first();
138
while ( window->parentWidget() != 0 )
139
window = window->parentWidget();
141
return window->winId();
145
void Session::setDarkBackground(bool darkBackground)
147
_hasDarkBackground = darkBackground;
149
bool Session::hasDarkBackground() const
151
return _hasDarkBackground;
153
bool Session::isRunning() const
155
return _shellProcess->isRunning();
158
void Session::setCodec(QTextCodec* codec)
160
emulation()->setCodec(codec);
163
void Session::setProgram(const QString& program)
165
_program = ShellCommand::expand(program);
167
void Session::setInitialWorkingDirectory(const QString& dir)
169
_initialWorkingDir = ShellCommand::expand(dir);
171
void Session::setArguments(const QStringList& arguments)
173
_arguments = ShellCommand::expand(arguments);
176
QList<TerminalDisplay*> Session::views() const
181
void Session::addView(TerminalDisplay* widget)
183
Q_ASSERT( !_views.contains(widget) );
185
_views.append(widget);
187
if ( _emulation != 0 )
189
// connect emulation - view signals and slots
190
connect( widget , SIGNAL(keyPressedSignal(QKeyEvent*)) , _emulation ,
191
SLOT(sendKeyEvent(QKeyEvent*)) );
192
connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation ,
193
SLOT(sendMouseEvent(int,int,int,int)) );
194
connect( widget , SIGNAL(sendStringToEmu(const char*)) , _emulation ,
195
SLOT(sendString(const char*)) );
197
// allow emulation to notify view when the foreground process
198
// indicates whether or not it is interested in mouse signals
199
connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget ,
200
SLOT(setUsesMouse(bool)) );
202
widget->setUsesMouse( _emulation->programUsesMouse() );
204
widget->setScreenWindow(_emulation->createWindow());
207
//connect view signals and slots
208
QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this,
209
SLOT(onViewSizeChange(int,int)));
211
QObject::connect( widget ,SIGNAL(destroyed(QObject*)) , this ,
212
SLOT(viewDestroyed(QObject*)) );
214
QObject::connect(this, SIGNAL(finished()), widget, SLOT(close()));
218
void Session::viewDestroyed(QObject* view)
220
TerminalDisplay* display = (TerminalDisplay*)view;
222
Q_ASSERT( _views.contains(display) );
227
void Session::removeView(TerminalDisplay* widget)
229
_views.removeAll(widget);
231
disconnect(widget,0,this,0);
233
if ( _emulation != 0 )
236
// - key presses signals from widget
237
// - mouse activity signals from widget
238
// - string sending signals from widget
240
// ... and any other signals connected in addView()
241
disconnect( widget, 0, _emulation, 0);
243
// disconnect state change signals emitted by emulation
244
disconnect( _emulation , 0 , widget , 0);
247
// close the session automatically when the last view is removed
248
if ( _views.count() == 0 )
256
//check that everything is in place to run the session
257
if (_program.isEmpty())
258
qDebug() << "Session::run() - program to run not set.";
259
if (_arguments.isEmpty())
260
qDebug() << "Session::run() - no command line arguments specified.";
262
// Upon a KPty error, there is no description on what that error was...
263
// Check to see if the given program is executable.
264
QString exec = QFile::encodeName(_program);
266
// if 'exec' is not specified, fall back to default shell. if that
267
// is not set then fall back to /bin/sh
268
if ( exec.isEmpty() )
269
exec = getenv("SHELL");
270
if ( exec.isEmpty() )
273
// if no arguments are specified, fall back to shell
274
QStringList arguments = _arguments.join(QChar(' ')).isEmpty() ?
275
QStringList() << exec : _arguments;
276
QString pexec = exec;
278
if ( pexec.isEmpty() ) {
279
qDebug()<<"can not execute "<<exec<<endl;
280
QTimer::singleShot(1, this, SIGNAL(finished()));
284
// QString cwd_save = QDir::currentPath();
285
QString cwd = QDir::currentPath();
286
if (!_initialWorkingDir.isEmpty())
287
_shellProcess->setWorkingDirectory(_initialWorkingDir);
289
_shellProcess->setWorkingDirectory(cwd);
290
// _shellProcess->setWorkingDirectory(QDir::homePath());
292
_shellProcess->setXonXoff(_flowControl);
293
_shellProcess->setErase(_emulation->getErase());
295
// this is not strictly accurate use of the COLORFGBG variable. This does not
296
// tell the terminal exactly which colors are being used, but instead approximates
297
// the color scheme as "black on white" or "white on black" depending on whether
298
// the background color is deemed dark or not
299
QString backgroundColorHint = _hasDarkBackground ? "COLORFGBG=15;0" : "COLORFGBG=0;15";
301
int result = _shellProcess->start(QFile::encodeName(_program),
303
_environment << backgroundColorHint,
312
_shellProcess->setWriteable(false); // We are reachable via kwrited.
317
void Session::setUserTitle( int what, const QString &caption )
319
//set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle )
320
bool modified = false;
322
// (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only _nameTitle
323
if ((what == 0) || (what == 2))
325
if ( _userTitle != caption ) {
326
_userTitle = caption;
331
if ((what == 0) || (what == 1))
333
if ( _iconText != caption ) {
341
QString colorString = caption.section(';',0,0);
342
qDebug() << __FILE__ << __LINE__ << ": setting background colour to " << colorString;
343
QColor backColor = QColor(colorString);
344
if (backColor.isValid()){// change color via \033]11;Color\007
345
if (backColor != _modifiedBackground)
347
_modifiedBackground = backColor;
349
// bail out here until the code to connect the terminal display
350
// to the changeBackgroundColor() signal has been written
351
// and tested - just so we don't forget to do this.
354
emit changeBackgroundColorRequest(backColor);
361
if ( _nameTitle != caption ) {
362
setTitle(Session::NameRole,caption);
370
cwd=cwd.replace( QRegExp("^~"), QDir::homePath() );
371
emit openUrlRequest(cwd);
374
// change icon via \033]32;Icon\007
377
if ( _iconName != caption ) {
386
emit profileChangeCommandReceived(caption);
394
QString Session::userTitle() const
398
void Session::setTabTitleFormat(TabTitleContext context , const QString& format)
400
if ( context == LocalTabTitle )
401
_localTabTitleFormat = format;
402
else if ( context == RemoteTabTitle )
403
_remoteTabTitleFormat = format;
405
QString Session::tabTitleFormat(TabTitleContext context) const
407
if ( context == LocalTabTitle )
408
return _localTabTitleFormat;
409
else if ( context == RemoteTabTitle )
410
return _remoteTabTitleFormat;
415
void Session::monitorTimerDone()
417
//FIXME: The idea here is that the notification popup will appear to tell the user than output from
418
//the terminal has stopped and the popup will disappear when the user activates the session.
420
//This breaks with the addition of multiple views of a session. The popup should disappear
421
//when any of the views of the session becomes active
424
//FIXME: Make message text for this notification and the activity notification more descriptive.
425
if (_monitorSilence) {
426
// KNotification::event("Silence", ("Silence in session '%1'", _nameTitle), QPixmap(),
427
// QApplication::activeWindow(),
428
// KNotification::CloseWhenWidgetActivated);
429
emit stateChanged(NOTIFYSILENCE);
433
emit stateChanged(NOTIFYNORMAL);
436
_notifiedActivity=false;
439
void Session::activityStateSet(int state)
441
if (state==NOTIFYBELL)
443
QString s; s.sprintf("Bell in session '%s'",_nameTitle.toAscii().data());
445
emit bellRequest( s );
447
else if (state==NOTIFYACTIVITY)
449
if (_monitorSilence) {
450
_monitorTimer->start(_silenceSeconds*1000);
453
if ( _monitorActivity ) {
454
//FIXME: See comments in Session::monitorTimerDone()
455
if (!_notifiedActivity) {
456
// KNotification::event("Activity", ("Activity in session '%1'", _nameTitle), QPixmap(),
457
// QApplication::activeWindow(),
458
// KNotification::CloseWhenWidgetActivated);
459
_notifiedActivity=true;
464
if ( state==NOTIFYACTIVITY && !_monitorActivity )
465
state = NOTIFYNORMAL;
466
if ( state==NOTIFYSILENCE && !_monitorSilence )
467
state = NOTIFYNORMAL;
469
emit stateChanged(state);
472
void Session::onViewSizeChange(int /*height*/, int /*width*/)
474
updateTerminalSize();
476
void Session::onEmulationSizeChange(int lines , int columns)
478
setSize( QSize(lines,columns) );
481
void Session::updateTerminalSize()
483
QListIterator<TerminalDisplay*> viewIter(_views);
488
// minimum number of lines and columns that views require for
489
// their size to be taken into consideration ( to avoid problems
490
// with new view widgets which haven't yet been set to their correct size )
491
const int VIEW_LINES_THRESHOLD = 2;
492
const int VIEW_COLUMNS_THRESHOLD = 2;
494
//select largest number of lines and columns that will fit in all visible views
495
while ( viewIter.hasNext() )
497
TerminalDisplay* view = viewIter.next();
498
if ( view->isHidden() == false &&
499
view->lines() >= VIEW_LINES_THRESHOLD &&
500
view->columns() >= VIEW_COLUMNS_THRESHOLD )
502
minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() );
503
minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() );
507
// backend emulation must have a _terminal of at least 1 column x 1 line in size
508
if ( minLines > 0 && minColumns > 0 )
510
_emulation->setImageSize( minLines , minColumns );
511
_shellProcess->setWindowSize( minLines , minColumns );
515
void Session::refresh()
517
// attempt to get the shell process to redraw the display
519
// this requires the program running in the shell
520
// to cooperate by sending an update in response to
521
// a window size change
523
// the window size is changed twice, first made slightly larger and then
524
// resized back to its normal size so that there is actually a change
525
// in the window size (some shells do nothing if the
526
// new and old sizes are the same)
528
// if there is a more 'correct' way to do this, please
529
// send an email with method or patches to konsole-devel@kde.org
531
const QSize existingSize = _shellProcess->windowSize();
532
_shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1);
533
_shellProcess->setWindowSize(existingSize.height(),existingSize.width());
536
bool Session::sendSignal(int signal)
538
return _shellProcess->kill(signal);
541
void Session::close()
545
if (!_shellProcess->isRunning() || !sendSignal(SIGHUP))
548
QTimer::singleShot(1, this, SIGNAL(finished()));
552
void Session::sendText(const QString &text) const
554
_emulation->sendText(text);
560
delete _shellProcess;
561
// delete _zmodemProc;
564
void Session::setProfileKey(const QString& key)
567
emit profileChanged(key);
569
QString Session::profileKey() const { return _profileKey; }
571
void Session::done(int exitStatus)
575
_userTitle = ("<Finished>");
579
if (!_wantedClose && (exitStatus || _shellProcess->signalled()))
583
if (_shellProcess->normalExit())
584
message.sprintf ("Session '%s' exited with status %d.", _nameTitle.toAscii().data(), exitStatus);
585
else if (_shellProcess->signalled())
587
if (_shellProcess->coreDumped())
590
message.sprintf("Session '%s' exited with signal %d and dumped core.", _nameTitle.toAscii().data(), _shellProcess->exitSignal());
593
message.sprintf("Session '%s' exited with signal %d.", _nameTitle.toAscii().data(), _shellProcess->exitSignal());
597
message.sprintf ("Session '%s' exited unexpectedly.", _nameTitle.toAscii().data());
599
//FIXME: See comments in Session::monitorTimerDone()
600
// KNotification::event("Finished", message , QPixmap(),
601
// QApplication::activeWindow(),
602
// KNotification::CloseWhenWidgetActivated);
607
Emulation* Session::emulation() const
612
QString Session::keyBindings() const
614
return _emulation->keyBindings();
617
QStringList Session::environment() const
622
void Session::setEnvironment(const QStringList& environment)
624
_environment = environment;
627
int Session::sessionId() const
632
void Session::setKeyBindings(const QString &id)
634
_emulation->setKeyBindings(id);
637
void Session::setTitle(TitleRole role , const QString& newTitle)
639
if ( title(role) != newTitle )
641
if ( role == NameRole )
642
_nameTitle = newTitle;
643
else if ( role == DisplayedTitleRole )
644
_displayTitle = newTitle;
650
QString Session::title(TitleRole role) const
652
if ( role == NameRole )
654
else if ( role == DisplayedTitleRole )
655
return _displayTitle;
660
void Session::setIconName(const QString& iconName)
662
if ( iconName != _iconName )
664
_iconName = iconName;
669
void Session::setIconText(const QString& iconText)
671
_iconText = iconText;
672
//kDebug(1211)<<"Session setIconText " << _iconText;
675
QString Session::iconName() const
680
QString Session::iconText() const
685
void Session::setHistoryType(const HistoryType &hType)
687
_emulation->setHistory(hType);
690
const HistoryType& Session::historyType() const
692
return _emulation->history();
695
void Session::clearHistory()
697
_emulation->clearHistory();
700
QStringList Session::arguments() const
705
QString Session::program() const
711
bool Session::isMonitorActivity() const { return _monitorActivity; }
713
bool Session::isMonitorSilence() const { return _monitorSilence; }
715
void Session::setMonitorActivity(bool _monitor)
717
_monitorActivity=_monitor;
718
_notifiedActivity=false;
720
activityStateSet(NOTIFYNORMAL);
723
void Session::setMonitorSilence(bool _monitor)
725
if (_monitorSilence==_monitor)
728
_monitorSilence=_monitor;
731
_monitorTimer->start(_silenceSeconds*1000);
734
_monitorTimer->stop();
736
activityStateSet(NOTIFYNORMAL);
739
void Session::setMonitorSilenceSeconds(int seconds)
741
_silenceSeconds=seconds;
742
if (_monitorSilence) {
743
_monitorTimer->start(_silenceSeconds*1000);
747
void Session::setAddToUtmp(bool set)
752
void Session::setFlowControlEnabled(bool enabled)
754
if (_flowControl == enabled)
757
_flowControl = enabled;
760
_shellProcess->setXonXoff(_flowControl);
762
emit flowControlEnabledChanged(enabled);
764
bool Session::flowControlEnabled() const
768
//void Session::fireZModemDetected()
772
// QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
773
// _zmodemBusy = true;
777
//void Session::cancelZModem()
779
// _shellProcess->sendData("\030\030\030\030", 4); // Abort
780
// _zmodemBusy = false;
783
//void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list)
785
// _zmodemBusy = true;
786
// _zmodemProc = new KProcess();
787
// _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels );
789
// *_zmodemProc << zmodem << "-v" << list;
791
// if (!dir.isEmpty())
792
// _zmodemProc->setWorkingDirectory(dir);
794
// _zmodemProc->start();
796
// connect(_zmodemProc,SIGNAL (readyReadStandardOutput()),
797
// this, SLOT(zmodemReadAndSendBlock()));
798
// connect(_zmodemProc,SIGNAL (readyReadStandardError()),
799
// this, SLOT(zmodemReadStatus()));
800
// connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)),
801
// this, SLOT(zmodemFinished()));
803
// disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
804
// connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) );
806
// _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false,
807
// i18n("ZModem Progress"));
809
// connect(_zmodemProgress, SIGNAL(user1Clicked()),
810
// this, SLOT(zmodemDone()));
812
// _zmodemProgress->show();
815
/*void Session::zmodemReadAndSendBlock()
817
_zmodemProc->setReadChannel( QProcess::StandardOutput );
818
QByteArray data = _zmodemProc->readAll();
820
if ( data.count() == 0 )
823
_shellProcess->sendData(data.constData(),data.count());
827
void Session::zmodemReadStatus()
829
_zmodemProc->setReadChannel( QProcess::StandardError );
830
QByteArray msg = _zmodemProc->readAll();
831
while(!msg.isEmpty())
833
int i = msg.indexOf('\015');
834
int j = msg.indexOf('\012');
836
if ((i != -1) && ((j == -1) || (i < j)))
851
_zmodemProgress->addProgressText(QString::fromLocal8Bit(txt));
856
void Session::zmodemRcvBlock(const char *data, int len)
858
QByteArray ba( data, len );
860
_zmodemProc->write( ba );
864
void Session::zmodemFinished()
872
disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) );
873
connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
875
_shellProcess->sendData("\030\030\030\030", 4); // Abort
876
_shellProcess->sendData("\001\013\n", 3); // Try to get prompt back
877
_zmodemProgress->transferDone();
881
void Session::onReceiveBlock( const char* buf, int len )
883
_emulation->receiveData( buf, len );
884
emit receivedData( QString::fromLatin1( buf, len ) );
887
QSize Session::size()
889
return _emulation->imageSize();
892
void Session::setSize(const QSize& size)
894
if ((size.width() <= 1) || (size.height() <= 1))
897
emit resizeRequest(size);
899
int Session::foregroundProcessId() const
901
return _shellProcess->foregroundProcessGroup();
903
int Session::processId() const
905
return _shellProcess->pid();
908
SessionGroup::SessionGroup()
912
SessionGroup::~SessionGroup()
917
int SessionGroup::masterMode() const { return _masterMode; }
918
QList<Session*> SessionGroup::sessions() const { return _sessions.keys(); }
919
bool SessionGroup::masterStatus(Session* session) const { return _sessions[session]; }
921
void SessionGroup::addSession(Session* session)
923
_sessions.insert(session,false);
925
QListIterator<Session*> masterIter(masters());
927
while ( masterIter.hasNext() )
928
connectPair(masterIter.next(),session);
930
void SessionGroup::removeSession(Session* session)
932
setMasterStatus(session,false);
934
QListIterator<Session*> masterIter(masters());
936
while ( masterIter.hasNext() )
937
disconnectPair(masterIter.next(),session);
939
_sessions.remove(session);
941
void SessionGroup::setMasterMode(int mode)
948
QList<Session*> SessionGroup::masters() const
950
return _sessions.keys(true);
952
void SessionGroup::connectAll(bool connect)
954
QListIterator<Session*> masterIter(masters());
956
while ( masterIter.hasNext() )
958
Session* master = masterIter.next();
960
QListIterator<Session*> otherIter(_sessions.keys());
961
while ( otherIter.hasNext() )
963
Session* other = otherIter.next();
965
if ( other != master )
968
connectPair(master,other);
970
disconnectPair(master,other);
975
void SessionGroup::setMasterStatus(Session* session, bool master) {
976
bool wasMaster = _sessions[session];
977
_sessions[session] = master;
979
if ((!wasMaster && !master)
980
|| (wasMaster && master)) {
984
QListIterator<Session*> iter(_sessions.keys());
985
while (iter.hasNext()) {
986
Session* other = iter.next();
988
if (other != session) {
990
connectPair(session, other);
992
disconnectPair(session, other);
998
void SessionGroup::connectPair(Session* master , Session* other)
1000
// qDebug() << k_funcinfo;
1002
if ( _masterMode & CopyInputToAll )
1004
qDebug() << "Connection session " << master->nameTitle() << "to" << other->nameTitle();
1006
connect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
1007
SLOT(sendString(const char*,int)) );
1010
void SessionGroup::disconnectPair(Session* master , Session* other)
1012
// qDebug() << k_funcinfo;
1014
if ( _masterMode & CopyInputToAll )
1016
qDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle();
1018
disconnect( master->emulation() , SIGNAL(sendData(const char*,int)) , other->emulation() ,
1019
SLOT(sendString(const char*,int)) );
1023
//#include "moc_Session.cpp"