Screen.cpp 47.1 KB
Newer Older
1
/*
2
3
4
5
   SPDX-FileCopyrightText: 2007-2008 Robert Knight <robert.knight@gmail.com>
   SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>

   SPDX-License-Identifier: GPL-2.0-or-later
6
   */
7

8
9
// Own
#include "Screen.h"
10

11
// Qt
12
#include <QSet>
13
#include <QTextStream>
14

15
16
17
18
// Konsole decoders
#include <PlainTextDecoder.h>
#include <HTMLDecoder.h>

Carlos Alves's avatar
Carlos Alves committed
19
20
21
22
#include "terminalDisplay/TerminalDisplay.h"
#include "session/SessionController.h"
#include "session/Session.h"

23
24
#include "history/HistoryType.h"
#include "history/HistoryScrollNone.h"
25
#include "characters/ExtendedCharTable.h"
26
#include "profile/Profile.h"
27
#include "EscapeSequenceUrlExtractor.h"
28

29
30
using namespace Konsole;

31
32
//Macro to convert x,y position on screen to position within an image.
//
Kurt Hindenburg's avatar
Kurt Hindenburg committed
33
//Originally the image was stored as one large contiguous block of
34
//memory, so a position within the image could be represented as an
35
//offset from the beginning of the block.  For efficiency reasons this
Kurt Hindenburg's avatar
Kurt Hindenburg committed
36
//is no longer the case.
37
//Many internal parts of this class still use this representation for parameters and so on,
38
39
//notably moveImage() and clearImage().
//This macro converts from an X,Y position into an image offset.
40
#ifndef loc
Jekyll Wu's avatar
Jekyll Wu committed
41
#define loc(X,Y) ((Y)*_columns+(X))
42
43
#endif

Jekyll Wu's avatar
Jekyll Wu committed
44
const Character Screen::DefaultChar = Character(' ',
Kurt Hindenburg's avatar
Kurt Hindenburg committed
45
46
47
48
                                      CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR),
                                      CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR),
                                      DEFAULT_RENDITION,
                                      false);
49

Jekyll Wu's avatar
Jekyll Wu committed
50
Screen::Screen(int lines, int columns):
51
    _currentTerminalDisplay(nullptr),
Jekyll Wu's avatar
Jekyll Wu committed
52
53
    _lines(lines),
    _columns(columns),
54
    _screenLines(_lines + 1),
55
    _screenLinesSize(_lines),
56
    _scrolledLines(0),
57
    _lastScrolledRegion(QRect()),
58
    _droppedLines(0),
59
60
    _oldTotalLines(0),
    _isResize(false),
61
    _enableReflowLines(false),
62
    _lineProperties(QVarLengthArray<LineProperty, 64>()),
63
    _history(std::make_unique<HistoryScrollNone>()),
Jekyll Wu's avatar
Jekyll Wu committed
64
65
    _cuX(0),
    _cuY(0),
66
67
    _currentForeground(CharacterColor()),
    _currentBackground(CharacterColor()),
Jekyll Wu's avatar
Jekyll Wu committed
68
69
70
    _currentRendition(DEFAULT_RENDITION),
    _topMargin(0),
    _bottomMargin(0),
71
    _tabStops(QBitArray()),
Jekyll Wu's avatar
Jekyll Wu committed
72
73
74
    _selBegin(0),
    _selTopLeft(0),
    _selBottomRight(0),
Jekyll Wu's avatar
Jekyll Wu committed
75
    _blockSelectionMode(false),
Jekyll Wu's avatar
Jekyll Wu committed
76
77
78
    _effectiveForeground(CharacterColor()),
    _effectiveBackground(CharacterColor()),
    _effectiveRendition(DEFAULT_RENDITION),
79
    _lastPos(-1),
80
    _lastDrawnChar(0),
81
    _escapeSequenceUrlExtractor(std::make_unique<EscapeSequenceUrlExtractor>())
