Screen.cpp 57.3 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
#include "profile/Profile.h"
30

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

40
41
using namespace Konsole;

42
// Macro to convert x,y position on screen to position within an image.
43
//
44
45
46
47
48
49
50
// 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.
51
#ifndef loc
52
#define loc(X, Y) ((Y)*_columns + (X))
53
54
#endif

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
86
87
88
89
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)
    , _escapeSequenceUrlExtractor(std::make_unique<EscapeSequenceUrlExtractor>())
90
{
91
    _escapeSequenceUrlExtractor->setScreen(this);
92
    std::fill(_lineProperties.begin(), _lineProperties.end(), LINE_DEFAULT);
93

94
    _graphicsPlacements = std::vector<std::unique_ptr<TerminalGraphicsPlacement_t>>();
95
96
    _hasGraphics = false;

97
98
99
    initTabStops();
    clearSelection();
    reset();
100
101
}

102
Screen::~Screen() = default;
103

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

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

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

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

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

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

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

197
int Screen::topMargin() const
198
{
199
    return _topMargin;
200
}
201
int Screen::bottomMargin() const
202
{
203
    return _bottomMargin;
204
205
}

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

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

226
void Screen::nextLine()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
227
//=NEL
228
{
229
230
    toStartOfLine();
    index();
231
232
}

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

245
246
247
248
249
250
251
252
253
void Screen::eraseBlock(int y, int x, int height, int width)
{
    width = qBound(0, width, _columns - x - 1);
    height = qBound(0, height, _lines - y - 1);
    for (int row = y; row < y + height; row++) {
        clearImage(loc(x, row), loc(x + width - 1, row), ' ', false);
    }
}

254
void Screen::deleteChars(int n)
255
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
256
    Q_ASSERT(n >= 0);
257

258
    // always delete at least one char
259
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
260
        n = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
261
    }
262

263
    // if cursor is beyond the end of the line there is nothing to do
264
    if (_cuX >= _screenLines.at(_cuY).count()) {
265
        return;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
266
    }
267

268
269
    if (_cuX + n > _screenLines.at(_cuY).count()) {
        n = _screenLines.at(_cuY).count() - _cuX;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
270
    }
271

Kurt Hindenburg's avatar
Kurt Hindenburg committed
272
    Q_ASSERT(n >= 0);
273
    Q_ASSERT(_cuX + n <= _screenLines.at(_cuY).count());
274

Jekyll Wu's avatar
Jekyll Wu committed
275
    _screenLines[_cuY].remove(_cuX, n);
276
277

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

280
    for (int i = 0; i < n; ++i) {
281
        _screenLines[_cuY].append(spaceWithCurrentAttrs);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
282
    }
283
284
}

285
void Screen::insertChars(int n)
286
{
287
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
288
289
        n = 1; // Default
    }
290

291
    if (_screenLines.at(_cuY).size() < _cuX) {
Jekyll Wu's avatar
Jekyll Wu committed
292
        _screenLines[_cuY].resize(_cuX);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
293
    }
294

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

297
    if (_screenLines.at(_cuY).count() > getScreenLineColumns(_cuY)) {
298
        _screenLines[_cuY].resize(getScreenLineColumns(_cuY));
Kurt Hindenburg's avatar
Kurt Hindenburg committed
299
    }
300
301
}

302
303
void Screen::repeatChars(int n)
{
304
    if (n < 1) {
305
306
307
308
309
310
311
312
313
314
        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.
315
    while (n > 0) {
316
        displayCharacter(_lastDrawnChar);
317
        --n;
318
319
320
    }
}

321
void Screen::deleteLines(int n)
322
{
323
324
325
    if (_cuY < _topMargin) {
        return;
    }
326
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
327
328
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
329
    scrollUp(_cuY, n);
330
331
}

332
void Screen::insertLines(int n)
333
{
334
335
336
    if (_cuY < _topMargin) {
        return;
    }
337
    if (n < 1) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
338
339
        n = 1; // Default
    }
Jekyll Wu's avatar
Jekyll Wu committed
340
    scrollDown(_cuY, n);
341
342
}

343
void Screen::setMode(int m)
344
{
345
    _currentModes[m] = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
346
    switch (m) {
347
    case MODE_Origin:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
348
349
        _cuX = 0;
        _cuY = _topMargin;
350
        break; // FIXME: home
351
    }
352
353
}

354
void Screen::resetMode(int m)
355
{
356
    _currentModes[m] = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
357
    switch (m) {
358
    case MODE_Origin:
Kurt Hindenburg's avatar
Kurt Hindenburg committed
359
360
        _cuX = 0;
        _cuY = 0;
361
        break; // FIXME: home
362
    }
363
364
}

