Screen.cpp 38.6 KB
Newer Older
1
/*
2
   This file is part of Konsole, an X terminal.
3

4 5
   Copyright 2007-2008 by Robert Knight <robert.knight@gmail.com>
   Copyright 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
6

7 8 9 10
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
11

12 13 14 15
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
16

17 18 19 20 21
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301  USA.
   */
22

23 24
// Own
#include "Screen.h"
25

26
// Qt
Dirk Mueller's avatar
Dirk Mueller committed
27
#include <QtCore/QTextStream>
28

29
// Konsole
30
#include "konsole_wcwidth.h"
31
#include "TerminalCharacterDecoder.h"
32
#include "History.h"
33
#include "ExtendedCharTable.h"
34

35 36
using namespace Konsole;

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

Jekyll Wu's avatar
Jekyll Wu committed
50
const Character Screen::DefaultChar = Character(' ',
Kurt Hindenburg's avatar
Kurt Hindenburg committed
51 52 53 54
                                      CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR),
                                      CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR),
                                      DEFAULT_RENDITION,
                                      false);
55

Jekyll Wu's avatar
Jekyll Wu committed
56 57 58 59
Screen::Screen(int lines, int columns):
    _lines(lines),
    _columns(columns),
    _screenLines(new ImageLine[_lines + 1]),
60
    _screenLinesSize(_lines),
61
    _scrolledLines(0),
62
    _droppedLines(0),
Jekyll Wu's avatar
Jekyll Wu committed
63
    _history(new HistoryScrollNone()),
Jekyll Wu's avatar
Jekyll Wu committed
64 65 66 67 68 69 70 71
    _cuX(0),
    _cuY(0),
    _currentRendition(DEFAULT_RENDITION),
    _topMargin(0),
    _bottomMargin(0),
    _selBegin(0),
    _selTopLeft(0),
    _selBottomRight(0),
Jekyll Wu's avatar
Jekyll Wu committed
72
    _blockSelectionMode(false),
Jekyll Wu's avatar
Jekyll Wu committed
73 74 75
    _effectiveForeground(CharacterColor()),
    _effectiveBackground(CharacterColor()),
    _effectiveRendition(DEFAULT_RENDITION),
Jekyll Wu's avatar
Jekyll Wu committed
76
    _lastPos(-1)
77
{
Jekyll Wu's avatar
Jekyll Wu committed
78 79 80
    _lineProperties.resize(_lines + 1);
    for (int i = 0; i < _lines + 1; i++)
        _lineProperties[i] = LINE_DEFAULT;
81

82 83 84
    initTabStops();
    clearSelection();
    reset();
85 86
}

87
Screen::~Screen()
88
{
Jekyll Wu's avatar
Jekyll Wu committed
89 90
    delete[] _screenLines;
    delete _history;
91 92
}

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

