2 * Copyright 2013, Haiku, Inc. All rights reserved.
3 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Distributed under the terms of the MIT License.
7 * Ingo Weinhold, ingo_weinhold@gmx.de
8 * Siarzhuk Zharski, zharik@gmx.li
11 #include "BasicTerminalBuffer.h"
21 #include <StackOrHeapArray.h>
24 #include "TermConst.h"
25 #include "TerminalCharClassifier.h"
26 #include "TerminalLine.h"
29 static const UTF8Char
kSpaceChar(' ');
31 // Soft size limits for the terminal buffer. The constants defined in
32 // TermConst.h are rather for the Terminal in general (i.e. the GUI).
33 static const int32 kMinRowCount
= 2;
34 static const int32 kMaxRowCount
= 1024;
35 static const int32 kMinColumnCount
= 4;
36 static const int32 kMaxColumnCount
= 1024;
39 #define ALLOC_LINE_ON_STACK(width) \
40 ((TerminalLine*)alloca(sizeof(TerminalLine) \
41 + sizeof(TerminalCell) * ((width) - 1)))
45 restrict_value(int32 value
, int32 min
, int32 max
)
47 return value
< min
? min
: (value
> max
? max
: value
);
51 // #pragma mark - private inline methods
55 BasicTerminalBuffer::_LineIndex(int32 index
) const
57 return (index
+ fScreenOffset
) % fHeight
;
62 BasicTerminalBuffer::_LineAt(int32 index
) const
64 return fScreen
[_LineIndex(index
)];
69 BasicTerminalBuffer::_HistoryLineAt(int32 index
, TerminalLine
* lineBuffer
) const
74 if (index
< 0 && fHistory
!= NULL
)
75 return fHistory
->GetTerminalLineAt(-index
- 1, lineBuffer
);
77 return _LineAt(index
+ fHeight
);
82 BasicTerminalBuffer::_Invalidate(int32 top
, int32 bottom
)
84 //debug_printf("%p->BasicTerminalBuffer::_Invalidate(%ld, %ld)\n", this, top, bottom);
85 fDirtyInfo
.ExtendDirtyRegion(top
, bottom
);
87 if (!fDirtyInfo
.messageSent
) {
89 fDirtyInfo
.messageSent
= true;
95 BasicTerminalBuffer::_CursorChanged()
97 if (!fDirtyInfo
.messageSent
) {
99 fDirtyInfo
.messageSent
= true;
104 // #pragma mark - public methods
107 BasicTerminalBuffer::BasicTerminalBuffer()
117 fSoftWrappedCursor(false),
118 fOverwriteMode(false),
119 fAlternateScreenActive(false),
121 fSavedOriginMode(false),
129 BasicTerminalBuffer::~BasicTerminalBuffer()
132 _FreeLines(fScreen
, fHeight
);
135 if (fCaptureFile
>= 0)
141 BasicTerminalBuffer::Init(int32 width
, int32 height
, int32 historySize
)
149 fScrollBottom
= fHeight
- 1;
153 fSoftWrappedCursor
= false;
157 fOverwriteMode
= true;
158 fAlternateScreenActive
= false;
159 fOriginMode
= fSavedOriginMode
= false;
161 fScreen
= _AllocateLines(width
, height
);
165 if (historySize
> 0) {
166 fHistory
= new(std::nothrow
) HistoryBuffer
;
167 if (fHistory
== NULL
)
170 error
= fHistory
->Init(width
, historySize
);
175 error
= _ResetTabStops(fWidth
);
179 for (int32 i
= 0; i
< fHeight
; i
++)
189 BasicTerminalBuffer::ResizeTo(int32 width
, int32 height
)
191 return ResizeTo(width
, height
, fHistory
!= NULL
? fHistory
->Capacity() : 0);
196 BasicTerminalBuffer::ResizeTo(int32 width
, int32 height
, int32 historyCapacity
)
198 if (height
< kMinRowCount
|| height
> kMaxRowCount
199 || width
< kMinColumnCount
|| width
> kMaxColumnCount
) {
203 if (width
== fWidth
&& height
== fHeight
)
204 return SetHistoryCapacity(historyCapacity
);
206 if (fAlternateScreenActive
)
207 return _ResizeSimple(width
, height
, historyCapacity
);
209 return _ResizeRewrap(width
, height
, historyCapacity
);
214 BasicTerminalBuffer::SetHistoryCapacity(int32 historyCapacity
)
216 return _ResizeHistory(fWidth
, historyCapacity
);
221 BasicTerminalBuffer::Clear(bool resetCursor
)
223 fSoftWrappedCursor
= false;
225 _ClearLines(0, fHeight
- 1);
230 if (fHistory
!= NULL
)
233 fDirtyInfo
.linesScrolled
= 0;
234 _Invalidate(0, fHeight
- 1);
239 BasicTerminalBuffer::SynchronizeWith(const BasicTerminalBuffer
* other
,
240 int32 offset
, int32 dirtyTop
, int32 dirtyBottom
)
242 //debug_printf("BasicTerminalBuffer::SynchronizeWith(%p, %ld, %ld - %ld)\n",
243 //other, offset, dirtyTop, dirtyBottom);
245 // intersect the visible region with the dirty region
247 int32 last
= fHeight
- 1;
249 dirtyBottom
-= offset
;
251 if (first
> dirtyBottom
|| dirtyTop
> last
)
254 if (first
< dirtyTop
)
256 if (last
> dirtyBottom
)
259 // update the dirty lines
260 //debug_printf(" updating: %ld - %ld\n", first, last);
261 for (int32 i
= first
; i
<= last
; i
++) {
262 TerminalLine
* destLine
= _LineAt(i
);
263 TerminalLine
* sourceLine
= other
->_HistoryLineAt(i
+ offset
, destLine
);
264 if (sourceLine
!= NULL
) {
265 if (sourceLine
!= destLine
) {
266 destLine
->length
= sourceLine
->length
;
267 destLine
->attributes
= sourceLine
->attributes
;
268 destLine
->softBreak
= sourceLine
->softBreak
;
269 if (destLine
->length
> 0) {
270 memcpy(destLine
->cells
, sourceLine
->cells
,
271 fWidth
* sizeof(TerminalCell
));
274 // The source line was a history line and has been copied
275 // directly into destLine.
278 destLine
->Clear(fAttributes
, fWidth
);
284 BasicTerminalBuffer::IsFullWidthChar(int32 row
, int32 column
) const
286 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
287 TerminalLine
* line
= _HistoryLineAt(row
, lineBuffer
);
288 return line
!= NULL
&& column
> 0 && column
< line
->length
289 && (line
->cells
[column
- 1].attributes
& A_WIDTH
) != 0;
294 BasicTerminalBuffer::GetChar(int32 row
, int32 column
, UTF8Char
& character
,
295 uint32
& attributes
) const
297 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
298 TerminalLine
* line
= _HistoryLineAt(row
, lineBuffer
);
302 if (column
< 0 || column
>= line
->length
)
305 if (column
> 0 && (line
->cells
[column
- 1].attributes
& A_WIDTH
) != 0)
308 TerminalCell
& cell
= line
->cells
[column
];
309 character
= cell
.character
;
310 attributes
= cell
.attributes
;
316 BasicTerminalBuffer::GetCellAttributes(int32 row
, int32 column
,
317 uint32
& attributes
, uint32
& count
) const
320 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
321 TerminalLine
* line
= _HistoryLineAt(row
, lineBuffer
);
322 if (line
== NULL
|| column
< 0)
326 for (; c
< fWidth
; c
++) {
327 TerminalCell
& cell
= line
->cells
[c
];
328 if (c
> column
&& attributes
!= cell
.attributes
)
330 attributes
= cell
.attributes
;
337 BasicTerminalBuffer::GetString(int32 row
, int32 firstColumn
, int32 lastColumn
,
338 char* buffer
, uint32
& attributes
) const
340 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
341 TerminalLine
* line
= _HistoryLineAt(row
, lineBuffer
);
345 if (lastColumn
>= line
->length
)
346 lastColumn
= line
->length
- 1;
348 int32 column
= firstColumn
;
349 if (column
<= lastColumn
)
350 attributes
= line
->cells
[column
].attributes
;
352 for (; column
<= lastColumn
; column
++) {
353 TerminalCell
& cell
= line
->cells
[column
];
354 if (cell
.attributes
!= attributes
)
357 int32 bytes
= cell
.character
.ByteCount();
358 for (int32 i
= 0; i
< bytes
; i
++)
359 *buffer
++ = cell
.character
.bytes
[i
];
364 return column
- firstColumn
;
369 BasicTerminalBuffer::GetStringFromRegion(BString
& string
, const TermPos
& start
,
370 const TermPos
& end
) const
372 //debug_printf("BasicTerminalBuffer::GetStringFromRegion((%ld, %ld), (%ld, %ld))\n",
373 //start.x, start.y, end.x, end.y);
379 if (IsFullWidthChar(pos
.y
, pos
.x
))
382 // get all but the last line
383 while (pos
.y
< end
.y
) {
384 TerminalLine
* line
= _GetPartialLineString(string
, pos
.y
, pos
.x
,
386 if (line
!= NULL
&& !line
->softBreak
)
387 string
.Append('\n', 1);
392 // get the last line, if not empty
394 _GetPartialLineString(string
, end
.y
, pos
.x
, end
.x
);
399 BasicTerminalBuffer::FindWord(const TermPos
& pos
,
400 TerminalCharClassifier
* classifier
, bool findNonWords
, TermPos
& _start
,
406 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
407 TerminalLine
* line
= _HistoryLineAt(y
, lineBuffer
);
408 if (line
== NULL
|| x
< 0 || x
>= fWidth
)
411 if (x
>= line
->length
) {
412 // beyond the end of the line -- select all space
416 _start
.SetTo(line
->length
, y
);
417 _end
.SetTo(fWidth
, y
);
421 if (x
> 0 && IS_WIDTH(line
->cells
[x
- 1].attributes
))
424 // get the char type at the given position
425 int type
= classifier
->Classify(line
->cells
[x
].character
);
427 // check whether we are supposed to find words only
428 if (type
!= CHAR_TYPE_WORD_CHAR
&& !findNonWords
)
431 // find the beginning
433 TermPos
end(x
+ (IS_WIDTH(line
->cells
[x
].attributes
)
434 ? FULL_WIDTH
: HALF_WIDTH
), y
);
436 TermPos previousPos
= start
;
437 if (!_PreviousLinePos(lineBuffer
, line
, previousPos
)
438 || classifier
->Classify(line
->cells
[previousPos
.x
].character
)
447 line
= _HistoryLineAt(end
.y
, lineBuffer
);
450 TermPos nextPos
= end
;
451 if (!_NormalizeLinePos(lineBuffer
, line
, nextPos
))
454 if (classifier
->Classify(line
->cells
[nextPos
.x
].character
) != type
)
457 nextPos
.x
+= IS_WIDTH(line
->cells
[nextPos
.x
].attributes
)
458 ? FULL_WIDTH
: HALF_WIDTH
;
469 BasicTerminalBuffer::PreviousLinePos(TermPos
& pos
) const
471 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
472 TerminalLine
* line
= _HistoryLineAt(pos
.y
, lineBuffer
);
473 if (line
== NULL
|| pos
.x
< 0 || pos
.x
>= fWidth
)
476 return _PreviousLinePos(lineBuffer
, line
, pos
);
481 BasicTerminalBuffer::NextLinePos(TermPos
& pos
, bool normalize
) const
483 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
484 TerminalLine
* line
= _HistoryLineAt(pos
.y
, lineBuffer
);
485 if (line
== NULL
|| pos
.x
< 0 || pos
.x
> fWidth
)
488 if (!_NormalizeLinePos(lineBuffer
, line
, pos
))
491 pos
.x
+= IS_WIDTH(line
->cells
[pos
.x
].attributes
) ? FULL_WIDTH
: HALF_WIDTH
;
492 return !normalize
|| _NormalizeLinePos(lineBuffer
, line
, pos
);
497 BasicTerminalBuffer::LineLength(int32 index
) const
499 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
500 TerminalLine
* line
= _HistoryLineAt(index
, lineBuffer
);
501 return line
!= NULL
? line
->length
: 0;
506 BasicTerminalBuffer::GetLineColor(int32 index
) const
508 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
509 TerminalLine
* line
= _HistoryLineAt(index
, lineBuffer
);
510 return line
!= NULL
? line
->attributes
: 0;
515 BasicTerminalBuffer::Find(const char* _pattern
, const TermPos
& start
,
516 bool forward
, bool caseSensitive
, bool matchWord
, TermPos
& _matchStart
,
517 TermPos
& _matchEnd
) const
519 //debug_printf("BasicTerminalBuffer::Find(\"%s\", (%ld, %ld), forward: %d, case: %d, "
520 //"word: %d)\n", _pattern, start.x, start.y, forward, caseSensitive, matchWord);
521 // normalize pos, so that _NextChar() and _PreviousChar() are happy
523 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
524 TerminalLine
* line
= _HistoryLineAt(pos
.y
, lineBuffer
);
527 while (line
!= NULL
&& pos
.x
>= line
->length
&& line
->softBreak
) {
530 line
= _HistoryLineAt(pos
.y
, lineBuffer
);
533 if (pos
.x
> line
->length
)
534 pos
.x
= line
->length
;
538 int32 patternByteLen
= strlen(_pattern
);
540 // convert pattern to UTF8Char array
541 BStackOrHeapArray
<UTF8Char
, 64> pattern(patternByteLen
);
542 int32 patternLen
= 0;
543 while (*_pattern
!= '\0') {
544 int32 charLen
= UTF8Char::ByteCount(*_pattern
);
546 pattern
[patternLen
].SetTo(_pattern
, charLen
);
548 // if not case sensitive, convert to lower case
549 if (!caseSensitive
&& charLen
== 1)
550 pattern
[patternLen
] = pattern
[patternLen
].ToLower();
557 //debug_printf(" pattern byte len: %ld, pattern len: %ld\n", patternByteLen, patternLen);
562 // reverse pattern, if searching backward
564 for (int32 i
= 0; i
< patternLen
/ 2; i
++)
565 std::swap(pattern
[i
], pattern
[patternLen
- i
- 1]);
569 int32 matchIndex
= 0;
572 //debug_printf(" (%ld, %ld): matchIndex: %ld\n", pos.x, pos.y, matchIndex);
573 TermPos
previousPos(pos
);
575 if (!(forward
? _NextChar(pos
, c
) : _PreviousChar(pos
, c
)))
578 if (caseSensitive
? (c
== pattern
[matchIndex
])
579 : (c
.ToLower() == pattern
[matchIndex
])) {
581 matchStart
= previousPos
;
585 if (matchIndex
== patternLen
) {
586 //debug_printf(" match!\n");
587 // compute the match range
588 TermPos
matchEnd(pos
);
590 std::swap(matchStart
, matchEnd
);
594 TermPos
tempPos(matchStart
);
595 if ((_PreviousChar(tempPos
, c
) && !c
.IsSpace())
596 || (_NextChar(tempPos
= matchEnd
, c
) && !c
.IsSpace())) {
597 //debug_printf(" but no word match!\n");
602 _matchStart
= matchStart
;
603 _matchEnd
= matchEnd
;
604 //debug_printf(" -> (%ld, %ld) - (%ld, %ld)\n", matchStart.x, matchStart.y,
605 //matchEnd.x, matchEnd.y);
608 } else if (matchIndex
> 0) {
609 // continue after the position where we started matching
614 _PreviousChar(pos
, c
);
622 BasicTerminalBuffer::InsertChar(UTF8Char c
)
624 //debug_printf("BasicTerminalBuffer::InsertChar('%.*s' (%d), %#lx)\n",
625 //(int)c.ByteCount(), c.bytes, c.bytes[0], attributes);
626 int32 width
= c
.IsFullWidth() ? FULL_WIDTH
: HALF_WIDTH
;
628 if (fSoftWrappedCursor
|| (fCursor
.x
+ width
) > fWidth
)
633 fSoftWrappedCursor
= false;
638 TerminalLine
* line
= _LineAt(fCursor
.y
);
639 line
->cells
[fCursor
.x
].character
= c
;
640 line
->cells
[fCursor
.x
].attributes
641 = fAttributes
| (width
== FULL_WIDTH
? A_WIDTH
: 0);
643 if (line
->length
< fCursor
.x
+ width
)
644 line
->length
= fCursor
.x
+ width
;
646 _Invalidate(fCursor
.y
, fCursor
.y
);
650 // TODO: Deal correctly with full-width chars! We must take care not to
651 // overwrite half of a full-width char. This holds also for other methods.
653 if (fCursor
.x
== fWidth
) {
655 fSoftWrappedCursor
= true;
661 BasicTerminalBuffer::FillScreen(UTF8Char c
, uint32 attributes
)
663 uint32 width
= HALF_WIDTH
;
664 if (c
.IsFullWidth()) {
665 attributes
|= A_WIDTH
;
669 fSoftWrappedCursor
= false;
671 for (int32 y
= 0; y
< fHeight
; y
++) {
672 TerminalLine
*line
= _LineAt(y
);
673 for (int32 x
= 0; x
< fWidth
/ (int32
)width
; x
++) {
674 line
->cells
[x
].character
= c
;
675 line
->cells
[x
].attributes
= attributes
;
677 line
->length
= fWidth
/ width
;
680 _Invalidate(0, fHeight
- 1);
685 BasicTerminalBuffer::InsertCR()
687 TerminalLine
* line
= _LineAt(fCursor
.y
);
689 line
->attributes
= fAttributes
;
690 line
->softBreak
= false;
691 fSoftWrappedCursor
= false;
693 _Invalidate(fCursor
.y
, fCursor
.y
);
699 BasicTerminalBuffer::InsertLF()
701 fSoftWrappedCursor
= false;
703 // If we're at the end of the scroll region, scroll. Otherwise just advance
705 if (fCursor
.y
== fScrollBottom
) {
706 _Scroll(fScrollTop
, fScrollBottom
, 1);
708 if (fCursor
.y
< fHeight
- 1)
716 BasicTerminalBuffer::InsertRI()
718 fSoftWrappedCursor
= false;
720 // If we're at the beginning of the scroll region, scroll. Otherwise just
721 // reverse the cursor.
722 if (fCursor
.y
== fScrollTop
) {
723 _Scroll(fScrollTop
, fScrollBottom
, -1);
733 BasicTerminalBuffer::InsertTab()
737 fSoftWrappedCursor
= false;
739 // Find the next tab stop
740 for (x
= fCursor
.x
+ 1; x
< fWidth
&& !fTabStops
[x
]; x
++)
742 // Ensure x stayx within the line bounds
743 x
= restrict_value(x
, 0, fWidth
- 1);
745 if (x
!= fCursor
.x
) {
746 TerminalLine
* line
= _LineAt(fCursor
.y
);
747 for (int32 i
= fCursor
.x
; i
<= x
; i
++) {
748 if (line
->length
<= i
) {
749 line
->cells
[i
].character
= ' ';
750 line
->cells
[i
].attributes
= fAttributes
;
754 if (line
->length
< fCursor
.x
)
755 line
->length
= fCursor
.x
;
762 BasicTerminalBuffer::InsertCursorBackTab(int32 numTabs
)
764 int32 x
= fCursor
.x
- 1;
766 fSoftWrappedCursor
= false;
768 // Find the next tab stop
769 while (numTabs
-- > 0)
770 for (; x
>=0 && !fTabStops
[x
]; x
--)
772 // Ensure x stays within the line bounds
773 x
= restrict_value(x
, 0, fWidth
- 1);
775 if (x
!= fCursor
.x
) {
783 BasicTerminalBuffer::InsertLines(int32 numLines
)
785 if (fCursor
.y
>= fScrollTop
&& fCursor
.y
< fScrollBottom
) {
786 fSoftWrappedCursor
= false;
787 _Scroll(fCursor
.y
, fScrollBottom
, -numLines
);
793 BasicTerminalBuffer::SetInsertMode(int flag
)
795 fOverwriteMode
= flag
== MODE_OVER
;
800 BasicTerminalBuffer::InsertSpace(int32 num
)
802 // TODO: Deal with full-width chars!
803 if (fCursor
.x
+ num
> fWidth
)
804 num
= fWidth
- fCursor
.x
;
807 fSoftWrappedCursor
= false;
811 TerminalLine
* line
= _LineAt(fCursor
.y
);
812 for (int32 i
= fCursor
.x
; i
< fCursor
.x
+ num
; i
++) {
813 line
->cells
[i
].character
= kSpaceChar
;
814 line
->cells
[i
].attributes
= line
->cells
[fCursor
.x
- 1].attributes
;
816 line
->attributes
= fAttributes
;
818 _Invalidate(fCursor
.y
, fCursor
.y
);
824 BasicTerminalBuffer::EraseCharsFrom(int32 first
, int32 numChars
)
826 TerminalLine
* line
= _LineAt(fCursor
.y
);
828 int32 end
= min_c(first
+ numChars
, fWidth
);
829 for (int32 i
= first
; i
< end
; i
++)
830 line
->cells
[i
].attributes
= fAttributes
;
832 line
->attributes
= fAttributes
;
834 fSoftWrappedCursor
= false;
836 end
= min_c(first
+ numChars
, line
->length
);
837 if (first
> 0 && IS_WIDTH(line
->cells
[first
- 1].attributes
))
839 if (end
> 0 && IS_WIDTH(line
->cells
[end
- 1].attributes
))
842 for (int32 i
= first
; i
< end
; i
++) {
843 line
->cells
[i
].character
= kSpaceChar
;
844 line
->cells
[i
].attributes
= fAttributes
;
847 _Invalidate(fCursor
.y
, fCursor
.y
);
852 BasicTerminalBuffer::EraseAbove()
854 // Clear the preceding lines.
856 _ClearLines(0, fCursor
.y
- 1);
858 fSoftWrappedCursor
= false;
860 // Delete the chars on the cursor line before (and including) the cursor.
861 TerminalLine
* line
= _LineAt(fCursor
.y
);
862 if (fCursor
.x
< line
->length
) {
863 int32 to
= fCursor
.x
;
864 if (IS_WIDTH(line
->cells
[fCursor
.x
].attributes
))
866 for (int32 i
= 0; i
<= to
; i
++) {
867 line
->cells
[i
].attributes
= fAttributes
;
868 line
->cells
[i
].character
= kSpaceChar
;
871 line
->Clear(fAttributes
, fWidth
);
873 _Invalidate(fCursor
.y
, fCursor
.y
);
878 BasicTerminalBuffer::EraseBelow()
880 fSoftWrappedCursor
= false;
882 // Clear the following lines.
883 if (fCursor
.y
< fHeight
- 1)
884 _ClearLines(fCursor
.y
+ 1, fHeight
- 1);
886 // Delete the chars on the cursor line after (and including) the cursor.
892 BasicTerminalBuffer::EraseAll()
894 fSoftWrappedCursor
= false;
895 _Scroll(0, fHeight
- 1, fHeight
);
900 BasicTerminalBuffer::DeleteChars(int32 numChars
)
902 fSoftWrappedCursor
= false;
904 TerminalLine
* line
= _LineAt(fCursor
.y
);
905 if (fCursor
.x
< line
->length
) {
906 if (fCursor
.x
+ numChars
< line
->length
) {
907 int32 left
= line
->length
- fCursor
.x
- numChars
;
908 memmove(line
->cells
+ fCursor
.x
, line
->cells
+ fCursor
.x
+ numChars
,
909 left
* sizeof(TerminalCell
));
910 line
->length
= fCursor
.x
+ left
;
911 // process BCE on freed tail cells
912 for (int i
= 0; i
< numChars
; i
++)
913 line
->cells
[fCursor
.x
+ left
+ i
].attributes
= fAttributes
;
915 // process BCE on freed tail cells
916 for (int i
= 0; i
< line
->length
- fCursor
.x
; i
++)
917 line
->cells
[fCursor
.x
+ i
].attributes
= fAttributes
;
918 // remove all remaining chars
919 line
->length
= fCursor
.x
;
922 _Invalidate(fCursor
.y
, fCursor
.y
);
928 BasicTerminalBuffer::DeleteColumnsFrom(int32 first
)
930 fSoftWrappedCursor
= false;
932 TerminalLine
* line
= _LineAt(fCursor
.y
);
934 for (int32 i
= first
; i
< fWidth
; i
++)
935 line
->cells
[i
].attributes
= fAttributes
;
937 if (first
<= line
->length
) {
938 line
->length
= first
;
939 line
->attributes
= fAttributes
;
941 _Invalidate(fCursor
.y
, fCursor
.y
);
946 BasicTerminalBuffer::DeleteLines(int32 numLines
)
948 if (fCursor
.y
>= fScrollTop
&& fCursor
.y
<= fScrollBottom
) {
949 fSoftWrappedCursor
= false;
950 _Scroll(fCursor
.y
, fScrollBottom
, numLines
);
956 BasicTerminalBuffer::SaveCursor()
958 fSavedCursors
.push(fCursor
);
963 BasicTerminalBuffer::RestoreCursor()
965 if (fSavedCursors
.size() == 0)
968 _SetCursor(fSavedCursors
.top().x
, fSavedCursors
.top().y
, true);
974 BasicTerminalBuffer::SetScrollRegion(int32 top
, int32 bottom
)
976 fScrollTop
= restrict_value(top
, 0, fHeight
- 1);
977 fScrollBottom
= restrict_value(bottom
, fScrollTop
, fHeight
- 1);
979 // also sets the cursor position
980 _SetCursor(0, 0, false);
985 BasicTerminalBuffer::SetOriginMode(bool enabled
)
987 fOriginMode
= enabled
;
988 _SetCursor(0, 0, false);
993 BasicTerminalBuffer::SaveOriginMode()
995 fSavedOriginMode
= fOriginMode
;
1000 BasicTerminalBuffer::RestoreOriginMode()
1002 fOriginMode
= fSavedOriginMode
;
1007 BasicTerminalBuffer::SetTabStop(int32 x
)
1009 x
= restrict_value(x
, 0, fWidth
- 1);
1010 fTabStops
[x
] = true;
1015 BasicTerminalBuffer::ClearTabStop(int32 x
)
1017 x
= restrict_value(x
, 0, fWidth
- 1);
1018 fTabStops
[x
] = false;
1023 BasicTerminalBuffer::ClearAllTabStops()
1025 for (int32 i
= 0; i
< fWidth
; i
++)
1026 fTabStops
[i
] = false;
1031 BasicTerminalBuffer::NotifyListener()
1033 // Implemented by derived classes.
1037 // #pragma mark - private methods
1041 BasicTerminalBuffer::_SetCursor(int32 x
, int32 y
, bool absolute
)
1043 //debug_printf("BasicTerminalBuffer::_SetCursor(%d, %d)\n", x, y);
1044 fSoftWrappedCursor
= false;
1046 x
= restrict_value(x
, 0, fWidth
- 1);
1047 if (fOriginMode
&& !absolute
) {
1049 y
= restrict_value(y
, fScrollTop
, fScrollBottom
);
1051 y
= restrict_value(y
, 0, fHeight
- 1);
1054 if (x
!= fCursor
.x
|| y
!= fCursor
.y
) {
1063 BasicTerminalBuffer::_InvalidateAll()
1065 fDirtyInfo
.invalidateAll
= true;
1067 if (!fDirtyInfo
.messageSent
) {
1069 fDirtyInfo
.messageSent
= true;
1074 /* static */ TerminalLine
**
1075 BasicTerminalBuffer::_AllocateLines(int32 width
, int32 count
)
1077 TerminalLine
** lines
= (TerminalLine
**)malloc(sizeof(TerminalLine
*) * count
);
1081 for (int32 i
= 0; i
< count
; i
++) {
1082 const int32 size
= sizeof(TerminalLine
)
1083 + sizeof(TerminalCell
) * (width
- 1);
1084 lines
[i
] = (TerminalLine
*)malloc(size
);
1085 if (lines
[i
] == NULL
) {
1086 _FreeLines(lines
, i
);
1089 memset(lines
[i
], 0, size
);
1097 BasicTerminalBuffer::_FreeLines(TerminalLine
** lines
, int32 count
)
1099 if (lines
!= NULL
) {
1100 for (int32 i
= 0; i
< count
; i
++)
1109 BasicTerminalBuffer::_ClearLines(int32 first
, int32 last
)
1111 int32 firstCleared
= -1;
1112 int32 lastCleared
= -1;
1114 for (int32 i
= first
; i
<= last
; i
++) {
1115 TerminalLine
* line
= _LineAt(i
);
1116 if (line
->length
> 0) {
1117 if (firstCleared
== -1)
1122 line
->Clear(fAttributes
, fWidth
);
1125 if (firstCleared
>= 0)
1126 _Invalidate(firstCleared
, lastCleared
);
1131 BasicTerminalBuffer::_ResizeHistory(int32 width
, int32 historyCapacity
)
1133 if (width
== fWidth
&& historyCapacity
== HistoryCapacity())
1136 if (historyCapacity
<= 0) {
1137 // new history capacity is 0 -- delete the old history object
1144 HistoryBuffer
* history
= new(std::nothrow
) HistoryBuffer
;
1145 if (history
== NULL
)
1148 status_t error
= history
->Init(width
, historyCapacity
);
1149 if (error
!= B_OK
) {
1154 // Transfer the lines from the old history to the new one.
1155 if (fHistory
!= NULL
) {
1156 int32 historySize
= min_c(HistorySize(), historyCapacity
);
1157 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
1158 for (int32 i
= historySize
- 1; i
>= 0; i
--) {
1159 TerminalLine
* line
= fHistory
->GetTerminalLineAt(i
, lineBuffer
);
1160 if (line
->length
> width
)
1161 _TruncateLine(line
, width
);
1162 history
->AddLine(line
);
1174 BasicTerminalBuffer::_ResizeSimple(int32 width
, int32 height
,
1175 int32 historyCapacity
)
1177 //debug_printf("BasicTerminalBuffer::_ResizeSimple(): (%ld, %ld) -> "
1178 //"(%ld, %ld)\n", fWidth, fHeight, width, height);
1179 if (width
== fWidth
&& height
== fHeight
)
1182 if (width
!= fWidth
|| historyCapacity
!= HistoryCapacity()) {
1183 status_t error
= _ResizeHistory(width
, historyCapacity
);
1188 TerminalLine
** lines
= _AllocateLines(width
, height
);
1191 // NOTE: If width or history capacity changed, the object will be in
1192 // an invalid state, since the history will already use the new values.
1194 int32 endLine
= min_c(fHeight
, height
);
1195 int32 firstLine
= 0;
1197 if (height
< fHeight
) {
1198 if (endLine
<= fCursor
.y
) {
1199 endLine
= fCursor
.y
+ 1;
1200 firstLine
= endLine
- height
;
1203 // push the first lines to the history
1204 if (fHistory
!= NULL
) {
1205 for (int32 i
= 0; i
< firstLine
; i
++) {
1206 TerminalLine
* line
= _LineAt(i
);
1208 _TruncateLine(line
, width
);
1209 fHistory
->AddLine(line
);
1214 // copy the lines we keep
1215 for (int32 i
= firstLine
; i
< endLine
; i
++) {
1216 TerminalLine
* sourceLine
= _LineAt(i
);
1217 TerminalLine
* destLine
= lines
[i
- firstLine
];
1219 _TruncateLine(sourceLine
, width
);
1220 memcpy(destLine
, sourceLine
, (int32
)sizeof(TerminalLine
)
1221 + (sourceLine
->length
- 1) * (int32
)sizeof(TerminalCell
));
1224 // clear the remaining lines
1225 for (int32 i
= endLine
- firstLine
; i
< height
; i
++)
1226 lines
[i
]->Clear(fAttributes
, width
);
1228 _FreeLines(fScreen
, fHeight
);
1231 if (fWidth
!= width
) {
1232 status_t error
= _ResetTabStops(width
);
1241 fScrollBottom
= fHeight
- 1;
1242 fOriginMode
= fSavedOriginMode
= false;
1246 if (fCursor
.x
> width
)
1248 fCursor
.y
-= firstLine
;
1249 fSoftWrappedCursor
= false;
1256 BasicTerminalBuffer::_ResizeRewrap(int32 width
, int32 height
,
1257 int32 historyCapacity
)
1259 //debug_printf("BasicTerminalBuffer::_ResizeRewrap(): (%ld, %ld, history: %ld) -> "
1260 //"(%ld, %ld, history: %ld)\n", fWidth, fHeight, HistoryCapacity(), width, height,
1263 // The width stays the same. _ResizeSimple() does exactly what we need.
1264 if (width
== fWidth
)
1265 return _ResizeSimple(width
, height
, historyCapacity
);
1267 // The width changes. We have to allocate a new line array, a new history
1268 // and re-wrap all lines.
1270 TerminalLine
** screen
= _AllocateLines(width
, height
);
1274 HistoryBuffer
* history
= NULL
;
1276 if (historyCapacity
> 0) {
1277 history
= new(std::nothrow
) HistoryBuffer
;
1278 if (history
== NULL
) {
1279 _FreeLines(screen
, height
);
1283 status_t error
= history
->Init(width
, historyCapacity
);
1284 if (error
!= B_OK
) {
1285 _FreeLines(screen
, height
);
1291 int32 historySize
= HistorySize();
1292 int32 totalLines
= historySize
+ fHeight
;
1295 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
1297 int32 destIndex
= 0;
1298 int32 sourceIndex
= 0;
1300 int32 destTotalLines
= 0;
1301 int32 destScreenOffset
= 0;
1302 int32 maxDestTotalLines
= INT_MAX
;
1303 bool newDestLine
= true;
1304 bool cursorSeen
= false;
1305 TerminalLine
* sourceLine
= _HistoryLineAt(-historySize
, lineBuffer
);
1307 while (sourceIndex
< totalLines
) {
1308 TerminalLine
* destLine
= screen
[destIndex
];
1311 // Clear a new dest line before using it. If we're about to
1312 // overwrite an previously written line, we push it to the
1313 // history first, though.
1314 if (history
!= NULL
&& destTotalLines
>= height
)
1315 history
->AddLine(screen
[destIndex
]);
1316 destLine
->Clear(fAttributes
, width
);
1317 newDestLine
= false;
1320 int32 sourceLeft
= sourceLine
->length
- sourceX
;
1321 int32 destLeft
= width
- destLine
->length
;
1322 //debug_printf(" source: %ld, left: %ld, dest: %ld, left: %ld\n",
1323 //sourceIndex, sourceLeft, destIndex, destLeft);
1325 if (sourceIndex
== historySize
&& sourceX
== 0) {
1326 destScreenOffset
= destTotalLines
;
1327 if (destLeft
== 0 && sourceLeft
> 0)
1329 maxDestTotalLines
= destScreenOffset
+ height
;
1330 //debug_printf(" destScreenOffset: %ld\n", destScreenOffset);
1333 int32 toCopy
= min_c(sourceLeft
, destLeft
);
1334 // If the last cell to copy is the first cell of a
1335 // full-width char, don't copy it yet.
1336 if (toCopy
> 0 && IS_WIDTH(
1337 sourceLine
->cells
[sourceX
+ toCopy
- 1].attributes
)) {
1338 //debug_printf(" -> last char is full-width -- don't copy it\n");
1342 // translate the cursor position
1343 if (fCursor
.y
+ historySize
== sourceIndex
1344 && fCursor
.x
>= sourceX
1345 && (fCursor
.x
< sourceX
+ toCopy
1346 || (destLeft
>= sourceLeft
1347 && sourceX
+ sourceLeft
<= fCursor
.x
))) {
1348 cursor
.x
= destLine
->length
+ fCursor
.x
- sourceX
;
1349 cursor
.y
= destTotalLines
;
1351 if (cursor
.x
>= width
) {
1352 // The cursor was in free space after the official end
1354 cursor
.x
= width
- 1;
1356 //debug_printf(" cursor: (%ld, %ld)\n", cursor.x, cursor.y);
1362 memcpy(destLine
->cells
+ destLine
->length
,
1363 sourceLine
->cells
+ sourceX
, toCopy
* sizeof(TerminalCell
));
1364 destLine
->length
+= toCopy
;
1367 destLine
->attributes
= sourceLine
->attributes
;
1369 bool nextDestLine
= false;
1370 if (toCopy
== sourceLeft
) {
1371 if (!sourceLine
->softBreak
)
1372 nextDestLine
= true;
1375 sourceLine
= _HistoryLineAt(sourceIndex
- historySize
,
1378 destLine
->softBreak
= true;
1379 nextDestLine
= true;
1384 destIndex
= (destIndex
+ 1) % height
;
1387 if (cursorSeen
&& destTotalLines
>= maxDestTotalLines
)
1392 // If the last source line had a soft break, the last dest line
1393 // won't have been counted yet.
1395 destIndex
= (destIndex
+ 1) % height
;
1399 //debug_printf(" total lines: %ld -> %ld\n", totalLines, destTotalLines);
1401 if (destTotalLines
- destScreenOffset
> height
)
1402 destScreenOffset
= destTotalLines
- height
;
1404 cursor
.y
-= destScreenOffset
;
1406 // When there are less lines (starting with the screen offset) than
1407 // there's room in the screen, clear the remaining screen lines.
1408 for (int32 i
= destTotalLines
; i
< destScreenOffset
+ height
; i
++) {
1409 // Move the line we're going to clear to the history, if that's a
1410 // line we've written earlier.
1411 TerminalLine
* line
= screen
[i
% height
];
1412 if (history
!= NULL
&& i
>= height
)
1413 history
->AddLine(line
);
1414 line
->Clear(fAttributes
, width
);
1417 // Update the values
1418 _FreeLines(fScreen
, fHeight
);
1424 if (fWidth
!= width
) {
1425 status_t error
= _ResetTabStops(width
);
1430 //debug_printf(" cursor: (%ld, %ld) -> (%ld, %ld)\n", fCursor.x, fCursor.y,
1431 //cursor.x, cursor.y);
1432 fCursor
.x
= cursor
.x
;
1433 fCursor
.y
= cursor
.y
;
1434 fSoftWrappedCursor
= false;
1435 //debug_printf(" screen offset: %ld -> %ld\n", fScreenOffset, destScreenOffset % height);
1436 fScreenOffset
= destScreenOffset
% height
;
1437 //debug_printf(" height %ld -> %ld\n", fHeight, height);
1438 //debug_printf(" width %ld -> %ld\n", fWidth, width);
1443 fScrollBottom
= fHeight
- 1;
1444 fOriginMode
= fSavedOriginMode
= false;
1451 BasicTerminalBuffer::_ResetTabStops(int32 width
)
1453 if (fTabStops
!= NULL
)
1456 fTabStops
= new(std::nothrow
) bool[width
];
1457 if (fTabStops
== NULL
)
1460 for (int32 i
= 0; i
< width
; i
++)
1461 fTabStops
[i
] = (i
% TAB_WIDTH
) == 0;
1467 BasicTerminalBuffer::_Scroll(int32 top
, int32 bottom
, int32 numLines
)
1475 // The lines scrolled out of the screen range are transferred to
1478 // add the lines to the history
1479 if (fHistory
!= NULL
) {
1480 int32 toHistory
= min_c(numLines
, bottom
- top
+ 1);
1481 for (int32 i
= 0; i
< toHistory
; i
++)
1482 fHistory
->AddLine(_LineAt(i
));
1484 if (toHistory
< numLines
)
1485 fHistory
->AddEmptyLines(numLines
- toHistory
);
1488 if (numLines
>= bottom
- top
+ 1) {
1489 // all lines are scrolled out of range -- just clear them
1490 _ClearLines(top
, bottom
);
1491 } else if (bottom
== fHeight
- 1) {
1492 // full screen scroll -- update the screen offset and clear new
1494 fScreenOffset
= (fScreenOffset
+ numLines
) % fHeight
;
1495 for (int32 i
= bottom
- numLines
+ 1; i
<= bottom
; i
++)
1496 _LineAt(i
)->Clear(fAttributes
, fWidth
);
1498 // Partial screen scroll. We move the screen offset anyway, but
1499 // have to move the unscrolled lines to their new location.
1500 // TODO: It may be more efficient to actually move the scrolled
1501 // lines only (might depend on the number of scrolled/unscrolled
1503 for (int32 i
= fHeight
- 1; i
> bottom
; i
--) {
1504 std::swap(fScreen
[_LineIndex(i
)],
1505 fScreen
[_LineIndex(i
+ numLines
)]);
1508 // update the screen offset and clear the new lines
1509 fScreenOffset
= (fScreenOffset
+ numLines
) % fHeight
;
1510 for (int32 i
= bottom
- numLines
+ 1; i
<= bottom
; i
++)
1511 _LineAt(i
)->Clear(fAttributes
, fWidth
);
1514 // scroll/extend dirty range
1516 if (fDirtyInfo
.dirtyTop
!= INT_MAX
) {
1517 // If the top or bottom of the dirty region are above the
1518 // bottom of the scroll region, we have to scroll them up.
1519 if (fDirtyInfo
.dirtyTop
<= bottom
) {
1520 fDirtyInfo
.dirtyTop
-= numLines
;
1521 if (fDirtyInfo
.dirtyBottom
<= bottom
)
1522 fDirtyInfo
.dirtyBottom
-= numLines
;
1525 // numLines above the bottom become dirty
1526 _Invalidate(bottom
- numLines
+ 1, bottom
);
1529 fDirtyInfo
.linesScrolled
+= numLines
;
1531 // invalidate new empty lines
1532 _Invalidate(bottom
+ 1 - numLines
, bottom
);
1534 // In case only part of the screen was scrolled, we invalidate also
1535 // the lines below the scroll region. Those remain unchanged, but
1536 // we can't convey that they have not been scrolled via
1537 // TerminalBufferDirtyInfo. So we need to force the view to sync
1539 if (bottom
< fHeight
- 1)
1540 _Invalidate(bottom
+ 1, fHeight
- 1);
1541 } else if (numLines
>= bottom
- top
+ 1) {
1542 // all lines are completely scrolled out of range -- just clear
1544 _ClearLines(top
, bottom
);
1546 // partial scroll -- clear the lines scrolled out of range and move
1548 for (int32 i
= top
+ numLines
; i
<= bottom
; i
++) {
1549 int32 lineToDrop
= _LineIndex(i
- numLines
);
1550 int32 lineToKeep
= _LineIndex(i
);
1551 fScreen
[lineToDrop
]->Clear(fAttributes
, fWidth
);
1552 std::swap(fScreen
[lineToDrop
], fScreen
[lineToKeep
]);
1554 // clear any lines between the two swapped ranges above
1555 for (int32 i
= bottom
- numLines
+ 1; i
< top
+ numLines
; i
++)
1556 _LineAt(i
)->Clear(fAttributes
, fWidth
);
1558 _Invalidate(top
, bottom
);
1562 numLines
= -numLines
;
1564 if (numLines
>= bottom
- top
+ 1) {
1565 // all lines are completely scrolled out of range -- just clear
1567 _ClearLines(top
, bottom
);
1569 // partial scroll -- clear the lines scrolled out of range and move
1571 // TODO: When scrolling the whole screen, we could just update fScreenOffset and
1572 // clear the respective lines.
1573 for (int32 i
= bottom
- numLines
; i
>= top
; i
--) {
1574 int32 lineToKeep
= _LineIndex(i
);
1575 int32 lineToDrop
= _LineIndex(i
+ numLines
);
1576 fScreen
[lineToDrop
]->Clear(fAttributes
, fWidth
);
1577 std::swap(fScreen
[lineToDrop
], fScreen
[lineToKeep
]);
1579 // clear any lines between the two swapped ranges above
1580 for (int32 i
= bottom
- numLines
+ 1; i
< top
+ numLines
; i
++)
1581 _LineAt(i
)->Clear(fAttributes
, fWidth
);
1583 _Invalidate(top
, bottom
);
1590 BasicTerminalBuffer::_SoftBreakLine()
1592 TerminalLine
* line
= _LineAt(fCursor
.y
);
1593 line
->softBreak
= true;
1596 if (fCursor
.y
== fScrollBottom
)
1597 _Scroll(fScrollTop
, fScrollBottom
, 1);
1604 BasicTerminalBuffer::_PadLineToCursor()
1606 TerminalLine
* line
= _LineAt(fCursor
.y
);
1607 if (line
->length
< fCursor
.x
)
1608 for (int32 i
= line
->length
; i
< fCursor
.x
; i
++)
1609 line
->cells
[i
].character
= kSpaceChar
;
1614 BasicTerminalBuffer::_TruncateLine(TerminalLine
* line
, int32 length
)
1616 if (line
->length
<= length
)
1619 if (length
> 0 && IS_WIDTH(line
->cells
[length
- 1].attributes
))
1622 line
->length
= length
;
1627 BasicTerminalBuffer::_InsertGap(int32 width
)
1629 // ASSERT(fCursor.x + width <= fWidth)
1630 TerminalLine
* line
= _LineAt(fCursor
.y
);
1632 int32 toMove
= min_c(line
->length
- fCursor
.x
, fWidth
- fCursor
.x
- width
);
1634 memmove(line
->cells
+ fCursor
.x
+ width
,
1635 line
->cells
+ fCursor
.x
, toMove
* sizeof(TerminalCell
));
1638 line
->length
= min_c(line
->length
+ width
, fWidth
);
1642 /*! \a endColumn is not inclusive.
1645 BasicTerminalBuffer::_GetPartialLineString(BString
& string
, int32 row
,
1646 int32 startColumn
, int32 endColumn
) const
1648 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
1649 TerminalLine
* line
= _HistoryLineAt(row
, lineBuffer
);
1653 if (endColumn
> line
->length
)
1654 endColumn
= line
->length
;
1656 for (int32 x
= startColumn
; x
< endColumn
; x
++) {
1657 const TerminalCell
& cell
= line
->cells
[x
];
1658 string
.Append(cell
.character
.bytes
, cell
.character
.ByteCount());
1660 if (IS_WIDTH(cell
.attributes
))
1668 /*! Decrement \a pos and return the char at that location.
1671 BasicTerminalBuffer::_PreviousChar(TermPos
& pos
, UTF8Char
& c
) const
1675 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
1676 TerminalLine
* line
= _HistoryLineAt(pos
.y
, lineBuffer
);
1681 line
= _HistoryLineAt(pos
.y
, lineBuffer
);
1685 pos
.x
= line
->length
;
1686 if (line
->softBreak
) {
1693 c
= line
->cells
[pos
.x
].character
;
1700 /*! Return the char at \a pos and increment it.
1703 BasicTerminalBuffer::_NextChar(TermPos
& pos
, UTF8Char
& c
) const
1705 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
1706 TerminalLine
* line
= _HistoryLineAt(pos
.y
, lineBuffer
);
1710 if (pos
.x
>= line
->length
) {
1717 c
= line
->cells
[pos
.x
].character
;
1720 while (line
!= NULL
&& pos
.x
>= line
->length
&& line
->softBreak
) {
1723 line
= _HistoryLineAt(pos
.y
, lineBuffer
);
1731 BasicTerminalBuffer::_PreviousLinePos(TerminalLine
* lineBuffer
,
1732 TerminalLine
*& line
, TermPos
& pos
) const
1735 // Hit the beginning of the line -- continue at the end of the
1736 // previous line, if it soft-breaks.
1738 if ((line
= _HistoryLineAt(pos
.y
, lineBuffer
)) == NULL
1739 || !line
->softBreak
|| line
->length
== 0) {
1742 pos
.x
= line
->length
- 1;
1744 if (pos
.x
> 0 && IS_WIDTH(line
->cells
[pos
.x
- 1].attributes
))
1752 BasicTerminalBuffer::_NormalizeLinePos(TerminalLine
* lineBuffer
,
1753 TerminalLine
*& line
, TermPos
& pos
) const
1755 if (pos
.x
< line
->length
)
1758 // Hit the end of the line -- if it soft-breaks continue with the
1760 if (!line
->softBreak
)
1765 line
= _HistoryLineAt(pos
.y
, lineBuffer
);
1766 return line
!= NULL
;
1770 #ifdef USE_DEBUG_SNAPSHOTS
1773 BasicTerminalBuffer::MakeLinesSnapshots(time_t timeStamp
, const char* fileName
)
1775 BString
str("/var/log/");
1776 struct tm
* ts
= gmtime(&timeStamp
);
1777 str
<< ts
->tm_hour
<< ts
->tm_min
<< ts
->tm_sec
;
1779 FILE* fileOut
= fopen(str
.String(), "w");
1781 bool dumpHistory
= false;
1783 if (dumpHistory
&& fHistory
== NULL
) {
1784 fprintf(fileOut
, "> History is empty <\n");
1788 int countLines
= dumpHistory
? fHistory
->Size() : fHeight
;
1789 fprintf(fileOut
, "> %s lines dump begin <\n",
1790 dumpHistory
? "History" : "Terminal");
1792 TerminalLine
* lineBuffer
= ALLOC_LINE_ON_STACK(fWidth
);
1793 for (int i
= 0; i
< countLines
; i
++) {
1794 TerminalLine
* line
= dumpHistory
1795 ? fHistory
->GetTerminalLineAt(i
, lineBuffer
)
1796 : fScreen
[_LineIndex(i
)];
1799 fprintf(fileOut
, "line: %d is NULL!!!\n", i
);
1803 fprintf(fileOut
, "%02" B_PRId16
":%02" B_PRId16
":%08" B_PRIx32
":\n",
1804 i
, line
->length
, line
->attributes
);
1805 for (int j
= 0; j
< line
->length
; j
++)
1806 if (line
->cells
[j
].character
.bytes
[0] != 0)
1807 fwrite(line
->cells
[j
].character
.bytes
, 1,
1808 line
->cells
[j
].character
.ByteCount(), fileOut
);
1810 fprintf(fileOut
, "\n");
1811 for (int s
= 28; s
>= 0; s
-= 4) {
1812 for (int j
= 0; j
< fWidth
; j
++)
1813 fprintf(fileOut
, "%01" B_PRIx32
,
1814 (line
->cells
[j
].attributes
>> s
) & 0x0F);
1816 fprintf(fileOut
, "\n");
1819 fprintf(fileOut
, "\n");
1822 fprintf(fileOut
, "> %s lines dump finished <\n",
1823 dumpHistory
? "History" : "Terminal");
1825 dumpHistory
= !dumpHistory
;
1826 } while (dumpHistory
);
1833 BasicTerminalBuffer::StartStopDebugCapture()
1835 if (fCaptureFile
>= 0) {
1836 close(fCaptureFile
);
1841 time_t timeStamp
= time(NULL
);
1842 BString
str("/var/log/");
1843 struct tm
* ts
= gmtime(&timeStamp
);
1844 str
<< ts
->tm_hour
<< ts
->tm_min
<< ts
->tm_sec
;
1845 str
<< ".Capture.log";
1846 fCaptureFile
= open(str
.String(), O_CREAT
| O_WRONLY
,
1847 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
1852 BasicTerminalBuffer::CaptureChar(char ch
)
1854 if (fCaptureFile
>= 0)
1855 write(fCaptureFile
, &ch
, 1);