365
void Screen::saveMode(int m)
366
{
Jekyll Wu's avatar
Jekyll Wu committed
367
    _savedModes[m] = _currentModes[m];
368
369
}

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

Robert Knight's avatar
   
Robert Knight committed
375
bool Screen::getMode(int m) const
376
{
377
    return _currentModes[m] != 0;
378
379
}

380
void Screen::saveCursor()
381
{
Jekyll Wu's avatar
Jekyll Wu committed
382
    _savedState.cursorColumn = _cuX;
383
    _savedState.cursorLine = _cuY;
Jekyll Wu's avatar
Jekyll Wu committed
384
385
386
    _savedState.rendition = _currentRendition;
    _savedState.foreground = _currentForeground;
    _savedState.background = _currentBackground;
387
    _savedState.originMode = _currentModes[MODE_Origin];
388
389
}

390
void Screen::restoreCursor()
391
{
392
393
394
395
396
    _cuY = qMin(_savedState.cursorLine, _lines - 1);
    _cuX = qMin(_savedState.cursorColumn, getScreenLineColumns(_cuY) - 1);
    _currentRendition = _savedState.rendition;
    _currentForeground = _savedState.foreground;
    _currentBackground = _savedState.background;
397
    updateEffectiveRendition();
398
399
400
401
402
403
404
405
406
    _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);
    }
    */
407
408
}

409
410
411
412
413
414
415
416
417
418
419
420
421
422
int Screen::getOldTotalLines()
{
    return _oldTotalLines;
}

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

Ahmad Samir's avatar
Ahmad Samir committed
423
void Screen::setReflowLines(bool enable)
424
425
426
427
{
    _enableReflowLines = enable;
}

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

int Screen::getCursorLine()
{
451
    if (isAppMode()) {
452
453
454
455
456
457
458
        return _savedState.cursorLine;
    }
    return _cuY;
}

void Screen::setCursorLine(int newLine)
{
459
    if (isAppMode()) {
460
        _savedState.cursorLine = newLine;
461
        _cuY = qBound(0, _cuY, _lines - 1);
462
463
464
465
466
    } else {
        _cuY = newLine;
    }
}

467
void Screen::resizeImage(int new_lines, int new_columns)
468
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
469
470
471
    if ((new_lines == _lines) && (new_columns == _columns)) {
        return;
    }
472

473
474
475
476
477
    // Adjust scroll position, and fix glitches
    _oldTotalLines = getLines() + getHistLines();
    _isResize = true;

    int cursorLine = getCursorLine();
478
    const int oldCursorLine = (cursorLine == _lines - 1 || cursorLine > new_lines - 1) ? new_lines - 1 : cursorLine;
479
480

    // Check if _history need to change
481
    if (_enableReflowLines && new_columns != _columns && _history->getLines() && _history->getMaxLines()) {
482
        // Join next line from _screenLine to _history
483
        while (!_screenLines.empty() && _history->isWrappedLine(_history->getLines() - 1)) {
484
            fastAddHistLine();
485
            --cursorLine;
486
        }
Carlos Alves's avatar
Carlos Alves committed
487
        auto removedLines = _history->reflowLines(new_columns);
488

Carlos Alves's avatar
Carlos Alves committed
489
490
491
492
        // 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
493
        }
494
495
    }

Carlos Alves's avatar
Carlos Alves committed
496
    if (_enableReflowLines && new_columns != _columns) {
Carlos Alves's avatar
Carlos Alves committed
497
498
        int cursorLineCorrection = 0;
        if (_currentTerminalDisplay) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
499
            // The 'zsh' works different from other shells when writing the command line.
Carlos Alves's avatar
Carlos Alves committed
500
501
502
            // It needs to identify the 'zsh' and calculate the new command line.
            auto sessionController = _currentTerminalDisplay->sessionController();
            auto terminal = sessionController->session()->foregroundProcessName();
503
504
            if (terminal == QLatin1String("zsh")) {
                while (cursorLine + cursorLineCorrection > 0 && (_lineProperties.at(cursorLine + cursorLineCorrection) & LINE_PROMPT_START) == 0) {
505
                    --cursorLineCorrection;
Carlos Alves's avatar
Carlos Alves committed
506
                }
507
508
509
510
511
512
513
514
                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
515
516
517
            }
        }

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

530
            // Ignore whitespaces at the end of the line
531
532
            int lineSize = _screenLines.at(currentPos).size();
            while (lineSize > 0 && QChar(_screenLines.at(currentPos).at(lineSize - 1).character).isSpace()) {
533
                --lineSize;
534
535
            }

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

    // Check if it need to move from _screenLine to _history
    while (cursorLine > new_lines - 1) {
Carlos Alves's avatar
Carlos Alves committed
551
        fastAddHistLine();
552
        --cursorLine;
553
    }
