thunderbrew/src/console/Line.cpp
2023-08-16 18:27:40 -04:00

249 lines
6.0 KiB
C++

#include "console/Line.hpp"
#include "console/Types.hpp"
#include "console/Console.hpp"
#include "console/Screen.hpp"
#include "gx/Device.hpp"
#include <storm/List.hpp>
#include <storm/thread/SCritSect.hpp>
#include <cstdio>
#include <cstdarg>
static SCritSect s_critsect;
// In this list:
// The head = the input line.
// The tail = the oldest line printed.
static STORM_LIST(CONSOLELINE) s_linelist;
// Pointer to the current line. Determines what region of the console history gets rendered.
static CONSOLELINE* s_currlineptr = nullptr;
static uint32_t s_NumLines = 0;
void EnforceMaxLines() {
if (s_NumLines <= CONSOLE_LINES_MAX) {
return;
}
// Pop oldest line off the list
auto lineptr = s_linelist.Tail();
if (lineptr == nullptr) {
lineptr = s_currlineptr;
}
if (lineptr == nullptr) {
return;
}
// Clean up oldest line.
s_linelist.UnlinkNode(lineptr);
s_linelist.DeleteNode(lineptr);
s_NumLines--;
}
CONSOLELINE* GetInputLine() {
auto head = s_linelist.Head();
// If the list is empty, or the list's head is an entered input-line,
// Create a fresh input line, with "> " prefixed before the caret.
if (!head || head->inputpos == 0) {
auto l = SMemAlloc(sizeof(CONSOLELINE), __FILE__, __LINE__, 0);
auto line = new(l) CONSOLELINE();
line->buffer = reinterpret_cast<char*>(SMemAlloc(CONSOLE_LINE_PREALLOC, __FILE__, __LINE__, 0));
line->charsalloc = CONSOLE_LINE_PREALLOC;
s_linelist.LinkToHead(line);
SStrCopy(line->buffer, "> ", line->charsalloc);
SetInputString(line->buffer);
auto chars = SStrLen(line->buffer);
s_NumLines++;
line->inputstart = chars;
line->inputpos = chars;
line->chars = chars;
line->colorType = INPUT_COLOR;
s_currlineptr = line;
EnforceMaxLines();
return line;
}
return head;
}
CONSOLELINE* GetLineAtMousePosition(float y) {
// Loop through linelist to find line at mouse position
int32_t linePos = static_cast<int32_t>((ConsoleGetHeight() - (1.0 - y)) / ConsoleGetFontHeight());
if (linePos == 1) {
return s_linelist.Head();
}
if (s_currlineptr != s_linelist.Head()) {
linePos--;
}
CONSOLELINE* line = s_currlineptr;
while (linePos > 1) {
linePos--;
if (!line) {
line = s_linelist.Head();
}
if (line == nullptr) {
return nullptr;
}
line = line->Next();
}
return line;
}
void ReserveInputSpace(CONSOLELINE* line, size_t len) {
size_t newsize = line->chars + len;
if (newsize >= line->charsalloc) {
while (line->charsalloc <= newsize) {
line->charsalloc += CONSOLE_LINE_PREALLOC;
}
auto buffer = reinterpret_cast<char*>(SMemAlloc(line->charsalloc, __FILE__, __LINE__, 0));
SStrCopy(buffer, line->buffer, line->charsalloc);
SMemFree(line->buffer, __FILE__, __LINE__, 0x0);
line->buffer = buffer;
}
}
void ConsoleWrite(const char* str, COLOR_T color) {
if (g_theGxDevicePtr == nullptr || str[0] == '\0') {
return;
}
s_critsect.Enter();
auto l = reinterpret_cast<char*>(SMemAlloc(sizeof(CONSOLELINE), __FILE__, __LINE__, 0));
auto lineptr = new(l) CONSOLELINE();
auto head = s_linelist.Head();
if (head == nullptr || head->inputpos == 0) {
// Attach console line to head
s_linelist.LinkToHead(lineptr);
} else {
// Attach console line between head and head-1
s_linelist.LinkNode(lineptr, 1, head->Prev());
}
size_t len = SStrLen(str) + 1;
lineptr->chars = len;
lineptr->charsalloc = len;
lineptr->buffer = reinterpret_cast<char*>(SMemAlloc(len, __FILE__, __LINE__, 0));
lineptr->colorType = color;
SStrCopy(lineptr->buffer, str, STORM_MAX_STR);
GenerateNodeString(lineptr);
s_NumLines++;
EnforceMaxLines();
//
s_critsect.Leave();
}
void ConsoleWriteA(const char* str, COLOR_T color, ...) {
char buffer[1024] = {0};
if (str != nullptr && str[0] != '\0') {
va_list list;
va_start(list, color);
vsnprintf(buffer, sizeof(buffer), str, list);
va_end(list);
ConsoleWrite(buffer, color);
}
}
void MoveLinePtr(int32_t direction, int32_t modifier) {
CONSOLELINE* lineptr = s_currlineptr;
auto anyControl = (1 << KEY_LCONTROL) | (1 << KEY_RCONTROL);
if (modifier & anyControl) {
for (int32_t i = 0; i < 10 && lineptr != nullptr; i++) {
CONSOLELINE* next;
if (direction == 1) {
next = lineptr->m_link.Next();
} else {
next = lineptr->m_link.Prev();
}
if (next != nullptr) {
lineptr = next;
}
}
} else {
// if (s_currlineptr == s_linelist.Head()) {
// s_currlineptr = s_currlineptr->Prev();
// }
if (direction == 1) {
lineptr = lineptr->m_link.Next();
} else {
lineptr = lineptr->m_link.Prev();
}
}
if (lineptr) {
s_currlineptr = lineptr;
}
}
void BackspaceLine(CONSOLELINE* line) {
if (line->inputstart <= line->inputpos && line->inputpos != line->inputstart) {
if (line->inputpos < line->chars) {
memmove(line->buffer + line->inputpos + -1, line->buffer + line->inputpos, (line->chars - line->inputpos) + 1);
} else {
line->buffer[line->inputpos - 1] = '\0';
}
line->chars--;
line->inputpos--;
SetInputString(line->buffer);
}
}
CONSOLELINE* GetCurrentLine() {
return s_currlineptr;
}
CONSOLELINE::~CONSOLELINE() {
if (this->buffer) {
SMemFree(this->buffer, __FILE__, __LINE__, 0);
}
if (this->fontPointer) {
GxuFontDestroyString(this->fontPointer);
}
}
void ConsoleClear() {
s_NumLines = 0;
auto ptr = s_linelist.Head();
while (ptr) {
s_linelist.UnlinkNode(ptr);
s_linelist.DeleteNode(ptr);
ptr = s_linelist.Head();
}
}