102
void Screen::cursorDown(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
103
//=CUD
104
{
105
    if (n == 0) n = 1; // Default
Jekyll Wu's avatar
Jekyll Wu committed
106 107 108
    const int stop = _cuY > _bottomMargin ? _lines - 1 : _bottomMargin;
    _cuX = qMin(_columns - 1, _cuX); // nowrap!
    _cuY = qMin(stop, _cuY + n);
109 110
}

111
void Screen::cursorLeft(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
112
//=CUB
113
{
114
    if (n == 0) n = 1; // Default
Jekyll Wu's avatar
Jekyll Wu committed
115 116
    _cuX = qMin(_columns - 1, _cuX); // nowrap!
    _cuX = qMax(0, _cuX - n);
117 118
}

119
void Screen::cursorRight(int n)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
120
//=CUF
121
{
122
    if (n == 0) n = 1; // Default
Jekyll Wu's avatar
Jekyll Wu committed
123
    _cuX = qMin(_columns - 1, _cuX + n);
124 125
}

126
void Screen::setMargins(int top, int bot)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
127
//=STBM
128 129
{
    if (top == 0) top = 1;      // Default
Jekyll Wu's avatar
Jekyll Wu committed
130
    if (bot == 0) bot = _lines;  // Default
131 132
    top = top - 1;              // Adjust to internal lineno
    bot = bot - 1;              // Adjust to internal lineno
Jekyll Wu's avatar
Jekyll Wu committed
133
    if (!(0 <= top && top < bot && bot < _lines)) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
134
        //Debug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
135 136 137 138
        return;                   // Default error action: ignore
    }
    _topMargin = top;
    _bottomMargin = bot;
Jekyll Wu's avatar
Jekyll Wu committed
139 140
    _cuX = 0;
    _cuY = getMode(MODE_Origin) ? top : 0;
141 142
}

143
int Screen::topMargin() const
144
{
145
    return _topMargin;
146
}
147
int Screen::bottomMargin() const
148
{
149
    return _bottomMargin;
150 151
}

152
void Screen::index()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
153
//=IND
154
{
Jekyll Wu's avatar
Jekyll Wu committed
155
    if (_cuY == _bottomMargin)
156
        scrollUp(1);
Jekyll Wu's avatar
Jekyll Wu committed
157 158
    else if (_cuY < _lines - 1)
        _cuY += 1;
159 160
}

161
void Screen::reverseIndex()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
162
//=RI
163
{
Jekyll Wu's avatar
Jekyll Wu committed
164
    if (_cuY == _topMargin)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
165
        scrollDown(_topMargin, 1);
Jekyll Wu's avatar
Jekyll Wu committed
166 167
    else if (_cuY > 0)
        _cuY -= 1;
168 169
}

170
void Screen::nextLine()
Kurt Hindenburg's avatar
Kurt Hindenburg committed
171
//=NEL
172
{
173 174
    toStartOfLine();
    index();
175 176
}

177
void Screen::eraseChars(int n)
178
{
179
    if (n == 0) n = 1; // Default
Jekyll Wu's avatar
Jekyll Wu committed
180 181
    const int p = qMax(0, qMin(_cuX + n - 1, _columns - 1));
    clearImage(loc(_cuX, _cuY), loc(p, _cuY), ' ');
182 183
}

184
void Screen::deleteChars(int n)
185
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
186
    Q_ASSERT(n >= 0);
187

188
    // always delete at least one char
Kurt Hindenburg's avatar
Kurt Hindenburg committed
189 190
    if (n == 0)
        n = 1;
191

192
    // if cursor is beyond the end of the line there is nothing to do
Jekyll Wu's avatar
Jekyll Wu committed
193
    if (_cuX >= _screenLines[_cuY].count())
194
        return;
195

Jekyll Wu's avatar
Jekyll Wu committed
196 197
    if (_cuX + n > _screenLines[_cuY].count())
        n = _screenLines[_cuY].count() - _cuX;
198

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

Jekyll Wu's avatar
Jekyll Wu committed
202
    _screenLines[_cuY].remove(_cuX, n);
203 204 205 206 207 208 209 210

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

    for (int i = 0; i < n; i++)
        _screenLines[_cuY].append(spaceWithCurrentAttrs);
211 212
}

213
void Screen::insertChars(int n)
214
{
215
    if (n == 0) n = 1; // Default
216

Jekyll Wu's avatar
Jekyll Wu committed
217 218
    if (_screenLines[_cuY].size() < _cuX)
        _screenLines[_cuY].resize(_cuX);
219

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

Jekyll Wu's avatar
Jekyll Wu committed
222 223
    if (_screenLines[_cuY].count() > _columns)
        _screenLines[_cuY].resize(_columns);
224 225
}

226
void Screen::deleteLines(int n)
227
{
228
    if (n == 0) n = 1; // Default
Jekyll Wu's avatar
Jekyll Wu committed
229
    scrollUp(_cuY, n);
230 231
}

232
void Screen::insertLines(int n)
233
{
234
    if (n == 0) n = 1; // Default
Jekyll Wu's avatar
Jekyll Wu committed
235
    scrollDown(_cuY, n);
236 237
}

238
void Screen::setMode(int m)
239
{
240
    _currentModes[m] = 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
241
    switch (m) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
242 243 244 245
    case MODE_Origin :
        _cuX = 0;
        _cuY = _topMargin;
        break; //FIXME: home
246
    }
247 248
}