554
555
556
557
558
559
560

    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);
561
            LineProperty lineProperty = _history->getLineProperty(histPos);
562
563
            histLine.resize(histLineLen);
            _history->getCells(histPos, 0, histLineLen, histLine.data());
564
            _screenLines.insert(_screenLines.begin(), std::move(histLine));
565
            _lineProperties.insert(_lineProperties.begin(), lineProperty);
566
            _history->removeCells();
567
            ++cursorLine;
568
        }
569
570
571
    }

    _lineProperties.resize(new_lines + 1);
572
    if (_lineProperties.size() > _screenLines.size()) {
573
        std::fill(_lineProperties.begin() + _screenLines.size(), _lineProperties.end(), LINE_DEFAULT);
Carlos Alves's avatar
Carlos Alves committed
574
    }
575
    _screenLines.resize(new_lines + 1);
576

577
    _screenLinesSize = new_lines;
Jekyll Wu's avatar
Jekyll Wu committed
578
579
580
    _lines = new_lines;
    _columns = new_columns;
    _cuX = qMin(_cuX, _columns - 1);
581
582
    cursorLine = qBound(0, cursorLine, _lines - 1);
    setCursorLine(cursorLine);
583
584

    // FIXME: try to keep values, evtl.
585
    setDefaultMargins();
586
587
    initTabStops();
    clearSelection();
588
589
}

590
void Screen::setDefaultMargins()
591
{
592
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
593
    _bottomMargin = _lines - 1;
594
595
}

596
/*
Robert Knight's avatar
   
Robert Knight committed
597
   Clarifying rendition here and in the display.
598

599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
   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.
615
   */
616

617
void Screen::reverseRendition(Character &p) const
Kurt Hindenburg's avatar
Kurt Hindenburg committed
618
619
{
    CharacterColor f = p.foregroundColor;
620
    CharacterColor b = p.backgroundColor;
621

Kurt Hindenburg's avatar
Kurt Hindenburg committed
622
    p.foregroundColor = b;
623
    p.backgroundColor = f; // p->r &= ~RE_TRANSPARENT;
624
625
}

626
void Screen::updateEffectiveRendition()
627
{
Jekyll Wu's avatar
Jekyll Wu committed
628
    _effectiveRendition = _currentRendition;
629
    if ((_currentRendition & RE_REVERSE) != 0) {
Jekyll Wu's avatar
Jekyll Wu committed
630
631
        _effectiveForeground = _currentBackground;
        _effectiveBackground = _currentForeground;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
632
    } else {
Jekyll Wu's avatar
Jekyll Wu committed
633
634
        _effectiveForeground = _currentForeground;
        _effectiveBackground = _currentBackground;
635
636
    }

637
638
639
640
641
642
    if ((_currentRendition & RE_BOLD) != 0) {
        if ((_currentRendition & RE_FAINT) == 0) {
            _effectiveForeground.setIntensive();
        }
    } else {
        if ((_currentRendition & RE_FAINT) != 0) {
643
            _effectiveForeground.setFaint();
644
        }
645
    }
646
647
}

648
void Screen::copyFromHistory(Character *dest, int startLine, int count) const
649
{
650
    const int columns = _columns;
651

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

654
    for (int line = startLine; line < startLine + count; ++line) {
655
656
657
        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
658

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

661
        if (length < columns) {
662
            const int begin = destLineOffset + length;
663
            const int end = destLineOffset + columns;
664
            std::fill(dest + begin, dest + end, Screen::DefaultChar);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
665
        }
666
667

        // invert selected text
Jekyll Wu's avatar
Jekyll Wu committed
668
        if (_selBegin != -1) {
669
            for (int column = 0; column < lastColumn; ++column) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
670
                if (isSelected(column, line)) {
671
                    dest[destLineOffset + column].rendition |= RE_SELECTED;
672
                }
673
            }
674
        }
675
    }
Robert Knight's avatar
   
Robert Knight committed
676
677
}

678
void Screen::copyFromScreen(Character *dest, int startLine, int count) const
Robert Knight's avatar
   
