btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / terminal / HistoryBuffer.cpp
blobada86f5deba1de103b738ff85edf9ea654acde4d
1 /*
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.
6 * Authors:
7 * Ingo Weinhold, ingo_weinhold@gmx.de
8 * Siarzhuk Zharski, zharik@gmx.li
9 */
11 #include "HistoryBuffer.h"
13 #include <new>
15 #include <OS.h>
17 #include "TermConst.h"
20 HistoryBuffer::HistoryBuffer()
22 fLines(NULL),
23 fWidth(0),
24 fCapacity(0),
25 fNextLine(0),
26 fSize(0),
27 fBuffer(NULL),
28 fBufferSize(0),
29 fBufferAllocationOffset(0)
34 HistoryBuffer::~HistoryBuffer()
36 delete[] fLines;
37 delete[] fBuffer;
41 status_t
42 HistoryBuffer::Init(int32 width, int32 capacity)
44 if (width <= 0 || capacity <= 0)
45 return B_BAD_VALUE;
47 int32 bufferSize = (width + 4) * capacity;
49 if (capacity > 0) {
50 fLines = new(std::nothrow) HistoryLine[capacity];
51 fBuffer = new(std::nothrow) uint8[bufferSize];
53 if (fLines == NULL || fBuffer == NULL)
54 return B_NO_MEMORY;
57 fWidth = width;
58 fCapacity = capacity;
59 fNextLine = 0;
60 fSize = 0;
61 fBufferSize = bufferSize;
62 fBufferAllocationOffset = 0;
64 return B_OK;
68 void
69 HistoryBuffer::Clear()
71 fNextLine = 0;
72 fSize = 0;
73 fBufferAllocationOffset = 0;
77 TerminalLine*
78 HistoryBuffer::GetTerminalLineAt(int32 index, TerminalLine* buffer) const
80 HistoryLine* line = LineAt(index);
81 if (line == NULL)
82 return NULL;
84 int32 charCount = 0;
85 const char* chars = line->Chars();
86 buffer->length = 0;
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;) {
94 // get attributes
95 if (charCount == nextAttributesAt) {
96 if (charCount < attributesRun->offset) {
97 // the "hole" in attributes run
98 attributes = 0;
99 nextAttributesAt = attributesRun->offset;
100 } else if (attributesRunCount > 0) {
101 attributes = attributesRun->attributes;
102 nextAttributesAt = attributesRun->offset
103 + attributesRun->length;
104 attributesRun++;
105 attributesRunCount--;
106 } else {
107 attributes = 0;
108 nextAttributesAt = INT_MAX;
112 // copy character
113 TerminalCell& cell = buffer->cells[charCount++];
114 int32 charLength = UTF8Char::ByteCount(chars[i]);
115 cell.character.SetTo(chars + i, charLength);
116 i += charLength;
118 // set attributes
119 cell.attributes = attributes;
121 // full width char?
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;
134 return buffer;
138 void
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;
151 if (attributes != 0)
152 attributesRuns++;
154 if (IS_WIDTH(cell.attributes))
155 i++;
158 //debug_printf(" attributesRuns: %ld, byteLength: %ld\n", attributesRuns, byteLength);
160 // allocate and translate the line
161 HistoryLine* historyLine = _AllocateLine(attributesRuns, byteLength);
163 attributes = 0;
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];
170 // copy char
171 int32 charLength = cell.character.ByteCount();
172 memcpy(chars, cell.character.bytes, charLength);
173 chars += 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;
180 attributesRun++;
183 attributes = cell.attributes & CHAR_ATTRIBUTES;
185 // init the new one
186 if (attributes != 0) {
187 attributesRun->attributes = attributes;
188 attributesRun->offset = i;
192 if (IS_WIDTH(cell.attributes))
193 i++;
196 // set the last attributes run's length
197 if (attributes != 0)
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);
206 void
207 HistoryBuffer::AddEmptyLines(int32 count)
209 if (count <= 0)
210 return;
212 if (count > fCapacity)
213 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;
231 fSize += count;
235 void
236 HistoryBuffer::DropLines(int32 count)
238 if (count <= 0)
239 return;
241 if (count < fSize) {
242 fSize -= count;
243 } else {
244 fSize = 0;
245 fNextLine = 0;
246 fBufferAllocationOffset = 0;
251 HistoryLine*
252 HistoryBuffer::_AllocateLine(int32 attributesRuns, int32 byteLength)
254 // we need at least one spare line slot
255 int32 toDrop = 0;
256 if (fSize == fCapacity)
257 toDrop = 1;
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)
267 break;
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) {
280 break;
284 DropLines(toDrop);
286 // init the line
287 HistoryLine* line = &fLines[fNextLine];
288 fNextLine = (fNextLine + 1) % fCapacity;
289 fSize++;
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
296 // nextOffset.
298 return line;