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 Attributes attributes; 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.Reset(); 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.Reset(); 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.state |= 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.Reset(); 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 Attributes attributes; 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) { 150 attributes.state = cell.attributes.state & CHAR_ATTRIBUTES; 151 attributes.foreground = cell.attributes.foreground; 152 attributes.background = cell.attributes.background; 153 if (attributes.state != 0) 154 attributesRuns++; 155 } 156 if (cell.attributes.IsWidth()) 157 i++; 158 } 159 160 //debug_printf(" attributesRuns: %ld, byteLength: %ld\n", attributesRuns, byteLength); 161 162 // allocate and translate the line 163 HistoryLine* historyLine = _AllocateLine(attributesRuns, byteLength); 164 165 attributes.Reset(); 166 AttributesRun* attributesRun = historyLine->AttributesRuns(); 167 168 char* chars = historyLine->Chars(); 169 for (int32 i = 0; i < line->length; i++) { 170 const TerminalCell& cell = line->cells[i]; 171 172 // copy char 173 int32 charLength = cell.character.ByteCount(); 174 memcpy(chars, cell.character.bytes, charLength); 175 chars += charLength; 176 177 // deal with attributes 178 if (cell != attributes) { 179 // terminate the previous attributes run 180 if (attributes.state != 0) { 181 attributesRun->length = i - attributesRun->offset; 182 attributesRun++; 183 } 184 185 attributes.state = cell.attributes.state & CHAR_ATTRIBUTES; 186 attributes.foreground = cell.attributes.foreground; 187 attributes.background = cell.attributes.background; 188 189 // init the new one 190 if (attributes.state != 0) { 191 attributesRun->attributes = attributes; 192 attributesRun->offset = i; 193 } 194 } 195 196 if (cell.attributes.IsWidth()) 197 i++; 198 } 199 200 // set the last attributes run's length 201 if (attributes.state != 0) 202 attributesRun->length = line->length - attributesRun->offset; 203 204 historyLine->softBreak = line->softBreak; 205 historyLine->attributes = line->attributes; 206 //debug_printf(" line: \"%.*s\", history size now: %ld\n", historyLine->byteLength, historyLine->Chars(), fSize); 207 } 208 209 210 void 211 HistoryBuffer::AddEmptyLines(int32 count) 212 { 213 if (count <= 0) 214 return; 215 216 if (count > fCapacity) 217 count = fCapacity; 218 219 if (count + fSize > fCapacity) 220 DropLines(count + fSize - fCapacity); 221 222 // All lines use the same buffer address, since they don't use any memory. 223 AttributesRun* attributesRun 224 = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 225 226 for (int32 i = 0; i < count; i++) { 227 HistoryLine* line = &fLines[fNextLine]; 228 fNextLine = (fNextLine + 1) % fCapacity; 229 line->attributesRuns = attributesRun; 230 line->attributesRunCount = 0; 231 line->byteLength = 0; 232 line->softBreak = false; 233 } 234 235 fSize += count; 236 } 237 238 239 void 240 HistoryBuffer::DropLines(int32 count) 241 { 242 if (count <= 0) 243 return; 244 245 if (count < fSize) { 246 fSize -= count; 247 } else { 248 fSize = 0; 249 fNextLine = 0; 250 fBufferAllocationOffset = 0; 251 } 252 } 253 254 255 HistoryLine* 256 HistoryBuffer::_AllocateLine(int32 attributesRuns, int32 byteLength) 257 { 258 // we need at least one spare line slot 259 int32 toDrop = 0; 260 if (fSize == fCapacity) 261 toDrop = 1; 262 263 int32 bytesNeeded = attributesRuns * sizeof(AttributesRun) + byteLength; 264 265 if (fBufferAllocationOffset + bytesNeeded > fBufferSize) { 266 // drop all lines after the allocation index 267 for (; toDrop < fSize; toDrop++) { 268 HistoryLine* line = _LineAt(fSize - toDrop - 1); 269 int32 offset = (uint8*)line->AttributesRuns() - fBuffer; 270 if (offset < fBufferAllocationOffset) 271 break; 272 } 273 274 fBufferAllocationOffset = 0; 275 } 276 277 // drop all lines interfering 278 int32 nextOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 279 for (; toDrop < fSize; toDrop++) { 280 HistoryLine* line = _LineAt(fSize - toDrop - 1); 281 int32 offset = (uint8*)line->AttributesRuns() - fBuffer; 282 if (offset + line->BufferSize() <= fBufferAllocationOffset 283 || offset >= nextOffset) { 284 break; 285 } 286 } 287 288 DropLines(toDrop); 289 290 // init the line 291 HistoryLine* line = &fLines[fNextLine]; 292 fNextLine = (fNextLine + 1) % fCapacity; 293 fSize++; 294 line->attributesRuns = (AttributesRun*)(fBuffer + fBufferAllocationOffset); 295 line->attributesRunCount = attributesRuns; 296 line->byteLength = byteLength; 297 298 fBufferAllocationOffset = (fBufferAllocationOffset + bytesNeeded + 1) & ~1; 299 // DropLines() may have changed fBufferAllocationOffset, so don't use 300 // nextOffset. 301 302 return line; 303 } 304