82
{
83
    _escapeSequenceUrlExtractor->setScreen(this);
Jekyll Wu's avatar
Jekyll Wu committed
84
    _lineProperties.resize(_lines + 1);
85
    std::fill(_lineProperties.begin(), _lineProperties.end(), LINE_DEFAULT);
86

87
88
89
    initTabStops();
    clearSelection();
    reset();
90
91
}

Ahmad Samir's avatar
Ahmad Samir committed
92
Screen::~Screen() = default;
93

94
void Screen::cursorUp(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
95
//=CUU
96
{
97
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
98
99
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
100
101
    const int stop = _cuY < _topMargin ? 0 : _topMargin;
    _cuY = qMax(stop, _cuY - n);
102
103
}

104
void Screen::cursorDown(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
105
//=CUD
106
{
107
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
108
109
       n = 1; // Default
    }
110
111
112
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
    }
Jekyll Wu's avatar
Jekyll Wu committed
113
114
    const int stop = _cuY > _bottomMargin ? _lines - 1 : _bottomMargin;
    _cuY = qMin(stop, _cuY + n);
115
116
}

117
void Screen::cursorLeft(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
118
//=CUB
119
{
120
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
121
122
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
123
    _cuX = qMax(0, _cuX - n);
124
125
}

126
127
128
void Screen::cursorNextLine(int n)
//=CNL
{
129
    if (n < 1) {
130
131
        n = 1; // Default
    }
132
133
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
134
    }
135
136
    _cuX = 0;
    _cuY = qMin(_cuY + n, _lines - 1);
137
138
139
140
141
}

void Screen::cursorPreviousLine(int n)
//=CPL
{
142
    if (n < 1) {
143
144
145
        n = 1; // Default
    }
    _cuX = 0;
146
    _cuY = qMax(0, _cuY - n);
147
148
}

149
void Screen::cursorRight(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
150
//=CUF
151
{
152
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
153
154
        n = 1; // Default
    }
155
156
157
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
    }
158
    _cuX = qMin(getScreenLineColumns(_cuY) - 1, _cuX + n);
159
160
}

161
void Screen::setMargins(int top, int bot)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
162
//=STBM
163
{
164
    if (top < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
165
166
        top = 1;      // Default
    }
167
    if (bot < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
168
169
        bot = _lines;  // Default
    }
170
171
    top = top - 1;              // Adjust to internal lineno
    bot = bot - 1;              // Adjust to internal lineno
Jekyll Wu's avatar
Jekyll Wu committed
172
    if (!(0 <= top && top < bot && bot < _lines)) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
173
        //Debug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
174
175
176
177
        return;                   // Default error action: ignore
    }
    _topMargin = top;
    _bottomMargin = bot;
Jekyll Wu's avatar
Jekyll Wu committed
178
179
    _cuX = 0;
    _cuY = getMode(MODE_Origin) ? top : 0;
180
181
}

182
int Screen::topMargin() const
183
{
184
    return _topMargin;
185
}
186
int Screen::bottomMargin() const
187
{
188
    return _bottomMargin;
189
190
}

191
void Screen::index()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
192
//=IND
193
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
194
    if (_cuY == _bottomMargin) {
195
        scrollUp(1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
196
    } else if (_cuY < _lines - 1) {
Jekyll Wu's avatar
Jekyll Wu committed
197
        _cuY += 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
198
    }
199
200
}

201
void Screen::reverseIndex()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
202
//=RI
203
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
204
    if (_cuY == _topMargin) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
205
        scrollDown(_topMargin, 1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
206
    } else if (_cuY > 0) {
Jekyll Wu's avatar
Jekyll Wu committed
207
        _cuY -= 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
208
    }
209
210
}

211
void Screen::nextLine()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
212
//=NEL
213
{
214
215
    toStartOfLine();
    index();
216
217
}

218
void Screen::eraseChars(int n)
219
{
220
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
221
222
        n = 1; // Default
    }
223
224
225
226
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
    }
    const int p = qBound(0, _cuX + n - 1, _columns - 1);
227
    clearImage(loc(_cuX, _cuY), loc(p, _cuY), ' ', false);
228
229
}

230
void Screen::deleteChars(int n)
231
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
232
    Q_ASSERT(n >= 0);
