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. 5 * 6 * Authors: 7 * Ingo Weinhold, ingo_weinhold@gmx.de 8 * Siarzhuk Zharski, zharik@gmx.li 9 */ 10 11 #include "HistoryBuffer.h" 12 13 #include <new> 14 15 #include <OS.h> 16 17 #include "TermConst.h" 18 19 20 HistoryBuffer::HistoryBuffer() 21 : 22 fLines(NULL), 23 fWidth(0), 24 fCapacity(0), 25 fNextLine(0), 26 fSize(0), 27 fBuffer(NULL), 28 fBufferSize(0), 29 fBufferAllocationOffset(0) 30 { 31 } 32 33 34 HistoryBuffer::~HistoryBuffer() 35 { 36 delete[] fLines; 37 delete[] fBuffer; 38 } 39 40 41 status_t 42 HistoryBuffer::Init(int32 width, int32 capacity) 43 { 44 if (width <= 0 || capacity <= 0) 45 return B_BAD_VALUE; 46 47 int32 bufferSize = (width + 4) * capacity; 48 49 if (capacity > 0) { 50 fLines = new(std::nothrow) HistoryLine[capacity]; 51 fBuffer = new(std::nothrow) uint8[bufferSize]; 52 53 if (fLines == NULL || fBuffer == NULL) 54 return B_NO_MEMORY; 55 } 56 57 fWidth = width; 58 fCapacity = capacity; 59 fNextLine = 0; 60 fSize = 0; 61 fBufferSize = bufferSize; 62 fBufferAllocationOffset = 0; 63 64 return B_OK; 65 } 66 67 68 void 69 HistoryBuffer::Clear() 70 { 71 fNextLine = 0; 72 fSize = 0; 73 fBufferAllocationOffset = 0; 74 } 75 76 77 TerminalLine* 78 HistoryBuffer::GetTerminalLineAt(int32 index, TerminalLine* buffer) const 79 { 80 HistoryLine* line = LineAt(index); 81 if (line == NULL) 82 return NULL; 83 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; 92 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; 109 } 110 } 111 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; 117 118 // set attributes 119 cell.attributes = attributes; 120 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; 127 } 128 } 129 130 buffer->length = charCount; 131 buffer->softBreak = line->softBreak; 132 buffer->attributes = line->attributes; 133 134 return buffer; 135 } 136 137 138 void 139 HistoryBuffer::AddLine(const TerminalLine* line) 140 { 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++; 153 } 154 if (IS_WIDTH(cell.attributes)) 155 i++; 156 } 157 158 //debug_printf(" attributesRuns: %ld, byteLength: %ld\n", attributesRuns, byteLength); 159 160 // allocate and translate the line 161 HistoryLine* historyLine = _AllocateLine(attributesRuns, byteLength); 162 163 attributes = 0; 164 AttributesRun* attributesRun = historyLine->AttributesRuns(); 165 166 char* chars = historyLine->Chars(); 167 for (int32 i = 0; i < line->length; i++) { 168 const TerminalCell& cell = line->cells[i]; 169 170 // copy char 171 int32 charLength = cell.character.ByteCount(); 172 memcpy(chars, cell.character.bytes, charLength); 173 chars += charLength; 174 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++; 181 } 182 183 attributes = cell.attributes & CHAR_ATTRIBUTES; 184 185 // init the new one 186 if (attributes != 0) { 187 attributesRun->attributes = attributes; 188 attributesRun->offset = i; 189 } 190 } 191 192 if (IS_WIDTH(cell.attributes)) 193 i++; 194 } 195 196 // set the last attributes run's length 197 if (attributes != 0) 198 attributesRun->length = line->length - attributesRun->offset; 199 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); 203 } 204 205 206 void 207 HistoryBuffer::AddEmptyLines(int32 count) 208 { 209 if (count <= 0) 210 return; 211 212 if (count > fCapacity) 213 count = fCapacity; 214 215 if (count + fSize > fCapacity) 216 DropLines(count + fSize - fCapacity); 217 218 // All lines use the same buffer address, since they don't use any memory. 219 AttributesRun* attributesRun 220 = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 221 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; 229 } 230 231 fSize += count; 232 } 233 234 235 void 236 HistoryBuffer::DropLines(int32 count) 237 { 238 if (count <= 0) 239 return; 240 241 if (count < fSize) { 242 fSize -= count; 243 } else { 244 fSize = 0; 245 fNextLine = 0; 246 fBufferAllocationOffset = 0; 247 } 248 } 249 250 251 HistoryLine* 252 HistoryBuffer::_AllocateLine(int32 attributesRuns, int32 byteLength) 253 { 254 // we need at least one spare line slot 255 int32 toDrop = 0; 256 if (fSize == fCapacity) 257 toDrop = 1; 258 259 int32 bytesNeeded = attributesRuns * sizeof(AttributesRun) + byteLength; 260 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; 268 } 269 270 fBufferAllocationOffset = 0; 271 } 272 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; 281 } 282 } 283 284 DropLines(toDrop); 285 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; 293 294 fBufferAllocationOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 295 // DropLines() may have changed fBufferAllocationOffset, so don't use 296 // nextOffset. 297 298 return line; 299 } 300