Screen.cpp 60.9 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
12
#include "config-konsole.h"

13
// Qt
14
#include <QTextStream>
15

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

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

25
#include "EscapeSequenceUrlExtractor.h"
26
#include "characters/ExtendedCharTable.h"
27
28
#include "history/HistoryScrollNone.h"
#include "history/HistoryType.h"
29

30
31
32
33
34
#ifdef HAVE_MALLOC_TRIM
// For malloc_trim, which is a GNU extension
extern "C" {
#include <malloc.h>
}
35
36
#endif

37
38
using namespace Konsole;

39
// Macro to convert x,y position on screen to position within an image.
40
//
41
42
43
44
45
46
47
// Originally the image was stored as one large contiguous block of
// memory, so a position within the image could be represented as an
// offset from the beginning of the block.  For efficiency reasons this
// is no longer the case.
// Many internal parts of this class still use this representation for parameters and so on,
// notably moveImage() and clearImage().
// This macro converts from an X,Y position into an image offset.
48
#ifndef loc
49
#define loc(X, Y) ((Y)*_columns + (X))
50
51
#endif

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const Character Screen::DefaultChar =
    Character(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), DEFAULT_RENDITION, false);

Screen::Screen(int lines, int columns)
    : _currentTerminalDisplay(nullptr)
    , _lines(lines)
    , _columns(columns)
    , _screenLines(_lines + 1)
    , _screenLinesSize(_lines)
    , _scrolledLines(0)
    , _lastScrolledRegion(QRect())
    , _droppedLines(0)
    , _oldTotalLines(0)
    , _isResize(false)
    , _enableReflowLines(false)
    , _lineProperties(_lines + 1)
    , _history(std::make_unique<HistoryScrollNone>())
    , _cuX(0)
    , _cuY(0)
    , _currentForeground(CharacterColor())
    , _currentBackground(CharacterColor())
    , _currentRendition(DEFAULT_RENDITION)
    , _topMargin(0)
    , _bottomMargin(0)
    , _tabStops(QBitArray())
    , _selBegin(0)
    , _selTopLeft(0)
    , _selBottomRight(0)
    , _blockSelectionMode(false)
    , _effectiveForeground(CharacterColor())
    , _effectiveBackground(CharacterColor())
    , _effectiveRendition(DEFAULT_RENDITION)
    , _lastPos(-1)
    , _lastDrawnChar(0)
86
    , _escapeSequenceUrlExtractor(nullptr)
87
{
88
    std::fill(_lineProperties.begin(), _lineProperties.end(), LINE_DEFAULT);
89

90
    _graphicsPlacements = std::vector<std::unique_ptr<TerminalGraphicsPlacement_t>>();
91
92
    _hasGraphics = false;

93
94
95
    initTabStops();
    clearSelection();
    reset();
96
97
}

98
Screen::~Screen() = default;
99

100
void Screen::cursorUp(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
101
//=CUU
102
{
103
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
104
105
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
106
    const int stop = _cuY < _topMargin ? 0 : _topMargin;
107
    _cuX = qMin(getScreenLineColumns(_cuY) - 1, _cuX); // nowrap!
Jekyll Wu's avatar
Jekyll Wu committed
108
    _cuY = qMax(stop, _cuY - n);
109
110
}

111
void Screen::cursorDown(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
112
//=CUD
113
{
114
    if (n < 1) {
115
        n = 1; // Default
Kurt Hindenburg's avatar
Kurt Hindenburg committed
116
    }
117
118
119
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
    }
Jekyll Wu's avatar
Jekyll Wu committed
120
    const int stop = _cuY > _bottomMargin ? _lines - 1 : _bottomMargin;
121
    _cuX = qMin(getScreenLineColumns(_cuY) - 1, _cuX); // nowrap!
Jekyll Wu's avatar
Jekyll Wu committed
122
    _cuY = qMin(stop, _cuY + n);
123
124
}

125
void Screen::cursorLeft(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
126
//=CUB
127
{
128
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
129
130
        n = 1; // Default
    }
131
    _cuX = qMin(getScreenLineColumns(_cuY) - 1, _cuX); // nowrap!
Jekyll Wu's avatar
Jekyll Wu committed
132
    _cuX = qMax(0, _cuX - n);
133
134
}

135
136
137
void Screen::cursorNextLine(int n)
//=CNL
{
138
    if (n < 1) {
139
140
        n = 1; // Default
    }
141
142
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
143
    }
144
    _cuX = 0;
145
146
    const int stop = _cuY > _bottomMargin ? _lines - 1 : _bottomMargin;
    _cuY = qMin(stop, _cuY + n);
147
148
149
150
151
}