249
void Screen::resetMode(int m)
250
{
251
    _currentModes[m] = 0;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
252
    switch (m) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
253 254 255 256
    case MODE_Origin :
        _cuX = 0;
        _cuY = 0;
        break; //FIXME: home
257
    }
258 259
}

260
void Screen::saveMode(int m)
261
{
Jekyll Wu's avatar
Jekyll Wu committed
262
    _savedModes[m] = _currentModes[m];
263 264
}

265
void Screen::restoreMode(int m)
266
{
Jekyll Wu's avatar
Jekyll Wu committed
267
    _currentModes[m] = _savedModes[m];
268 269
}

Robert Knight's avatar
 
Robert Knight committed
270
bool Screen::getMode(int m) const
271
{
272
    return _currentModes[m] != 0;
273 274
}

275
void Screen::saveCursor()
276
{
Jekyll Wu's avatar
Jekyll Wu committed
277 278 279 280 281
    _savedState.cursorColumn = _cuX;
    _savedState.cursorLine  = _cuY;
    _savedState.rendition = _currentRendition;
    _savedState.foreground = _currentForeground;
    _savedState.background = _currentBackground;
282 283
}

284
void Screen::restoreCursor()
285
{
Jekyll Wu's avatar
Jekyll Wu committed
286 287 288 289 290
    _cuX     = qMin(_savedState.cursorColumn, _columns - 1);
    _cuY     = qMin(_savedState.cursorLine, _lines - 1);
    _currentRendition   = _savedState.rendition;
    _currentForeground   = _savedState.foreground;
    _currentBackground   = _savedState.background;
291
    updateEffectiveRendition();
292 293
}

294
void Screen::resizeImage(int new_lines, int new_columns)
295
{
Jekyll Wu's avatar
Jekyll Wu committed
296
    if ((new_lines == _lines) && (new_columns == _columns)) return;
297

Jekyll Wu's avatar
Jekyll Wu committed
298 299 300 301
    if (_cuY > new_lines - 1) {
        // attempt to preserve focus and _lines
        _bottomMargin = _lines - 1; //FIXME: margin lost
        for (int i = 0; i < _cuY - (new_lines - 1); i++) {
302 303
            addHistLine();
            scrollUp(0, 1);
304
        }
305
    }
306

Jekyll Wu's avatar
Jekyll Wu committed
307
    // create new screen _lines and copy from old to new
308

Kurt Hindenburg's avatar
Kurt Hindenburg committed
309
    auto newScreenLines = new ImageLine[new_lines + 1];
Jekyll Wu's avatar
Jekyll Wu committed
310 311 312
    for (int i = 0; i < qMin(_lines, new_lines + 1) ; i++)
        newScreenLines[i] = _screenLines[i];
    for (int i = _lines; (i > 0) && (i < new_lines + 1); i++)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
313
        newScreenLines[i].resize(new_columns);
314

Jekyll Wu's avatar
Jekyll Wu committed
315 316 317
    _lineProperties.resize(new_lines + 1);
    for (int i = _lines; (i > 0) && (i < new_lines + 1); i++)
        _lineProperties[i] = LINE_DEFAULT;
318 319 320

    clearSelection();

Jekyll Wu's avatar
Jekyll Wu committed
321 322
    delete[] _screenLines;
    _screenLines = newScreenLines;
323
    _screenLinesSize = new_lines;
324

Jekyll Wu's avatar
Jekyll Wu committed
325 326 327 328
    _lines = new_lines;
    _columns = new_columns;
    _cuX = qMin(_cuX, _columns - 1);
    _cuY = qMin(_cuY, _lines - 1);
329 330

    // FIXME: try to keep values, evtl.
Kurt Hindenburg's avatar
Kurt Hindenburg committed
331
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
332
    _bottomMargin = _lines - 1;
333 334
    initTabStops();
    clearSelection();
335 336
}

337
void Screen::setDefaultMargins()
338
{
339
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
340
    _bottomMargin = _lines - 1;
341 342
}

343
/*
Robert Knight's avatar
 
Robert Knight committed
344
   Clarifying rendition here and in the display.
345

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
   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.
362
   */
