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.
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 QString start(QString train="")
Q_SCRIPTABLE Q_NOREPLY void start()
void resize(qsizetype size)
pointer data()
void resize(qsizetype size)
qsizetype size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:50:35 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.