* 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:
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user