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

jenkinsmain
Last change on this file since c0cec9d was fedf2a2, checked in by Vadim Troshchinskiy Shmelev <vtroshchinskiy@…>, 18 months ago

Update Qtermwidget to Qt6 version
Remove build files

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