233

234
    // always delete at least one char
235
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
236
        n = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
237
    }
238

239
    // if cursor is beyond the end of the line there is nothing to do
Kurt Hindenburg's avatar
Kurt Hindenburg committed
240
    if (_cuX >= _screenLines[_cuY].count()) {
241
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
242
    }
243

Kurt Hindenburg's avatar
Kurt Hindenburg committed
244
    if (_cuX + n > _screenLines[_cuY].count()) {
Jekyll Wu's avatar
Jekyll Wu committed
245
        n = _screenLines[_cuY].count() - _cuX;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
246
    }
247

Kurt Hindenburg's avatar
Kurt Hindenburg committed
248
    Q_ASSERT(n >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
249
    Q_ASSERT(_cuX + n <= _screenLines[_cuY].count());
250

Jekyll Wu's avatar
Jekyll Wu committed
251
    _screenLines[_cuY].remove(_cuX, n);
252
253
254
255
256
257

    // Append space(s) with current attributes
    Character spaceWithCurrentAttrs(' ', _effectiveForeground,
                                    _effectiveBackground,
                                    _effectiveRendition, false);

Kurt Hindenburg's avatar
Kurt Hindenburg committed
258
    for (int i = 0; i < n; i++) {
259
        _screenLines[_cuY].append(spaceWithCurrentAttrs);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
260
    }
261
262
}

263
void Screen::insertChars(int n)
264
{
265
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
266
267
        n = 1; // Default
    }
268

Kurt Hindenburg's avatar
Kurt Hindenburg committed
269
    if (_screenLines[_cuY].size() < _cuX) {
Jekyll Wu's avatar
Jekyll Wu committed
270
        _screenLines[_cuY].resize(_cuX);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
271
    }
272

Jekyll Wu's avatar
Jekyll Wu committed
273
    _screenLines[_cuY].insert(_cuX, n, Character(' '));
274

275
276
    if (_screenLines[_cuY].count() > getScreenLineColumns(_cuY)) {
        _screenLines[_cuY].resize(getScreenLineColumns(_cuY));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
277
    }
278
279
}

280
281
void Screen::repeatChars(int n)
{
282
    if (n < 1) {
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
        n = 1; // Default
    }

    // From ECMA-48 version 5, section 8.3.103:
    // "If the character preceding REP is a control function or part of a
    // control function, the effect of REP is not defined by this Standard."
    //
    // So, a "normal" program should always use REP immediately after a visible
    // character (those other than escape sequences). So, _lastDrawnChar can be
    // safely used.
    for (int i = 0; i < n; i++) {
        displayCharacter(_lastDrawnChar);
    }
}

298
void Screen::deleteLines(int n)
299
{
300
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
301
302
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
303
    scrollUp(_cuY, n);
304
305
}

306
void Screen::insertLines(int n)
307
{
308
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
309
310
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
311
    scrollDown(_cuY, n);
312
313
}

314
void Screen::setMode(int m)
315
{
316
    _currentModes[m] = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
317
    switch (m) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
318
319
320
321
    case MODE_Origin :
        _cuX = 0;
        _cuY = _topMargin;
        break; //FIXME: home
322
    }
323
324
}

325
void Screen::resetMode(int m)
326
{
327
    _currentModes[m] = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
328
    switch (m) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
329
330
331
332
    case MODE_Origin :
        _cuX = 0;
        _cuY = 0;
        break; //FIXME: home
333
    }
334
335
}

336
void Screen::saveMode(int m)
337
{
Jekyll Wu's avatar
Jekyll Wu committed
338
    _savedModes[m] = _currentModes[m];
339
340
}

341
void Screen::restoreMode(int m)
342
{
Jekyll Wu's avatar
Jekyll Wu committed
343
    _currentModes[m] = _savedModes[m];
344
345
}

Robert Knight's avatar
   
Robert Knight committed
346
bool Screen::getMode(int m) const
347
{
348
    return _currentModes[m] != 0;
349
350
}