void Screen::cursorPreviousLine(int n)
//=CPL
{
152
    if (n < 1) {
153
154
155
        n = 1; // Default
    }
    _cuX = 0;
156
157
    const int stop = _cuY < _topMargin ? 0 : _topMargin;
    _cuY = qMax(stop, _cuY - n);
158
159
}

160
void Screen::cursorRight(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
161
//=CUF
162
{
163
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
164
165
        n = 1; // Default
    }
166
167
168
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
    }
169
    _cuX = qMin(getScreenLineColumns(_cuY) - 1, _cuX + n);
170
171
}

172
void Screen::setMargins(int top, int bot)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
173
//=STBM
174
{
175
    if (top < 1) {
176
        top = 1; // Default
Kurt Hindenburg's avatar
Kurt Hindenburg committed
177
    }
178
    if (bot < 1) {
179
        bot = _lines; // Default
Kurt Hindenburg's avatar
Kurt Hindenburg committed
180
    }
181
182
    top = top - 1; // Adjust to internal lineno
    bot = bot - 1; // Adjust to internal lineno
Jekyll Wu's avatar
Jekyll Wu committed
183
    if (!(0 <= top && top < bot && bot < _lines)) {
184
185
        // Debug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
        return; // Default error action: ignore
186
187
188
    }
    _topMargin = top;
    _bottomMargin = bot;
Jekyll Wu's avatar
Jekyll Wu committed
189
190
    _cuX = 0;
    _cuY = getMode(MODE_Origin) ? top : 0;
191
192
}

193
int Screen::topMargin() const
194
{
195
    return _topMargin;
196
}
197
int Screen::bottomMargin() const
198
{
199
    return _bottomMargin;
200
201
}

202
void Screen::index()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
203
//=IND
204
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
205
    if (_cuY == _bottomMargin) {
206
        scrollUp(1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
207
    } else if (_cuY < _lines - 1) {
Jekyll Wu's avatar
Jekyll Wu committed
208
        _cuY += 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
209
    }
210
211
}

212
void Screen::reverseIndex()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
213
//=RI
214
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
215
    if (_cuY == _topMargin) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
216
        scrollDown(_topMargin, 1);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
217
    } else if (_cuY > 0) {
Jekyll Wu's avatar
Jekyll Wu committed
218
        _cuY -= 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
219
    }
220
221
}

222
void Screen::nextLine()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
223
//=NEL
224
{
225
226
    toStartOfLine();
    index();
227
228
}

229
void Screen::eraseChars(int n)
230
{
231
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
232
233
        n = 1; // Default
    }
234
235
236
237
    if (n > MAX_SCREEN_ARGUMENT) {
        n = MAX_SCREEN_ARGUMENT;
    }
    const int p = qBound(0, _cuX + n - 1, _columns - 1);
238
    clearImage(loc(_cuX, _cuY), loc(p, _cuY), ' ', false);
239
240
}

241
242
243
void Screen::eraseBlock(int y, int x, int height, int width)
{
    width = qBound(0, width, _columns - x - 1);
244
    int endCol = x + width;
245
    height = qBound(0, height, _lines - y - 1);
246
    Character chr(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), RE_CONCEAL, false);
247
    for (int row = y; row < y + height; row++) {
248
249
250
251
252
253
254
255
256
257
        QVector<Character> &line = _screenLines[row];
        if (line.size() < endCol + 1) {
            line.resize(endCol + 1);
        }
        if (endCol == _columns - 1) {
            line.resize(endCol + 1);
        }
        if (x <= endCol) {
            std::fill(line.begin() + x, line.begin() + (endCol + 1), chr);
        }
258
259
260
    }
}

261
void Screen::deleteChars(int n)
262
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
263
    Q_ASSERT(n >= 0);