363

Robert Knight's avatar
 
Robert Knight committed
364
void Screen::reverseRendition(Character& p) const
Kurt Hindenburg's avatar
Kurt Hindenburg committed
365 366
{
    CharacterColor f = p.foregroundColor;
367
    CharacterColor b = p.backgroundColor;
368

Kurt Hindenburg's avatar
Kurt Hindenburg committed
369
    p.foregroundColor = b;
370
    p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT;
371 372
}

373
void Screen::updateEffectiveRendition()
374
{
Jekyll Wu's avatar
Jekyll Wu committed
375
    _effectiveRendition = _currentRendition;
376
    if ((_currentRendition & RE_REVERSE) != 0) {
Jekyll Wu's avatar
Jekyll Wu committed
377 378
        _effectiveForeground = _currentBackground;
        _effectiveBackground = _currentForeground;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
379
    } else {
Jekyll Wu's avatar
Jekyll Wu committed
380 381
        _effectiveForeground = _currentForeground;
        _effectiveBackground = _currentBackground;
382 383
    }

384 385
    if ((_currentRendition & RE_BOLD) != 0) {
        if ((_currentRendition & RE_FAINT) == 0)
386 387
            _effectiveForeground.setIntensive();
    } else {
388
        if ((_currentRendition & RE_FAINT) != 0)
389 390
            _effectiveForeground.setFaint();
    }
391 392
}

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
397
    for (int line = startLine; line < startLine + count; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
398 399
        const int length = qMin(_columns, _history->getLineLen(line));
        const int destLineOffset  = (line - startLine) * _columns;
Robert Knight's avatar
 
Robert Knight committed
400

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

Jekyll Wu's avatar
Jekyll Wu committed
403
        for (int column = length; column < _columns; column++)
Jekyll Wu's avatar
Jekyll Wu committed
404
            dest[destLineOffset + column] = Screen::DefaultChar;
405 406

        // invert selected text
Jekyll Wu's avatar
Jekyll Wu committed
407 408
        if (_selBegin != -1) {
            for (int column = 0; column < _columns; column++) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
409 410
                if (isSelected(column, line)) {
                    reverseRendition(dest[destLineOffset + column]);
411
                }
412
            }
413
        }
414
    }
Robert Knight's avatar
 
Robert Knight committed
415 416 417 418
}

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

Kurt Hindenburg's avatar
Kurt Hindenburg committed
421
    for (int line = startLine; line < (startLine + count) ; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
422 423
        int srcLineStartIndex  = line * _columns;
        int destLineStartIndex = (line - startLine) * _columns;
424

Jekyll Wu's avatar
Jekyll Wu committed
425
        for (int column = 0; column < _columns; column++) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
426
            int srcIndex = srcLineStartIndex + column;
427
            int destIndex = destLineStartIndex + column;
428

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

431
            // invert selected text
Jekyll Wu's avatar
Jekyll Wu committed
432
            if (_selBegin != -1 && isSelected(column, line + _history->getLines()))
Kurt Hindenburg's avatar
Kurt Hindenburg committed
433
                reverseRendition(dest[destIndex]);
434
        }
435
    }
Robert Knight's avatar
 
Robert Knight committed
436 437
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
438
void Screen::getImage(Character* dest, int size, int startLine, int endLine) const
Robert Knight's avatar
 
Robert Knight committed
439
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
440
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
441
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
442 443

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

Jekyll Wu's avatar
Jekyll Wu committed
445
    Q_ASSERT(size >= mergedLines * _columns);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
446
    Q_UNUSED(size);
Robert Knight's avatar
 
Robert Knight committed
447

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

Jekyll Wu's avatar
Jekyll Wu committed
451
    // copy _lines from history buffer
452
    if (linesInHistoryBuffer > 0)
Kurt Hindenburg's avatar
Kurt Hindenburg committed
453
        copyFromHistory(dest, startLine, linesInHistoryBuffer);
Robert Knight's avatar
 
Robert Knight committed
454

Jekyll Wu's avatar
Jekyll Wu committed
455
    // copy _lines from screen buffer
456
    if (linesInScreenBuffer > 0)