351
void Screen::saveCursor()
352
{
Jekyll Wu's avatar
Jekyll Wu committed
353
354
355
356
357
    _savedState.cursorColumn = _cuX;
    _savedState.cursorLine  = _cuY;
    _savedState.rendition = _currentRendition;
    _savedState.foreground = _currentForeground;
    _savedState.background = _currentBackground;
358
359
}

360
void Screen::restoreCursor()
361
{
Jekyll Wu's avatar
Jekyll Wu committed
362
    _cuY     = qMin(_savedState.cursorLine, _lines - 1);
363
    _cuX     = qMin(_savedState.cursorColumn, getScreenLineColumns(_cuY) - 1);
Jekyll Wu's avatar
Jekyll Wu committed
364
365
366
    _currentRendition   = _savedState.rendition;
    _currentForeground   = _savedState.foreground;
    _currentBackground   = _savedState.background;
367
    updateEffectiveRendition();
368
369
}

370
371
372
373
374
375
376
377
378
379
380
381
382
383
int Screen::getOldTotalLines()
{
    return _oldTotalLines;
}

bool Screen::isResize()
{
    if (_isResize) {
        _isResize = false;
        return true;
    }
    return _isResize;
}

Ahmad Samir's avatar
Ahmad Samir committed
384
void Screen::setReflowLines(bool enable)
385
386
387
388
{
    _enableReflowLines = enable;
}

389
390
391
392
/* Note that if you use these debugging functions, it will
   fail to compile on gcc 8.3.1 as of Feb 2021 due to for_each_n().
   See BKO: 432639

Kurt Hindenburg's avatar
Kurt Hindenburg committed
393
// Debugging auxiliar functions to show what is written in screen or history
Carlos Alves's avatar
Carlos Alves committed
394
395
void toDebug(const Character *s, int count, bool wrapped = false)
{
396
    QString out;
Carlos Alves's avatar
Carlos Alves committed
397
398
399
400
401
    std::for_each_n(s, count, [&out](const Character &i) { out += i.character; });
    if (wrapped) {
        qDebug() << out << "*wrapped*";
    } else {
        qDebug() << out;
402
403
    }
}
Carlos Alves's avatar
Carlos Alves committed
404
405
void toDebug(const QVector<Character> &s, bool wrapped = false)
{
406
407
    toDebug(s.data(), s.size(), wrapped);
}
408
*/
409
410
411
412
413
414
415
416
417
418
419
420
421

int Screen::getCursorLine()
{
    if (_currentModes[MODE_AppScreen] == 1) {
        return _savedState.cursorLine;
    }
    return _cuY;
}

void Screen::setCursorLine(int newLine)
{
    if (_currentModes[MODE_AppScreen] == 1) {
        _savedState.cursorLine = newLine;
422
        _cuY = qBound(0, _cuY, _lines - 1);
423
424
425
426
427
    } else {
        _cuY = newLine;
    }
}

428
void Screen::resizeImage(int new_lines, int new_columns)
429
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
430
431
432
    if ((new_lines == _lines) && (new_columns == _columns)) {
        return;
    }
433

434
435
436
437
438
    // Adjust scroll position, and fix glitches
    _oldTotalLines = getLines() + getHistLines();
    _isResize = true;

    int cursorLine = getCursorLine();
439
    const int oldCursorLine = (cursorLine == _lines - 1 || cursorLine > new_lines - 1) ? new_lines - 1 : cursorLine;
440
441

    // Check if _history need to change
442
    if (_enableReflowLines && new_columns != _columns && _history->getLines() && _history->getMaxLines()) {
443
444
445
446
447
        // Join next line from _screenLine to _history
        while (_history->isWrappedLine(_history->getLines() - 1)) {
            fastAddHistLine();
            cursorLine--;
        }
Carlos Alves's avatar
Carlos Alves committed
448
        auto removedLines = _history->reflowLines(new_columns);
449

Carlos Alves's avatar
Carlos Alves committed
450
451
452
453
        // If _history size > max history size it will drop a line from _history.
        // We need to verify if we need to remove a URL.
        if (removedLines) {
            _escapeSequenceUrlExtractor->historyLinesRemoved(removedLines);
Carlos Alves's avatar
Carlos Alves committed
454
        }
455
456
    }

