~verzegnassi-stefano/+junk/ubuntu-terminal-app-uitk13

« back to all changes in this revision

Viewing changes to src/plugin/qmltermwidget/qtermwidget/lib/Session.cpp

  • Committer: Filippo Scognamiglio
  • Date: 2014-10-25 04:42:31 UTC
  • Revision ID: flscogna@gmail.com-20141025044231-javjhusbqa171127
Initial reboot commit.

Show diffs side-by-side

added added

removed removed

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