264

265
    // always delete at least one char
266
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
267
        n = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
268
    }
269

270
    // if cursor is beyond the end of the line there is nothing to do
271
    if (_cuX >= _screenLines.at(_cuY).count()) {
272
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
273
    }
274

275
276
    if (_cuX + n > _screenLines.at(_cuY).count()) {
        n = _screenLines.at(_cuY).count() - _cuX;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
277
    }
278

Kurt Hindenburg's avatar
Kurt Hindenburg committed
279
    Q_ASSERT(n >= 0);
280
    Q_ASSERT(_cuX + n <= _screenLines.at(_cuY).count());
281

Jekyll Wu's avatar
Jekyll Wu committed
282
    _screenLines[_cuY].remove(_cuX, n);
283
284

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

287
    for (int i = 0; i < n; ++i) {
288
        _screenLines[_cuY].append(spaceWithCurrentAttrs);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
289
    }
290
291
}

292
void Screen::insertChars(int n)
293
{
294
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
295
296
        n = 1; // Default
    }
297

298
    if (_screenLines.at(_cuY).size() < _cuX) {
Jekyll Wu's avatar
Jekyll Wu committed
299
        _screenLines[_cuY].resize(_cuX);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
300
    }
301

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

304
    if (_screenLines.at(_cuY).count() > getScreenLineColumns(_cuY)) {
305
        _screenLines[_cuY].resize(getScreenLineColumns(_cuY));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
306
    }
307
308
}

