revises log display

This commit is contained in:
tosh 2013-05-19 14:52:01 +02:00
parent 2120ab9b1e
commit cfd4100c6e
3 changed files with 310 additions and 296 deletions

View file

@ -1690,7 +1690,7 @@ void Application::displayOverlay() {
glPointSize(1.0f);
if (_renderStatsOn->isChecked()) { displayStats(); }
if (_logOn->isChecked()) { logger.render(_glWidget->width(), _glWidget->height()); }
if (_logOn->isChecked()) { logdisplay::Render(_glWidget->width(), _glWidget->height()); }
// Show chat entry field
if (_chatEntryOn) {

View file

@ -8,51 +8,73 @@
#include "Log.h"
#include "InterfaceConfig.h"
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
#include "InterfaceConfig.h"
#include "Util.h"
#include "ui/TextRenderer.h"
namespace {
// anonymous namespace - everything in here only exists within this very .cpp file
// just as 'static' on every effective line in plain C
unsigned const CHARACTER_BUFFER_SIZE = 16384; // number of character that are buffered
unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered
unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message
namespace logdisplay {
const char* FONT_FAMILY = SANS_FONT_FAMILY;
class Logger {
public:
Logger();
~Logger();
bool const TEXT_MONOSPACED = true;
inline void render(unsigned screenWidth, unsigned screenHeight);
float const TEXT_RED = 0.7f;
float const TEXT_GREEN = 0.6f;
float const TEXT_BLUE = 1.0f;
inline void setStream(FILE* stream);
inline void setLogWidth(unsigned pixels);
inline void setCharacterSize(unsigned width, unsigned height);
// magic constants from the GLUT spec
// http://www.opengl.org/resources/libraries/glut/spec3/node78.html
// ultimately this stuff should be in Util.h??
float const CHAR_UP = 119.05f;
float const CHAR_DOWN = 33.33f;
float const CHAR_WIDTH = 104.76f;
// derived values
float const CHAR_HEIGHT = CHAR_UP + CHAR_DOWN;
float const CHAR_FRACT_BASELINE = CHAR_DOWN / CHAR_HEIGHT;
// format, eventually forward, and add the log message (called by printLog)
inline int vprint(char const* fmt, va_list);
// unsigned integer division rounded towards infinity
unsigned divRoundUp(unsigned l, unsigned r) { return (l + r - 1) / r; }
}
private:
// don't copy/assign
Logger(Logger const&); // = delete;
Logger& operator=(Logger const&); // = delete;
Log::Log(FILE* tPipeTo, unsigned bufferedLines,
unsigned defaultLogWidth, unsigned defaultCharWidth, unsigned defaultCharHeight) :
// add formatted message for console diplay (called by vprint)
inline void addMessage(char const*);
_stream(tPipeTo),
TextRenderer _textRenderer;
FILE* _stream; // FILE as secondary destination for log messages
char* _chars; // character buffer base address
char* _charsEnd; // character buffer, exclusive end
char** _lines; // line buffer base address
char** _linesEnd; // line buffer, exclusive end
char* _writePos; // character position to write to
char* _writeLineStartPos; // character position where line being written starts
char** _lastLinePos; // last line in the log
unsigned _writtenInLine; // character counter for line wrapping
unsigned _lineLength; // number of characters before line wrap
unsigned _logWidth; // width of the log in pixels
unsigned _charWidth; // width of a character in pixels
unsigned _charHeight; // height of a character in pixels
pthread_mutex_t _mutex;
};
//
// Initialization / state management
//
Logger::Logger() :
_textRenderer(MONO_FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT),
_stream(DEFAULT_STREAM),
_chars(0l),
_lines(0l),
_logWidth(defaultLogWidth) {
_logWidth(DEFAULT_CONSOLE_WIDTH) {
pthread_mutex_init(& _mutex, 0l);
@ -71,17 +93,67 @@ Log::Log(FILE* tPipeTo, unsigned bufferedLines,
_writtenInLine = 0;
memset(_lines, 0, LINE_BUFFER_SIZE * sizeof(char*));
setCharacterSize(defaultCharWidth, defaultCharHeight);
setCharacterSize(DEFAULT_CHAR_WIDTH, DEFAULT_CHAR_HEIGHT);
}
Log::~Log() {
Logger::~Logger() {
delete[] _chars;
delete[] _lines;
}
inline void Log::addMessage(char const* ptr) {
inline void Logger::setStream(FILE* stream) {
pthread_mutex_lock(& _mutex);
_stream = stream;
pthread_mutex_unlock(& _mutex);
}
inline void Logger::setLogWidth(unsigned pixels) {
pthread_mutex_lock(& _mutex);
_logWidth = pixels;
_lineLength = _logWidth / _charWidth;
pthread_mutex_unlock(& _mutex);
}
inline void Logger::setCharacterSize(unsigned width, unsigned height) {
pthread_mutex_lock(& _mutex);
_charWidth = width;
_charHeight = height;
_lineLength = _logWidth / _charWidth;
pthread_mutex_unlock(& _mutex);
}
//
// Logging
//
inline int Logger::vprint(char const* fmt, va_list args) {
pthread_mutex_lock(& _mutex);
// print to buffer
char buf[MAX_MESSAGE_LENGTH];
int n = vsnprintf(buf, MAX_MESSAGE_LENGTH, fmt, args);
if (n > 0) {
// all fine? log the message
addMessage(buf);
} else {
// error? -> mutter on stream or stderr
fprintf(_stream != 0l ? _stream : stderr,
"Log: Failed to log message with format string = \"%s\".\n", fmt);
}
pthread_mutex_unlock(& _mutex);
return n;
}
inline void Logger::addMessage(char const* ptr) {
// precondition: mutex is locked so noone gets in our way
@ -147,69 +219,18 @@ inline void Log::addMessage(char const* ptr) {
}
int Log::vprint(char const* fmt, va_list args) {
pthread_mutex_lock(& _mutex);
//
// Rendering
//
// print to buffer
char buf[MAX_MESSAGE_LENGTH];
int n = vsnprintf(buf, MAX_MESSAGE_LENGTH, fmt, args);
if (n > 0) {
// all fine? log the message
addMessage(buf);
} else {
// error? -> mutter on stream or stderr
fprintf(_stream != 0l ? _stream : stderr,
"Log: Failed to log message with format string = \"%s\".\n", fmt);
}
pthread_mutex_unlock(& _mutex);
return n;
}
void Log::operator()(char const* fmt, ...) {
va_list args;
va_start(args,fmt);
vprint(fmt, args);
va_end(args);
}
void Log::setLogWidth(unsigned pixels) {
pthread_mutex_lock(& _mutex);
_logWidth = pixels;
_lineLength = _logWidth / _charWidth;
pthread_mutex_unlock(& _mutex);
}
void Log::setCharacterSize(unsigned width, unsigned height) {
pthread_mutex_lock(& _mutex);
_charWidth = width;
_charHeight = height;
_charYoffset = height * CHAR_FRACT_BASELINE;
_charScale = float(width) / CHAR_WIDTH;
_charAspect = (height * CHAR_WIDTH) / (width * CHAR_HEIGHT);
_lineLength = _logWidth / _charWidth;
pthread_mutex_unlock(& _mutex);
}
static TextRenderer* textRenderer() {
static TextRenderer* renderer = new TextRenderer(FONT_FAMILY, -1, -1, false, TextRenderer::SHADOW_EFFECT);
return renderer;
}
void Log::render(unsigned screenWidth, unsigned screenHeight) {
inline void Logger::render(unsigned screenWidth, unsigned screenHeight) {
// rendering might take some time, so create a local copy of the portion we need
// instead of having to hold the mutex all the time
pthread_mutex_lock(& _mutex);
// determine number of visible lines
unsigned showLines = divRoundUp(screenHeight, _charHeight);
// determine number of visible lines (integer division rounded up)
unsigned showLines = (screenHeight + _charHeight - 1) / _charHeight;
char** lastLine = _lastLinePos;
char** firstLine = _lastLinePos;
@ -268,17 +289,25 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) {
charOffsetBeforeFirst = _charsEnd + atEnd - _chars;
}
// get values for rendering
int yStep = textRenderer()->metrics().lineSpacing();
int yStart = screenHeight - textRenderer()->metrics().descent();
// determine geometry information from font metrics
QFontMetrics const& fontMetrics = _textRenderer.metrics();
int yStep = fontMetrics.lineSpacing();
// scale
float xScale = float(_charWidth) / fontMetrics.width('*');
float yScale = float(_charHeight) / yStep;
// scaled translation
int xStart = int((screenWidth - _logWidth) / xScale);
int yStart = screenHeight / yScale - fontMetrics.descent();
// render text
// first line to render
char** line = _linesEnd + showLines;
int x = screenWidth - _logWidth;
// ok, now the lock can be released - we have all we need
// and won't hold it while talking to OpenGL
pthread_mutex_unlock(& _mutex);
// ok, we got all we need
glPushMatrix();
glScalef(xScale, yScale, 1.0f);
for (int y = yStart; y > 0; y -= yStep) {
// debug mode: check line pointer is valid
@ -300,21 +329,35 @@ void Log::render(unsigned screenWidth, unsigned screenHeight) {
assert(! (chars < _charsEnd || chars >= _charsEnd + (_charsEnd - _chars)));
// render the string
glColor3f(TEXT_RED, TEXT_GREEN, TEXT_BLUE);
textRenderer()->draw(x, y, chars);
glColor3f(TEXT_COLOR_RED, TEXT_COLOR_GREEN, TEXT_COLOR_BLUE);
_textRenderer.draw(xStart, y, chars);
//fprintf(stderr, "Log::render, message = \"%s\"\n", chars);
//fprintf(stderr, "Logger::render, message = \"%s\"\n", chars);
}
glPopMatrix();
}
Log logger;
//
// There's one Logger and it exists globally...
//
Logger logger;
// Entrypoints
void Render(unsigned screenWidth, unsigned screenHeight) { logger.render(screenWidth, screenHeight); }
void SetStream(FILE* stream) { logger.setStream(stream); }
void SetLogWidth(unsigned pixels) { logger.setLogWidth(pixels); }
void SetCharacterSize(unsigned width, unsigned height) { logger.setCharacterSize(width, height); }
} // namespace logdisplay
int printLog(char const* fmt, ...) {
int result;
va_list args;
va_start(args,fmt);
result = logger.vprint(fmt, args);
result = logdisplay::logger.vprint(fmt, args);
va_end(args);
return result;
}

View file

@ -11,87 +11,58 @@
#include <stdio.h>
#include <stdarg.h>
#include <glm/glm.hpp>
#include <pthread.h>
class Log;
//
// Call it as you would call 'printf'.
// Log function. Call it as you would call 'printf'.
//
int printLog(char const* fmt, ...);
//
// Macro to log OpenGl errors.
// Example: oglLog( glPushMatrix() );
// Logging control.
//
#define oGlLog(stmt) \
namespace logdisplay {
void Render(unsigned screenWidth, unsigned screenHeight);
// settings
static float const TEXT_COLOR_RED = 0.7f;
static float const TEXT_COLOR_GREEN = 0.6f;
static float const TEXT_COLOR_BLUE = 1.0f;
static FILE* DEFAULT_STREAM = stdout;
static unsigned const DEFAULT_CHAR_WIDTH = 7;
static unsigned const DEFAULT_CHAR_HEIGHT = 16;
static unsigned const DEFAULT_CONSOLE_WIDTH = 400;
void SetStream(FILE* stream);
void SetLogWidth(unsigned pixels);
void SetCharacterSize(unsigned width, unsigned height);
// limits
unsigned const CHARACTER_BUFFER_SIZE = 16384; // number of character that are buffered
unsigned const LINE_BUFFER_SIZE = 256; // number of lines that are buffered
unsigned const MAX_MESSAGE_LENGTH = 512; // maximum number of characters for a message
}
//
// Macro to log OpenGl errors.
// Example: printGlError( glPushMatrix() );
//
#define printLogGlError(stmt) \
stmt; \
{ \
GLenum e = glGetError(); \
if (e != GL_NO_ERROR) { \
printLog(__FILE__ ":" oGlLog_stringize(__LINE__) \
printLog(__FILE__ ":" printLogGlError_stringize(__LINE__) \
" [OpenGL] %s\n", gluErrorString(e)); \
} \
} \
(void) 0
#define oGlLog_stringize(x) oGlLog_stringize_i(x)
#define oGlLog_stringize_i(x) # x
//
// Global instance.
//
extern Log logger;
//
// Logging subsystem.
//
class Log {
FILE* _stream;
char* _chars;
char* _charsEnd;
char** _lines;
char** _linesEnd;
char* _writePos; // character position to write to
char* _writeLineStartPos; // character position where line being written starts
char** _lastLinePos; // last line in the log
unsigned _writtenInLine; // character counter for line wrapping
unsigned _lineLength; // number of characters before line wrap
unsigned _logWidth; // width of the log in pixels
unsigned _charWidth; // width of a character in pixels
unsigned _charHeight; // height of a character in pixels
unsigned _charYoffset; // baseline offset in pixels
float _charScale; // scale factor
float _charAspect; // aspect (h/w)
pthread_mutex_t _mutex;
public:
explicit Log(FILE* tPipeTo = stdout, unsigned bufferedLines = 1024,
unsigned defaultLogWidth = 400, unsigned defaultCharWidth = 6, unsigned defaultCharHeight = 20);
~Log();
void setLogWidth(unsigned pixels);
void setCharacterSize(unsigned width, unsigned height);
void render(unsigned screenWidth, unsigned screenHeight);
void operator()(char const* fmt, ...);
int vprint(char const* fmt, va_list);
private:
// don't copy/assign
Log(Log const&); // = delete;
Log& operator=(Log const&); // = delete;
inline void addMessage(char const*);
friend class LogStream; // for optional iostream-style interface that has to be #included separately
};
#define printLogGlError_stringize(x) printLogGlError_stringize_i(x)
#define printLogGlError_stringize_i(x) # x
#endif