MauiKit Terminal

Screen.cpp
1/*
2 This file is part of Konsole, an X terminal.
3
4 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robert.knight@gmail.com>
5 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301 USA.
18 */
19
20// Own
21#include "Screen.h"
22
23// Standard
24#include <cctype>
25#include <cstdio>
26#include <cstdlib>
27#include <cstring>
28#include <unistd.h>
29
30// Qt
31#include <QDate>
32#include <QTextStream>
33
34// KDE
35// #include <kdebug.h>
36
37// Konsole
38#include "TerminalCharacterDecoder.h"
39#include "konsole_wcwidth.h"
40
41using namespace Konsole;
42
43// FIXME: this is emulation specific. Use false for xterm, true for ANSI.
44// FIXME: see if we can get this from terminfo.
45#define BS_CLEARS false
46
47// Macro to convert x,y position on screen to position within an image.
48//
49// Originally the image was stored as one large contiguous block of
50// memory, so a position within the image could be represented as an
51// offset from the beginning of the block. For efficiency reasons this
52// is no longer the case.
53// Many internal parts of this class still use this representation for parameters and so on,
54// notably moveImage() and clearImage().
55// This macro converts from an X,Y position into an image offset.
56#ifndef loc
57#define loc(X, Y) ((Y)*columns + (X))
58#endif
59
60Character Screen::defaultChar =
61 Character(' ', CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR), CharacterColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR), DEFAULT_RENDITION);
62
63// #define REVERSE_WRAPPED_LINES // for wrapped line debug
64
65Screen::Screen(int l, int c)
66 : lines(l)
67 , columns(c)
68 , screenLines(lines + 1)
69 , _scrolledLines(0)
70 , _droppedLines(0)
71 , history(std::make_unique<HistoryScrollNone>())
72 , cuX(0)
73 , cuY(0)
74 , currentRendition(0)
75 , _topMargin(0)
76 , _bottomMargin(0)
77 , selBegin(0)
78 , selTopLeft(0)
79 , selBottomRight(0)
80 , blockSelectionMode(false)
81 , effectiveForeground(CharacterColor())
82 , effectiveBackground(CharacterColor())
83 , effectiveRendition(0)
84 , lastPos(-1)
85{
86 lineProperties.resize(lines + 1);
87 for (int i = 0; i < lines + 1; i++)
88 lineProperties[i] = LINE_DEFAULT;
89
90 initTabStops();
92 reset();
93}
94
95/*! Destructor
96 */
97
99{
100}
101
103//=CUU
104{
105 if (n == 0)
106 n = 1; // Default
107 int stop = cuY < _topMargin ? 0 : _topMargin;
108 cuX = qMin(columns - 1, cuX); // nowrap!
109 cuY = qMax(stop, cuY - n);
110}
111
113//=CUD
114{
115 if (n == 0)
116 n = 1; // Default
117 int stop = cuY > _bottomMargin ? lines - 1 : _bottomMargin;
118 cuX = qMin(columns - 1, cuX); // nowrap!
119 cuY = qMin(stop, cuY + n);
120}
121
123//=CUB
124{
125 if (n == 0)
126 n = 1; // Default
127 cuX = qMin(columns - 1, cuX); // nowrap!
128 cuX = qMax(0, cuX - n);
129}
130
132//=CUF
133{
134 if (n == 0)
135 n = 1; // Default
136 cuX = qMin(columns - 1, cuX + n);
137}
138
139void Screen::setMargins(int top, int bot)
140//=STBM
141{
142 if (top == 0)
143 top = 1; // Default
144 if (bot == 0)
145 bot = lines; // Default
146 top = top - 1; // Adjust to internal lineno
147 bot = bot - 1; // Adjust to internal lineno
148 if (!(0 <= top && top < bot && bot < lines)) { // Debug()<<" setRegion("<<top<<","<<bot<<") : bad range.";
149 return; // Default error action: ignore
150 }
151 _topMargin = top;
152 _bottomMargin = bot;
153 cuX = 0;
154 cuY = getMode(MODE_Origin) ? top : 0;
155}
156
158{
159 return _topMargin;
160}
162{
163 return _bottomMargin;
164}
165
167//=IND
168{
169 if (cuY == _bottomMargin)
170 scrollUp(1);
171 else if (cuY < lines - 1)
172 cuY += 1;
173}
174
176//=RI
177{
178 if (cuY == _topMargin)
179 scrollDown(_topMargin, 1);
180 else if (cuY > 0)
181 cuY -= 1;
182}
183
185//=NEL
186{
188 index();
189}
190
192{
193 if (n == 0)
194 n = 1; // Default
195 int p = qMax(0, qMin(cuX + n - 1, columns - 1));
196 clearImage(loc(cuX, cuY), loc(p, cuY), ' ');
197}
198
200{
201 Q_ASSERT(n >= 0);
202
203 // always delete at least one char
204 if (n == 0)
205 n = 1;
206
207 // if cursor is beyond the end of the line there is nothing to do
208 if (cuX >= screenLines[cuY].size())
209 return;
210
211 if (cuX + n > screenLines[cuY].size())
212 n = screenLines[cuY].size() - cuX;
213
214 Q_ASSERT(n >= 0);
215 Q_ASSERT(cuX + n <= screenLines[cuY].size());
216
217 screenLines[cuY].remove(cuX, n);
218}
219
221{
222 if (n == 0)
223 n = 1; // Default
224
225 if (screenLines[cuY].size() < cuX)
226 screenLines[cuY].resize(cuX);
227
228 screenLines[cuY].insert(cuX, n, ' ');
229
230 if (screenLines[cuY].size() > columns)
231 screenLines[cuY].resize(columns);
232}
233
234void Screen::repeatChars(int count)
235//=REP
236{
237 if (count == 0) {
238 count = 1;
239 }
240 /**
241 * From ECMA-48 version 5, section 8.3.103
242 * If the character preceding REP is a control function or part of a
243 * control function, the effect of REP is not defined by this Standard.
244 *
245 * So, a "normal" program should always use REP immediately after a visible
246 * character (those other than escape sequences). So, lastDrawnChar can be
247 * safely used.
248 */
249 for (int i = 0; i < count; i++) {
250 displayCharacter(lastDrawnChar);
251 }
252}
253
255{
256 if (n == 0)
257 n = 1; // Default
258 scrollUp(cuY, n);
259}
260
262{
263 if (n == 0)
264 n = 1; // Default
265 scrollDown(cuY, n);
266}
267
269{
270 currentModes[m] = true;
271 switch (m) {
272 case MODE_Origin:
273 cuX = 0;
274 cuY = _topMargin;
275 break; // FIXME: home
276 }
277}
278
280{
281 currentModes[m] = false;
282 switch (m) {
283 case MODE_Origin:
284 cuX = 0;
285 cuY = 0;
286 break; // FIXME: home
287 }
288}
289
291{
292 savedModes[m] = currentModes[m];
293}
294
296{
297 currentModes[m] = savedModes[m];
298}
299
300bool Screen::getMode(int m) const
301{
302 return currentModes[m];
303}
304
306{
307 savedState.cursorColumn = cuX;
308 savedState.cursorLine = cuY;
309 savedState.rendition = currentRendition;
310 savedState.foreground = currentForeground;
311 savedState.background = currentBackground;
312}
313
315{
316 cuX = qMin(savedState.cursorColumn, columns - 1);
317 cuY = qMin(savedState.cursorLine, lines - 1);
318 currentRendition = savedState.rendition;
319 currentForeground = savedState.foreground;
320 currentBackground = savedState.background;
321 updateEffectiveRendition();
322}
323
324void Screen::resizeImage(int new_lines, int new_columns)
325{
326 if ((new_lines == lines) && (new_columns == columns))
327 return;
328
329 if (cuY > new_lines - 1) { // attempt to preserve focus and lines
330 _bottomMargin = lines - 1; // FIXME: margin lost
331 for (int i = 0; i < cuY - (new_lines - 1); i++) {
332 addHistLine();
333 scrollUp(0, 1);
334 }
335 }
336
337 // create new screen lines and copy from old to new
338
339 auto newScreenLines = QVector<ImageLine>(new_lines + 1);
340 for (int i = 0; i < qMin(lines, new_lines + 1); i++)
341 newScreenLines[i] = screenLines[i];
342 for (int i = lines; (i > 0) && (i < new_lines + 1); i++)
343 newScreenLines[i].resize(new_columns);
344
345 lineProperties.resize(new_lines + 1);
346 for (int i = lines; (i > 0) && (i < new_lines + 1); i++)
347 lineProperties[i] = LINE_DEFAULT;
348
350
351 screenLines.clear();
352 screenLines = std::move(newScreenLines);
353
354 lines = new_lines;
355 columns = new_columns;
356 cuX = qMin(cuX, columns - 1);
357 cuY = qMin(cuY, lines - 1);
358
359 // FIXME: try to keep values, evtl.
360 _topMargin = 0;
361 _bottomMargin = lines - 1;
362 initTabStops();
364}
365
367{
368 _topMargin = 0;
369 _bottomMargin = lines - 1;
370}
371
372/*
373 Clarifying rendition here and in the display.
374
375 currently, the display's color table is
376 0 1 2 .. 9 10 .. 17
377 dft_fg, dft_bg, dim 0..7, intensive 0..7
378
379 currentForeground, currentBackground contain values 0..8;
380 - 0 = default color
381 - 1..8 = ansi specified color
382
383 re_fg, re_bg contain values 0..17
384 due to the TerminalDisplay's color table
385
386 rendition attributes are
387
388 attr widget screen
389 -------------- ------ ------
390 RE_UNDERLINE XX XX affects foreground only
391 RE_BLINK XX XX affects foreground only
392 RE_BOLD XX XX affects foreground only
393 RE_REVERSE -- XX
394 RE_TRANSPARENT XX -- affects background only
395 RE_INTENSIVE XX -- affects foreground only
396
397 Note that RE_BOLD is used in both widget
398 and screen rendition. Since xterm/vt102
399 is to poor to distinguish between bold
400 (which is a font attribute) and intensive
401 (which is a color attribute), we translate
402 this and RE_BOLD in falls eventually appart
403 into RE_BOLD and RE_INTENSIVE.
404 */
405
406void Screen::reverseRendition(Character &p) const
407{
410
411 p.foregroundColor = b;
412 p.backgroundColor = f; // p->r &= ~RE_TRANSPARENT;
413}
414
415void Screen::updateEffectiveRendition()
416{
417 effectiveRendition = currentRendition;
418 if (currentRendition & RE_REVERSE) {
419 effectiveForeground = currentBackground;
420 effectiveBackground = currentForeground;
421 } else {
422 effectiveForeground = currentForeground;
423 effectiveBackground = currentBackground;
424 }
425
426 if (currentRendition & RE_BOLD)
427 effectiveForeground.setIntensive();
428}
429
430void Screen::copyFromHistory(std::span<Character> dest, int startLine, int count) const
431{
432 Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= history->getLines());
433
434 for (int line = startLine; line < startLine + count; line++) {
435 const int length = qMin(columns, history->getLineLen(line));
436 const int destLineOffset = (line - startLine) * columns;
437
438 history->getCells(line, 0, length, dest.subspan(destLineOffset));
439
440 for (int column = length; column < columns; column++)
441 dest[destLineOffset + column] = defaultChar;
442
443 // invert selected text
444 if (selBegin != -1) {
445 for (int column = 0; column < columns; column++) {
446 if (isSelected(column, line)) {
447 reverseRendition(dest[destLineOffset + column]);
448 }
449 }
450 }
451 }
452}
453
454void Screen::copyFromScreen(std::span<Character> dest, int startLine, int count) const
455{
456 Q_ASSERT(startLine >= 0 && count > 0 && startLine + count <= lines);
457
458 for (int line = startLine; line < (startLine + count); line++) {
459 int srcLineStartIndex = line * columns;
460 int destLineStartIndex = (line - startLine) * columns;
461
462 for (int column = 0; column < columns; column++) {
463 int srcIndex = srcLineStartIndex + column;
464 int destIndex = destLineStartIndex + column;
465
466 dest[destIndex] = screenLines[srcIndex / columns].value(srcIndex % columns, defaultChar);
467
468 // invert selected text
469 if (selBegin != -1 && isSelected(column, line + history->getLines()))
470 reverseRendition(dest[destIndex]);
471 }
472 }
473}
474
475void Screen::getImage(std::span<Character> dest, int size, int startLine, int endLine) const
476{
477 Q_ASSERT(startLine >= 0);
478 Q_ASSERT(endLine >= startLine && endLine < history->getLines() + lines);
479
480 const int mergedLines = endLine - startLine + 1;
481
482 Q_ASSERT(size >= mergedLines * columns);
483 Q_UNUSED(size);
484
485 const int linesInHistoryBuffer = qBound(0, history->getLines() - startLine, mergedLines);
486 const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer;
487
488 // copy lines from history buffer
489 if (linesInHistoryBuffer > 0)
490 copyFromHistory(dest, startLine, linesInHistoryBuffer);
491
492 // copy lines from screen buffer
493 if (linesInScreenBuffer > 0)
494 copyFromScreen(dest.subspan(linesInHistoryBuffer * columns), startLine + linesInHistoryBuffer - history->getLines(), linesInScreenBuffer);
495
496 // invert display when in screen mode
497 if (getMode(MODE_Screen)) {
498 for (int i = 0; i < mergedLines * columns; i++)
499 reverseRendition(dest[i]); // for reverse display
500 }
501
502 // mark the character at the current cursor position
503 int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer);
504 if (getMode(MODE_Cursor) && cursorIndex < columns * mergedLines)
505 dest[cursorIndex].rendition |= RE_CURSOR;
506}
507
508QVector<LineProperty> Screen::getLineProperties(int startLine, int endLine) const
509{
510 Q_ASSERT(startLine >= 0);
511 Q_ASSERT(endLine >= startLine && endLine < history->getLines() + lines);
512
513 const int mergedLines = endLine - startLine + 1;
514 const int linesInHistory = qBound(0, history->getLines() - startLine, mergedLines);
515 const int linesInScreen = mergedLines - linesInHistory;
516
517 QVector<LineProperty> result(mergedLines);
518 int index = 0;
519
520 // copy properties for lines in history
521 for (int line = startLine; line < startLine + linesInHistory; line++) {
522 // TODO Support for line properties other than wrapped lines
523 if (history->isWrappedLine(line)) {
524 result[index] = (LineProperty)(result[index] | LINE_WRAPPED);
525 }
526 index++;
527 }
528
529 // copy properties for lines in screen buffer
530 const int firstScreenLine = startLine + linesInHistory - history->getLines();
531 for (int line = firstScreenLine; line < firstScreenLine + linesInScreen; line++) {
532 result[index] = lineProperties[line];
533 index++;
534 }
535
536 return result;
537}
538
539void Screen::reset(bool clearScreen)
540{
541 setMode(MODE_Wrap);
542 saveMode(MODE_Wrap); // wrap at end of margin
543 resetMode(MODE_Origin);
544 saveMode(MODE_Origin); // position refere to [1,1]
545 resetMode(MODE_Insert);
546 saveMode(MODE_Insert); // overstroke
547 setMode(MODE_Cursor); // cursor visible
548 resetMode(MODE_Screen); // screen not inverse
549 resetMode(MODE_NewLine);
550
551 _topMargin = 0;
552 _bottomMargin = lines - 1;
553
555 saveCursor();
556
557 if (clearScreen)
558 clear();
559}
560
562{
564 home();
565}
566
568{
569 cuX = qMin(columns - 1, cuX); // nowrap!
570 cuX = qMax(0, cuX - 1);
571
572 if (screenLines[cuY].size() < cuX + 1)
573 screenLines[cuY].resize(cuX + 1);
574
575 if (BS_CLEARS)
576 screenLines[cuY][cuX].character = u' ';
577}
578
579void Screen::tab(int n)
580{
581 // note that TAB is a format effector (does not write ' ');
582 if (n == 0)
583 n = 1;
584 while ((n > 0) && (cuX < columns - 1)) {
585 cursorRight(1);
586 while ((cuX < columns - 1) && !tabStops[cuX])
587 cursorRight(1);
588 n--;
589 }
590}
591
593{
594 // note that TAB is a format effector (does not write ' ');
595 if (n == 0)
596 n = 1;
597 while ((n > 0) && (cuX > 0)) {
598 cursorLeft(1);
599 while ((cuX > 0) && !tabStops[cuX])
600 cursorLeft(1);
601 n--;
602 }
603}
604
606{
607 for (int i = 0; i < columns; i++)
608 tabStops[i] = false;
609}
610
612{
613 if (cuX >= columns)
614 return;
615 tabStops[cuX] = set;
616}
617
618void Screen::initTabStops()
619{
620 tabStops.resize(columns);
621
622 // Arrg! The 1st tabstop has to be one longer than the other.
623 // i.e. the kids start counting from 0 instead of 1.
624 // Other programs might behave correctly. Be aware.
625 for (int i = 0; i < columns; i++)
626 tabStops[i] = (i % 8 == 0 && i != 0);
627}
628
630{
631 if (getMode(MODE_NewLine))
633 index();
634}
635
636void Screen::checkSelection(int from, int to)
637{
638 if (selBegin == -1)
639 return;
640 int scr_TL = loc(0, history->getLines());
641 // Clear entire selection if it overlaps region [from, to]
642 if ((selBottomRight >= (from + scr_TL)) && (selTopLeft <= (to + scr_TL)))
644}
645
647{
648 // Note that VT100 does wrapping BEFORE putting the character.
649 // This has impact on the assumption of valid cursor positions.
650 // We indicate the fact that a newline has to be triggered by
651 // putting the cursor one right to the last column of the screen.
652
653 int w = konsole_wcwidth(c);
654 if (w <= 0)
655 return;
656
657 if (cuX + w > columns) {
658 if (getMode(MODE_Wrap)) {
659 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | LINE_WRAPPED);
660 nextLine();
661 } else
662 cuX = columns - w;
663 }
664
665 // ensure current line vector has enough elements
666 int size = screenLines[cuY].size();
667 if (size < cuX + w) {
668 screenLines[cuY].resize(cuX + w);
669 }
670
671 if (getMode(MODE_Insert))
672 insertChars(w);
673
674 lastPos = loc(cuX, cuY);
675
676 // check if selection is still valid.
677 checkSelection(lastPos, lastPos);
678
679 Character &currentChar = screenLines[cuY][cuX];
680
681 currentChar.character = c;
682 currentChar.foregroundColor = effectiveForeground;
683 currentChar.backgroundColor = effectiveBackground;
684 currentChar.rendition = effectiveRendition;
685
686 lastDrawnChar = c;
687
688 int i = 0;
689 int newCursorX = cuX + w--;
690 while (w) {
691 i++;
692
693 if (screenLines[cuY].size() < cuX + i + 1)
694 screenLines[cuY].resize(cuX + i + 1);
695
696 Character &ch = screenLines[cuY][cuX + i];
697 ch.character = QChar(0);
698 ch.foregroundColor = effectiveForeground;
699 ch.backgroundColor = effectiveBackground;
700 ch.rendition = effectiveRendition;
701
702 w--;
703 }
704 cuX = newCursorX;
705}
706
707void Screen::compose(const QString & /*compose*/)
708{
709 Q_ASSERT(0 /*Not implemented yet*/);
710
711 /* if (lastPos == -1)
712 return;
713
714 QChar c(image[lastPos].character);
715 compose.prepend(c);
716 //compose.compose(); ### FIXME!
717 image[lastPos].character = compose[0].unicode();*/
718}
719
721{
722 return _scrolledLines;
723}
725{
726 return _droppedLines;
727}
729{
730 _droppedLines = 0;
731}
733{
734 _scrolledLines = 0;
735}
736
738{
739 if (n == 0)
740 n = 1; // Default
741 if (_topMargin == 0)
742 addHistLine(); // history.history
743 scrollUp(_topMargin, n);
744}
745
747{
748 return _lastScrolledRegion;
749}
750
751void Screen::scrollUp(int from, int n)
752{
753 if (n <= 0)
754 return;
755 if (from > _bottomMargin)
756 return;
757 if (from + n > _bottomMargin)
758 n = _bottomMargin + 1 - from;
759
760 _scrolledLines -= n;
761 _lastScrolledRegion = QRect(0, _topMargin, columns - 1, (_bottomMargin - _topMargin));
762
763 // FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
764 moveImage(loc(0, from), loc(0, from + n), loc(columns, _bottomMargin));
765 clearImage(loc(0, _bottomMargin - n + 1), loc(columns - 1, _bottomMargin), ' ');
766}
767
769{
770 if (n == 0)
771 n = 1; // Default
772 scrollDown(_topMargin, n);
773}
774
775void Screen::scrollDown(int from, int n)
776{
777 _scrolledLines += n;
778
779 // FIXME: make sure `topMargin', `bottomMargin', `from', `n' is in bounds.
780 if (n <= 0)
781 return;
782 if (from > _bottomMargin)
783 return;
784 if (from + n > _bottomMargin)
785 n = _bottomMargin - from;
786 moveImage(loc(0, from + n), loc(0, from), loc(columns - 1, _bottomMargin - n));
787 clearImage(loc(0, from), loc(columns - 1, from + n - 1), ' ');
788}
789
790void Screen::setCursorYX(int y, int x)
791{
792 setCursorY(y);
793 setCursorX(x);
794}
795
797{
798 if (x == 0)
799 x = 1; // Default
800 x -= 1; // Adjust
801 cuX = qMax(0, qMin(columns - 1, x));
802}
803
805{
806 if (y == 0)
807 y = 1; // Default
808 y -= 1; // Adjust
809 cuY = qMax(0, qMin(lines - 1, y + (getMode(MODE_Origin) ? _topMargin : 0)));
810}
811
813{
814 cuX = 0;
815 cuY = 0;
816}
817
819{
820 cuX = 0;
821}
822
824{
825 return cuX;
826}
827
829{
830 return cuY;
831}
832
833void Screen::clearImage(int loca, int loce, char c)
834{
835 int scr_TL = loc(0, history->getLines());
836 // FIXME: check positions
837
838 // Clear entire selection if it overlaps region to be moved...
839 if ((selBottomRight > (loca + scr_TL)) && (selTopLeft < (loce + scr_TL))) {
841 }
842
843 int topLine = loca / columns;
844 int bottomLine = loce / columns;
845
846 Character clearCh(c, currentForeground, currentBackground, DEFAULT_RENDITION);
847
848 // if the character being used to clear the area is the same as the
849 // default character, the affected lines can simply be shrunk.
850 bool isDefaultCh = (clearCh == Character());
851
852 for (int y = topLine; y <= bottomLine; y++) {
853 lineProperties[y] = 0;
854
855 int endCol = (y == bottomLine) ? loce % columns : columns - 1;
856 int startCol = (y == topLine) ? loca % columns : 0;
857
858 QVector<Character> &line = screenLines[y];
859
860 if (isDefaultCh && endCol == columns - 1) {
861 line.resize(startCol);
862 } else {
863 if (line.size() < endCol + 1)
864 line.resize(endCol + 1);
865
866 Character *data = line.data();
867 for (int i = startCol; i <= endCol; i++)
868 data[i] = clearCh;
869 }
870 }
871}
872
873void Screen::moveImage(int dest, int sourceBegin, int sourceEnd)
874{
875 Q_ASSERT(sourceBegin <= sourceEnd);
876
877 int lines = (sourceEnd - sourceBegin) / columns;
878
879 // move screen image and line properties:
880 // the source and destination areas of the image may overlap,
881 // so it matters that we do the copy in the right order -
882 // forwards if dest < sourceBegin or backwards otherwise.
883 //(search the web for 'memmove implementation' for details)
884 if (dest < sourceBegin) {
885 for (int i = 0; i <= lines; i++) {
886 screenLines[(dest / columns) + i] = screenLines[(sourceBegin / columns) + i];
887 lineProperties[(dest / columns) + i] = lineProperties[(sourceBegin / columns) + i];
888 }
889 } else {
890 for (int i = lines; i >= 0; i--) {
891 screenLines[(dest / columns) + i] = screenLines[(sourceBegin / columns) + i];
892 lineProperties[(dest / columns) + i] = lineProperties[(sourceBegin / columns) + i];
893 }
894 }
895
896 if (lastPos != -1) {
897 int diff = dest - sourceBegin; // Scroll by this amount
898 lastPos += diff;
899 if ((lastPos < 0) || (lastPos >= (lines * columns)))
900 lastPos = -1;
901 }
902
903 // Adjust selection to follow scroll.
904 if (selBegin != -1) {
905 bool beginIsTL = (selBegin == selTopLeft);
906 int diff = dest - sourceBegin; // Scroll by this amount
907 int scr_TL = loc(0, history->getLines());
908 int srca = sourceBegin + scr_TL; // Translate index from screen to global
909 int srce = sourceEnd + scr_TL; // Translate index from screen to global
910 int desta = srca + diff;
911 int deste = srce + diff;
912
913 if ((selTopLeft >= srca) && (selTopLeft <= srce))
914 selTopLeft += diff;
915 else if ((selTopLeft >= desta) && (selTopLeft <= deste))
916 selBottomRight = -1; // Clear selection (see below)
917
918 if ((selBottomRight >= srca) && (selBottomRight <= srce))
919 selBottomRight += diff;
920 else if ((selBottomRight >= desta) && (selBottomRight <= deste))
921 selBottomRight = -1; // Clear selection (see below)
922
923 if (selBottomRight < 0) {
925 } else {
926 if (selTopLeft < 0)
927 selTopLeft = 0;
928 }
929
930 if (beginIsTL)
931 selBegin = selTopLeft;
932 else
933 selBegin = selBottomRight;
934 }
935}
936
938{
939 clearImage(loc(cuX, cuY), loc(columns - 1, lines - 1), ' ');
940}
941
943{
944 clearImage(loc(0, 0), loc(cuX, cuY), ' ');
945}
946
948{
949 // Add entire screen to history
950 for (int i = 0; i < (lines - 1); i++) {
951 addHistLine();
952 scrollUp(0, 1);
953 }
954
955 clearImage(loc(0, 0), loc(columns - 1, lines - 1), ' ');
956}
957
958/*! fill screen with 'E'
959 This is to aid screen alignment
960 */
961
963{
964 clearImage(loc(0, 0), loc(columns - 1, lines - 1), 'E');
965}
966
968{
969 clearImage(loc(cuX, cuY), loc(columns - 1, cuY), ' ');
970}
971
973{
974 clearImage(loc(0, cuY), loc(cuX, cuY), ' ');
975}
976
978{
979 clearImage(loc(0, cuY), loc(columns - 1, cuY), ' ');
980}
981
983{
984 currentRendition |= re;
985 updateEffectiveRendition();
986}
987
989{
990 currentRendition &= ~re;
991 updateEffectiveRendition();
992}
993
995{
996 setForeColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR);
997 setBackColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR);
998 currentRendition = DEFAULT_RENDITION;
999 updateEffectiveRendition();
1000}
1001
1002void Screen::setForeColor(int space, int color)
1003{
1004 currentForeground = CharacterColor(space, color);
1005
1006 if (currentForeground.isValid())
1007 updateEffectiveRendition();
1008 else
1009 setForeColor(COLOR_SPACE_DEFAULT, DEFAULT_FORE_COLOR);
1010}
1011
1012void Screen::setBackColor(int space, int color)
1013{
1014 currentBackground = CharacterColor(space, color);
1015
1016 if (currentBackground.isValid())
1017 updateEffectiveRendition();
1018 else
1019 setBackColor(COLOR_SPACE_DEFAULT, DEFAULT_BACK_COLOR);
1020}
1021
1023{
1024 selBottomRight = -1;
1025 selTopLeft = -1;
1026 selBegin = -1;
1027}
1028
1029void Screen::getSelectionStart(int &column, int &line) const
1030{
1031 if (selTopLeft != -1) {
1032 column = selTopLeft % columns;
1033 line = selTopLeft / columns;
1034 } else {
1035 column = cuX + getHistLines();
1036 line = cuY + getHistLines();
1037 }
1038}
1039void Screen::getSelectionEnd(int &column, int &line) const
1040{
1041 if (selBottomRight != -1) {
1042 column = selBottomRight % columns;
1043 line = selBottomRight / columns;
1044 } else {
1045 column = cuX + getHistLines();
1046 line = cuY + getHistLines();
1047 }
1048}
1049void Screen::setSelectionStart(const int x, const int y, const bool mode)
1050{
1051 selBegin = loc(x, y);
1052 /* FIXME, HACK to correct for x too far to the right... */
1053 if (x == columns)
1054 selBegin--;
1055
1056 selBottomRight = selBegin;
1057 selTopLeft = selBegin;
1058 blockSelectionMode = mode;
1059}
1060
1061void Screen::setSelectionEnd(const int x, const int y)
1062{
1063 if (selBegin == -1)
1064 return;
1065
1066 int endPos = loc(x, y);
1067
1068 if (endPos < selBegin) {
1069 selTopLeft = endPos;
1070 selBottomRight = selBegin;
1071 } else {
1072 /* FIXME, HACK to correct for x too far to the right... */
1073 if (x == columns)
1074 endPos--;
1075
1076 selTopLeft = selBegin;
1077 selBottomRight = endPos;
1078 }
1079
1080 // Normalize the selection in column mode
1081 if (blockSelectionMode) {
1082 int topRow = selTopLeft / columns;
1083 int topColumn = selTopLeft % columns;
1084 int bottomRow = selBottomRight / columns;
1085 int bottomColumn = selBottomRight % columns;
1086
1087 selTopLeft = loc(qMin(topColumn, bottomColumn), topRow);
1088 selBottomRight = loc(qMax(topColumn, bottomColumn), bottomRow);
1089 }
1090}
1091
1092bool Screen::isSelected(const int x, const int y) const
1093{
1094 bool columnInSelection = true;
1095 if (blockSelectionMode) {
1096 columnInSelection = x >= (selTopLeft % columns) && x <= (selBottomRight % columns);
1097 }
1098
1099 int pos = loc(x, y);
1100 return pos >= selTopLeft && pos <= selBottomRight && columnInSelection;
1101}
1102
1103QString Screen::selectedText(bool preserveLineBreaks) const
1104{
1105 QString result;
1106 QTextStream stream(&result, QIODevice::ReadWrite);
1107
1108 PlainTextDecoder decoder;
1109 decoder.begin(&stream);
1110 writeSelectionToStream(&decoder, preserveLineBreaks);
1111 decoder.end();
1112
1113 return result;
1114}
1115
1116bool Screen::isSelectionValid() const
1117{
1118 return selTopLeft >= 0 && selBottomRight >= 0;
1119}
1120
1121void Screen::writeSelectionToStream(TerminalCharacterDecoder *decoder, bool preserveLineBreaks) const
1122{
1123 if (!isSelectionValid())
1124 return;
1125 writeToStream(decoder, selTopLeft, selBottomRight, preserveLineBreaks);
1126}
1127
1128void Screen::writeToStream(TerminalCharacterDecoder *decoder, int startIndex, int endIndex, bool preserveLineBreaks) const
1129{
1130 int top = startIndex / columns;
1131 int left = startIndex % columns;
1132
1133 int bottom = endIndex / columns;
1134 int right = endIndex % columns;
1135
1136 Q_ASSERT(top >= 0 && left >= 0 && bottom >= 0 && right >= 0);
1137
1138 for (int y = top; y <= bottom; y++) {
1139 int start = 0;
1140 if (y == top || blockSelectionMode)
1141 start = left;
1142
1143 int count = -1;
1144 if (y == bottom || blockSelectionMode)
1145 count = right - start + 1;
1146
1147 const bool appendNewLine = (y != bottom);
1148 int copied = copyLineToStream(y, start, count, decoder, appendNewLine, preserveLineBreaks);
1149
1150 // if the selection goes beyond the end of the last line then
1151 // append a new line character.
1152 //
1153 // this makes it possible to 'select' a trailing new line character after
1154 // the text on a line.
1155 if (y == bottom && copied < count) {
1156 Character newLineChar('\n');
1157 decoder->decodeLine(std::span(&newLineChar, 1), 0);
1158 }
1159 }
1160}
1161
1162int Screen::copyLineToStream(int line, int start, int count, TerminalCharacterDecoder *decoder, bool appendNewLine, bool preserveLineBreaks) const
1163{
1164 // buffer to hold characters for decoding
1165 // the buffer is static to avoid initialising every
1166 // element on each call to copyLineToStream
1167 //(which is unnecessary since all elements will be overwritten anyway)
1168 static const int MAX_CHARS = 1024;
1169 static std::array<Character, MAX_CHARS> characterBuffer;
1170
1171 Q_ASSERT(count < MAX_CHARS);
1172
1173 LineProperty currentLineProperties = 0;
1174
1175 // determine if the line is in the history buffer or the screen image
1176 if (line < history->getLines()) {
1177 const int lineLength = history->getLineLen(line);
1178
1179 // ensure that start position is before end of line
1180 start = qMin(start, qMax(0, lineLength - 1));
1181
1182 // retrieve line from history buffer. It is assumed
1183 // that the history buffer does not store trailing white space
1184 // at the end of the line, so it does not need to be trimmed here
1185 if (count == -1) {
1186 count = lineLength - start;
1187 } else {
1188 count = qMin(start + count, lineLength) - start;
1189 }
1190
1191 // safety checks
1192 Q_ASSERT(start >= 0);
1193 Q_ASSERT(count >= 0);
1194 Q_ASSERT((start + count) <= history->getLineLen(line));
1195
1196 history->getCells(line, start, count, characterBuffer);
1197
1198 if (history->isWrappedLine(line))
1199 currentLineProperties |= LINE_WRAPPED;
1200 } else {
1201 if (count == -1)
1202 count = columns - start;
1203
1204 Q_ASSERT(count >= 0);
1205
1206 const int screenLine = line - history->getLines();
1207
1208 auto data = screenLines[screenLine].data();
1209 int length = screenLines[screenLine].size();
1210
1211 // retrieve line from screen image
1212 for (int i = start; i < qMin(start + count, length); i++) {
1213 characterBuffer[i - start] = data[i];
1214 }
1215
1216 // count cannot be any greater than length
1217 count = qBound(0, length - start, count);
1218
1219 Q_ASSERT(screenLine < lineProperties.size());
1220 currentLineProperties |= lineProperties[screenLine];
1221 }
1222
1223 // add new line character at end
1224 const bool omitLineBreak = (currentLineProperties & LINE_WRAPPED) || !preserveLineBreaks;
1225
1226 if (!omitLineBreak && appendNewLine && (count + 1 < MAX_CHARS)) {
1227 characterBuffer[count] = '\n';
1228 count++;
1229 }
1230
1231 // decode line and write to text stream
1232 decoder->decodeLine(std::span(characterBuffer).subspan(0, count), currentLineProperties);
1233
1234 return count;
1235}
1236
1237void Screen::writeLinesToStream(TerminalCharacterDecoder *decoder, int fromLine, int toLine) const
1238{
1239 writeToStream(decoder, loc(0, fromLine), loc(columns - 1, toLine));
1240}
1241
1242void Screen::addHistLine()
1243{
1244 // add line to history buffer
1245 // we have to take care about scrolling, too...
1246
1247 if (hasScroll()) {
1248 int oldHistLines = history->getLines();
1249
1250 history->addCellsVector(screenLines[0]);
1251 history->addLine(lineProperties[0] & LINE_WRAPPED);
1252
1253 int newHistLines = history->getLines();
1254
1255 bool beginIsTL = (selBegin == selTopLeft);
1256
1257 // If the history is full, increment the count
1258 // of dropped lines
1259 if (newHistLines == oldHistLines)
1260 _droppedLines++;
1261
1262 // Adjust selection for the new point of reference
1263 if (newHistLines > oldHistLines) {
1264 if (selBegin != -1) {
1265 selTopLeft += columns;
1266 selBottomRight += columns;
1267 }
1268 }
1269
1270 if (selBegin != -1) {
1271 // Scroll selection in history up
1272 int top_BR = loc(0, 1 + newHistLines);
1273
1274 if (selTopLeft < top_BR)
1275 selTopLeft -= columns;
1276
1277 if (selBottomRight < top_BR)
1278 selBottomRight -= columns;
1279
1280 if (selBottomRight < 0)
1282 else {
1283 if (selTopLeft < 0)
1284 selTopLeft = 0;
1285 }
1286
1287 if (beginIsTL)
1288 selBegin = selTopLeft;
1289 else
1290 selBegin = selBottomRight;
1291 }
1292 }
1293}
1294
1296{
1297 return history->getLines();
1298}
1299
1300void Screen::setScroll(const HistoryType &t, bool copyPreviousScroll)
1301{
1303
1304 if (copyPreviousScroll)
1305 history = t.scroll(std::move(history));
1306 else {
1307 auto oldScroll = std::move(history);
1308 history = t.scroll(nullptr);
1309 }
1310}
1311
1313{
1314 return history->hasScroll();
1315}
1316
1317const HistoryType &Screen::getScroll() const
1318{
1319 return history->getType();
1320}
1321
1322void Screen::setLineProperty(LineProperty property, bool enable)
1323{
1324 if (enable)
1325 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] | property);
1326 else
1327 lineProperties[cuY] = (LineProperty)(lineProperties[cuY] & ~property);
1328}
1329void Screen::fillWithDefaultChar(std::span<Character> dest, int count)
1330{
1331 for (int i = 0; i < count; i++)
1332 dest[i] = defaultChar;
1333}
Describes the color of a single character in the terminal.
void setIntensive()
Set the value of this color from a normal system color to the corresponding intensive system color if...
constexpr bool isValid() const
Returns true if this character color entry is valid.
A single character in the terminal which consists of a unicode character value, foreground and backgr...
Definition Character.h:63
QChar character
The unicode character value for this character.
Definition Character.h:86
CharacterColor foregroundColor
The foreground color used to draw this character.
Definition Character.h:101
CharacterColor backgroundColor
The color used to draw this character's background.
Definition Character.h:103
quint8 rendition
A combination of RENDITION flags which specify options for drawing the character.
Definition Character.h:98
A terminal character decoder which produces plain text, ignoring colours and other appearance-related...
void end() override
End decoding.
void begin(QTextStream *output) override
Begin decoding characters.
void setCursorYX(int y, int x)
Position the cursor at line y, column x.
Definition Screen.cpp:790
void saveMode(int mode)
Saves the state of the specified screen mode.
Definition Screen.cpp:290
int getCursorY() const
Returns the line which the cursor is positioned on.
Definition Screen.cpp:828
void clearToEndOfScreen()
Clear the area of the screen from the current cursor position to the end of the screen.
Definition Screen.cpp:937
void resizeImage(int new_lines, int new_columns)
Resizes the image to a new fixed size of new_lines by new_columns.
Definition Screen.cpp:324
void deleteChars(int n)
Delete n characters beginning from the current cursor position.
Definition Screen.cpp:199
void setMargins(int topLine, int bottomLine)
Sets the margins for scrolling the screen.
Definition Screen.cpp:139
void checkSelection(int from, int to)
Checks if the text between from and to is inside the current selection.
Definition Screen.cpp:636
void setScroll(const HistoryType &, bool copyPreviousScroll=true)
Sets the type of storage used to keep lines in the history.
Definition Screen.cpp:1300
void reset(bool clearScreen=true)
Resets the state of the screen.
Definition Screen.cpp:539
void clearEntireScreen()
Clear the whole screen, moving the current screen contents into the history first.
Definition Screen.cpp:947
void getImage(std::span< Character > dest, int size, int startLine, int endLine) const
Returns the current screen image.
Definition Screen.cpp:475
void saveCursor()
Saves the current position and appearance (text color and style) of the cursor.
Definition Screen.cpp:305
void clearToBeginOfLine()
Clears from the current cursor position to the beginning of the line.
Definition Screen.cpp:972
void clearEntireLine()
Clears the whole of the line on which the cursor is currently positioned.
Definition Screen.cpp:977
void clear()
Clear the entire screen and move the cursor to the home position.
Definition Screen.cpp:561
void setCursorY(int y)
Position the cursor on line y.
Definition Screen.cpp:804
const HistoryType & getScroll() const
Returns the type of storage used to keep lines in the history.
Definition Screen.cpp:1317
void insertChars(int n)
Insert n blank characters beginning from the current cursor position.
Definition Screen.cpp:220
Screen(int lines, int columns)
Construct a new screen image of size lines by columns.
Definition Screen.cpp:65
void writeSelectionToStream(TerminalCharacterDecoder *decoder, bool preserveLineBreaks=true) const
Copies the selected characters, set using.
Definition Screen.cpp:1121
void tab(int n=1)
Moves the cursor n tab-stops to the right.
Definition Screen.cpp:579
void changeTabStop(bool set)
Sets or removes a tab stop at the cursor's current column.
Definition Screen.cpp:611
void cursorDown(int n)
Move the cursor down by n lines.
Definition Screen.cpp:112
void setDefaultMargins()
Resets the scrolling margins back to the top and bottom lines of the screen.
Definition Screen.cpp:366
void home()
Sets the position of the cursor to the 'home' position at the top-left corner of the screen (0,...
Definition Screen.cpp:812
void backtab(int n)
Moves the cursor n tab-stops to the left.
Definition Screen.cpp:592
void setCursorX(int x)
Position the cursor at column x.
Definition Screen.cpp:796
void setSelectionEnd(const int column, const int line)
Sets the end of the current selection.
Definition Screen.cpp:1061
void displayCharacter(QChar c)
Displays a new character at the current cursor position.
Definition Screen.cpp:646
void clearTabStops()
Clears all the tab stops.
Definition Screen.cpp:605
void clearToBeginOfScreen()
Clear the area of the screen from the current cursor position to the start of the screen.
Definition Screen.cpp:942
void eraseChars(int n)
Erase n characters beginning from the current cursor position.
Definition Screen.cpp:191
static void fillWithDefaultChar(std::span< Character > dest, int count)
Fills the buffer dest with count instances of the default (ie.
Definition Screen.cpp:1329
void writeLinesToStream(TerminalCharacterDecoder *decoder, int fromLine, int toLine) const
Copies part of the output to a stream.
Definition Screen.cpp:1237
void repeatChars(int count)
Repeat the preceeding graphic character @count times, including SPACE.
Definition Screen.cpp:234
void getSelectionStart(int &column, int &line) const
Retrieves the start of the selection or the cursor position if there is no selection.
Definition Screen.cpp:1029
int getHistLines() const
Return the number of lines in the history buffer.
Definition Screen.cpp:1295
void helpAlign()
Fills the entire screen with the letter 'E'.
Definition Screen.cpp:962
void getSelectionEnd(int &column, int &line) const
Retrieves the end of the selection or the cursor position if there is no selection.
Definition Screen.cpp:1039
int getCursorX() const
Returns the column which the cursor is positioned at.
Definition Screen.cpp:823
QRect lastScrolledRegion() const
Returns the region of the image which was last scrolled.
Definition Screen.cpp:746
void cursorUp(int n)
Move the cursor up by n lines.
Definition Screen.cpp:102
int topMargin() const
Returns the top line of the scrolling region.
Definition Screen.cpp:157
int getLines() const
Return the number of lines.
Definition Screen.h:385
int scrolledLines() const
Returns the number of lines that the image has been scrolled up or down by, since the last call to re...
Definition Screen.cpp:720
void clearSelection()
Clears the current selection.
Definition Screen.cpp:1022
void setSelectionStart(const int column, const int line, const bool blockSelectionMode)
Sets the start of the selection.
Definition Screen.cpp:1049
bool getMode(int mode) const
Returns whether the specified screen mode is enabled or not .
Definition Screen.cpp:300
void setLineProperty(LineProperty property, bool enable)
Sets or clears an attribute of the current line.
Definition Screen.cpp:1322
void setMode(int mode)
Sets (enables) the specified screen mode.
Definition Screen.cpp:268
bool isSelected(const int column, const int line) const
Returns true if the character at (column, line) is part of the current selection.
Definition Screen.cpp:1092
void setDefaultRendition()
Resets the cursor's color back to the default and sets the character's rendition flags back to the de...
Definition Screen.cpp:994
void resetScrolledLines()
Resets the count of the number of lines that the image has been scrolled up or down by,...
Definition Screen.cpp:732
void backspace()
Moves the cursor one column to the left and erases the character at the new cursor position.
Definition Screen.cpp:567
void scrollDown(int n)
Scroll the scrolling region of the screen down by n lines.
Definition Screen.cpp:768
void scrollUp(int n)
Scroll the scrolling region of the screen up by n lines.
Definition Screen.cpp:737
void cursorRight(int n)
Move the cursor to the right by n columns.
Definition Screen.cpp:131
void restoreMode(int mode)
Restores the state of a screen mode saved by calling saveMode()
Definition Screen.cpp:295
int bottomMargin() const
Returns the bottom line of the scrolling region.
Definition Screen.cpp:161
QVector< LineProperty > getLineProperties(int startLine, int endLine) const
Returns the additional attributes associated with lines in the image.
Definition Screen.cpp:508
void resetRendition(int rendition)
Disables the given rendition flag.
Definition Screen.cpp:988
void toStartOfLine()
Moves the cursor to the beginning of the current line.
Definition Screen.cpp:818
void resetDroppedLines()
Resets the count of the number of lines dropped from the history.
Definition Screen.cpp:728
void newLine()
Moves the cursor down one line, if the MODE_NewLine mode flag is enabled then the cursor is returned ...
Definition Screen.cpp:629
void restoreCursor()
Restores the position and appearance of the cursor.
Definition Screen.cpp:314
void setRendition(int rendition)
Enables the given rendition flag.
Definition Screen.cpp:982
void setBackColor(int space, int color)
Sets the cursor's background color.
Definition Screen.cpp:1012
int droppedLines() const
Returns the number of lines of output which have been dropped from the history since the last call to...
Definition Screen.cpp:724
void deleteLines(int n)
Removes n lines beginning from the current cursor position.
Definition Screen.cpp:254
void resetMode(int mode)
Resets (clears) the specified screen mode.
Definition Screen.cpp:279
QString selectedText(bool preserveLineBreaks) const
Convenience method.
Definition Screen.cpp:1103
void index()
Move the cursor down one line.
Definition Screen.cpp:166
void nextLine()
Moves the cursor down one line and positions it at the beginning of the line.
Definition Screen.cpp:184
void reverseIndex()
Move the cursor up one line.
Definition Screen.cpp:175
void clearToEndOfLine()
Clears from the current cursor position to the end of the line.
Definition Screen.cpp:967
void cursorLeft(int n)
Move the cursor to the left by n columns.
Definition Screen.cpp:122
bool hasScroll() const
Returns true if this screen keeps lines that are scrolled off the screen in a history buffer.
Definition Screen.cpp:1312
void setForeColor(int space, int color)
Sets the cursor's foreground color.
Definition Screen.cpp:1002
void insertLines(int n)
Inserts lines beginning from the current cursor position.
Definition Screen.cpp:261
Base class for terminal character decoders.
virtual void decodeLine(std::span< const Character > characters, LineProperty properties)=0
Converts a line of terminal characters with associated properties into a text string and writes the s...
void stop(Ekos::AlignState mode)
Q_SCRIPTABLE Q_NOREPLY void start()
void resize(qsizetype size)
pointer data()
void resize(qsizetype size)
qsizetype size() const const
void resize(qsizetype size)
qsizetype size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 22 2024 12:08:41 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.