From ada0d91b821016fab99a4e19cdd5b624f37fccbc Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Tue, 18 Dec 2007 03:25:22 +0000 Subject: [PATCH] * Avoid allocating a new buffer to hold character data on every screen update. Instead a single buffer is created and maintained by ScreenWindow. * Re-write Screen::getCookedImage() and rename to getImage() to make it possible to retrieve an image of a section of the screen which is not the same size as the terminal screen. * Make various non-mutating Screen methods const svn path=/trunk/KDE/kdebase/apps/konsole/; revision=749864 --- src/Screen.cpp | 132 ++++++++++++++++++++++++---------------- src/Screen.h | 24 ++++++-- src/ScreenWindow.cpp | 38 ++++++++++-- src/ScreenWindow.h | 12 ++-- src/TerminalDisplay.cpp | 3 - 5 files changed, 136 insertions(+), 73 deletions(-) diff --git a/src/Screen.cpp b/src/Screen.cpp index c398875e4..ac77ff04b 100644 --- a/src/Screen.cpp +++ b/src/Screen.cpp @@ -333,7 +333,7 @@ void Screen::restoreMode(int m) currParm.mode[m] = saveParm.mode[m]; } -bool Screen::getMode(int m) +bool Screen::getMode(int m) const { return currParm.mode[m]; } @@ -460,9 +460,13 @@ void Screen::setDefaultMargins() into RE_BOLD and RE_INTENSIVE. */ -void Screen::reverseRendition(Character* p) -{ CharacterColor f = p->foregroundColor; CharacterColor b = p->backgroundColor; - p->foregroundColor = b; p->backgroundColor = f; //p->r &= ~RE_TRANSPARENT; +void Screen::reverseRendition(Character& p) const +{ + CharacterColor f = p.foregroundColor; + CharacterColor b = p.backgroundColor; + + p.foregroundColor = b; + p.backgroundColor = f; //p->r &= ~RE_TRANSPARENT; } void Screen::effectiveRendition() @@ -505,71 +509,91 @@ void Screen::effectiveRendition() */ -Character* Screen::getCookedImage( int startLine ) +void Screen::copyFromHistory(Character* dest, int startLine, int count) const { - Q_ASSERT( startLine <= hist->getLines() ); + Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= hist->getLines() ); - int x,y; - Character* merged = new Character[lines*columns+1]; - merged[lines*columns] = defaultChar; - - for (y = 0; (y < lines) && (y < (hist->getLines()-startLine)); y++) + for (int line = startLine; line < startLine + count; line++) { - int len = qMin(columns,hist->getLineLen(y+startLine)); - int yp = y*columns; + const int length = qMin(columns,hist->getLineLen(line)); + const int destLineOffset = (line-startLine)*columns; - hist->getCells(y+startLine,0,len,merged+yp); - for (x = len; x < columns; x++) merged[yp+x] = defaultChar; - if (sel_begin !=-1) - for (x = 0; x < columns; x++) - { -#ifdef REVERSE_WRAPPED_LINES - if (hist->isLINE_WRAPPED(y+startLine)) - reverseRendition(&merged[p]); -#endif - if (isSelected(x,y+startLine)) { - int p=x + yp; - reverseRendition(&merged[p]); // for selection - } + hist->getCells(line,0,length,dest + destLineOffset); + + for (int column = length; column < columns; column++) + dest[destLineOffset+column] = defaultChar; + + // invert selected text + if (sel_begin !=-1) + { + for (int column = 0; column < columns; column++) + { + if (isSelected(column,line)) + { + reverseRendition(dest[destLineOffset + column]); + } + } + } } - } - if (lines >= hist->getLines()-startLine) - { - for (y = (hist->getLines()-startLine); y < lines ; y++) +} + +void Screen::copyFromScreen(Character* dest , int startLine , int count) const +{ + Q_ASSERT( startLine >= 0 && count > 0 && startLine + count <= lines ); + + for (int line = startLine; line < (startLine+count) ; line++) { - int yp = y*columns; - int yr = (y-hist->getLines()+startLine)*columns; - for (x = 0; x < columns; x++) - { int p = x + yp; int r = x + yr; + int srcLineStartIndex = line*columns; + int destLineStartIndex = (line-startLine)*columns; - // sanity checks - assert( p >= 0 ); - assert( p < (lines*columns+1) ); + for (int column = 0; column < columns; column++) + { + int srcIndex = srcLineStartIndex + column; + int destIndex = destLineStartIndex + column; - merged[p] = screenLines[r/columns].value(r%columns,defaultChar); + dest[destIndex] = screenLines[srcIndex/columns].value(srcIndex%columns,defaultChar); -#ifdef REVERSE_WRAPPED_LINES - if (lineProperties[y- hist->getLines() +startLine] & LINE_WRAPPED) - reverseRendition(&merged[p]); -#endif - if (sel_begin != -1 && isSelected(x,y+startLine)) - reverseRendition(&merged[p]); // for selection + // invert selected text + if (sel_begin != -1 && isSelected(column,line + hist->getLines())) + reverseRendition(dest[destIndex]); } } - } - // evtl. inverse display +} + +void Screen::getImage( Character* dest, int size, int startLine, int endLine ) const +{ + Q_ASSERT( startLine >= 0 ); + Q_ASSERT( endLine >= startLine && endLine < hist->getLines() + lines ); + + const int mergedLines = endLine - startLine + 1; + + Q_ASSERT( size >= mergedLines * columns ); + + const int linesInHistoryBuffer = qBound(0,hist->getLines()-startLine,mergedLines); + const int linesInScreenBuffer = mergedLines - linesInHistoryBuffer; + + // copy lines from history buffer + if (linesInHistoryBuffer > 0) + copyFromHistory(dest,startLine,linesInHistoryBuffer); + + // copy lines from screen buffer + if (linesInScreenBuffer > 0) + copyFromScreen(dest + linesInHistoryBuffer*columns, + startLine + linesInHistoryBuffer - hist->getLines(), + linesInScreenBuffer); + + // invert display when in screen mode if (getMode(MODE_Screen)) { - for (int i = 0; i < lines*columns; i++) - reverseRendition(&merged[i]); // for reverse display + for (int i = 0; i < mergedLines*columns; i++) + reverseRendition(dest[i]); // for reverse display } -// if (getMode(MODE_Cursor) && (cuY+(hist->getLines()-startLine) < lines)) // cursor visible - int loc_ = loc(cuX, cuY+hist->getLines()-startLine); - if(getMode(MODE_Cursor) && loc_ < columns*lines) - merged[loc(cuX,cuY+(hist->getLines()-startLine))].rendition|=RE_CURSOR; - return merged; + // mark the character at the current cursor position + int cursorIndex = loc(cuX, cuY + linesInHistoryBuffer); + if(getMode(MODE_Cursor) && cursorIndex < columns*mergedLines) + dest[cursorIndex].rendition |= RE_CURSOR; } QVector Screen::getCookedLineProperties( int startLine ) @@ -1228,7 +1252,7 @@ void Screen::setSelectionEnd( const int x, const int y) } } -bool Screen::isSelected( const int x,const int y) +bool Screen::isSelected( const int x,const int y) const { if (columnmode) { int sel_Left,sel_Right; diff --git a/src/Screen.h b/src/Screen.h index 29069c7d9..3897ddb56 100644 --- a/src/Screen.h +++ b/src/Screen.h @@ -61,7 +61,7 @@ class TerminalCharacterDecoder; rendered by the display widget ( TerminalDisplay ). Some types of emulation may have more than one screen image. - getCookedImage() is used to retrieve the currently visible image + getImage() is used to retrieve the currently visible image which is then used by the display widget to draw the output from the terminal. @@ -72,7 +72,7 @@ class TerminalCharacterDecoder; The screen image has a selection associated with it, specified using setSelectionStart() and setSelectionEnd(). The selected text can be retrieved - using selectedText(). When getCookedImage() is used to retrieve the the visible image, + using selectedText(). When getImage() is used to retrieve the the visible image, characters which are part of the selection have their colours inverted. */ class Screen @@ -226,7 +226,7 @@ public: /** Restores the state of a screen @p mode saved by calling saveMode() */ void restoreMode (int mode); /** Returns whether the specified screen @p mode is enabled or not .*/ - bool getMode (int mode); + bool getMode (int mode) const; /** * Saves the current position and appearence (text color and style) of the cursor. @@ -360,8 +360,13 @@ public: * Returns the current screen image. * The result is an array of Characters of size [getLines()][getColumns()] which * must be freed by the caller after use. + * + * @param dest Buffer to copy the characters into + * @param size Size of @p dest in Characters + * @param startLine Index of first line to copy + * @param endLine Index of last line to copy */ - Character* getCookedImage( int line ); + void getImage( Character* dest , int size , int startLine , int endLine ) const; /** * Returns the additional attributes associated with lines in the image. @@ -426,7 +431,7 @@ public: void setBusySelecting(bool busy) { sel_busy = busy; } /** Returns true if the character at (@p column, @p line) is part of the current selection. */ - bool isSelected(const int column,const int line); + bool isSelected(const int column,const int line) const; /** * Convenience method. Returns the currently selected text. @@ -557,10 +562,17 @@ private: void initTabStops(); void effectiveRendition(); - void reverseRendition(Character* p); + void reverseRendition(Character& p) const; bool isSelectionValid() const; + // copies 'count' lines from the screen buffer into 'dest', + // starting from 'startLine', where 0 is the first line in the screen buffer + void copyFromScreen(Character* dest, int startLine, int count) const; + // copies 'count' lines from the history buffer into 'dest', + // starting from 'startLine', where 0 is the first line in the history + void copyFromHistory(Character* dest, int startLine, int count) const; + /* The state of the screen is more complex as one would expect first. The screem does really do part of the diff --git a/src/ScreenWindow.cpp b/src/ScreenWindow.cpp index e63022f78..e9d74cc0d 100644 --- a/src/ScreenWindow.cpp +++ b/src/ScreenWindow.cpp @@ -30,12 +30,18 @@ using namespace Konsole; ScreenWindow::ScreenWindow(QObject* parent) : QObject(parent) + , _windowBuffer(0) + , _windowBufferSize(0) + , _bufferNeedsUpdate(true) , _currentLine(0) , _trackOutput(true) , _scrollCount(0) { } - +ScreenWindow::~ScreenWindow() +{ + delete[] _windowBuffer; +} void ScreenWindow::setScreen(Screen* screen) { Q_ASSERT( screen ); @@ -50,7 +56,24 @@ Screen* ScreenWindow::screen() const Character* ScreenWindow::getImage() { - return _screen->getCookedImage(_currentLine); + // reallocate internal buffer if the window size has changed + int size = windowLines() * windowColumns(); + if (_windowBuffer == 0 || _windowBufferSize != size) + { + delete[] _windowBuffer; + _windowBufferSize = size; + _windowBuffer = new Character[size]; + _bufferNeedsUpdate = true; + } + + if (!_bufferNeedsUpdate) + return _windowBuffer; + + _screen->getImage(_windowBuffer,size, + _currentLine,_currentLine + windowLines() - 1); + _bufferNeedsUpdate = false; + + return _windowBuffer; } QVector ScreenWindow::getLineProperties() @@ -75,11 +98,11 @@ void ScreenWindow::getSelectionEnd( int& column , int& line ) } void ScreenWindow::setSelectionStart( int column , int line , bool columnMode ) { - /* FIXME - I'm not sure what the columnmode parameter ( the last argument to setSelectionStart ) - does, check it out and fix */ + #warning "FIXME: The columnMode parameter is handled correctly when visually selecting an area, but copy/select and paste produces the wrong results." _screen->setSelectionStart( column , line + _currentLine , columnMode); - + + _bufferNeedsUpdate = true; emit selectionChanged(); } @@ -87,6 +110,7 @@ void ScreenWindow::setSelectionEnd( int column , int line ) { _screen->setSelectionEnd( column , line + _currentLine ); + _bufferNeedsUpdate = true; emit selectionChanged(); } @@ -171,6 +195,8 @@ void ScreenWindow::scrollTo( int line ) // this can be reset by calling resetScrollCount() _scrollCount += delta; + _bufferNeedsUpdate = true; + emit scrolled(_currentLine); } @@ -233,6 +259,8 @@ void ScreenWindow::notifyOutputChanged() _currentLine = qMin( _currentLine , _screen->getHistLines() ); } + _bufferNeedsUpdate = true; + emit outputChanged(); } diff --git a/src/ScreenWindow.h b/src/ScreenWindow.h index 99f6dfd57..24d4b1a08 100644 --- a/src/ScreenWindow.h +++ b/src/ScreenWindow.h @@ -64,6 +64,7 @@ public: * between all views on a session. */ ScreenWindow(QObject* parent = 0); + virtual ~ScreenWindow(); /** Sets the screen which this window looks onto */ void setScreen(Screen* screen); @@ -74,13 +75,11 @@ public: * Returns the image of characters which are currently visible through this window * onto the screen. * - * getImage() creates a new buffer consisting of lines() * columns() characters and - * copies the characters from the appropriate part of the screen into the buffer. - * It is the caller's responsibility to free the buffer when they have finished using - * it using delete[] - * + * The buffer is managed by the ScreenWindow instance and does not need to be + * deleted by the caller. */ Character* getImage(); + /** * Returns the line attributes associated with the lines of characters which * are currently visible through this window @@ -235,6 +234,9 @@ signals: private: Screen* _screen; // see setScreen() , screen() + Character* _windowBuffer; + int _windowBufferSize; + bool _bufferNeedsUpdate; int _currentLine; // see scrollTo() , currentLine() bool _trackOutput; // see setTrackOutput() , trackOutput() diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp index 8356da6f4..a6f1b0e9e 100644 --- a/src/TerminalDisplay.cpp +++ b/src/TerminalDisplay.cpp @@ -951,9 +951,6 @@ void TerminalDisplay::updateImage() memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character)); } - // free the image from the screen window - delete[] newimg; - // if the new _image is smaller than the previous _image, then ensure that the area // outside the new _image is cleared if ( linesToUpdate < _usedLines )