- replaced console buffer with a significantly more efficient new version that also can hold a lot more data.

This commit is contained in:
Christoph Oelckers 2014-05-24 21:05:00 +02:00
parent 01e909070a
commit 9d846395bc
8 changed files with 458 additions and 366 deletions

View file

@ -789,6 +789,7 @@ add_executable( zdoom WIN32
c_bind.cpp c_bind.cpp
c_cmds.cpp c_cmds.cpp
c_console.cpp c_console.cpp
c_consolebuffer.cpp
c_cvars.cpp c_cvars.cpp
c_dispatch.cpp c_dispatch.cpp
c_expr.cpp c_expr.cpp

View file

@ -65,22 +65,29 @@
#include "g_level.h" #include "g_level.h"
#include "d_event.h" #include "d_event.h"
#include "d_player.h" #include "d_player.h"
#include "c_consolebuffer.h"
#include "gi.h" #include "gi.h"
#define CONSOLESIZE 16384 // Number of characters to store in console
#define CONSOLELINES 256 // Max number of lines of console text
#define LINEMASK (CONSOLELINES-1)
#define LEFTMARGIN 8 #define LEFTMARGIN 8
#define RIGHTMARGIN 8 #define RIGHTMARGIN 8
#define BOTTOMARGIN 12 #define BOTTOMARGIN 12
CUSTOM_CVAR(Int, con_buffersize, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
// ensure a minimum size
if (self >= 0 && self < 128) self = 128;
}
FConsoleBuffer *conbuffer;
static void C_TabComplete (bool goForward); static void C_TabComplete (bool goForward);
static bool C_TabCompleteList (); static bool C_TabCompleteList ();
static bool TabbedLast; // True if last key pressed was tab static bool TabbedLast; // True if last key pressed was tab
static bool TabbedList; // True if tab list was shown static bool TabbedList; // True if tab list was shown
CVAR (Bool, con_notablist, false, CVAR_ARCHIVE) CVAR(Bool, con_notablist, false, CVAR_ARCHIVE)
static FTextureID conback; static FTextureID conback;
static DWORD conshade; static DWORD conshade;
@ -94,18 +101,15 @@ extern FBaseCVar *CVars;
extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
int ConCols, PhysRows; int ConCols, PhysRows;
int ConWidth;
bool vidactive = false; bool vidactive = false;
bool cursoron = false; bool cursoron = false;
int ConBottom, ConScroll, RowAdjust; int ConBottom, ConScroll, RowAdjust;
int CursorTicker; int CursorTicker;
constate_e ConsoleState = c_up; constate_e ConsoleState = c_up;
static char ConsoleBuffer[CONSOLESIZE];
static char *Lines[CONSOLELINES];
static bool LineJoins[CONSOLELINES];
static int TopLine, InsertLine; static int TopLine, InsertLine;
static char *BufferRover = ConsoleBuffer;
static void ClearConsole (); static void ClearConsole ();
static void C_PasteText(FString clip, BYTE *buffer, int len); static void C_PasteText(FString clip, BYTE *buffer, int len);
@ -182,7 +186,6 @@ static struct NotifyText
static int NotifyTop, NotifyTopGoal; static int NotifyTop, NotifyTopGoal;
#define PRINTLEVELS 5
int PrintColors[PRINTLEVELS+2] = { CR_RED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_GOLD }; int PrintColors[PRINTLEVELS+2] = { CR_RED, CR_GOLD, CR_GRAY, CR_GREEN, CR_GREEN, CR_GOLD };
static void setmsgcolor (int index, int color); static void setmsgcolor (int index, int color);
@ -328,78 +331,11 @@ void C_InitConsole (int width, int height, bool ingame)
{ {
cwidth = cheight = 8; cwidth = cheight = 8;
} }
ConCols = (width - LEFTMARGIN - RIGHTMARGIN) / cwidth; ConWidth = (width - LEFTMARGIN - RIGHTMARGIN);
ConCols = ConWidth / cwidth;
PhysRows = height / cheight; PhysRows = height / cheight;
// If there is some text in the console buffer, reformat it if (conbuffer == NULL) conbuffer = new FConsoleBuffer;
// for the new resolution.
if (TopLine != InsertLine)
{
// Note: Don't use new here, because we attach a handler to new in
// i_main.cpp that calls I_FatalError if the allocation fails,
// but we can gracefully handle such a condition here by just
// clearing the console buffer. (OTOH, what are the chances that
// any other memory allocations would succeed if we can't get
// these mallocs here?)
char *fmtBuff = (char *)malloc (CONSOLESIZE);
char **fmtLines = (char **)malloc (CONSOLELINES*sizeof(char*)*4);
int out = 0;
if (fmtBuff && fmtLines)
{
int in;
char *fmtpos;
bool newline = true;
fmtpos = fmtBuff;
memset (fmtBuff, 0, CONSOLESIZE);
for (in = TopLine; in != InsertLine; in = (in + 1) & LINEMASK)
{
size_t len = strlen (Lines[in]);
if (fmtpos + len + 2 - fmtBuff > CONSOLESIZE)
{
break;
}
if (newline)
{
newline = false;
fmtLines[out++] = fmtpos;
}
strcpy (fmtpos, Lines[in]);
fmtpos += len;
if (!LineJoins[in])
{
*fmtpos++ = '\n';
fmtpos++;
if (out == CONSOLELINES*4)
{
break;
}
newline = true;
}
}
}
ClearConsole ();
if (fmtBuff && fmtLines)
{
int i;
for (i = 0; i < out; i++)
{
AddToConsole (-1, fmtLines[i]);
}
}
if (fmtBuff)
free (fmtBuff);
if (fmtLines)
free (fmtLines);
}
} }
//========================================================================== //==========================================================================
@ -507,16 +443,21 @@ void C_DeinitConsole ()
work = NULL; work = NULL;
worklen = 0; worklen = 0;
} }
if (conbuffer != NULL)
{
delete conbuffer;
conbuffer = NULL;
}
} }
static void ClearConsole () static void ClearConsole ()
{ {
RowAdjust = 0; if (conbuffer != NULL)
{
conbuffer->Clear();
}
TopLine = InsertLine = 0; TopLine = InsertLine = 0;
BufferRover = ConsoleBuffer;
memset (ConsoleBuffer, 0, CONSOLESIZE);
memset (Lines, 0, sizeof(Lines));
memset (LineJoins, 0, sizeof(LineJoins));
} }
static void setmsgcolor (int index, int color) static void setmsgcolor (int index, int color)
@ -596,223 +537,9 @@ void C_AddNotifyString (int printlevel, const char *source)
NotifyTopGoal = 0; NotifyTopGoal = 0;
} }
static int FlushLines (const char *start, const char *stop)
{
int i;
for (i = TopLine; i != InsertLine; i = (i + 1) & LINEMASK)
{
if (Lines[i] < stop && Lines[i] + strlen (Lines[i]) > start)
{
Lines[i] = NULL;
}
else
{
break;
}
}
return i;
}
static void AddLine (const char *text, bool more, size_t len)
{
if (BufferRover + len + 1 - ConsoleBuffer > CONSOLESIZE)
{
TopLine = FlushLines (BufferRover, ConsoleBuffer + CONSOLESIZE);
BufferRover = ConsoleBuffer;
}
if (len >= CONSOLESIZE - 1)
{
text = text + len - CONSOLESIZE + 1;
len = CONSOLESIZE - 1;
}
TopLine = FlushLines (BufferRover, BufferRover + len + 1);
memcpy (BufferRover, text, len);
BufferRover[len] = 0;
Lines[InsertLine] = BufferRover;
BufferRover += len + 1;
LineJoins[InsertLine] = more;
InsertLine = (InsertLine + 1) & LINEMASK;
if (InsertLine == TopLine)
{
TopLine = (TopLine + 1) & LINEMASK;
}
}
void AddToConsole (int printlevel, const char *text) void AddToConsole (int printlevel, const char *text)
{ {
static enum conbuffer->AddText(printlevel, text, Logfile);
{
NEWLINE,
APPENDLINE,
REPLACELINE
} addtype = NEWLINE;
char *work_p;
char *linestart;
FString cc('A' + char(CR_TAN));
int size, len;
int x;
int maxwidth;
if (ConsoleDrawing)
{
EnqueueConsoleText (false, printlevel, text);
return;
}
len = (int)strlen (text);
size = len + 20;
if (addtype != NEWLINE)
{
InsertLine = (InsertLine - 1) & LINEMASK;
if (Lines[InsertLine] == NULL)
{
InsertLine = (InsertLine + 1) & LINEMASK;
addtype = NEWLINE;
}
else
{
BufferRover = Lines[InsertLine];
}
}
if (addtype == APPENDLINE)
{
size += (int)strlen (Lines[InsertLine]);
}
if (size > worklen)
{
work = (char *)M_Realloc (work, size);
worklen = size;
}
if (work == NULL)
{
static char oom[] = TEXTCOLOR_RED "*** OUT OF MEMORY ***";
work = oom;
worklen = 0;
}
else
{
if (addtype == APPENDLINE)
{
strcpy (work, Lines[InsertLine]);
strcat (work, text);
}
else if (printlevel >= 0)
{
work[0] = TEXTCOLOR_ESCAPE;
work[1] = 'A' + (printlevel == PRINT_HIGH ? CR_TAN :
printlevel == 200 ? CR_GREEN :
printlevel < PRINTLEVELS ? PrintColors[printlevel] :
CR_TAN);
cc = work[1];
strcpy (work + 2, text);
}
else
{
strcpy (work, text);
}
}
work_p = linestart = work;
if (ConFont != NULL && screen != NULL)
{
x = 0;
maxwidth = screen->GetWidth() - LEFTMARGIN - RIGHTMARGIN;
while (*work_p)
{
if (*work_p == TEXTCOLOR_ESCAPE)
{
work_p++;
if (*work_p == '[')
{
char *start = work_p;
while (*work_p != ']' && *work_p != '\0')
{
work_p++;
}
if (*work_p != '\0')
{
work_p++;
}
cc = FString(start, work_p - start);
}
else if (*work_p != '\0')
{
cc = *work_p++;
}
continue;
}
int w = ConFont->GetCharWidth (*work_p);
if (*work_p == '\n' || x + w > maxwidth)
{
AddLine (linestart, *work_p != '\n', work_p - linestart);
if (*work_p == '\n')
{
x = 0;
work_p++;
}
else
{
x = w;
}
if (*work_p)
{
linestart = work_p - 1 - cc.Len();
if (linestart < work)
{
// The line start is outside the buffer.
// Make space for the newly inserted stuff.
size_t movesize = work-linestart;
memmove(work + movesize, work, strlen(work)+1);
work_p += movesize;
linestart = work;
}
linestart[0] = TEXTCOLOR_ESCAPE;
strncpy (linestart + 1, cc, cc.Len());
}
else
{
linestart = work_p;
}
}
else
{
x += w;
work_p++;
}
}
if (*linestart)
{
AddLine (linestart, true, work_p - linestart);
}
}
else
{
while (*work_p)
{
if (*work_p++ == '\n')
{
AddLine (linestart, false, work_p - linestart - 1);
linestart = work_p;
}
}
if (*linestart)
{
AddLine (linestart, true, work_p - linestart);
}
}
switch (text[len-1])
{
case '\r': addtype = REPLACELINE; break;
case '\n': addtype = NEWLINE; break;
default: addtype = APPENDLINE; break;
}
} }
/* Adds a string to the console and also to the notify buffer */ /* Adds a string to the console and also to the notify buffer */
@ -823,34 +550,6 @@ int PrintString (int printlevel, const char *outline)
return 0; return 0;
} }
if (Logfile)
{
// Strip out any color escape sequences before writing to the log file
char * copy = new char[strlen(outline)+1];
const char * srcp = outline;
char * dstp = copy;
while (*srcp != 0)
{
if (*srcp!=0x1c)
{
*dstp++=*srcp++;
}
else
{
if (srcp[1]!=0) srcp+=2;
else break;
}
}
*dstp=0;
fputs (copy, Logfile);
delete [] copy;
//#ifdef _DEBUG
fflush (Logfile);
//#endif
}
if (printlevel != PRINT_LOG) if (printlevel != PRINT_LOG)
{ {
I_PrintStr (outline); I_PrintStr (outline);
@ -949,6 +648,11 @@ void C_Ticker ()
if (lasttic == 0) if (lasttic == 0)
lasttic = gametic - 1; lasttic = gametic - 1;
if (con_buffersize > 0)
{
conbuffer->ResizeBuffer(con_buffersize);
}
if (ConsoleState != c_up) if (ConsoleState != c_up)
{ {
if (ConsoleState == c_falling) if (ConsoleState == c_falling)
@ -1236,38 +940,22 @@ void C_DrawConsole (bool hw2d)
if (lines > 0) if (lines > 0)
{ {
// No more enqueuing because adding new text to the console won't touch the actual print data.
conbuffer->FormatText(ConFont, ConWidth);
unsigned int consolelines = conbuffer->GetFormattedLineCount();
FBrokenLines **blines = conbuffer->GetLines();
FBrokenLines **printline = blines + consolelines - 1 - RowAdjust;
int bottomline = ConBottom - ConFont->GetHeight()*2 - 4; int bottomline = ConBottom - ConFont->GetHeight()*2 - 4;
int pos = (InsertLine - 1) & LINEMASK;
int i;
ConsoleDrawing = true; ConsoleDrawing = true;
for (i = RowAdjust; i; i--) for(FBrokenLines **p = printline; p >= blines && lines > 0; p--, lines--)
{ {
if (pos == TopLine) screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE);
{
RowAdjust = RowAdjust - i;
break;
} }
else
{
pos = (pos - 1) & LINEMASK;
}
}
pos++;
do
{
pos = (pos - 1) & LINEMASK;
if (Lines[pos] != NULL)
{
screen->DrawText (ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(),
Lines[pos], TAG_DONE);
}
lines--;
} while (pos != TopLine && lines > 0);
ConsoleDrawing = false; ConsoleDrawing = false;
DequeueConsoleText ();
if (ConBottom >= 20) if (ConBottom >= 20)
{ {
@ -1293,7 +981,7 @@ void C_DrawConsole (bool hw2d)
{ {
// Indicate that the view has been scrolled up (10) // Indicate that the view has been scrolled up (10)
// and if we can scroll no further (12) // and if we can scroll no further (12)
screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, pos == TopLine ? 12 : 10, TAG_DONE); screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE);
} }
} }
} }
@ -1445,7 +1133,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
RowAdjust += (SCREENHEIGHT-4) / RowAdjust += (SCREENHEIGHT-4) /
((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3; ((gamestate == GS_FULLCONSOLE || gamestate == GS_STARTUP) ? ConFont->GetHeight() : ConFont->GetHeight()*2) - 3;
} }
else if (RowAdjust < CONSOLELINES) else if (RowAdjust < conbuffer->GetFormattedLineCount())
{ // Scroll console buffer up { // Scroll console buffer up
if (ev->subtype == EV_GUI_WheelUp) if (ev->subtype == EV_GUI_WheelUp)
{ {
@ -1455,6 +1143,10 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
{ {
RowAdjust++; RowAdjust++;
} }
if (RowAdjust > conbuffer->GetFormattedLineCount())
{
RowAdjust = conbuffer->GetFormattedLineCount();
}
} }
break; break;
@ -1488,7 +1180,7 @@ static bool C_HandleKey (event_t *ev, BYTE *buffer, int len)
case GK_HOME: case GK_HOME:
if (ev->data3 & GKM_CTRL) if (ev->data3 & GKM_CTRL)
{ // Move to top of console buffer { // Move to top of console buffer
RowAdjust = CONSOLELINES; RowAdjust = conbuffer->GetFormattedLineCount();
} }
else else
{ // Move cursor to start of line { // Move cursor to start of line

View file

@ -47,6 +47,9 @@ typedef enum cstate_t
} }
constate_e; constate_e;
#define PRINTLEVELS 5
extern int PrintColors[PRINTLEVELS + 2];
extern constate_e ConsoleState; extern constate_e ConsoleState;
extern int ConBottom; extern int ConBottom;