Jekyll Wu's avatar
Jekyll Wu committed
457 458
        copyFromScreen(dest + linesInHistoryBuffer * _columns,
                       startLine + linesInHistoryBuffer - _history->getLines(),
Kurt Hindenburg's avatar
Kurt Hindenburg committed
459
                       linesInScreenBuffer);
Robert Knight's avatar
 
Robert Knight committed
460

461
    // invert display when in screen mode
Kurt Hindenburg's avatar
Kurt Hindenburg committed
462
    if (getMode(MODE_Screen)) {
Jekyll Wu's avatar
Jekyll Wu committed
463
        for (int i = 0; i < mergedLines * _columns; i++)
464 465
            reverseRendition(dest[i]); // for reverse display
    }
466

467
    // mark the character at the current cursor position
Jekyll Wu's avatar
Jekyll Wu committed
468 469
    int cursorIndex = loc(_cuX, _cuY + linesInHistoryBuffer);
    if (getMode(MODE_Cursor) && cursorIndex < _columns * mergedLines)
470
        dest[cursorIndex].rendition |= RE_CURSOR;
471 472
}

Kurt Hindenburg's avatar
Kurt Hindenburg committed
473
QVector<LineProperty> Screen::getLineProperties(int startLine , int endLine) const
474
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
475
    Q_ASSERT(startLine >= 0);
Jekyll Wu's avatar
Jekyll Wu committed
476
    Q_ASSERT(endLine >= startLine && endLine < _history->getLines() + _lines);
477

Kurt Hindenburg's avatar
Kurt Hindenburg committed
478
    const int mergedLines = endLine - startLine + 1;
Jekyll Wu's avatar
Jekyll Wu committed
479
    const int linesInHistory = qBound(0, _history->getLines() - startLine, mergedLines);
480
    const int linesInScreen = mergedLines - linesInHistory;
481

482 483
    QVector<LineProperty> result(mergedLines);
    int index = 0;
484

Jekyll Wu's avatar
Jekyll Wu committed
485
    // copy properties for _lines in history
Kurt Hindenburg's avatar
Kurt Hindenburg committed
486
    for (int line = startLine; line < startLine + linesInHistory; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
487 488
        //TODO Support for line properties other than wrapped _lines
        if (_history->isWrappedLine(line)) {
489 490 491 492 493
            result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
        }
        index++;
    }

Jekyll Wu's avatar
Jekyll Wu committed
494 495
    // copy properties for _lines in screen buffer
    const int firstScreenLine = startLine + linesInHistory - _history->getLines();
Kurt Hindenburg's avatar
Kurt Hindenburg committed
496
    for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; line++) {
Jekyll Wu's avatar
Jekyll Wu committed
497
        result[index] = _lineProperties[line];
498
        index++;
499
    }
500

501
    return result;
502
}
503

504
void Screen::reset()
505
{
506 507 508 509
    // Clear screen, but preserve the current line
    scrollUp(0, _cuY);
    _cuY = 0;

510 511
    _currentModes[MODE_Origin] = 0;
    _savedModes[MODE_Origin] = 0;
512

513 514 515 516 517 518
    setMode(MODE_Wrap);
    saveMode(MODE_Wrap);      // wrap at end of margin

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

519
    setMode(MODE_Cursor);                         // cursor visible
520 521
    resetMode(MODE_Screen);                         // screen not inverse
    resetMode(MODE_NewLine);
522

Kurt Hindenburg's avatar
Kurt Hindenburg committed
523
    _topMargin = 0;
Jekyll Wu's avatar
Jekyll Wu committed
524
    _bottomMargin = _lines - 1;
525

526 527 528
    // Other terminal emulators reset the entire scroll history during a reset
//    setScroll(getScroll(), false);

529 530
    setDefaultRendition();
    saveCursor();
531 532
}

533
void Screen::clear()
534
{
535 536
    clearEntireScreen();
    home();
537 538
}

