source: ogBrowser-Git/qtermwidget/lib/Session.cpp @ 9004d96

jenkinsmain
Last change on this file since 9004d96 was 64efc22, checked in by Vadim Troshchinskiy <vtroshchinskiy@…>, 19 months ago

Update qtermwidget to modern version

  • Property mode set to 100644
File size: 29.4 KB
Line 
1/*
2    This file is part of Konsole
3
4    Copyright (C) 2006-2007 by Robert Knight <robertknight@gmail.com>
5    Copyright (C) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6
7    Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008
8
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.
13
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.
18
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
22    02110-1301  USA.
23*/
24
25// Own
26#include "Session.h"
27
28// Standard
29#include <cstdlib>
30
31// Qt
32#include <QApplication>
33#include <QByteRef>
34#include <QDir>
35#include <QFile>
36#include <QRegExp>
37#include <QStringList>
38#include <QFile>
39#include <QtDebug>
40
41#include "Pty.h"
42//#include "kptyprocess.h"
43#include "TerminalDisplay.h"
44#include "ShellCommand.h"
45#include "Vt102Emulation.h"
46
47using namespace Konsole;
48
49int Session::lastSessionId = 0;
50
51Session::Session(QObject* parent) :
52    QObject(parent),
53        _shellProcess(nullptr)
54        , _emulation(nullptr)
55        , _monitorActivity(false)
56        , _monitorSilence(false)
57        , _notifiedActivity(false)
58        , _autoClose(true)
59        , _wantedClose(false)
60        , _silenceSeconds(10)
61        , _isTitleChanged(false)
62        , _addToUtmp(false)  // disabled by default because of a bug encountered on certain systems
63        // which caused Konsole to hang when closing a tab and then opening a new
64        // one.  A 'QProcess destroyed while still running' warning was being
65        // printed to the terminal.  Likely a problem in KPty::logout()
66        // or KPty::login() which uses a QProcess to start /usr/bin/utempter
67        , _flowControl(true)
68        , _fullScripting(false)
69        , _sessionId(0)
70//   , _zmodemBusy(false)
71//   , _zmodemProc(0)
72//   , _zmodemProgress(0)
73        , _hasDarkBackground(false)
74{
75    //prepare DBus communication
76//    new SessionAdaptor(this);
77    _sessionId = ++lastSessionId;
78//    QDBusConnection::sessionBus().registerObject(QLatin1String("/Sessions/")+QString::number(_sessionId), this);
79
80    //create teletype for I/O with shell process
81    _shellProcess = new Pty();
82    ptySlaveFd = _shellProcess->pty()->slaveFd();
83
84    //create emulation backend
85    _emulation = new Vt102Emulation();
86
87    connect( _emulation, SIGNAL( titleChanged( int, const QString & ) ),
88             this, SLOT( setUserTitle( int, const QString & ) ) );
89    connect( _emulation, SIGNAL( stateSet(int) ),
90             this, SLOT( activityStateSet(int) ) );
91//    connect( _emulation, SIGNAL( zmodemDetected() ), this ,
92//            SLOT( fireZModemDetected() ) );
93    connect( _emulation, SIGNAL( changeTabTextColorRequest( int ) ),
94             this, SIGNAL( changeTabTextColorRequest( int ) ) );
95    connect( _emulation, SIGNAL(profileChangeCommandReceived(const QString &)),
96             this, SIGNAL( profileChangeCommandReceived(const QString &)) );
97
98    connect(_emulation, SIGNAL(imageResizeRequest(QSize)),
99            this, SLOT(onEmulationSizeChange(QSize)));
100    connect(_emulation, SIGNAL(imageSizeChanged(int, int)),
101            this, SLOT(onViewSizeChange(int, int)));
102    connect(_emulation, &Vt102Emulation::cursorChanged,
103            this, &Session::cursorChanged);
104
105    //connect teletype to emulation backend
106    _shellProcess->setUtf8Mode(_emulation->utf8());
107
108    connect( _shellProcess,SIGNAL(receivedData(const char *,int)),this,
109             SLOT(onReceiveBlock(const char *,int)) );
110    connect( _emulation,SIGNAL(sendData(const char *,int)),_shellProcess,
111             SLOT(sendData(const char *,int)) );
112    connect( _emulation,SIGNAL(lockPtyRequest(bool)),_shellProcess,SLOT(lockPty(bool)) );
113    connect( _emulation,SIGNAL(useUtf8Request(bool)),_shellProcess,SLOT(setUtf8Mode(bool)) );
114
115    connect( _shellProcess,SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(done(int)) );
116    // not in kprocess anymore connect( _shellProcess,SIGNAL(done(int)), this, SLOT(done(int)) );
117
118    //setup timer for monitoring session activity
119    _monitorTimer = new QTimer(this);
120    _monitorTimer->setSingleShot(true);
121    connect(_monitorTimer, SIGNAL(timeout()), this, SLOT(monitorTimerDone()));
122}
123
124WId Session::windowId() const
125{
126    // On Qt5, requesting window IDs breaks QQuickWidget and the likes,
127    // for example, see the following bug reports:
128    // https://bugreports.qt.io/browse/QTBUG-40765
129    // https://codereview.qt-project.org/#/c/94880/
130    return 0;
131}
132
133void Session::setDarkBackground(bool darkBackground)
134{
135    _hasDarkBackground = darkBackground;
136}
137bool Session::hasDarkBackground() const
138{
139    return _hasDarkBackground;
140}
141bool Session::isRunning() const
142{
143    return _shellProcess->state() == QProcess::Running;
144}
145
146void Session::setCodec(QTextCodec * codec) const
147{
148    emulation()->setCodec(codec);
149}
150
151void Session::setProgram(const QString & program)
152{
153    _program = ShellCommand::expand(program);
154}
155void Session::setInitialWorkingDirectory(const QString & dir)
156{
157    _initialWorkingDir = ShellCommand::expand(dir);
158}
159void Session::setArguments(const QStringList & arguments)
160{
161    _arguments = ShellCommand::expand(arguments);
162}
163
164QList<TerminalDisplay *> Session::views() const
165{
166    return _views;
167}
168
169void Session::addView(TerminalDisplay * widget)
170{
171    Q_ASSERT( !_views.contains(widget) );
172
173    _views.append(widget);
174
175    if ( _emulation != nullptr ) {
176        // connect emulation - view signals and slots
177        connect( widget , &TerminalDisplay::keyPressedSignal, _emulation ,
178                 &Emulation::sendKeyEvent);
179        connect( widget , SIGNAL(mouseSignal(int,int,int,int)) , _emulation ,
180                 SLOT(sendMouseEvent(int,int,int,int)) );
181        connect( widget , SIGNAL(sendStringToEmu(const char *)) , _emulation ,
182                 SLOT(sendString(const char *)) );
183
184        // allow emulation to notify view when the foreground process
185        // indicates whether or not it is interested in mouse signals
186        connect( _emulation , SIGNAL(programUsesMouseChanged(bool)) , widget ,
187                 SLOT(setUsesMouse(bool)) );
188
189        widget->setUsesMouse( _emulation->programUsesMouse() );
190
191        connect( _emulation , SIGNAL(programBracketedPasteModeChanged(bool)) ,
192                 widget , SLOT(setBracketedPasteMode(bool)) );
193
194        widget->setBracketedPasteMode(_emulation->programBracketedPasteMode());
195
196        widget->setScreenWindow(_emulation->createWindow());
197    }
198
199    //connect view signals and slots
200    QObject::connect( widget ,SIGNAL(changedContentSizeSignal(int,int)),this,
201                      SLOT(onViewSizeChange(int,int)));
202
203    QObject::connect( widget ,SIGNAL(destroyed(QObject *)) , this ,
204                      SLOT(viewDestroyed(QObject *)) );
205//slot for close
206    QObject::connect(this, SIGNAL(finished()), widget, SLOT(close()));
207
208}
209
210void Session::viewDestroyed(QObject * view)
211{
212    TerminalDisplay * display = (TerminalDisplay *)view;
213
214    Q_ASSERT( _views.contains(display) );
215
216    removeView(display);
217}
218
219void Session::removeView(TerminalDisplay * widget)
220{
221    _views.removeAll(widget);
222
223    disconnect(widget,nullptr,this,nullptr);
224
225    if ( _emulation != nullptr ) {
226        // disconnect
227        //  - key presses signals from widget
228        //  - mouse activity signals from widget
229        //  - string sending signals from widget
230        //
231        //  ... and any other signals connected in addView()
232        disconnect( widget, nullptr, _emulation, nullptr);
233
234        // disconnect state change signals emitted by emulation
235        disconnect( _emulation , nullptr , widget , nullptr);
236    }
237
238    // close the session automatically when the last view is removed
239    if ( _views.count() == 0 ) {
240        close();
241    }
242}
243
244void Session::run()
245{
246    // Upon a KPty error, there is no description on what that error was...
247    // Check to see if the given program is executable.
248
249    /* ok I'm not exactly sure where _program comes from - however it was set to /bin/bash on my system
250     * That's bad for BSD as its /usr/local/bin/bash there - its also bad for arch as its /usr/bin/bash there too!
251     * So i added a check to see if /bin/bash exists - if no then we use $SHELL - if that does not exist either, we fall back to /bin/sh
252     * As far as i know /bin/sh exists on every unix system.. You could also just put some ifdef __FREEBSD__ here but i think these 2 filechecks are worth
253     * their computing time on any system - especially with the problem on arch linux being there too.
254     */
255    QString exec = QString::fromLocal8Bit(QFile::encodeName(_program));
256    // if 'exec' is not specified, fall back to default shell.  if that
257    // is not set then fall back to /bin/sh
258
259    // here we expect full path. If there is no fullpath let's expect it's
260    // a custom shell (eg. python, etc.) available in the PATH.
261    if (exec.startsWith(QLatin1Char('/')) || exec.isEmpty())
262    {
263        const QString defaultShell{QLatin1String("/bin/sh")};
264
265        QFile excheck(exec);
266        if ( exec.isEmpty() || !excheck.exists() ) {
267            exec = QString::fromLocal8Bit(qgetenv("SHELL"));
268        }
269        excheck.setFileName(exec);
270
271        if ( exec.isEmpty() || !excheck.exists() ) {
272            qWarning() << "Neither default shell nor $SHELL is set to a correct path. Fallback to" << defaultShell;
273            exec = defaultShell;
274        }
275    }
276
277    // _arguments sometimes contain ("") so isEmpty()
278    // or count() does not work as expected...
279    QString argsTmp(_arguments.join(QLatin1Char(' ')).trimmed());
280    QStringList arguments;
281    arguments << exec;
282    if (argsTmp.length())
283        arguments << _arguments;
284
285    QString cwd = QDir::currentPath();
286    if (!_initialWorkingDir.isEmpty()) {
287        _shellProcess->setWorkingDirectory(_initialWorkingDir);
288    } else {
289        _shellProcess->setWorkingDirectory(cwd);
290    }
291
292    _shellProcess->setFlowControlEnabled(_flowControl);
293    _shellProcess->setErase(_emulation->eraseChar());
294
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 ? QLatin1String("COLORFGBG=15;0") : QLatin1String("COLORFGBG=0;15");
300
301    /* if we do all the checking if this shell exists then we use it ;)
302     * Dont know about the arguments though.. maybe youll need some more checking im not sure
303     * However this works on Arch and FreeBSD now.
304     */
305    int result = _shellProcess->start(exec,
306                                      arguments,
307                                      _environment << backgroundColorHint,
308                                      windowId(),
309                                      _addToUtmp);
310
311    if (result < 0) {
312        qDebug() << "CRASHED! result: " << result;
313        return;
314    }
315
316    _shellProcess->setWriteable(false);  // We are reachable via kwrited.
317    emit started();
318}
319
320void Session::runEmptyPTY()
321{
322    _shellProcess->setFlowControlEnabled(_flowControl);
323    _shellProcess->setErase(_emulation->eraseChar());
324    _shellProcess->setWriteable(false);
325
326    // disconnect send data from emulator to internal terminal process
327    disconnect( _emulation,SIGNAL(sendData(const char *,int)),
328                _shellProcess, SLOT(sendData(const char *,int)) );
329
330    _shellProcess->setEmptyPTYProperties();
331    emit started();
332}
333
334void Session::setUserTitle( int what, const QString & caption )
335{
336    //set to true if anything is actually changed (eg. old _nameTitle != new _nameTitle )
337    bool modified = false;
338
339    // (btw: what=0 changes _userTitle and icon, what=1 only icon, what=2 only _nameTitle
340    if ((what == 0) || (what == 2)) {
341        _isTitleChanged = true;
342        if ( _userTitle != caption ) {
343            _userTitle = caption;
344            modified = true;
345        }
346    }
347
348    if ((what == 0) || (what == 1)) {
349        _isTitleChanged = true;
350        if ( _iconText != caption ) {
351            _iconText = caption;
352            modified = true;
353        }
354    }
355
356    if (what == 11) {
357        QString colorString = caption.section(QLatin1Char(';'),0,0);
358        //qDebug() << __FILE__ << __LINE__ << ": setting background colour to " << colorString;
359        QColor backColor = QColor(colorString);
360        if (backColor.isValid()) { // change color via \033]11;Color\007
361            if (backColor != _modifiedBackground) {
362                _modifiedBackground = backColor;
363
364                // bail out here until the code to connect the terminal display
365                // to the changeBackgroundColor() signal has been written
366                // and tested - just so we don't forget to do this.
367                Q_ASSERT( 0 );
368
369                emit changeBackgroundColorRequest(backColor);
370            }
371        }
372    }
373
374    if (what == 30) {
375        _isTitleChanged = true;
376        if ( _nameTitle != caption ) {
377            setTitle(Session::NameRole,caption);
378            return;
379        }
380    }
381
382    if (what == 31) {
383        QString cwd=caption;
384        cwd=cwd.replace( QRegExp(QLatin1String("^~")), QDir::homePath() );
385        emit openUrlRequest(cwd);
386    }
387
388    // change icon via \033]32;Icon\007
389    if (what == 32) {
390        _isTitleChanged = true;
391        if ( _iconName != caption ) {
392            _iconName = caption;
393
394            modified = true;
395        }
396    }
397
398    if (what == 50) {
399        emit profileChangeCommandReceived(caption);
400        return;
401    }
402
403    if ( modified ) {
404        emit titleChanged();
405    }
406}
407
408QString Session::userTitle() const
409{
410    return _userTitle;
411}
412void Session::setTabTitleFormat(TabTitleContext context , const QString & format)
413{
414    if ( context == LocalTabTitle ) {
415        _localTabTitleFormat = format;
416    } else if ( context == RemoteTabTitle ) {
417        _remoteTabTitleFormat = format;
418    }
419}
420QString Session::tabTitleFormat(TabTitleContext context) const
421{
422    if ( context == LocalTabTitle ) {
423        return _localTabTitleFormat;
424    } else if ( context == RemoteTabTitle ) {
425        return _remoteTabTitleFormat;
426    }
427
428    return QString();
429}
430
431void Session::monitorTimerDone()
432{
433    //FIXME: The idea here is that the notification popup will appear to tell the user than output from
434    //the terminal has stopped and the popup will disappear when the user activates the session.
435    //
436    //This breaks with the addition of multiple views of a session.  The popup should disappear
437    //when any of the views of the session becomes active
438
439
440    //FIXME: Make message text for this notification and the activity notification more descriptive.
441    if (_monitorSilence) {
442        emit silence();
443        emit stateChanged(NOTIFYSILENCE);
444    } else {
445        emit stateChanged(NOTIFYNORMAL);
446    }
447
448    _notifiedActivity=false;
449}
450
451void Session::activityStateSet(int state)
452{
453    if (state==NOTIFYBELL) {
454        emit bellRequest(tr("Bell in session '%1'").arg(_nameTitle));
455    } else if (state==NOTIFYACTIVITY) {
456        if (_monitorSilence) {
457            _monitorTimer->start(_silenceSeconds*1000);
458        }
459
460        if ( _monitorActivity ) {
461            //FIXME:  See comments in Session::monitorTimerDone()
462            if (!_notifiedActivity) {
463                _notifiedActivity=true;
464                emit activity();
465            }
466        }
467    }
468
469    if ( state==NOTIFYACTIVITY && !_monitorActivity ) {
470        state = NOTIFYNORMAL;
471    }
472    if ( state==NOTIFYSILENCE && !_monitorSilence ) {
473        state = NOTIFYNORMAL;
474    }
475
476    emit stateChanged(state);
477}
478
479void Session::onViewSizeChange(int /*height*/, int /*width*/)
480{
481    updateTerminalSize();
482}
483void Session::onEmulationSizeChange(QSize size)
484{
485    setSize(size);
486}
487
488void Session::updateTerminalSize()
489{
490    QListIterator<TerminalDisplay *> viewIter(_views);
491
492    int minLines = -1;
493    int minColumns = -1;
494
495    // minimum number of lines and columns that views require for
496    // their size to be taken into consideration ( to avoid problems
497    // with new view widgets which haven't yet been set to their correct size )
498    const int VIEW_LINES_THRESHOLD = 2;
499    const int VIEW_COLUMNS_THRESHOLD = 2;
500
501    //select largest number of lines and columns that will fit in all visible views
502    while ( viewIter.hasNext() ) {
503        TerminalDisplay * view = viewIter.next();
504        if ( view->isHidden() == false &&
505                view->lines() >= VIEW_LINES_THRESHOLD &&
506                view->columns() >= VIEW_COLUMNS_THRESHOLD ) {
507            minLines = (minLines == -1) ? view->lines() : qMin( minLines , view->lines() );
508            minColumns = (minColumns == -1) ? view->columns() : qMin( minColumns , view->columns() );
509        }
510    }
511
512    // backend emulation must have a _terminal of at least 1 column x 1 line in size
513    if ( minLines > 0 && minColumns > 0 ) {
514        _emulation->setImageSize( minLines , minColumns );
515        _shellProcess->setWindowSize( minLines , minColumns );
516    }
517}
518
519void Session::refresh()
520{
521    // attempt to get the shell process to redraw the display
522    //
523    // this requires the program running in the shell
524    // to cooperate by sending an update in response to
525    // a window size change
526    //
527    // the window size is changed twice, first made slightly larger and then
528    // resized back to its normal size so that there is actually a change
529    // in the window size (some shells do nothing if the
530    // new and old sizes are the same)
531    //
532    // if there is a more 'correct' way to do this, please
533    // send an email with method or patches to konsole-devel@kde.org
534
535    const QSize existingSize = _shellProcess->windowSize();
536    _shellProcess->setWindowSize(existingSize.height(),existingSize.width()+1);
537    _shellProcess->setWindowSize(existingSize.height(),existingSize.width());
538}
539
540bool Session::sendSignal(int signal)
541{
542    int result = ::kill(static_cast<pid_t>(_shellProcess->processId()),signal);
543
544     if ( result == 0 )
545     {
546         _shellProcess->waitForFinished();
547         return true;
548     }
549     else
550         return false;
551}
552
553void Session::close()
554{
555    _autoClose = true;
556    _wantedClose = true;
557    if (!_shellProcess->isRunning() || !sendSignal(SIGHUP)) {
558        // Forced close.
559        QTimer::singleShot(1, this, SIGNAL(finished()));
560    }
561}
562
563void Session::sendText(const QString & text) const
564{
565    _emulation->sendText(text);
566}
567
568void Session::sendKeyEvent(QKeyEvent* e) const
569{
570    _emulation->sendKeyEvent(e, false);
571}
572
573Session::~Session()
574{
575    delete _emulation;
576    delete _shellProcess;
577//  delete _zmodemProc;
578}
579
580void Session::setProfileKey(const QString & key)
581{
582    _profileKey = key;
583    emit profileChanged(key);
584}
585QString Session::profileKey() const
586{
587    return _profileKey;
588}
589
590void Session::done(int exitStatus)
591{
592    if (!_autoClose) {
593        _userTitle = QString::fromLatin1("This session is done. Finished");
594        emit titleChanged();
595        return;
596    }
597
598    // message is not being used. But in the original kpty.cpp file
599    // (https://cgit.kde.org/kpty.git/) it's part of a notification.
600    // So, we make it translatable, hoping that in the future it will
601    // be used in some kind of notification.
602    QString message;
603    if (!_wantedClose || exitStatus != 0) {
604
605        if (_shellProcess->exitStatus() == QProcess::NormalExit) {
606            message = tr("Session '%1' exited with status %2.").arg(_nameTitle).arg(exitStatus);
607        } else {
608            message = tr("Session '%1' crashed.").arg(_nameTitle);
609        }
610    }
611
612    if ( !_wantedClose && _shellProcess->exitStatus() != QProcess::NormalExit )
613        message = tr("Session '%1' exited unexpectedly.").arg(_nameTitle);
614    else
615        emit finished();
616
617}
618
619Emulation * Session::emulation() const
620{
621    return _emulation;
622}
623
624QString Session::keyBindings() const
625{
626    return _emulation->keyBindings();
627}
628
629QStringList Session::environment() const
630{
631    return _environment;
632}
633
634void Session::setEnvironment(const QStringList & environment)
635{
636    _environment = environment;
637}
638
639int Session::sessionId() const
640{
641    return _sessionId;
642}
643
644void Session::setKeyBindings(const QString & id)
645{
646    _emulation->setKeyBindings(id);
647}
648
649void Session::setTitle(TitleRole role , const QString & newTitle)
650{
651    if ( title(role) != newTitle ) {
652        if ( role == NameRole ) {
653            _nameTitle = newTitle;
654        } else if ( role == DisplayedTitleRole ) {
655            _displayTitle = newTitle;
656        }
657
658        emit titleChanged();
659    }
660}
661
662QString Session::title(TitleRole role) const
663{
664    if ( role == NameRole ) {
665        return _nameTitle;
666    } else if ( role == DisplayedTitleRole ) {
667        return _displayTitle;
668    } else {
669        return QString();
670    }
671}
672
673void Session::setIconName(const QString & iconName)
674{
675    if ( iconName != _iconName ) {
676        _iconName = iconName;
677        emit titleChanged();
678    }
679}
680
681void Session::setIconText(const QString & iconText)
682{
683    _iconText = iconText;
684    //kDebug(1211)<<"Session setIconText " <<  _iconText;
685}
686
687QString Session::iconName() const
688{
689    return _iconName;
690}
691
692QString Session::iconText() const
693{
694    return _iconText;
695}
696
697bool Session::isTitleChanged() const
698{
699    return _isTitleChanged;
700}
701
702void Session::setHistoryType(const HistoryType & hType)
703{
704    _emulation->setHistory(hType);
705}
706
707const HistoryType & Session::historyType() const
708{
709    return _emulation->history();
710}
711
712void Session::clearHistory()
713{
714    _emulation->clearHistory();
715}
716
717QStringList Session::arguments() const
718{
719    return _arguments;
720}
721
722QString Session::program() const
723{
724    return _program;
725}
726
727// unused currently
728bool Session::isMonitorActivity() const
729{
730    return _monitorActivity;
731}
732// unused currently
733bool Session::isMonitorSilence()  const
734{
735    return _monitorSilence;
736}
737
738void Session::setMonitorActivity(bool _monitor)
739{
740    _monitorActivity=_monitor;
741    _notifiedActivity=false;
742
743    activityStateSet(NOTIFYNORMAL);
744}
745
746void Session::setMonitorSilence(bool _monitor)
747{
748    if (_monitorSilence==_monitor) {
749        return;
750    }
751
752    _monitorSilence=_monitor;
753    if (_monitorSilence) {
754        _monitorTimer->start(_silenceSeconds*1000);
755    } else {
756        _monitorTimer->stop();
757    }
758
759    activityStateSet(NOTIFYNORMAL);
760}
761
762void Session::setMonitorSilenceSeconds(int seconds)
763{
764    _silenceSeconds=seconds;
765    if (_monitorSilence) {
766        _monitorTimer->start(_silenceSeconds*1000);
767    }
768}
769
770void Session::setAddToUtmp(bool set)
771{
772    _addToUtmp = set;
773}
774
775void Session::setFlowControlEnabled(bool enabled)
776{
777    if (_flowControl == enabled) {
778        return;
779    }
780
781    _flowControl = enabled;
782
783    if (_shellProcess) {
784        _shellProcess->setFlowControlEnabled(_flowControl);
785    }
786
787    emit flowControlEnabledChanged(enabled);
788}
789bool Session::flowControlEnabled() const
790{
791    return _flowControl;
792}
793//void Session::fireZModemDetected()
794//{
795//  if (!_zmodemBusy)
796//  {
797//    QTimer::singleShot(10, this, SIGNAL(zmodemDetected()));
798//    _zmodemBusy = true;
799//  }
800//}
801
802//void Session::cancelZModem()
803//{
804//  _shellProcess->sendData("\030\030\030\030", 4); // Abort
805//  _zmodemBusy = false;
806//}
807
808//void Session::startZModem(const QString &zmodem, const QString &dir, const QStringList &list)
809//{
810//  _zmodemBusy = true;
811//  _zmodemProc = new KProcess();
812//  _zmodemProc->setOutputChannelMode( KProcess::SeparateChannels );
813//
814//  *_zmodemProc << zmodem << "-v" << list;
815//
816//  if (!dir.isEmpty())
817//     _zmodemProc->setWorkingDirectory(dir);
818//
819//  _zmodemProc->start();
820//
821//  connect(_zmodemProc,SIGNAL (readyReadStandardOutput()),
822//          this, SLOT(zmodemReadAndSendBlock()));
823//  connect(_zmodemProc,SIGNAL (readyReadStandardError()),
824//          this, SLOT(zmodemReadStatus()));
825//  connect(_zmodemProc,SIGNAL (finished(int,QProcess::ExitStatus)),
826//          this, SLOT(zmodemFinished()));
827//
828//  disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
829//  connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(zmodemRcvBlock(const char*,int)) );
830//
831//  _zmodemProgress = new ZModemDialog(QApplication::activeWindow(), false,
832//                                    i18n("ZModem Progress"));
833//
834//  connect(_zmodemProgress, SIGNAL(user1Clicked()),
835//          this, SLOT(zmodemDone()));
836//
837//  _zmodemProgress->show();
838//}
839
840/*void Session::zmodemReadAndSendBlock()
841{
842  _zmodemProc->setReadChannel( QProcess::StandardOutput );
843  QByteArray data = _zmodemProc->readAll();
844
845  if ( data.count() == 0 )
846      return;
847
848  _shellProcess->sendData(data.constData(),data.count());
849}
850*/
851/*
852void Session::zmodemReadStatus()
853{
854  _zmodemProc->setReadChannel( QProcess::StandardError );
855  QByteArray msg = _zmodemProc->readAll();
856  while(!msg.isEmpty())
857  {
858     int i = msg.indexOf('\015');
859     int j = msg.indexOf('\012');
860     QByteArray txt;
861     if ((i != -1) && ((j == -1) || (i < j)))
862     {
863       msg = msg.mid(i+1);
864     }
865     else if (j != -1)
866     {
867       txt = msg.left(j);
868       msg = msg.mid(j+1);
869     }
870     else
871     {
872       txt = msg;
873       msg.truncate(0);
874     }
875     if (!txt.isEmpty())
876       _zmodemProgress->addProgressText(QString::fromLocal8Bit(txt));
877  }
878}
879*/
880/*
881void Session::zmodemRcvBlock(const char *data, int len)
882{
883  QByteArray ba( data, len );
884
885  _zmodemProc->write( ba );
886}
887*/
888/*
889void Session::zmodemFinished()
890{
891  if (_zmodemProc)
892  {
893    delete _zmodemProc;
894    _zmodemProc = 0;
895    _zmodemBusy = false;
896
897    disconnect( _shellProcess,SIGNAL(block_in(const char*,int)), this ,SLOT(zmodemRcvBlock(const char*,int)) );
898    connect( _shellProcess,SIGNAL(block_in(const char*,int)), this, SLOT(onReceiveBlock(const char*,int)) );
899
900    _shellProcess->sendData("\030\030\030\030", 4); // Abort
901    _shellProcess->sendData("\001\013\n", 3); // Try to get prompt back
902    _zmodemProgress->transferDone();
903  }
904}
905*/
906void Session::onReceiveBlock( const char * buf, int len )
907{
908    _emulation->receiveData( buf, len );
909    emit receivedData( QString::fromLatin1( buf, len ) );
910}
911
912QSize Session::size()
913{
914    return _emulation->imageSize();
915}
916
917void Session::setSize(const QSize & size)
918{
919    if ((size.width() <= 1) || (size.height() <= 1)) {
920        return;
921    }
922
923    emit resizeRequest(size);
924}
925int Session::foregroundProcessId() const
926{
927    return _shellProcess->foregroundProcessGroup();
928}
929int Session::processId() const
930{
931    return static_cast<int>(_shellProcess->processId());
932}
933int Session::getPtySlaveFd() const
934{
935    return ptySlaveFd;
936}
937
938SessionGroup::SessionGroup()
939        : _masterMode(0)
940{
941}
942SessionGroup::~SessionGroup()
943{
944    // disconnect all
945    connectAll(false);
946}
947int SessionGroup::masterMode() const
948{
949    return _masterMode;
950}
951QList<Session *> SessionGroup::sessions() const
952{
953    return _sessions.keys();
954}
955bool SessionGroup::masterStatus(Session * session) const
956{
957    return _sessions[session];
958}
959
960void SessionGroup::addSession(Session * session)
961{
962    _sessions.insert(session,false);
963
964    QListIterator<Session *> masterIter(masters());
965
966    while ( masterIter.hasNext() ) {
967        connectPair(masterIter.next(),session);
968    }
969}
970void SessionGroup::removeSession(Session * session)
971{
972    setMasterStatus(session,false);
973
974    QListIterator<Session *> masterIter(masters());
975
976    while ( masterIter.hasNext() ) {
977        disconnectPair(masterIter.next(),session);
978    }
979
980    _sessions.remove(session);
981}
982void SessionGroup::setMasterMode(int mode)
983{
984    _masterMode = mode;
985
986    connectAll(false);
987    connectAll(true);
988}
989QList<Session *> SessionGroup::masters() const
990{
991    return _sessions.keys(true);
992}
993void SessionGroup::connectAll(bool connect)
994{
995    QListIterator<Session *> masterIter(masters());
996
997    while ( masterIter.hasNext() ) {
998        Session * master = masterIter.next();
999
1000        QListIterator<Session *> otherIter(_sessions.keys());
1001        while ( otherIter.hasNext() ) {
1002            Session * other = otherIter.next();
1003
1004            if ( other != master ) {
1005                if ( connect ) {
1006                    connectPair(master,other);
1007                } else {
1008                    disconnectPair(master,other);
1009                }
1010            }
1011        }
1012    }
1013}
1014void SessionGroup::setMasterStatus(Session * session, bool master)
1015{
1016    bool wasMaster = _sessions[session];
1017    _sessions[session] = master;
1018
1019    if (wasMaster == master) {
1020        return;
1021    }
1022
1023    QListIterator<Session *> iter(_sessions.keys());
1024    while (iter.hasNext()) {
1025        Session * other = iter.next();
1026
1027        if (other != session) {
1028            if (master) {
1029                connectPair(session, other);
1030            } else {
1031                disconnectPair(session, other);
1032            }
1033        }
1034    }
1035}
1036
1037void SessionGroup::connectPair(Session * master , Session * other) const
1038{
1039//    qDebug() << k_funcinfo;
1040
1041    if ( _masterMode & CopyInputToAll ) {
1042        qDebug() << "Connection session " << master->nameTitle() << "to" << other->nameTitle();
1043
1044        connect( master->emulation() , SIGNAL(sendData(const char *,int)) , other->emulation() ,
1045                 SLOT(sendString(const char *,int)) );
1046    }
1047}
1048void SessionGroup::disconnectPair(Session * master , Session * other) const
1049{
1050//    qDebug() << k_funcinfo;
1051
1052    if ( _masterMode & CopyInputToAll ) {
1053        qDebug() << "Disconnecting session " << master->nameTitle() << "from" << other->nameTitle();
1054
1055        disconnect( master->emulation() , SIGNAL(sendData(const char *,int)) , other->emulation() ,
1056                    SLOT(sendString(const char *,int)) );
1057    }
1058}
1059
1060//#include "moc_Session.cpp"
Note: See TracBrowser for help on using the repository browser.