* Added a SetHistoryCapacity() method.

* Added a ResizeTo() version that allows to set the history capacity at
  the same time.
* Added a SynchronizeWith() method that allows synchronizing with (a
  part of) another buffer.
* Fixed TODO in _Scroll().
* Improved the dirty region update code in _Scroll(). It would in
  certain (not exactly uncommon) situations needlessly invalidate the
  whole screen.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25912 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-06-10 21:43:42 +00:00
parent 540e66d6a6
commit 90ab7ea343
2 changed files with 168 additions and 58 deletions
+151 -57
View File
@@ -57,11 +57,8 @@ BasicTerminalBuffer::_HistoryLineAt(int32 index) const
inline void inline void
BasicTerminalBuffer::_Invalidate(int32 top, int32 bottom) BasicTerminalBuffer::_Invalidate(int32 top, int32 bottom)
{ {
//debug_printf("BasicTerminalBuffer::_Invalidate(%ld, %ld)\n", top, bottom); //debug_printf("%p->BasicTerminalBuffer::_Invalidate(%ld, %ld)\n", this, top, bottom);
if (top < fDirtyInfo.dirtyTop) fDirtyInfo.ExtendDirtyRegion(top, bottom);
fDirtyInfo.dirtyTop = top;
if (bottom > fDirtyInfo.dirtyBottom)
fDirtyInfo.dirtyBottom = bottom;
if (!fDirtyInfo.messageSent) { if (!fDirtyInfo.messageSent) {
NotifyListener(); NotifyListener();
@@ -130,19 +127,27 @@ BasicTerminalBuffer::Init(int32 width, int32 height, int32 historySize)
status_t status_t
BasicTerminalBuffer::ResizeTo(int32 width, int32 height) BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
{
return ResizeTo(width, height, fHistoryCapacity);
}
status_t
BasicTerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity)
{ {
if (height < MIN_ROWS || height > MAX_ROWS || width < MIN_COLS if (height < MIN_ROWS || height > MAX_ROWS || width < MIN_COLS
|| width > MAX_COLS) { || width > MAX_COLS || height > historyCapacity) {
return B_BAD_VALUE; return B_BAD_VALUE;
} }
//debug_printf("BasicTerminalBuffer::ResizeTo(): (%ld, %ld) -> (%ld, %ld)\n", //debug_printf("BasicTerminalBuffer::ResizeTo(): (%ld, %ld, history: %ld) -> "
//fWidth, fHeight, width, height); //"(%ld, %ld, history: %ld)\n", fWidth, fHeight, fHistoryCapacity, width, height,
//historyCapacity);
if (width != fWidth) { if (width != fWidth) {
// The width changes. We have to allocate a new line array and re-wrap // The width changes. We have to allocate a new line array and re-wrap
// all lines. // all lines.
Line** history = _AllocateLines(width, fHistoryCapacity); Line** history = _AllocateLines(width, historyCapacity);
if (history == NULL) if (history == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
@@ -181,34 +186,35 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
} }
int32 toCopy = min_c(sourceLeft, destLeft); int32 toCopy = min_c(sourceLeft, destLeft);
if (toCopy > 0) { // If the last cell to copy is the first cell of a
// If the last cell to copy is the first cell of a // full-width char, don't copy it yet.
// full-width char, don't copy it yet. if (toCopy > 0 && IS_WIDTH(
if (sourceX + toCopy > 0 && IS_WIDTH( sourceLine->cells[sourceX + toCopy - 1].attributes)) {
sourceLine->cells[sourceX + toCopy - 1].attributes)) {
//debug_printf(" -> last char is full-width -- don't copy it\n"); //debug_printf(" -> last char is full-width -- don't copy it\n");
toCopy--; toCopy--;
}
// translate the cursor position
if (fCursor.y + fHistorySize == 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;
} }
// translate the cursor position
if (fCursor.y + fHistorySize == sourceIndex
&& fCursor.x >= sourceX
&& (fCursor.x < sourceX + toCopy
|| 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); //debug_printf(" cursor: (%ld, %ld)\n", cursor.x, cursor.y);
// don't allow the cursor to get out of screen // don't allow the cursor to get out of screen
maxDestTotalLines = cursor.y + fHeight; maxDestTotalLines = cursor.y + height - 1;
} }
if (toCopy > 0) {
memcpy(destLine->cells + destLine->length, memcpy(destLine->cells + destLine->length,
sourceLine->cells + sourceX, toCopy * sizeof(Cell)); sourceLine->cells + sourceX, toCopy * sizeof(Cell));
destLine->length += toCopy; destLine->length += toCopy;
@@ -227,10 +233,10 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
} }
if (nextDestLine) { if (nextDestLine) {
destIndex = (destIndex + 1) % fHistoryCapacity; destIndex = (destIndex + 1) % historyCapacity;
destTotalLines++; destTotalLines++;
newDestLine = true; newDestLine = true;
if (destTotalLines == maxDestTotalLines) if (destTotalLines >= maxDestTotalLines)
break; break;
} }
} }
@@ -238,7 +244,7 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
// If the last source line had a soft break, the last dest line // If the last source line had a soft break, the last dest line
// won't have been counted yet. // won't have been counted yet.
if (!newDestLine) { if (!newDestLine) {
destIndex = (destIndex + 1) % fHistoryCapacity; destIndex = (destIndex + 1) % historyCapacity;
destTotalLines++; destTotalLines++;
} }
@@ -248,8 +254,8 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
cursor.y -= destScreenOffset; cursor.y -= destScreenOffset;
// Re-wrapping might have produced more lines than we have room for. // Re-wrapping might have produced more lines than we have room for.
if (destTotalLines > fHistoryCapacity) if (destTotalLines > historyCapacity)
destTotalLines = fHistoryCapacity; destTotalLines = historyCapacity;
// Update the values // Update the values
//debug_printf(" cursor: (%ld, %ld) -> (%ld, %ld)\n", fCursor.x, fCursor.y, //debug_printf(" cursor: (%ld, %ld) -> (%ld, %ld)\n", fCursor.x, fCursor.y,
@@ -258,7 +264,7 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
fCursor.y = cursor.y; fCursor.y = cursor.y;
//debug_printf(" screen offset: %ld -> %ld\n", fScreenOffset, //debug_printf(" screen offset: %ld -> %ld\n", fScreenOffset,
//destScreenOffset % fHistoryCapacity); //destScreenOffset % fHistoryCapacity);
fScreenOffset = destScreenOffset % fHistoryCapacity; fScreenOffset = destScreenOffset % historyCapacity;
//debug_printf(" history size: %ld -> %ld\n", fHistorySize, destTotalLines - fHeight); //debug_printf(" history size: %ld -> %ld\n", fHistorySize, destTotalLines - fHeight);
fHistorySize = destTotalLines - tempHeight; fHistorySize = destTotalLines - tempHeight;
//debug_printf(" height %ld -> %ld\n", fHeight, tempHeight); //debug_printf(" height %ld -> %ld\n", fHeight, tempHeight);
@@ -267,8 +273,12 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
_FreeLines(fHistory, fHistoryCapacity); _FreeLines(fHistory, fHistoryCapacity);
fHistory = history; fHistory = history;
fHistoryCapacity = historyCapacity;
} }
if (historyCapacity > fHistoryCapacity)
SetHistoryCapacity(historyCapacity);
if (height == fHeight) if (height == fHeight)
return B_OK; return B_OK;
@@ -304,6 +314,55 @@ BasicTerminalBuffer::ResizeTo(int32 width, int32 height)
fScrollTop = 0; fScrollTop = 0;
fScrollBottom = fHeight - 1; fScrollBottom = fHeight - 1;
if (historyCapacity < fHistoryCapacity)
SetHistoryCapacity(historyCapacity);
return B_OK;
}
status_t
BasicTerminalBuffer::SetHistoryCapacity(int32 historyCapacity)
{
if (historyCapacity < fHeight)
return B_BAD_VALUE;
if (fHistoryCapacity == historyCapacity)
return B_OK;
// The history capacity changes.
Line** history = _AllocateLines(fWidth, historyCapacity);
if (history == NULL)
return B_NO_MEMORY;
int32 totalLines = fHistorySize + fHeight;
int32 historyOffset = fHistoryCapacity - fHistorySize;
// base for _LineAt() invocations to iterate through the history
if (totalLines > historyCapacity) {
// Our new history capacity is smaller than currently stored line,
// so we have to drop lines.
historyOffset += totalLines - historyCapacity;
totalLines = historyCapacity;
}
for (int32 i = 0; i < totalLines; i++) {
Line* sourceLine = _LineAt(historyOffset + i);
Line* destLine = history[i];
destLine->length = sourceLine->length;
destLine->softBreak = sourceLine->softBreak;
if (destLine->length > 0) {
memcpy(destLine->cells, sourceLine->cells,
destLine->length * sizeof(Cell));
}
}
_FreeLines(fHistory, fHistoryCapacity);
fHistory = history;
fHistoryCapacity = historyCapacity;
fHistorySize = totalLines - fHeight;
fScreenOffset = fHistorySize;
return B_OK; return B_OK;
} }
@@ -323,6 +382,45 @@ BasicTerminalBuffer::Clear()
} }
void
BasicTerminalBuffer::SynchronizeWith(const BasicTerminalBuffer* other,
int32 offset, int32 dirtyTop, int32 dirtyBottom)
{
//debug_printf("BasicTerminalBuffer::SynchronizeWith(%p, %ld, %ld - %ld)\n",
//other, offset, dirtyTop, dirtyBottom);
// intersect the visible region with the dirty region
int32 first = 0;
int32 last = fHeight - 1;
dirtyTop -= offset;
dirtyBottom -= offset;
if (first > dirtyBottom || dirtyTop > last)
return;
if (first < dirtyTop)
first = dirtyTop;
if (last > dirtyBottom)
last = dirtyBottom;
// update the dirty lines
//debug_printf(" updating: %ld - %ld\n", first, last);
for (int32 i = first; i <= last; i++) {
Line* sourceLine = other->_HistoryLineAt(i + offset);
Line* destLine = _LineAt(i);
if (sourceLine != NULL) {
destLine->length = sourceLine->length;
destLine->softBreak = sourceLine->softBreak;
if (destLine->length > 0) {
memcpy(destLine->cells, sourceLine->cells,
destLine->length * sizeof(Cell));
}
} else
destLine->Clear();
}
}
bool bool
BasicTerminalBuffer::IsFullWidthChar(int32 row, int32 column) const BasicTerminalBuffer::IsFullWidthChar(int32 row, int32 column) const
{ {
@@ -359,7 +457,7 @@ BasicTerminalBuffer::GetString(int32 row, int32 firstColumn, int32 lastColumn,
{ {
Line* line = _HistoryLineAt(row); Line* line = _HistoryLineAt(row);
if (line == NULL) if (line == NULL)
return NO_CHAR; return 0;
if (lastColumn >= line->length) if (lastColumn >= line->length)
lastColumn = line->length - 1; lastColumn = line->length - 1;
@@ -861,22 +959,21 @@ BasicTerminalBuffer::_Scroll(int32 top, int32 bottom, int32 numLines)
return; return;
if (numLines > 0) { if (numLines > 0) {
if (numLines > fHistoryCapacity - fHeight) {
numLines = fHistoryCapacity - fHeight;
// TODO: This is not quite correct -- we should replace the complete
// history and screen with empty lines.
}
// scroll text up // scroll text up
if (top == 0) { if (top == 0) {
// The lines scrolled out of the screen range are transferred to // The lines scrolled out of the screen range are transferred to
// the history. // the history.
if (numLines > fHistoryCapacity)
numLines = fHistoryCapacity;
// make room for numLines new lines // make room for numLines new lines
if (fHistorySize + fHeight + numLines > fHistoryCapacity) { if (fHistorySize + fHeight + numLines > fHistoryCapacity) {
int32 toDrop = fHistorySize + fHeight + numLines int32 toDrop = fHistorySize + fHeight + numLines
- fHistoryCapacity; - fHistoryCapacity;
fHistorySize -= toDrop; fHistorySize -= toDrop;
// Note that fHistorySize can temporarily become negative,
// but all will be well again, when we offset the screen.
} }
// clear numLines after the current screen // clear numLines after the current screen
@@ -897,21 +994,18 @@ BasicTerminalBuffer::_Scroll(int32 top, int32 bottom, int32 numLines)
fHistorySize += numLines; fHistorySize += numLines;
// scroll/extend dirty range // scroll/extend dirty range
if (fDirtyInfo.dirtyTop != INT_MAX) { if (fDirtyInfo.dirtyTop != INT_MAX) {
if (fDirtyInfo.dirtyTop <= bottom // If the top or bottom of the dirty region are above the
&& top <= fDirtyInfo.dirtyBottom) { // bottom of the scroll region, we have to scroll them up.
// intersects with the scroll region -- extend if (fDirtyInfo.dirtyTop <= bottom) {
if (top < fDirtyInfo.dirtyTop)
fDirtyInfo.dirtyTop = top - numLines;
else
fDirtyInfo.dirtyTop -= numLines;
if (bottom > fDirtyInfo.dirtyBottom)
fDirtyInfo.dirtyBottom = bottom;
} else if (fDirtyInfo.dirtyBottom < top) {
// does not intersect with the scroll region -- just offset
fDirtyInfo.dirtyTop -= numLines; fDirtyInfo.dirtyTop -= numLines;
fDirtyInfo.dirtyBottom -= numLines; if (fDirtyInfo.dirtyBottom <= bottom)
fDirtyInfo.dirtyBottom -= numLines;
} }
// numLines above the bottom become dirty
_Invalidate(bottom - numLines + 1, bottom);
} }
fDirtyInfo.linesScrolled += numLines; fDirtyInfo.linesScrolled += numLines;
+17 -1
View File
@@ -26,6 +26,14 @@ struct TerminalBufferDirtyInfo {
return dirtyTop <= dirtyBottom; return dirtyTop <= dirtyBottom;
} }
void ExtendDirtyRegion(int32 top, int32 bottom)
{
if (top < dirtyTop)
dirtyTop = top;
if (bottom > dirtyBottom)
dirtyBottom = bottom;
}
void Reset() void Reset()
{ {
linesScrolled = 0; linesScrolled = 0;
@@ -42,7 +50,7 @@ public:
virtual ~BasicTerminalBuffer(); virtual ~BasicTerminalBuffer();
status_t Init(int32 width, int32 height, status_t Init(int32 width, int32 height,
int32 historySize); int32 historyCapacity);
int32 Width() const { return fWidth; } int32 Width() const { return fWidth; }
int32 Height() const { return fHeight; } int32 Height() const { return fHeight; }
@@ -51,8 +59,16 @@ public:
TerminalBufferDirtyInfo& DirtyInfo() { return fDirtyInfo; } TerminalBufferDirtyInfo& DirtyInfo() { return fDirtyInfo; }
status_t ResizeTo(int32 width, int32 height); status_t ResizeTo(int32 width, int32 height);
status_t ResizeTo(int32 width, int32 height,
int32 historyCapacity);
status_t SetHistoryCapacity(int32 historyCapacity);
void Clear(); void Clear();
void SynchronizeWith(
const BasicTerminalBuffer* other,
int32 offset, int32 dirtyTop,
int32 dirtyBottom);
bool IsFullWidthChar(int32 row, int32 column) const; bool IsFullWidthChar(int32 row, int32 column) const;
int GetChar(int32 row, int32 column, int GetChar(int32 row, int32 column,
UTF8Char& character, UTF8Char& character,