Carlos Alves's avatar
Carlos Alves committed
457
    if (_enableReflowLines && new_columns != _columns) {
Carlos Alves's avatar
Carlos Alves committed
458
459
        int cursorLineCorrection = 0;
        if (_currentTerminalDisplay) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
460
            // The 'zsh' works different from other shells when writing the command line.
Carlos Alves's avatar
Carlos Alves committed
461
462
463
464
465
466
467
468
469
470
            // It needs to identify the 'zsh' and calculate the new command line.
            auto sessionController = _currentTerminalDisplay->sessionController();
            auto terminal = sessionController->session()->foregroundProcessName();
            if (terminal == QLatin1String("zsh") && cursorLine > 0 && (_lineProperties[cursorLine - 1] & LINE_WRAPPED) != 0) {
                while (cursorLine + cursorLineCorrection > 0 && (_lineProperties[cursorLine + cursorLineCorrection - 1] & LINE_WRAPPED) != 0) {
                    cursorLineCorrection--;
                }
            }
        }

471
        // Analize the lines and move the data to lines below.
Carlos Alves's avatar
Carlos Alves committed
472
        int currentPos = 0;
Carlos Alves's avatar
Carlos Alves committed
473
        while (currentPos < (cursorLine + cursorLineCorrection) && currentPos < _screenLines.count() - 1) {
474
            // Join wrapped line in current position
475
            if ((_lineProperties[currentPos] & LINE_WRAPPED) != 0) {
476
                _screenLines[currentPos].append(std::move(_screenLines[currentPos + 1]));
477
478
479
480
481
                _screenLines.remove(currentPos + 1);
                _lineProperties.remove(currentPos);
                cursorLine--;
                continue;
            }
482

483
484
485
486
487
488
            // Ignore whitespaces at the end of the line
            int lineSize = _screenLines[currentPos].size();
            while (lineSize > 0 && QChar(_screenLines[currentPos][lineSize - 1].character).isSpace()) {
                lineSize--;
            }

489
            // If need to move to line below, copy from the current line, to the next one.
490
            if (lineSize > new_columns && !(_lineProperties[currentPos] & (LINE_DOUBLEHEIGHT_BOTTOM | LINE_DOUBLEHEIGHT_TOP))) {
491
                auto values = _screenLines[currentPos].mid(new_columns);
492
                _screenLines[currentPos].resize(new_columns);
493
                _lineProperties.insert(currentPos + 1, _lineProperties[currentPos]);
494
                _screenLines.insert(currentPos + 1, std::move(values));
495
496
497
498
                _lineProperties[currentPos] |= LINE_WRAPPED;
                cursorLine++;
            }
            currentPos += 1;
499
500
501
502
503
        }
    }

    // Check if it need to move from _screenLine to _history
    while (cursorLine > new_lines - 1) {
Carlos Alves's avatar
Carlos Alves committed
504
        fastAddHistLine();
505
506
        cursorLine--;
    }
507
508
509
510
511
512
513
514

    if (_enableReflowLines) {
        // Check cursor position and send from _history to _screenLines
        ImageLine histLine;
        histLine.reserve(1024);
        while (cursorLine < oldCursorLine && _history->getLines()) {
            int histPos = _history->getLines() - 1;
            int histLineLen = _history->getLineLen(histPos);
515
            LineProperty lineProperty = _history->getLineProperty(histPos);
516
517
518
            histLine.resize(histLineLen);
            _history->getCells(histPos, 0, histLineLen, histLine.data());
            _screenLines.insert(0, histLine);
519
            _lineProperties.insert(0, lineProperty);
520
            _history->removeCells();
521
522
            cursorLine++;
        }
523
524
525
    }

    _lineProperties.resize(new_lines + 1);
526
527
    if (_lineProperties.size() > _screenLines.size()) {
        std::fill(_lineProperties.begin() + _screenLines.size(), _lineProperties.end(), LINE_DEFAULT);
Carlos Alves's avatar
Carlos Alves committed
528
    }