309
310
void Screen::repeatChars(int n)
{
311
    if (n < 1) {
312
313
314
315
316
317
318
319
320
321
        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.
322
    while (n > 0) {
323
        displayCharacter(_lastDrawnChar);
324
        --n;
325
326
327
    }
}

328
void Screen::deleteLines(int n)
329
{
330
331
332
    if (_cuY < _topMargin) {
        return;
    }
333
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
334
335
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
336
    scrollUp(_cuY, n);
337
338
}

339
void Screen::insertLines(int n)
340
{
341
342
343
    if (_cuY < _topMargin) {
        return;
    }
344
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
345
346
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
347
    scrollDown(_cuY, n);
348
349
}

350
void Screen::setMode(int m)
351
{
352
    _currentModes[m] = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
353
    switch (m) {
354
    case MODE_Origin:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
355
356
        _cuX = 0;
        _cuY = _topMargin;
357
        break; // FIXME: home
358
    }
359
360
}

361
void Screen::resetMode(int m)
362
{
363
    _currentModes[m] = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
364
    switch (m) {
365
    case MODE_Origin:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
366
367
        _cuX = 0;
        _cuY = 0;
368
        break; // FIXME: home
369
    }
370
371
}

372
void Screen::saveMode(int m)
373
{
Jekyll Wu's avatar
Jekyll Wu committed
374
    _savedModes[m] = _currentModes[m];
375
376
}

377
void Screen::restoreMode(int m)
378
{
Jekyll Wu's avatar
Jekyll Wu committed
379
    _currentModes[m] = _savedModes[m];
380
381
}

Robert Knight's avatar
   
Robert Knight committed
382
bool Screen::getMode(int m) const
383
{
384
    return _currentModes[m] != 0;
385
386
}

387
void Screen::saveCursor()
388
{
Jekyll Wu's avatar
Jekyll Wu committed
389
    _savedState.cursorColumn = _cuX;
390
    _savedState.cursorLine = _cuY;
Jekyll Wu's avatar
Jekyll Wu committed
391
392
393
    _savedState.rendition = _currentRendition;
    _savedState.foreground = _currentForeground;
    _savedState.background = _currentBackground;
394
    _savedState.originMode = _currentModes[MODE_Origin];
395
396
}

397
void Screen::restoreCursor()
398
{
399
400
401
402
403
    _cuY = qMin(_savedState.cursorLine, _lines - 1);
    _cuX = qMin(_savedState.cursorColumn, getScreenLineColumns(_cuY) - 1);
    _currentRendition = _savedState.rendition;
    _currentForeground = _savedState.foreground;
    _currentBackground = _savedState.background;
404
    updateEffectiveRendition();
405
406
407
408
409
410
411
412
413
    _currentModes[MODE_Origin] = _savedState.originMode;
    /* XXX: DEC STD-070 states that DECRC should make sure the cursor lies
     * inside the scrolling region, but that behavior doesn't seem to be
     * widespread (neither VT1xx, VT240, mlterm, vte do it, and xterm
     * only limits the bottom margin).
    if (getMode(MODE_Origin)) {
        _cuY = qBound(_topMargin, _cuY, _bottomMargin);
    }
    */
414
415
}

416
417
418
419
420
421
422
423
424
425
426
427
428
429
int Screen::getOldTotalLines()
{
    return _oldTotalLines;
}

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

Ahmad Samir's avatar
Ahmad Samir committed
430
void Screen::setReflowLines(bool enable)
431
432
433
434
{
    _enableReflowLines = enable;
}

435
436
437
438
/* 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
439
// Debugging auxiliary functions to show what is written in screen or history
Carlos Alves's avatar
Carlos Alves committed
440
441
void toDebug(const Character *s, int count, bool wrapped = false)
{
442
    QString out;
Carlos Alves's avatar
Carlos Alves committed
443
444
445
446
447
    std::for_each_n(s, count, [&out](const Character &i) { out += i.character; });
    if (wrapped) {
        qDebug() << out << "*wrapped*";
    } else {
        qDebug() << out;
448
449
    }
}
Carlos Alves's avatar
Carlos Alves committed
450
451
void toDebug(const QVector<Character> &s, bool wrapped = false)
{
452
453
    toDebug(s.data(), s.size(), wrapped);
}
454
*/
455
456
457

int Screen::getCursorLine()
{
458
    if (isAppMode()) {
459
460
461
462
463
464
465
        return _savedState.cursorLine;
    }
    return _cuY;
}

void Screen::setCursorLine(int newLine)
{
466
    if (isAppMode()) {
467
        _savedState.cursorLine = newLine;
468
        _cuY = qBound(0, _cuY, _lines - 1);
469
470
471
472
473
    } else {
        _cuY = newLine;
    }
}

474
void Screen::resizeImage(int new_lines, int new_columns)
475
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
476
477
478
    if ((new_lines == _lines) && (new_columns == _columns)) {
        return;
    }
479

480
481
482
483
484
    // Adjust scroll position, and fix glitches
    _oldTotalLines = getLines() + getHistLines();
    _isResize = true;

    int cursorLine = getCursorLine();
485
    const int oldCursorLine = (cursorLine == _lines - 1 || cursorLine > new_lines - 1) ? new_lines - 1 : cursorLine;
486
487

    // Check if _history need to change
488
    if (_enableReflowLines && new_columns != _columns && _history->getLines() && _history->getMaxLines()) {
489
        // Join next line from _screenLine to _history
490
        while (!_screenLines.empty() && _history->isWrappedLine(_history->getLines() - 1)) {
491
            fastAddHistLine();
492
            --cursorLine;
493
        }
Carlos Alves's avatar
Carlos Alves committed
494
        auto removedLines = _history->reflowLines(new_columns);
495

Carlos Alves's avatar
Carlos Alves committed
496
497
        // If _history size > max history size it will drop a line from _history.
        // We need to verify if we need to remove a URL.
498
        if (removedLines && _escapeSequenceUrlExtractor) {
Carlos Alves's avatar
Carlos Alves committed
499
            _escapeSequenceUrlExtractor->historyLinesRemoved(removedLines);
Carlos Alves's avatar
Carlos Alves committed
500
        }
501
502
    }

Carlos Alves's avatar
Carlos Alves committed
503
    if (_enableReflowLines && new_columns != _columns) {
Carlos Alves's avatar
Carlos Alves committed
504
505
        int cursorLineCorrection = 0;
        if (_currentTerminalDisplay) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
506
            // The 'zsh' works different from other shells when writing the command line.
Carlos Alves's avatar
Carlos Alves committed
507
508
509
            // It needs to identify the 'zsh' and calculate the new command line.
            auto sessionController = _currentTerminalDisplay->sessionController();
            auto terminal = sessionController->session()->foregroundProcessName();
510
511
            if (terminal == QLatin1String("zsh")) {
                while (cursorLine + cursorLineCorrection > 0 && (_lineProperties.at(cursorLine + cursorLineCorrection) & LINE_PROMPT_START) == 0) {
512
                    --cursorLineCorrection;
Carlos Alves's avatar
Carlos Alves committed
513
                }
514
515
516
517
518
519
520
521
                if (cursorLine + cursorLineCorrection > 0 && (_lineProperties.at(cursorLine + cursorLineCorrection) & LINE_PROMPT_START) != 0) {
                    _lineProperties[cursorLine + cursorLineCorrection - 1] &= ~LINE_WRAPPED;
                } else {
                    cursorLineCorrection = 0;
                    while (cursorLine + cursorLineCorrection > 0 && (_lineProperties.at(cursorLine + cursorLineCorrection - 1) & LINE_WRAPPED) != 0) {
                        --cursorLineCorrection;
                    }
                }
Carlos Alves's avatar
Carlos Alves committed
522
523
524
            }
        }

Kurt Hindenburg's avatar
Kurt Hindenburg committed
525
        // Analyze the lines and move the data to lines below.
Carlos Alves's avatar
Carlos Alves committed
526
        int currentPos = 0;
527
        while (currentPos < (cursorLine + cursorLineCorrection) && currentPos < (int)_screenLines.size() - 1) {
528
            // Join wrapped line in current position
529
530
            if ((_lineProperties.at(currentPos) & LINE_WRAPPED) != 0) {
                _screenLines[currentPos].append(_screenLines.at(currentPos + 1));
531
                _screenLines.erase(_screenLines.begin() + currentPos + 1);
532
                _lineProperties.erase(_lineProperties.begin() + currentPos);
533
                --cursorLine;
534
535
                continue;
            }
536

537
            // Ignore whitespaces at the end of the line
538
539
            int lineSize = _screenLines.at(currentPos).size();
            while (lineSize > 0 && QChar(_screenLines.at(currentPos).at(lineSize - 1).character).isSpace()) {
540
                --lineSize;
541
542
            }

543
            // If need to move to line below, copy from the current line, to the next one.
544
545
            if (lineSize > new_columns && !(_lineProperties.at(currentPos) & (LINE_DOUBLEHEIGHT_BOTTOM | LINE_DOUBLEHEIGHT_TOP))) {
                auto values = _screenLines.at(currentPos).mid(new_columns);
546
                _screenLines[currentPos].resize(new_columns);
547
                _lineProperties.insert(_lineProperties.begin() + currentPos + 1, _lineProperties.at(currentPos));
548
                _screenLines.insert(_screenLines.begin() + currentPos + 1, std::move(values));
549
                _lineProperties[currentPos] |= LINE_WRAPPED;
550
                ++cursorLine;
551
552
            }
            currentPos += 1;
553
554
555
556
557
        }
    }

    // Check if it need to move from _screenLine to _history
    while (cursorLine > new_lines - 1) {
Carlos Alves's avatar
Carlos Alves committed
558
        fastAddHistLine();
559
        --cursorLine;
560
    }