Robert Knight committed
679
{
Ahmad Samir's avatar
Ahmad Samir committed
680
    const int endLine = startLine + count;
681
682
    const int columns = _columns;
    const int historyLines = _history->getLines();
Robert Knight's avatar
   
Robert Knight committed
683

Ahmad Samir's avatar
Ahmad Samir committed
684
685
686
    Q_ASSERT(startLine >= 0 && count > 0 && endLine <= _lines);

    for (int line = startLine; line < endLine; ++line) {
687
688
689
690
        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());
691

692
        std::copy(srcLine.cbegin(), srcLine.cbegin() + length, dest + destLineOffset);
693

694
695
696
697
698
        if (length < columns) {
            const int begin = destLineOffset + length;
            const int end = destLineOffset + columns;
            std::fill(dest + begin, dest + end, Screen::DefaultChar);
        }
699

700
701
702
        if (_selBegin != -1) {
            for (int column = 0; column < lastColumn; ++column) {
                if (isSelected(column, line + historyLines)) {
703
                    dest[destLineOffset + column].rendition |= RE_SELECTED;
704
                }
Kurt Hindenburg's avatar
Kurt Hindenburg committed
705
            }
706
        }
707
    }
Robert Knight's avatar
   
Robert Knight committed
708
709
}

710
void Screen::getImage(Character *dest, int size, int startLine, int endLine) const
Robert Knight's avatar
   
Robert Knight committed
711
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
712
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
713
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
714
715

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

Jekyll Wu's avatar
Jekyll Wu committed
717
    Q_ASSERT(size >= mergedLines * _columns);
718
    Q_UNUSED(size)
Robert Knight's avatar
   
Robert Knight committed
719

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

Jekyll Wu's avatar
Jekyll Wu committed
723
    // copy _lines from history buffer
Kurt Hindenburg's avatar
Kurt Hindenburg committed
724
    if (linesInHistoryBuffer > 0) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
725
        copyFromHistory(dest, startLine, linesInHistoryBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
726
    }
Robert Knight's avatar
   
Robert Knight committed
727

Jekyll Wu's avatar
Jekyll Wu committed
728
    // copy _lines from screen buffer
Kurt Hindenburg's avatar
Kurt Hindenburg committed
729
    if (linesInScreenBuffer > 0) {
730
        copyFromScreen(dest + linesInHistoryBuffer * _columns, startLine + linesInHistoryBuffer - _history->getLines(), linesInScreenBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
731
    }
Robert Knight's avatar
   
Robert Knight committed
732

733
    // invert display when in screen mode
Kurt Hindenburg's avatar
Kurt Hindenburg committed
734
    if (getMode(MODE_Screen)) {
735
        for (int i = 0; i < mergedLines * _columns; ++i) {
736
            reverseRendition(dest[i]); // for reverse display
Kurt Hindenburg's avatar
Kurt Hindenburg committed
737
        }
738
    }
739

740
    int visX = qMin(_cuX, getScreenLineColumns(_cuY) - 1);
741
    // mark the character at the current cursor position
742
    int cursorIndex = loc(visX, _cuY + linesInHistoryBuffer);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
743
    if (getMode(MODE_Cursor) && cursorIndex < _columns * mergedLines) {
744
        dest[cursorIndex].rendition |= RE_CURSOR;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
745
    }
746
747
}

748
QVector<LineProperty> Screen::getLineProperties(int startLine, int endLine) const
749
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
750
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
751
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
752

Kurt Hindenburg's avatar
Kurt Hindenburg committed
753
    const int mergedLines = endLine - startLine + 1;
Jekyll Wu's avatar
Jekyll Wu committed
754
    const int linesInHistory = qBound(0, _history->getLines() - startLine, mergedLines);
755
    const int linesInScreen = mergedLines - linesInHistory;
756

757
758
    QVector<LineProperty> result(mergedLines);
    int index = 0;
759

Jekyll Wu's avatar
Jekyll Wu committed
760
    // copy properties for _lines in history
761
    for (int line = startLine; line < startLine + linesInHistory; ++line) {
762
        result[index] = _history->getLineProperty(line);
763
        ++index;
764
765
    }

Jekyll Wu's avatar
Jekyll Wu committed
766
767
    // copy properties for _lines in screen buffer
    const int firstScreenLine = startLine + linesInHistory - _history->getLines();
768
769
770
    for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; ++line) {
        result[index] = _lineProperties.at(line);
        ++index;
771
    }
772

773
    return result;
774
}
775

776
777
int Screen::getScreenLineColumns(const int line) const
{
778
    if (line < (int)_lineProperties.size() && _lineProperties.at(line) & LINE_DOUBLEWIDTH) {
779
780
781
782
783
784
        return _columns / 2;
    }

    return _columns;
}

785
void Screen::reset(bool softReset)
786
{
787
    // Clear screen, but preserve the current line
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
    if (!softReset) {
        scrollUp(0, _cuY);
        _cuY = 0;
        _cuX = 0;

        resetMode(MODE_Screen