529
    _screenLines.resize(new_lines + 1);
530

531
    _screenLinesSize = new_lines;
Jekyll Wu's avatar
Jekyll Wu committed
532
533
534
    _lines = new_lines;
    _columns = new_columns;
    _cuX = qMin(_cuX, _columns - 1);
535
536
    cursorLine = qBound(0, cursorLine, _lines - 1);
    setCursorLine(cursorLine);
537
538

    // FIXME: try to keep values, evtl.
539
    setDefaultMargins();
540
541
    initTabStops();
    clearSelection();
542
543
}

544
void Screen::setDefaultMargins()
545
{
546
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
547
    _bottomMargin = _lines - 1;
548
549
}

550
/*
Robert Knight's avatar
   
Robert Knight committed
551
   Clarifying rendition here and in the display.
552

553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
   The rendition attributes are

   attribute
   --------------
   RE_UNDERLINE
   RE_BLINK
   RE_BOLD
   RE_REVERSE
   RE_TRANSPARENT
   RE_FAINT
   RE_STRIKEOUT
   RE_CONCEAL
   RE_OVERLINE

   Depending on settings, bold may be rendered as a heavier font
   in addition to a different color.
569
   */
570

Robert Knight's avatar
   
Robert Knight committed
571
void Screen::reverseRendition(Character& p) const
Kurt Hindenburg's avatar
Kurt Hindenburg committed
572
573
{
    CharacterColor f = p.foregroundColor;
574
    CharacterColor b = p.backgroundColor;
575

Kurt Hindenburg's avatar
Kurt Hindenburg committed
576
    p.foregroundColor = b;
577
    p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
578
579
}

580
void Screen::updateEffectiveRendition()
581
{
Jekyll Wu's avatar
Jekyll Wu committed
582
    _effectiveRendition = _currentRendition;
583
    if ((_currentRendition & RE_REVERSE) != 0) {
Jekyll Wu's avatar
Jekyll Wu committed
584
585
        _effectiveForeground = _currentBackground;
        _effectiveBackground = _currentForeground;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
586
    } else {
Jekyll Wu's avatar
Jekyll Wu committed
587
588
        _effectiveForeground = _currentForeground;
        _effectiveBackground = _currentBackground;
589
590
    }

591
592
593
594
595
596
    if ((_currentRendition & RE_BOLD) != 0) {
        if ((_currentRendition & RE_FAINT) == 0) {
            _effectiveForeground.setIntensive();
        }
    } else {
        if ((_currentRendition & RE_FAINT) != 0) {
597
            _effectiveForeground.setFaint();
598
        }
599
    }
600
601
}

Robert Knight's avatar
   
Robert Knight committed
602
void Screen::copyFromHistory(Character* dest, int startLine, int count) const
603
{
Jekyll Wu's avatar
Jekyll Wu committed
604
    Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= _history->getLines());
605

Kurt Hindenburg's avatar
Kurt Hindenburg committed
606
    for (int line = startLine; line < startLine + count; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
607
608
        const int length = qMin(_columns, _history->getLineLen(line));
        const int destLineOffset  = (line - startLine) * _columns;
609
        const int lastColumn = (_history->getLineProperty(line) & LINE_DOUBLEWIDTH) ? _columns / 2 : _columns;
Robert Knight's avatar
   
Robert Knight committed
610

Jekyll Wu's avatar
Jekyll Wu committed
611
        _history->getCells(line, 0, length, dest + destLineOffset);
Robert Knight's avatar
   
Robert Knight committed
612

Carlos Alves's avatar
Carlos Alves committed
613
614
        if (length < _columns) {
            std::fill(&dest[destLineOffset + length], &dest[destLineOffset + _columns], Screen::DefaultChar);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
615
        }
616
617

        // invert selected text
Jekyll Wu's avatar
Jekyll Wu committed
618
        if (_selBegin != -1) {
619
            for (int column = 0; column < lastColumn; column++) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
620
                if (isSelected(column, line)) {
621
                    dest[destLineOffset + column].rendition |= RE_SELECTED;
622
                }
623
            }
624
        }