561
562
563
564
565
566
567

    if (_enableReflowLines) {
        // Check cursor position and send from _history to _screenLines
        ImageLine histLine;
        while (cursorLine < oldCursorLine && _history->getLines()) {
            int histPos = _history->getLines() - 1;
            int histLineLen = _history->getLineLen(histPos);
568
            LineProperty lineProperty = _history->getLineProperty(histPos);
569
570
            histLine.resize(histLineLen);
            _history->getCells(histPos, 0, histLineLen, histLine.data());
571
            _screenLines.insert(_screenLines.begin(), std::move(histLine));
572
            _lineProperties.insert(_lineProperties.begin(), lineProperty);
573
            _history->removeCells();
574
            ++cursorLine;
575
        }
576
577
578
    }

    _lineProperties.resize(new_lines + 1);
579
    if (_lineProperties.size() > _screenLines.size()) {
580
        std::fill(_lineProperties.begin() + _screenLines.size(), _lineProperties.end(), LINE_DEFAULT);
Carlos Alves's avatar
Carlos Alves committed
581
    }
582
    _screenLines.resize(new_lines + 1);
583

584
    _screenLinesSize = new_lines;
Jekyll Wu's avatar
Jekyll Wu committed
585
586
587
    _lines = new_lines;
    _columns = new_columns;
    _cuX = qMin(_cuX, _columns - 1);
