mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
260 lines
6.8 KiB
C++
260 lines
6.8 KiB
C++
//
|
|
//
|
|
|
|
#include "DebugWindow.h"
|
|
|
|
#include "globalincs/alphacolors.h"
|
|
#include "osapi/osapi.h"
|
|
|
|
namespace {
|
|
SDL_DisplayID get_debug_display() {
|
|
// If we have multiple displays then try to put the debug window on the one
|
|
// that isn't used by the main game window
|
|
auto primaryDisplay = SDL_GetPrimaryDisplay();
|
|
auto gameDisplay = SDL_GetDisplayForWindow(os::getSDLMainWindow());
|
|
|
|
if (primaryDisplay != gameDisplay) {
|
|
return primaryDisplay;
|
|
}
|
|
|
|
auto debugDisplay = primaryDisplay;
|
|
|
|
auto displays = SDL_GetDisplays(nullptr);
|
|
|
|
if (displays) {
|
|
for (int i = 0; displays[i]; ++i) {
|
|
if (displays[i] != gameDisplay) {
|
|
debugDisplay = displays[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_free(displays);
|
|
}
|
|
|
|
return debugDisplay;
|
|
}
|
|
}
|
|
|
|
namespace osapi {
|
|
|
|
DebugWindow::DebugWindow() {
|
|
os::ViewPortProperties attrs = {};
|
|
uint32_t display = get_debug_display();
|
|
attrs.display = display;
|
|
|
|
SDL_Rect size;
|
|
SDL_GetDisplayBounds(display, &size);
|
|
// Make the window a bit smaller so that it doesn't take up the whole screen
|
|
attrs.width = (uint32_t) (size.w / 1.3f);
|
|
attrs.height = (uint32_t) (size.h / 1.3f);
|
|
|
|
attrs.title = "FreeSpace Open - Debug Window";
|
|
|
|
attrs.flags.set(os::ViewPortFlags::Resizeable); // Make this window resizeable
|
|
|
|
auto debugView = gr_create_viewport(attrs);
|
|
if (debugView) {
|
|
debug_view = os::addViewport(std::move(debugView));
|
|
debug_sdl_window = debug_view->toSDLWindow();
|
|
|
|
if (debug_sdl_window != nullptr) {
|
|
os::events::addEventListener(SDL_EVENT_KEY_UP,
|
|
os::events::DEFAULT_LISTENER_WEIGHT - 5,
|
|
[this](const SDL_Event& e) { return this->debug_key_handler(e); });
|
|
}
|
|
}
|
|
|
|
// try and return focus to the game window (this may be ignored by the DE)
|
|
if (debug_sdl_window != nullptr && os::getSDLMainWindow() != nullptr) {
|
|
SDL_RaiseWindow(os::getSDLMainWindow());
|
|
}
|
|
}
|
|
|
|
DebugWindow::~DebugWindow() {
|
|
}
|
|
|
|
void DebugWindow::doFrame(float) {
|
|
if (!debug_view) {
|
|
// Failed to create debug window, nothing to do here
|
|
return;
|
|
}
|
|
|
|
gr_use_viewport(debug_view);
|
|
|
|
gr_clear();
|
|
font::set_font(font::FONT1);
|
|
|
|
if (max_category_width != 0) {
|
|
// Draw a line to separate the category from the text
|
|
auto x_pos = max_category_width + 14;
|
|
gr_set_color_fast(&Color_grey);
|
|
gr_line(x_pos, 0, x_pos, gr_screen.max_h, GR_RESIZE_NONE);
|
|
}
|
|
|
|
// Print the saved debug log lines from the bottom up
|
|
float current_y = i2fl(gr_screen.max_h - 10);
|
|
|
|
// If the current view offset is at the end of the list then we also need to display the line that is currently being constructed
|
|
if (log_view_offset == lines.size() - 1) {
|
|
if (!current_line.empty()) {
|
|
LineInfo info;
|
|
info.text = current_line;
|
|
info.category = current_category;
|
|
|
|
current_y = print_line(current_y, info);
|
|
}
|
|
}
|
|
// Now print all the stored lines
|
|
// Backwards iteration of the vector comes from here: http://stackoverflow.com/a/4206815
|
|
for (auto i = log_view_offset + 1; i-- > 0;) {
|
|
auto& line = lines[i];
|
|
|
|
current_y = print_line(current_y, line);
|
|
|
|
if (current_y < 0.f) {
|
|
// End to iteration if the rendered string would be invisible
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Don't run scripting here
|
|
gr_flip(false);
|
|
|
|
gr_use_viewport(os::getMainViewport());
|
|
}
|
|
|
|
float DebugWindow::print_line(float bottom_y, const LineInfo& line) {
|
|
int category_width;
|
|
gr_get_string_size(&category_width, nullptr, line.category.c_str());
|
|
|
|
// Wrap the string to make sure everything can be read
|
|
SCP_vector<const char*> split_lines;
|
|
SCP_vector<int> line_lengths;
|
|
|
|
// Substract 40 so that we can have margins on both sides
|
|
// Make sure that the width doesn't go too low or else split_str will not be able to fit enough characters in one line
|
|
auto max_w = std::max(40, gr_screen.max_w - max_category_width - 40);
|
|
|
|
split_str(line.text.c_str(), max_w, line_lengths, split_lines);
|
|
|
|
auto text_height = split_lines.size() * font::get_current_font()->getHeight();
|
|
|
|
float y_pos = bottom_y - text_height;
|
|
|
|
float cat_x_pos = max_category_width - category_width + 10.f;
|
|
// Give each category a unique color. We do this by hashing the string and using that to construct the RGB values
|
|
auto hash = std::hash<SCP_string>()(line.category);
|
|
gr_set_color((int) (hash & 0xFF), (int) ((hash & 0xFF00) >> 8), (int) ((hash & 0xFF0000) >> 16));
|
|
gr_string(cat_x_pos, y_pos, line.category.c_str(), GR_RESIZE_NONE);
|
|
|
|
gr_set_color_fast(&Color_white);
|
|
|
|
for (size_t i = 0; i < split_lines.size(); ++i) {
|
|
gr_string(max_category_width + 18.f, y_pos, split_lines[i], GR_RESIZE_NONE, 1.0f, static_cast<size_t>(line_lengths[i]));
|
|
|
|
y_pos += font::get_current_font()->getHeight();
|
|
}
|
|
|
|
return bottom_y - text_height;
|
|
}
|
|
|
|
bool DebugWindow::debug_key_handler(const SDL_Event& evt) {
|
|
if (!os::events::isWindowEvent(evt, debug_sdl_window)) {
|
|
// Event belongs to another window
|
|
return false;
|
|
}
|
|
|
|
ptrdiff_t diff = 0;
|
|
switch(evt.key.key) {
|
|
case SDLK_DOWN:
|
|
diff = 1;
|
|
break;
|
|
case SDLK_UP:
|
|
diff = -1;
|
|
break;
|
|
case SDLK_PAGEDOWN:
|
|
diff = 60; // It's not actually a page but this should still work
|
|
break;
|
|
case SDLK_PAGEUP:
|
|
diff = -60;
|
|
break;
|
|
case SDLK_END:
|
|
log_view_offset = lines.size() - 1;
|
|
return true;
|
|
case SDLK_HOME:
|
|
log_view_offset = 0;
|
|
return true;
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
auto prev = log_view_offset;
|
|
|
|
log_view_offset += diff;
|
|
|
|
if (diff < 0 && log_view_offset > prev) {
|
|
// We had an overflow of the counter
|
|
log_view_offset = 0;
|
|
}
|
|
log_view_offset = std::min(log_view_offset, lines.size() - 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
void DebugWindow::addDebugMessage(const char* category, const char* text) {
|
|
if (!debug_view) {
|
|
// Failed to create debug window, nothing to do here
|
|
return;
|
|
}
|
|
|
|
if (category != current_category) {
|
|
// There is a new category so we need to write the old string to our log
|
|
|
|
// Find finished lines and add them to our vector
|
|
split_current_and_add_to_log(category);
|
|
|
|
if (!current_line.empty()) {
|
|
// Write the last line to our log
|
|
LineInfo info;
|
|
info.text = current_line;
|
|
info.category = category;
|
|
addToLog(std::move(info));
|
|
|
|
current_line.clear();
|
|
}
|
|
}
|
|
|
|
current_category = category;
|
|
current_line += text;
|
|
|
|
split_current_and_add_to_log(category);
|
|
}
|
|
void DebugWindow::split_current_and_add_to_log(const SCP_string& category) {
|
|
size_t pos;
|
|
while ((pos = current_line.find('\n')) != SCP_string::npos) {
|
|
LineInfo info;
|
|
info.text = current_line.substr(0, pos);
|
|
info.category = category;
|
|
addToLog(std::move(info));
|
|
|
|
// Since pos refers to a valid position in the string, the expression pos + 1 is always valid since substr
|
|
// allows values up to str.size()
|
|
current_line = current_line.substr(pos + 1);
|
|
}
|
|
}
|
|
void DebugWindow::addToLog(LineInfo&& line) {
|
|
int cat_width;
|
|
gr_get_string_size(&cat_width, nullptr, line.category.c_str());
|
|
|
|
max_category_width = std::max(cat_width, max_category_width);
|
|
|
|
if (log_view_offset == lines.size() - 1) {
|
|
// We are currently showing the last line so we need to follow new entries
|
|
++log_view_offset;
|
|
}
|
|
|
|
lines.push_back(std::move(line));
|
|
}
|
|
}
|