539
void Screen::backspace()
540
{
Jekyll Wu's avatar
Jekyll Wu committed
541 542
    _cuX = qMin(_columns - 1, _cuX); // nowrap!
    _cuX = qMax(0, _cuX - 1);
543

Jekyll Wu's avatar
Jekyll Wu committed
544 545
    if (_screenLines[_cuY].size() < _cuX + 1)
        _screenLines[_cuY].resize(_cuX + 1);
546 547
}

548
void Screen::tab(int n)
549
{
550 551
    // note that TAB is a format effector (does not write ' ');
    if (n == 0) n = 1;
Jekyll Wu's avatar
Jekyll Wu committed
552
    while ((n > 0) && (_cuX < _columns - 1)) {
Kurt Hindenburg's avatar
Kurt Hindenburg committed
553
        cursorRight(1);
Jekyll Wu's avatar
Jekyll Wu committed
554
        while ((_cuX < _columns - 1) && !_tabStops[_cuX])
555 556 557
            cursorRight(1);
        n--;
    }
558 559
}

560
void Screen::backtab(int n)
561
{
562 563
    // note that TAB is a format effector (does not write ' ');
    if (n == 0) n = 1;
Jekyll Wu's avatar
Jekyll Wu committed
564
    while ((n > 0) && (_cuX > 0)) {
565 566 567 568
        cursorLeft(1);
        while ((_cuX > 0) && !_tabStops[_cuX]) {
            cursorLeft(1);
        }
569 570
        n--;
    }
571 572
}

573
void Screen::clearTabStops()
574
{
Jekyll Wu's avatar
Jekyll Wu committed
575 576
    for (int i = 0; i < _columns; i++)
        _tabStops[i] = false;
577 578
}

579
void Screen::changeTabStop(bool set)
580
{
Jekyll Wu's avatar
Jekyll Wu committed
581
    if (_cuX >= _columns)
582 583
        return;

Jekyll Wu's avatar
Jekyll Wu committed
584
    _tabStops[_cuX] = set;
585 586
}

587
void Screen::initTabStops()
588
{
Jekyll Wu's avatar
Jekyll Wu committed
589
    _tabStops.resize(_columns);
590

591
    // The 1st tabstop has to be one longer than the other.
592 593
    // i.e. the kids start counting from 0 instead of 1.
    // Other programs might behave correctly. Be aware.
Jekyll Wu's avatar
Jekyll Wu committed
594 595
    for (int i = 0; i < _columns; i++)
        _tabStops[i] = (i % 8 == 0 && i != 0);
596 597
}

598
void Screen::newLine()
599
{
Kurt Hindenburg's avatar
Kurt Hindenburg committed
600
    if (getMode(MODE_NewLine))
601
        toStartOfLine();
602

603
    index();
604 605
}

606
void Screen::checkSelection(int from, int to)
607
{
Jekyll Wu's avatar
Jekyll Wu committed
608
    if (_selBegin == -1)
609
        return;
Jekyll Wu's avatar
Jekyll Wu committed
610
    const int scr_TL = loc(0, _history->getLines());
611
    //Clear entire selection if it overlaps region [from, to]
Jekyll Wu's avatar
Jekyll Wu committed
612
    if ((_selBottomRight >= (from + scr_TL)) && (_selTopLeft <= (to + scr_TL)))
613
        clearSelection();
614 615
}