588
589
    cursorLine = qBound(0, cursorLine, _lines - 1);
    setCursorLine(cursorLine);
590
591

    // FIXME: try to keep values, evtl.
592
    setDefaultMargins();
593
594
    initTabStops();
    clearSelection();
595
596
}

597
void Screen::setDefaultMargins()
598
{
599
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
600
    _bottomMargin = _lines - 1;
601
602
}

603
/*
Robert Knight's avatar
   
Robert Knight committed
604
   Clarifying rendition here and in the display.
605

606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
   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.
622
   */
623

624
void Screen::reverseRendition(Character &p) const
Kurt Hindenburg's avatar
Kurt Hindenburg committed
625
626
{
    CharacterColor f = p.foregroundColor;
627
    CharacterColor b = p.backgroundColor;
628

Kurt Hindenburg's avatar
Kurt Hindenburg committed
629
    p.foregroundColor = b;
630
    p.backgroundColor = f; // p->r &= ~RE_TRANSPARENT;
631
632
}

633
void Screen::updateEffectiveRendition()
634
{
Jekyll Wu's avatar
Jekyll Wu committed
635
    _effectiveRendition = _currentRendition;
636
    if ((_currentRendition & RE_REVERSE) != 0) {
Jekyll Wu's avatar
Jekyll Wu committed
637
638
        _effectiveForeground = _currentBackground;
        _effectiveBackground = _currentForeground;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
639
    } else {
Jekyll Wu's avatar
Jekyll Wu committed
640
641
        _effectiveForeground = _currentForeground;
        _effectiveBackground = _currentBackground;
642
643
    }

644
645
646
647
648
649
    if ((_currentRendition & RE_BOLD) != 0) {
        if ((_currentRendition & RE_FAINT) == 0) {
            _effectiveForeground.setIntensive();
        }
    } else {
        if ((_currentRendition & RE_FAINT) != 0) {
650
            _effectiveForeground.setFaint();
651
        }
652
    }
653
654
}

655
void Screen::copyFromHistory(Character *dest, int startLine, int count) const
656
{
657
    const int columns = _columns;
658

Jekyll Wu's avatar
Jekyll Wu committed
659
    Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= _history->getLines());
660

661
    for (int line = startLine; line < startLine + count; ++line) {
662
663
664
        const int length = qMin(columns, _history->getLineLen(line));
        const int destLineOffset = (line - startLine) * columns;
        const int lastColumn = (_history->getLineProperty(line) & LINE_DOUBLEWIDTH) ? columns / 2 : columns;
Robert Knight's avatar
   
Robert Knight committed
665

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

668
        if (length < columns) {
669
            const int begin = destLineOffset + length;
670
            const int end = destLineOffset + columns;
671
            std::fill(dest + begin, dest + end, Screen::DefaultChar);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
672
        }
673
674

        // invert selected text
Jekyll Wu's avatar
Jekyll Wu committed
675
        if (_selBegin != -1) {
676
            for (int column = 0; column < lastColumn; ++column) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
677
                if (isSelected(column, line)) {
678
                    dest[destLineOffset + column].rendition |= RE_SELECTED;
679
                }
680
            }
681
        }
682
    }
Robert Knight's avatar
   
Robert Knight committed
683
684
}

685
void Screen::copyFromScreen(Character *dest, int startLine, int count) const
Robert Knight's avatar
   
