2 * Copyright 2013, Haiku, Inc. All rights reserved.
3 * Copyright 2008, 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 "HistoryBuffer.h"
17 #include "TermConst.h"
20 HistoryBuffer::HistoryBuffer()
29 fBufferAllocationOffset(0)
34 HistoryBuffer::~HistoryBuffer()
42 HistoryBuffer::Init(int32 width
, int32 capacity
)
44 if (width
<= 0 || capacity
<= 0)
47 int32 bufferSize
= (width
+ 4) * capacity
;
50 fLines
= new(std::nothrow
) HistoryLine
[capacity
];
51 fBuffer
= new(std::nothrow
) uint8
[bufferSize
];
53 if (fLines
== NULL
|| fBuffer
== NULL
)
61 fBufferSize
= bufferSize
;
62 fBufferAllocationOffset
= 0;
69 HistoryBuffer::Clear()
73 fBufferAllocationOffset
= 0;
78 HistoryBuffer::GetTerminalLineAt(int32 index
, TerminalLine
* buffer
) const
80 HistoryLine
* line
= LineAt(index
);
85 const char* chars
= line
->Chars();
87 uint32 attributes
= 0;
88 AttributesRun
* attributesRun
= line
->AttributesRuns();
89 int32 attributesRunCount
= line
->attributesRunCount
;
90 int32 nextAttributesAt
= attributesRunCount
> 0
91 ? attributesRun
->offset
: INT_MAX
;
93 for (int32 i
= 0; i
< line
->byteLength
;) {
95 if (charCount
== nextAttributesAt
) {
96 if (charCount
< attributesRun
->offset
) {
97 // the "hole" in attributes run
99 nextAttributesAt
= attributesRun
->offset
;
100 } else if (attributesRunCount
> 0) {
101 attributes
= attributesRun
->attributes
;
102 nextAttributesAt
= attributesRun
->offset
103 + attributesRun
->length
;
105 attributesRunCount
--;
108 nextAttributesAt
= INT_MAX
;
113 TerminalCell
& cell
= buffer
->cells
[charCount
++];
114 int32 charLength
= UTF8Char::ByteCount(chars
[i
]);
115 cell
.character
.SetTo(chars
+ i
, charLength
);
119 cell
.attributes
= attributes
;
122 if (cell
.character
.IsFullWidth()) {
123 cell
.attributes
|= A_WIDTH
;
124 // attributes of the second, "invisible" cell must be
125 // cleared to let full-width chars detection work properly
126 buffer
->cells
[charCount
++].attributes
= 0;
130 buffer
->length
= charCount
;
131 buffer
->softBreak
= line
->softBreak
;
132 buffer
->attributes
= line
->attributes
;
139 HistoryBuffer::AddLine(const TerminalLine
* line
)
141 //debug_printf("HistoryBuffer::AddLine(%p): length: %d\n", line, line->length);
142 // determine the amount of memory we need for the line
143 uint32 attributes
= 0;
144 int32 attributesRuns
= 0;
145 int32 byteLength
= 0;
146 for (int32 i
= 0; i
< line
->length
; i
++) {
147 const TerminalCell
& cell
= line
->cells
[i
];
148 byteLength
+= cell
.character
.ByteCount();
149 if ((cell
.attributes
& CHAR_ATTRIBUTES
) != attributes
) {
150 attributes
= cell
.attributes
& CHAR_ATTRIBUTES
;
154 if (IS_WIDTH(cell
.attributes
))
158 //debug_printf(" attributesRuns: %ld, byteLength: %ld\n", attributesRuns, byteLength);
160 // allocate and translate the line
161 HistoryLine
* historyLine
= _AllocateLine(attributesRuns
, byteLength
);
164 AttributesRun
* attributesRun
= historyLine
->AttributesRuns();
166 char* chars
= historyLine
->Chars();
167 for (int32 i
= 0; i
< line
->length
; i
++) {
168 const TerminalCell
& cell
= line
->cells
[i
];
171 int32 charLength
= cell
.character
.ByteCount();
172 memcpy(chars
, cell
.character
.bytes
, charLength
);
175 // deal with attributes
176 if ((cell
.attributes
& CHAR_ATTRIBUTES
) != attributes
) {
177 // terminate the previous attributes run
178 if (attributes
!= 0) {
179 attributesRun
->length
= i
- attributesRun
->offset
;
183 attributes
= cell
.attributes
& CHAR_ATTRIBUTES
;
186 if (attributes
!= 0) {
187 attributesRun
->attributes
= attributes
;
188 attributesRun
->offset
= i
;
192 if (IS_WIDTH(cell
.attributes
))
196 // set the last attributes run's length
198 attributesRun
->length
= line
->length
- attributesRun
->offset
;
200 historyLine
->softBreak
= line
->softBreak
;
201 historyLine
->attributes
= line
->attributes
;
202 //debug_printf(" line: \"%.*s\", history size now: %ld\n", historyLine->byteLength, historyLine->Chars(), fSize);
207 HistoryBuffer::AddEmptyLines(int32 count
)
212 if (count
> fCapacity
)
215 if (count
+ fSize
> fCapacity
)
216 DropLines(count
+ fSize
- fCapacity
);
218 // All lines use the same buffer address, since they don't use any memory.
219 AttributesRun
* attributesRun
220 = (AttributesRun
*)(fBuffer
+ fBufferAllocationOffset
);
222 for (int32 i
= 0; i
< count
; i
++) {
223 HistoryLine
* line
= &fLines
[fNextLine
];
224 fNextLine
= (fNextLine
+ 1) % fCapacity
;
225 line
->attributesRuns
= attributesRun
;
226 line
->attributesRunCount
= 0;
227 line
->byteLength
= 0;
228 line
->softBreak
= false;
236 HistoryBuffer::DropLines(int32 count
)
246 fBufferAllocationOffset
= 0;
252 HistoryBuffer::_AllocateLine(int32 attributesRuns
, int32 byteLength
)
254 // we need at least one spare line slot
256 if (fSize
== fCapacity
)
259 int32 bytesNeeded
= attributesRuns
* sizeof(AttributesRun
) + byteLength
;
261 if (fBufferAllocationOffset
+ bytesNeeded
> fBufferSize
) {
262 // drop all lines after the allocation index
263 for (; toDrop
< fSize
; toDrop
++) {
264 HistoryLine
* line
= _LineAt(fSize
- toDrop
- 1);
265 int32 offset
= (uint8
*)line
->AttributesRuns() - fBuffer
;
266 if (offset
< fBufferAllocationOffset
)
270 fBufferAllocationOffset
= 0;
273 // drop all lines interfering
274 int32 nextOffset
= (fBufferAllocationOffset
+ bytesNeeded
+ 1) & ~1;
275 for (; toDrop
< fSize
; toDrop
++) {
276 HistoryLine
* line
= _LineAt(fSize
- toDrop
- 1);
277 int32 offset
= (uint8
*)line
->AttributesRuns() - fBuffer
;
278 if (offset
+ line
->BufferSize() <= fBufferAllocationOffset
279 || offset
>= nextOffset
) {
287 HistoryLine
* line
= &fLines
[fNextLine
];
288 fNextLine
= (fNextLine
+ 1) % fCapacity
;
290 line
->attributesRuns
= (AttributesRun
*)(fBuffer
+ fBufferAllocationOffset
);
291 line
->attributesRunCount
= attributesRuns
;
292 line
->byteLength
= byteLength
;
294 fBufferAllocationOffset
= (fBufferAllocationOffset
+ bytesNeeded
+ 1) & ~1;
295 // DropLines() may have changed fBufferAllocationOffset, so don't use