616
void Screen::displayCharacter(unsigned short c)
617
{
618 619 620 621
    // Note that VT100 does wrapping BEFORE putting the character.
    // This has impact on the assumption of valid cursor positions.
    // We indicate the fact that a newline has to be triggered by
    // putting the cursor one right to the last column of the screen.
622

623
    int w = konsole_wcwidth(c);
624 625 626

    if (w < 0) {
        // Non-printable character
627
        return;
628 629
    } else if (w == 0) {
        const QChar::Category category = QChar(c).category();
630
        if (category != QChar::Mark_NonSpacing && category != QChar::Letter_Other && !QChar::isLowSurrogate(c)) {
631
            return;
632 633
        }
        // Find previous "real character" to try to combine with
634
        int charToCombineWithX = qMin(_cuX, _screenLines[_cuY].length());
635 636 637 638 639 640 641
        int charToCombineWithY = _cuY;
        do {
            if (charToCombineWithX > 0) {
                charToCombineWithX--;
            } else if (charToCombineWithY > 0) { // Try previous line
                charToCombineWithY--;
                charToCombineWithX = _screenLines[charToCombineWithY].length() - 1;
Kurt Hindenburg's avatar
Kurt Hindenburg committed
642
            } else {
643
                // Give up
644 645
                return;
            }
646

647 648 649 650 651
            // Failsafe
            if (charToCombineWithX < 0) {
                return;
            }
        } while(!_screenLines[charToCombineWithY][charToCombineWithX].isRealCharacter);
652

Jekyll Wu's avatar
Jekyll Wu committed
653
        Character& currentChar = _screenLines[charToCombineWithY][charToCombineWithX];
Kurt Hindenburg's avatar
Kurt Hindenburg committed
654
        if ((currentChar.rendition & RE_EXTENDED_CHAR) == 0) {
655 656 657
            const ushort chars[2] = { currentChar.character, c };
            currentChar.rendition |= RE_EXTENDED_CHAR;
            currentChar.character = ExtendedCharTable::instance.createExtendedChar(chars, 2);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
658
        } else {
659 660 661
            ushort extendedCharLength;
            const ushort* oldChars = ExtendedCharTable::instance.lookupExtendedChar(currentChar.character, extendedCharLength);
            Q_ASSERT(oldChars);
662
            if (((oldChars) != nullptr) && extendedCharLength < 3) {
663 664
                Q_ASSERT(extendedCharLength > 1);
                Q_ASSERT(extendedCharLength < 65535);
Kurt Hindenburg's avatar
Kurt Hindenburg committed
665
                auto chars = new ushort[extendedCharLength + 1];
Kurt Hindenburg's avatar
Kurt Hindenburg committed
666
                memcpy(chars, oldChars, sizeof(ushort) * extendedCharLength);
667 668 669 670 671
                chars[extendedCharLength] = c;
                currentChar.character = ExtendedCharTable::instance.createExtendedChar(chars, extendedCharLength + 1);
                delete[] chars;
            }
        }
672
        return;
673
    }
674

Jekyll Wu's avatar
Jekyll Wu committed
675
    if (_cuX + w > _columns) {
676
        if (getMode(MODE_Wrap)) {
Jekyll Wu's avatar
Jekyll Wu committed
677
            _lineProperties[_cuY] = (LineProperty)(_lineProperties[_cuY] | LINE_WRAPPED);
678
            nextLine();
679
        } else {
Jekyll Wu's avatar
Jekyll Wu committed
680
            _cuX = _columns - w;
681
        }
682
    }
Waldo Bastian's avatar
Waldo Bastian committed
683

684
    // ensure current line vector has enough elements
Jekyll Wu's avatar
Jekyll Wu committed
685 686
    if (_screenLines[_cuY].size() < _cuX + w) {
        _screenLines[_cuY].resize(_cuX + w);
687
    }
688

689 690
    if (getMode(MODE_Insert)) insertChars(w);

Jekyll Wu's avatar
Jekyll Wu committed
691
    _lastPos = loc(_cuX, _cuY);
692

693
    // check if selection is still valid.
Jekyll Wu's avatar
Jekyll Wu committed
694
    checkSelection(_lastPos, _lastPos);
695

Jekyll Wu's avatar
Jekyll Wu committed
696
    Character& currentChar = _screenLines[_cuY][_cuX];
697

698
    currentChar.character = c;
Jekyll Wu's avatar
Jekyll Wu committed
699 700 701
    currentChar.foregroundColor = _effectiveForeground;
    currentChar.backgroundColor = _effectiveBackground;
    currentChar.rendition = _effectiveRendition;
702
    currentChar.isRealCharacter = true;
703

704
    int i = 0;
Jekyll Wu's avatar
Jekyll Wu committed
705
    const int newCursorX = _cuX + w--;
706
    while (w != 0) {
707 708
        i++;

Jekyll Wu's avatar
Jekyll Wu committed
709 710
        if (_screenLines[_cuY].size() < _cuX + i + 1)
            _screenLines[_cuY].resize(_cuX + i + 1);
711

Jekyll Wu's avatar
Jekyll Wu committed