Robert Knight committed
686
{
Ahmad Samir's avatar
Ahmad Samir committed
687
    const int endLine = startLine + count;
688
689
    const int columns = _columns;
    const int historyLines = _history->getLines();
Robert Knight's avatar
   
Robert Knight committed
690

Ahmad Samir's avatar
Ahmad Samir committed
691
692
693
    Q_ASSERT(startLine >= 0 && count > 0 && endLine <= _lines);

    for (int line = startLine; line < endLine; ++line) {
694
695
696
697
        const int destLineOffset = (line - startLine) * columns;
        const int lastColumn = (line < (int)_lineProperties.size() && _lineProperties[line] & LINE_DOUBLEWIDTH) ? columns / 2 : columns;
        const ImageLine srcLine = _screenLines.at(line);
        const int length = qMin(columns, srcLine.size());
698

699
        std::copy(srcLine.cbegin(), srcLine.cbegin() + length, dest + destLineOffset);
700

701
702
703
704
705
        if (length < columns) {
            const int begin = destLineOffset + length;
            const int end = destLineOffset + columns;
            std::fill(dest + begin, dest + end, Screen::DefaultChar);
        }
706

707
708
709
        if (_selBegin != -1) {
            for (int column = 0; column < lastColumn; ++column) {
                if (isSelected(column, line + historyLines)) {
710
                    dest[destLineOffset + column].rendition |= RE_SELECTED;
711
                }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
712
            }
713
        }
714
    }
Robert Knight's avatar
   
Robert Knight committed
715
716
}

717
void Screen::getImage(Character *dest, int size, int startLine, int endLine) const
Robert Knight's avatar
   
Robert Knight committed
718
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
719
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
720
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
721
722

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

Jekyll Wu's avatar
Jekyll Wu committed
724
    Q_ASSERT(size >= mergedLines * _columns);
725
    Q_UNUSED(size)
Robert Knight's avatar
   
Robert Knight committed
726

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

Jekyll Wu's avatar
Jekyll Wu committed
730
    // copy _lines from history buffer
Kurt Hindenburg's avatar
Kurt Hindenburg committed
731
    if (linesInHistoryBuffer > 0) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
732
        copyFromHistory(dest, startLine, linesInHistoryBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
733
    }
Robert Knight's avatar
   
Robert Knight committed
734

Jekyll Wu's avatar
Jekyll Wu committed
735
    // copy _lines from screen buffer
Kurt Hindenburg's avatar
Kurt Hindenburg committed
736
    if (linesInScreenBuffer > 0) {
737
        copyFromScreen(dest + linesInHistoryBuffer * _columns, startLine + linesInHistoryBuffer - _history->getLines(), linesInScreenBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
738
    }
Robert Knight's avatar
   
Robert Knight committed
739

740
    // invert display when in screen mode
Kurt Hindenburg's avatar
Kurt Hindenburg committed
741
    if (getMode(MODE_Screen)) {
742
        for (int i = 0; i < mergedLines * _columns; ++i) {
743
            reverseRendition(dest[i]); // for reverse display
Kurt Hindenburg's avatar
Kurt Hindenburg committed
744
        }
745
    }
746

747
    int visX = qMin(_cuX, getScreenLineColumns(_cuY) - 1);
748
    // mark the character at the current cursor position
749
    int cursorIndex = loc(visX, _cuY + linesInHistoryBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
750
    if (getMode(MODE_Cursor) && cursorIndex < _columns * mergedLines) {
751
        dest[cursorIndex].rendition |= RE_CURSOR;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
752
    }
753
754
}

755
QVector<LineProperty> Screen::getLineProperties(int startLine, int endLine) const
756
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
757
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
758
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
759

Kurt Hindenburg's avatar
Kurt Hindenburg committed
760
    const int mergedLines = endLine - startLine + 1;
Jekyll Wu's avatar
Jekyll Wu committed
761
    const int linesInHistory = qBound(0, _history->getLines() - startLine, mergedLines);
762
    const int linesInScreen = mergedLines - linesInHistory;
763

764
765
    QVector<LineProperty> result(mergedLines);
    int index = 0;
766

Jekyll Wu's avatar
Jekyll Wu committed
767
    // copy properties for _lines in history
768
    for (int line = startLine; line < startLine + linesInHistory; ++line) {
769
        result[index] = _history->getLineProperty(line);
770
        ++index;
771
772
    }

Jekyll Wu's avatar
Jekyll Wu committed
773
774
    // copy properties for _lines in screen buffer
    const int firstScreenLine = startLine + linesInHistory - _history->getLines();
775
776
777
    for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; ++line) {
        result[index] = _lineProperties.at(line);
        ++index;
778
    }
779

780
    return result;
781
}
782

783
784
int Screen::getScreenLineColumns(const int line) const
{
785
    if