625
    }
Robert Knight's avatar
   
Robert Knight committed
626
627
628
629
}

void Screen::copyFromScreen(Character* dest , int startLine , int count) const
{
Jekyll Wu's avatar
Jekyll Wu committed
630
    Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= _lines);
Robert Knight's avatar
   
Robert Knight committed
631

Kurt Hindenburg's avatar
Kurt Hindenburg committed
632
    for (int line = startLine; line < (startLine + count) ; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
633
634
        int srcLineStartIndex  = line * _columns;
        int destLineStartIndex = (line - startLine) * _columns;
635
        int lastColumn = (line < _lineProperties.size() && _lineProperties[line] & LINE_DOUBLEWIDTH) ? _columns / 2 : _columns;
636

Jekyll Wu's avatar
Jekyll Wu committed
637
        for (int column = 0; column < _columns; column++) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
638
            int srcIndex = srcLineStartIndex + column;
639
            int destIndex = destLineStartIndex + column;
640

Jekyll Wu's avatar
Jekyll Wu committed
641
            dest[destIndex] = _screenLines[srcIndex / _columns].value(srcIndex % _columns, Screen::DefaultChar);
642

643
            // invert selected text
644
            if (_selBegin != -1 && isSelected(column, line + _history->getLines()) && column < lastColumn) {
645
                dest[destIndex].rendition |= RE_SELECTED;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
646
            }
647
        }
648
    }
Robert Knight's avatar
   
Robert Knight committed
649
650
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
651
void Screen::getImage(Character* dest, int size, int startLine, int endLine) const
Robert Knight's avatar
   
Robert Knight committed
652
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
653
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
654
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
655
656

    const int mergedLines = endLine - startLine + 1;
Robert Knight's avatar
   
Robert Knight committed
657

Jekyll Wu's avatar
Jekyll Wu committed
658
    Q_ASSERT(size >= mergedLines * _columns);
659
    Q_UNUSED(size)
Robert Knight's avatar
   
Robert Knight committed
660

Jekyll Wu's avatar
Jekyll Wu committed
661
    const int linesInHistoryBuffer = qBound(0, _history->getLines() - startLine, mergedLines);
662
    const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
Robert Knight's avatar
   
Robert Knight committed
663

Jekyll Wu's avatar
Jekyll Wu committed
664
    // copy _lines from history buffer
Kurt Hindenburg's avatar
Kurt Hindenburg committed
665
    if (linesInHistoryBuffer > 0) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
666
        copyFromHistory(dest, startLine, linesInHistoryBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
667
    }
Robert Knight's avatar
   
Robert Knight committed
668

Jekyll Wu's avatar
Jekyll Wu committed
669
    // copy _lines from screen buffer
Kurt Hindenburg's avatar
Kurt Hindenburg committed
670
    if (linesInScreenBuffer > 0) {
Jekyll Wu's avatar
Jekyll Wu committed
671
672
        copyFromScreen(dest + linesInHistoryBuffer * _columns,
                       startLine + linesInHistoryBuffer - _history->getLines(),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
673
                       linesInScreenBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
674
    }
Robert Knight's avatar
   
Robert Knight committed
675

676
    // invert display when in screen mode
Kurt Hindenburg's avatar
Kurt Hindenburg committed
677
    if (getMode(MODE_Screen)) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
678
        for (int i = 0; i < mergedLines * _columns; i++) {
679
            reverseRendition(dest[i]); // for reverse display
Kurt Hindenburg's avatar
Kurt Hindenburg committed
680
        }
681
    }
682

683
    int visX = qMin(_cuX, getScreenLineColumns(_cuY) - 1);
684
    // mark the character at the current cursor position
