1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "HistoryBuffer.h" 7 8 #include <new> 9 10 #include <OS.h> 11 12 #include "TermConst.h" 13 14 15 HistoryBuffer::HistoryBuffer() 16 : 17 fLines(NULL), 18 fWidth(0), 19 fCapacity(0), 20 fNextLine(0), 21 fSize(0), 22 fBuffer(NULL), 23 fBufferSize(0), 24 fBufferAllocationOffset(0) 25 { 26 } 27 28 29 HistoryBuffer::~HistoryBuffer() 30 { 31 delete[] fLines; 32 delete[] fBuffer; 33 } 34 35 36 status_t 37 HistoryBuffer::Init(int32 width, int32 capacity) 38 { 39 if (width <= 0 || capacity <= 0) 40 return B_BAD_VALUE; 41 42 int32 bufferSize = (width + 4) * capacity; 43 44 if (capacity > 0) { 45 fLines = new(std::nothrow) HistoryLine[capacity]; 46 fBuffer = new(std::nothrow) uint8[bufferSize]; 47 48 if (fLines == NULL || fBuffer == NULL) 49 return B_NO_MEMORY; 50 } 51 52 fWidth = width; 53 fCapacity = capacity; 54 fNextLine = 0; 55 fSize = 0; 56 fBufferSize = bufferSize; 57 fBufferAllocationOffset = 0; 58 59 return B_OK; 60 } 61 62 63 void 64 HistoryBuffer::Clear() 65 { 66 fNextLine = 0; 67 fSize = 0; 68 fBufferAllocationOffset = 0; 69 } 70 71 72 TerminalLine* 73 HistoryBuffer::GetTerminalLineAt(int32 index, TerminalLine* buffer) const 74 { 75 HistoryLine* line = LineAt(index); 76 if (line == NULL) 77 return NULL; 78 79 int32 charCount = 0; 80 const char* chars = line->Chars(); 81 buffer->length = 0; 82 uint32 attributes = 0; 83 AttributesRun* attributesRun = line->AttributesRuns(); 84 int32 attributesRunCount = line->attributesRunCount; 85 int32 nextAttributesAt = attributesRunCount > 0 86 ? attributesRun->offset : INT_MAX; 87 88 for (int32 i = 0; i < line->byteLength;) { 89 // get attributes 90 if (charCount == nextAttributesAt) { 91 if (attributesRunCount > 0) { 92 attributes = attributesRun->attributes; 93 nextAttributesAt = attributesRun->offset 94 + attributesRun->length; 95 attributesRun++; 96 attributesRunCount--; 97 } else { 98 attributes = 0; 99 nextAttributesAt = INT_MAX; 100 } 101 } 102 103 // copy character 104 TerminalCell& cell = buffer->cells[charCount++]; 105 int32 charLength = UTF8Char::ByteCount(chars[i]); 106 cell.character.SetTo(chars + i, charLength); 107 i += charLength; 108 109 // set attributes 110 cell.attributes = attributes; 111 112 // full width char? 113 if (cell.character.IsFullWidth()) { 114 cell.attributes |= A_WIDTH; 115 charCount++; 116 } 117 } 118 119 buffer->length = charCount; 120 buffer->softBreak = line->softBreak; 121 buffer->attributes = line->attributes; 122 123 return buffer; 124 } 125 126 127 void 128 HistoryBuffer::AddLine(const TerminalLine* line) 129 { 130 //debug_printf("HistoryBuffer::AddLine(%p): length: %d\n", line, line->length); 131 // determine the amount of memory we need for the line 132 uint32 attributes = 0; 133 int32 attributesRuns = 0; 134 int32 byteLength = 0; 135 for (int32 i = 0; i < line->length; i++) { 136 const TerminalCell& cell = line->cells[i]; 137 byteLength += cell.character.ByteCount(); 138 if ((cell.attributes & CHAR_ATTRIBUTES) != attributes) { 139 attributes = cell.attributes & CHAR_ATTRIBUTES; 140 if (attributes != 0) 141 attributesRuns++; 142 } 143 if (IS_WIDTH(cell.attributes)) 144 i++; 145 } 146 147 //debug_printf(" attributesRuns: %ld, byteLength: %ld\n", attributesRuns, byteLength); 148 149 // allocate and translate the line 150 HistoryLine* historyLine = _AllocateLine(attributesRuns, byteLength); 151 152 attributes = 0; 153 AttributesRun* attributesRun = historyLine->AttributesRuns(); 154 155 char* chars = historyLine->Chars(); 156 for (int32 i = 0; i < line->length; i++) { 157 const TerminalCell& cell = line->cells[i]; 158 159 // copy char 160 int32 charLength = cell.character.ByteCount(); 161 memcpy(chars, cell.character.bytes, charLength); 162 chars += charLength; 163 164 // deal with attributes 165 if ((cell.attributes & CHAR_ATTRIBUTES) != attributes) { 166 // terminate the previous attributes run 167 if (attributes != 0) { 168 attributesRun->length = i - attributesRun->offset; 169 attributesRun++; 170 } 171 172 attributes = cell.attributes & CHAR_ATTRIBUTES; 173 174 // init the new one 175 if (attributes != 0) { 176 attributesRun->attributes = attributes; 177 attributesRun->offset = i; 178 } 179 } 180 181 if (IS_WIDTH(cell.attributes)) 182 i++; 183 } 184 185 // set the last attributes run's length 186 if (attributes != 0) 187 attributesRun->length = line->length - attributesRun->offset; 188 189 historyLine->softBreak = line->softBreak; 190 historyLine->attributes = line->attributes; 191 //debug_printf(" line: \"%.*s\", history size now: %ld\n", historyLine->byteLength, historyLine->Chars(), fSize); 192 } 193 194 195 void 196 HistoryBuffer::AddEmptyLines(int32 count) 197 { 198 if (count <= 0) 199 return; 200 201 if (count > fCapacity) 202 count = fCapacity; 203 204 if (count + fSize > fCapacity) 205 DropLines(count + fSize - fCapacity); 206 207 // All lines use the same buffer address, since they don't use any memory. 208 AttributesRun* attributesRun 209 = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 210 211 for (int32 i = 0; i < count; i++) { 212 HistoryLine* line = &fLines[fNextLine]; 213 fNextLine = (fNextLine + 1) % fCapacity; 214 line->attributesRuns = attributesRun; 215 line->attributesRunCount = 0; 216 line->byteLength = 0; 217 line->softBreak = false; 218 } 219 220 fSize += count; 221 } 222 223 224 void 225 HistoryBuffer::DropLines(int32 count) 226 { 227 if (count <= 0) 228 return; 229 230 if (count < fSize) { 231 fSize -= count; 232 } else { 233 fSize = 0; 234 fNextLine = 0; 235 fBufferAllocationOffset = 0; 236 } 237 } 238 239 240 HistoryLine* 241 HistoryBuffer::_AllocateLine(int32 attributesRuns, int32 byteLength) 242 { 243 // we need at least one spare line slot 244 int32 toDrop = 0; 245 if (fSize == fCapacity) 246 toDrop = 1; 247 248 int32 bytesNeeded = attributesRuns * sizeof(AttributesRun) + byteLength; 249 250 if (fBufferAllocationOffset + bytesNeeded > fBufferSize) { 251 // drop all lines after the allocation index 252 for (; toDrop < fSize; toDrop++) { 253 HistoryLine* line = _LineAt(fSize - toDrop - 1); 254 int32 offset = (uint8*)line->AttributesRuns() - fBuffer; 255 if (offset < fBufferAllocationOffset) 256 break; 257 } 258 259 fBufferAllocationOffset = 0; 260 } 261 262 // drop all lines interfering 263 int32 nextOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 264 for (; toDrop < fSize; toDrop++) { 265 HistoryLine* line = _LineAt(fSize - toDrop - 1); 266 int32 offset = (uint8*)line->AttributesRuns() - fBuffer; 267 if (offset + line->BufferSize() <= fBufferAllocationOffset 268 || offset >= nextOffset) { 269 break; 270 } 271 } 272 273 DropLines(toDrop); 274 275 // init the line 276 HistoryLine* line = &fLines[fNextLine]; 277 fNextLine = (fNextLine + 1) % fCapacity; 278 fSize++; 279 line->attributesRuns = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 280 line->attributesRunCount = attributesRuns; 281 line->byteLength = byteLength; 282 283 fBufferAllocationOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 284 // DropLines() may have changed fBufferAllocationOffset, so don't use 285 // nextOffset. 286 287 return line; 288 } 289