317
src/c_consolebuffer.cpp Normal file
View file

@ -0,0 +1,317 @@
/*
** consolebuffer.cpp
**
** manages the text for the console
**
**---------------------------------------------------------------------------
** Copyright 2014 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "c_console.h"
#include "c_consolebuffer.h"
//==========================================================================
//
//
//
//==========================================================================
FConsoleBuffer::FConsoleBuffer()
{
mLogFile = NULL;
mAddType = NEWLINE;
mLastFont = NULL;
mLastDisplayWidth = -1;
mLastLineNeedsUpdate = false;
mTextLines = 0;
mBufferWasCleared = true;
mBrokenStart.Push(0);
}
//==========================================================================
//
//
//
//==========================================================================
FConsoleBuffer::~FConsoleBuffer()
{
FreeBrokenText();
}
//==========================================================================
//
//
//
//==========================================================================
void FConsoleBuffer::FreeBrokenText(unsigned start, unsigned end)
{
if (end > mBrokenConsoleText.Size()) end = mBrokenConsoleText.Size();
for (unsigned i = start; i < end; i++)
{
if (mBrokenConsoleText[i] != NULL) V_FreeBrokenLines(mBrokenConsoleText[i]);
mBrokenConsoleText[i] = NULL;
}
}
//==========================================================================
//
// Adds a new line of text to the console
// This is kept as simple as possible. This function does not:
// - remove old text if the buffer gets larger than the specified size
// - format the text for the current screen layout
//
// These tasks will only be be performed once per frame because they are
// relatively expensive. The old console did them each time text was added
// resulting in extremely bad performance with a high output rate.
//
//==========================================================================
void FConsoleBuffer::AddText(int printlevel, const char *text, FILE *logfile)
{
FString build = TEXTCOLOR_TAN;
if (mAddType == REPLACELINE)
{
// Just wondering: Do we actually need this case? If so, it may need some work.
mConsoleText.Pop(); // remove the line to be replaced
mLastLineNeedsUpdate = true;
}
else if (mAddType == APPENDLINE)
{
mConsoleText.Pop(build);
printlevel = -1;
mLastLineNeedsUpdate = true;
}
if (printlevel >= 0 && printlevel != PRINT_HIGH)
{
if (printlevel == 200) build = TEXTCOLOR_GREEN;
else if (printlevel < PRINTLEVELS) build.Format("%c%c", TEXTCOLOR_ESCAPE, PrintColors[printlevel]);
}
size_t textsize = strlen(text);
if (text[textsize-1] == '\r')
{
textsize--;
mAddType = REPLACELINE;
}
else if (text[textsize-1] == '\n')
{
textsize--;
mAddType = NEWLINE;
}
else
{
mAddType = APPENDLINE;
}
// don't bother about linefeeds etc. inside the text, we'll let the formatter sort this out later.
build.AppendCStrPart(text, textsize);
mConsoleText.Push(build);
if (logfile != NULL) WriteLineToLog(logfile, text);
}
//==========================================================================
//
//
//
//==========================================================================
void FConsoleBuffer::WriteLineToLog(FILE *LogFile, const char *outline)
{
// Strip out any color escape sequences before writing to the log file
char * copy = new char[strlen(outline)+1];
const char * srcp = outline;
char * dstp = copy;
while (*srcp != 0)
{
if (*srcp != TEXTCOLOR_ESCAPE)
{
switch (*srcp)
{
case '\35':
*dstp++ = '<';
break;
case '\36':
*dstp++ = '-';
break;
case '\37':
*dstp++ = '>';
break;
default:
*dstp++=*srcp;
break;
}
srcp++;
}
else if (srcp[1] == '[')
{
srcp+=2;
while (*srcp != ']' && *srcp != 0) srcp++;
if (*srcp == ']') srcp++;
}
else
{
if (srcp[1]!=0) srcp+=2;
else break;
}
}
*dstp=0;
fputs (copy, LogFile);
delete [] copy;
fflush (LogFile);
}
//==========================================================================
//
//
//
//==========================================================================
void FConsoleBuffer::WriteContentToLog(FILE *LogFile)
{
if (LogFile != NULL)
{
for (unsigned i = 0; i < mConsoleText.Size(); i++)
{
WriteLineToLog(LogFile, mConsoleText[i]);
}
}
}
//==========================================================================
//
// ensures that the following text is not appended to the current line.
//
//==========================================================================
void FConsoleBuffer::Linefeed(FILE *Logfile)
{
if (mAddType != NEWLINE && Logfile != NULL) fputc('\n', Logfile);
mAddType = NEWLINE;
}
//==========================================================================
//
//
//
//==========================================================================
static const char bar1[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_TAN "\n";
static const char bar2[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_GREEN "\n";
static const char bar3[] = TEXTCOLOR_RED "\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\36\36\36\36\36\36\36\36\36\37" TEXTCOLOR_NORMAL "\n";
static const char logbar[] = "\n<------------------------------->\n";
void FConsoleBuffer::AddMidText(const char *string, bool bold, FILE *Logfile)
{
Linefeed(Logfile);
AddText (-1, bold? bar2 : bar1, Logfile);
AddText (-1, string, Logfile);
Linefeed(Logfile);
AddText(-1, bar3, Logfile);
}
//==========================================================================
//
// Format the text for output
//
//==========================================================================
void FConsoleBuffer::FormatText(FFont *formatfont, int displaywidth)
{
if (formatfont != mLastFont || displaywidth != mLastDisplayWidth || mBufferWasCleared)
{
FreeBrokenText();
mBrokenConsoleText.Clear();
mBrokenStart.Clear();
mBrokenStart.Push(0);
mBrokenLines.Clear();
mLastFont = formatfont;
mLastDisplayWidth = displaywidth;
mBufferWasCleared = false;
}
unsigned brokensize = mBrokenConsoleText.Size();
if (brokensize == mConsoleText.Size())
{
// The last line got text appended. We have to wait until here to format it because
// it is possible that during display new text will be added from the NetUpdate calls in the software version of DrawTextureV.
if (mLastLineNeedsUpdate)
{
brokensize--;
V_FreeBrokenLines(mBrokenConsoleText[brokensize]);
mBrokenConsoleText.Resize(brokensize);
}
}
mBrokenLines.Resize(mBrokenStart[brokensize]);
mBrokenStart.Resize(brokensize);
for (unsigned i = brokensize; i < mConsoleText.Size(); i++)
{
FBrokenLines *bl = V_BreakLines(formatfont, displaywidth, mConsoleText[i], true);
mBrokenConsoleText.Push(bl);
mBrokenStart.Push(mBrokenLines.Size());
while (bl->Width != -1)
{
mBrokenLines.Push(bl);
bl++;
}
}
mTextLines = mBrokenLines.Size();
mBrokenStart.Push(mTextLines);
mLastLineNeedsUpdate = false;
}
//==========================================================================
//
// Delete old content if number of lines gets too large
//
//==========================================================================
void FConsoleBuffer::ResizeBuffer(unsigned newsize)
{
if (mConsoleText.Size() > newsize)
{
unsigned todelete = mConsoleText.Size() - newsize;
mConsoleText.Delete(0, todelete);
mBufferWasCleared = true;
}
}

85
src/c_consolebuffer.h Normal file
View file

@ -0,0 +1,85 @@
/*
** consolebuffer.h
**
** manages the text for the console
**
**---------------------------------------------------------------------------
** Copyright 2014 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <limits.h>
#include <stdio.h>
#include "zstring.h"
#include "tarray.h"
#include "v_text.h"
enum EAddType
{
NEWLINE,
APPENDLINE,
REPLACELINE
};
class FConsoleBuffer
{
TArray<FString> mConsoleText;
TArray<FBrokenLines *> mBrokenConsoleText; // This holds the structures returned by V_BreakLines and is used for memory management.
TArray<unsigned int> mBrokenStart;
TArray<FBrokenLines *> mBrokenLines; // This holds the single lines, indexed by mBrokenStart and is used for printing.
FILE * mLogFile;
EAddType mAddType;
int mTextLines;
bool mBufferWasCleared;
FFont *mLastFont;
int mLastDisplayWidth;
bool mLastLineNeedsUpdate;
void WriteLineToLog(FILE *LogFile, const char *outline);
void FreeBrokenText(unsigned int start = 0, unsigned int end = INT_MAX);
void Linefeed(FILE *Logfile);
public:
FConsoleBuffer();
~FConsoleBuffer();
void AddText(int printlevel, const char *string, FILE *logfile = NULL);
void AddMidText(const char *string, bool bold, FILE *logfile);
void FormatText(FFont *formatfont, int displaywidth);
void ResizeBuffer(unsigned newsize);
void WriteContentToLog(FILE *logfile);
void Clear()
{
mBufferWasCleared = true;
mConsoleText.Clear();
}
int GetFormattedLineCount() { return mTextLines; }
FBrokenLines **GetLines() { return &mBrokenLines[0]; }
};

View file

@ -125,12 +125,10 @@ void FNodeBuilder::BuildTree ()
{ {
fixed_t bbox[4]; fixed_t bbox[4];
C_InitTicker ("Building BSP", FRACUNIT);
HackSeg = DWORD_MAX; HackSeg = DWORD_MAX;
HackMate = DWORD_MAX; HackMate = DWORD_MAX;
CreateNode (0, Segs.Size(), bbox); CreateNode (0, Segs.Size(), bbox);
CreateSubsectorsForReal (); CreateSubsectorsForReal ();
C_InitTicker (NULL, 0);
} }
int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]) int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4])
@ -199,10 +197,6 @@ int FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4])
} }
SegsStuffed += count; SegsStuffed += count;
if ((SegsStuffed & ~127) != ((SegsStuffed - count) & ~127))
{
C_SetTicker (MulScale16 (SegsStuffed, (SDWORD)Segs.Size()));
}
D(Printf (PRINT_LOG, "bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16)); D(Printf (PRINT_LOG, "bbox (%d,%d)-(%d,%d)\n", bbox[BOXLEFT]>>16, bbox[BOXBOTTOM]>>16, bbox[BOXRIGHT]>>16, bbox[BOXTOP]>>16));

View file

@ -336,7 +336,7 @@ static void breakit (FBrokenLines *line, FFont *font, const BYTE *start, const B
line->Width = font->StringWidth (line->Text); line->Width = font->StringWidth (line->Text);
} }
FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string) FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string, bool preservecolor)
{ {
FBrokenLines lines[128]; // Support up to 128 lines (should be plenty) FBrokenLines lines[128]; // Support up to 128 lines (should be plenty)
@ -397,7 +397,7 @@ FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *string)
space = string - 1; space = string - 1;
breakit (&lines[i], font, start, space, linecolor); breakit (&lines[i], font, start, space, linecolor);
if (c == '\n') if (c == '\n' && !preservecolor)
{ {
lastcolor = ""; // Why, oh why, did I do it like this? lastcolor = ""; // Why, oh why, did I do it like this?
} }

View file

@ -75,9 +75,9 @@ struct FBrokenLines
#define TEXTCOLOR_CHAT "\034*" #define TEXTCOLOR_CHAT "\034*"
#define TEXTCOLOR_TEAMCHAT "\034!" #define TEXTCOLOR_TEAMCHAT "\034!"
FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str); FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const BYTE *str, bool preservecolor = false);
void V_FreeBrokenLines (FBrokenLines *lines); void V_FreeBrokenLines (FBrokenLines *lines);
inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str) inline FBrokenLines *V_BreakLines (FFont *font, int maxwidth, const char *str, bool preservecolor = false)
{ return V_BreakLines (font, maxwidth, (const BYTE *)str); } { return V_BreakLines (font, maxwidth, (const BYTE *)str, preservecolor); }
#endif //__V_TEXT_H__ #endif //__V_TEXT_H__