685
    int cursorIndex = loc(visX, _cuY + linesInHistoryBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
686
    if (getMode(MODE_Cursor) && cursorIndex < _columns * mergedLines) {
687
        dest[cursorIndex].rendition |= RE_CURSOR;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
688
    }
689
690
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
691
QVector<LineProperty> Screen::getLineProperties(int startLine , int endLine) const
692
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
693
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
694
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
695

Kurt Hindenburg's avatar
Kurt Hindenburg committed
696
    const int mergedLines = endLine - startLine + 1;
Jekyll Wu's avatar
Jekyll Wu committed
697
    const int linesInHistory = qBound(0, _history->getLines() - startLine, mergedLines);
698
    const int linesInScreen = mergedLines - linesInHistory;
699

700
701
    QVector<LineProperty> result(mergedLines);
    int index = 0;
702

Jekyll Wu's avatar
Jekyll Wu committed
703
    // copy properties for _lines in history
Kurt Hindenburg's avatar
Kurt Hindenburg committed
704
    for (int line = startLine; line < startLine + linesInHistory; line++) {
705
        result[index] = _history->getLineProperty(line);
706
707
708
        index++;
    }

Jekyll Wu's avatar
Jekyll Wu committed
709
710
    // copy properties for _lines in screen buffer
    const int firstScreenLine = startLine + linesInHistory - _history->getLines();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
711
    for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
712
        result[index] = _lineProperties[line];
713
        index++;
714
    }
715

716
    return result;
717
}
718

719
720
int Screen::getScreenLineColumns(const int line) const
{
721
    if (line < _lineProperties.size() && _lineProperties[line] & LINE_DOUBLEWIDTH) {
722
723
724
725
726
727
        return _columns / 2;
    }

    return _columns;
}

728
void Screen::reset()
729
{
730
731
732
733
    // Clear screen, but preserve the current line
    scrollUp(0, _cuY);
    _cuY = 0;

734
735
    _currentModes[MODE_Origin] = 0;
    _savedModes[MODE_Origin] = 0;
736

737
738
739
740
741
742
    setMode(MODE_Wrap);
    saveMode(MODE_Wrap);      // wrap at end of margin

    resetMode(MODE_Insert);
    saveMode(MODE_Insert);  // overstroke

743
    setMode(MODE_Cursor);                         // cursor visible
744
745
    resetMode(MODE_Screen);                         // screen not inverse
    resetMode(MODE_NewLine);
746

Kurt Hindenburg's avatar
Kurt Hindenburg committed
747
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
748
    _bottomMargin = _lines - 1;
749

750
751
752
    // Other terminal emulators reset the entire scroll history during a reset
//    setScroll(getScroll(), false);

753
754
    setDefaultRendition();
    saveCursor();
755
756
}

757
void Screen::backspace()
758
{
Jekyll Wu's avatar
Jekyll Wu committed
759
    _cuX = qMax(0, _cuX - 1);
760

Kurt Hindenburg's avatar
Kurt Hindenburg committed
761
    if (_screenLines[_cuY].size() < _cuX + 1) {
Jekyll Wu's avatar
Jekyll Wu committed
762
        _screenLines[_cuY].resize(_cuX + 1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
763
    }
764
765
}

766
void Screen::tab(int n)
767
{
768
    // note that TAB is a format effector (does not write ' ');
769
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
770
771
        n = 1;
    }
772
    while ((n > 0) && (_cuX < getScreenLineColumns(_cuY) - 1)) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
773
        cursorRight(1);
774
        while ((_cuX < getScreenLineColumns(_cuY) - 1) && !_tabStops[_cuX]) {
775
            cursorRight(1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
776
        }
777
778
        n--;
    }
779
780
}

781
void Screen::backtab(int n)
782
{
783
    // note that TAB is a format effector (does not write ' ');
784
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
785
786
        n = 1;
    }
Jekyll Wu's avatar
Jekyll Wu committed
787
    while ((n > 0) && (_cuX > 0)) {
788
789
790
791
        cursorLeft(1);
        while ((_cuX > 0) && !_tabStops[_cuX]) {
            cursorLeft(1);
        }
792
793
        n--;
    }
794
795
}

796
void Screen::clearTabStops()
797
{
Carlos Alves's avatar
Carlos Alves committed
798
    _tabStops.fill(false);
799
800
}

801
void Screen::changeTabStop(bool set)
802
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed