status_t BasicTerminalBuffer::_ResizeHistory(int32 width, int32 historyCapacity) { if (width == fWidth && historyCapacity == HistoryCapacity()) return B_OK; if (historyCapacity <= 0) { // new history capacity is 0 -- delete the old history object delete fHistory; fHistory = NULL; return B_OK; } HistoryBuffer* history = new(std::nothrow) HistoryBuffer; if (history == NULL) return B_NO_MEMORY; status_t error = history->Init(width, historyCapacity); if (error != B_OK) { delete history; return error; } // Transfer the lines from the old history to the new one. if (fHistory != NULL) { int32 historySize = min_c(HistorySize(), historyCapacity); TerminalLine* lineBuffer = ALLOC_LINE_ON_STACK(fWidth); for (int32 i = historySize - 1; i >= 0; i--) { TerminalLine* line = fHistory->GetTerminalLineAt(i, lineBuffer); if (line->length > width) _TruncateLine(line, width); history->AddLine(line); } } delete fHistory; fHistory = history; return B_OK; }
status_t BasicTerminalBuffer::_ResizeRewrap(int32 width, int32 height, int32 historyCapacity) { //debug_printf("BasicTerminalBuffer::_ResizeRewrap(): (%ld, %ld, history: %ld) -> " //"(%ld, %ld, history: %ld)\n", fWidth, fHeight, HistoryCapacity(), width, height, //historyCapacity); // The width stays the same. _ResizeSimple() does exactly what we need. if (width == fWidth) return _ResizeSimple(width, height, historyCapacity); // The width changes. We have to allocate a new line array, a new history // and re-wrap all lines. TerminalLine** screen = _AllocateLines(width, height); if (screen == NULL) return B_NO_MEMORY; HistoryBuffer* history = NULL; if (historyCapacity > 0) { history = new(std::nothrow) HistoryBuffer; if (history == NULL) { _FreeLines(screen, height); return B_NO_MEMORY; } status_t error = history->Init(width, historyCapacity); if (error != B_OK) { _FreeLines(screen, height); delete history; return error; } } int32 historySize = HistorySize(); int32 totalLines = historySize + fHeight; // re-wrap TerminalLine* lineBuffer = ALLOC_LINE_ON_STACK(fWidth); TermPos cursor; int32 destIndex = 0; int32 sourceIndex = 0; int32 sourceX = 0; int32 destTotalLines = 0; int32 destScreenOffset = 0; int32 maxDestTotalLines = INT_MAX; bool newDestLine = true; bool cursorSeen = false; TerminalLine* sourceLine = _HistoryLineAt(-historySize, lineBuffer); while (sourceIndex < totalLines) { TerminalLine* destLine = screen[destIndex]; if (newDestLine) { // Clear a new dest line before using it. If we're about to // overwrite an previously written line, we push it to the // history first, though. if (history != NULL && destTotalLines >= height) history->AddLine(screen[destIndex]); destLine->Clear(fAttributes, width); newDestLine = false; } int32 sourceLeft = sourceLine->length - sourceX; int32 destLeft = width - destLine->length; //debug_printf(" source: %ld, left: %ld, dest: %ld, left: %ld\n", //sourceIndex, sourceLeft, destIndex, destLeft); if (sourceIndex == historySize && sourceX == 0) { destScreenOffset = destTotalLines; if (destLeft == 0 && sourceLeft > 0) destScreenOffset++; maxDestTotalLines = destScreenOffset + height; //debug_printf(" destScreenOffset: %ld\n", destScreenOffset); } int32 toCopy = min_c(sourceLeft, destLeft); // If the last cell to copy is the first cell of a // full-width char, don't copy it yet. if (toCopy > 0 && IS_WIDTH( sourceLine->cells[sourceX + toCopy - 1].attributes)) { //debug_printf(" -> last char is full-width -- don't copy it\n"); toCopy--; } // translate the cursor position if (fCursor.y + historySize == sourceIndex && fCursor.x >= sourceX && (fCursor.x < sourceX + toCopy || (destLeft >= sourceLeft && sourceX + sourceLeft <= fCursor.x))) { cursor.x = destLine->length + fCursor.x - sourceX; cursor.y = destTotalLines; if (cursor.x >= width) { // The cursor was in free space after the official end // of line. cursor.x = width - 1; } //debug_printf(" cursor: (%ld, %ld)\n", cursor.x, cursor.y); cursorSeen = true; } if (toCopy > 0) { memcpy(destLine->cells + destLine->length, sourceLine->cells + sourceX, toCopy * sizeof(TerminalCell)); destLine->length += toCopy; } destLine->attributes = sourceLine->attributes; bool nextDestLine = false; if (toCopy == sourceLeft) { if (!sourceLine->softBreak) nextDestLine = true; sourceIndex++; sourceX = 0; sourceLine = _HistoryLineAt(sourceIndex - historySize, lineBuffer); } else { destLine->softBreak = true; nextDestLine = true; sourceX += toCopy; } if (nextDestLine) { destIndex = (destIndex + 1) % height; destTotalLines++; newDestLine = true; if (cursorSeen && destTotalLines >= maxDestTotalLines) break; } } // If the last source line had a soft break, the last dest line // won't have been counted yet. if (!newDestLine) { destIndex = (destIndex + 1) % height; destTotalLines++; } //debug_printf(" total lines: %ld -> %ld\n", totalLines, destTotalLines); if (destTotalLines - destScreenOffset > height) destScreenOffset = destTotalLines - height; cursor.y -= destScreenOffset; // When there are less lines (starting with the screen offset) than // there's room in the screen, clear the remaining screen lines. for (int32 i = destTotalLines; i < destScreenOffset + height; i++) { // Move the line we're going to clear to the history, if that's a // line we've written earlier. TerminalLine* line = screen[i % height]; if (history != NULL && i >= height) history->AddLine(line); line->Clear(fAttributes, width); } // Update the values _FreeLines(fScreen, fHeight); delete fHistory; fScreen = screen; fHistory = history; if (fWidth != width) { status_t error = _ResetTabStops(width); if (error != B_OK) return error; } //debug_printf(" cursor: (%ld, %ld) -> (%ld, %ld)\n", fCursor.x, fCursor.y, //cursor.x, cursor.y); fCursor.x = cursor.x; fCursor.y = cursor.y; fSoftWrappedCursor = false; //debug_printf(" screen offset: %ld -> %ld\n", fScreenOffset, destScreenOffset % height); fScreenOffset = destScreenOffset % height; //debug_printf(" height %ld -> %ld\n", fHeight, height); //debug_printf(" width %ld -> %ld\n", fWidth, width); fHeight = height; fWidth = width; fScrollTop = 0; fScrollBottom = fHeight - 1; fOriginMode = fSavedOriginMode = false; return B_OK; }