#include "DebugPrint.h" #include "CoreMinimal.h" using DPrintCallback = DebugPrintControl::DPrintCallback; ////////////////////////////////////////////////////////////// // // DPrintState // // The global state of the DPrint routine. There is only // ever one of these, and it is owned by DPrintAccess below. // ////////////////////////////////////////////////////////////// struct DPrintState { // True if buffering is enabled. bool Collect; // The array of buffered messages. TArray Messages; // The array of callback functions. TArray Callbacks; }; ////////////////////////////////////////////////////////////// // // DPrintAccess // // This class grants you safe access to the global state // of the DPrint routine. Constructing an object of // class DPrintAccess will lock the global mutex and make // sure that the DPrintState is initialized. Then it will // give you unrestricted access to the DPrintState through // operator right arrow. // ////////////////////////////////////////////////////////////// class DPrintAccess { private: // The Mutex that protects the global state. static FCriticalSection Mutex; // The Global State. A pointer, to avoid static init issues. static DPrintState* State; public: // Constructor. Locks mutex and initializes state if necessary. DPrintAccess() { Mutex.Lock(); if (State == nullptr) { State = new DPrintState; State->Collect = false; } } // Destructor. Releases mutex. ~DPrintAccess() { Mutex.Unlock(); } // Access the DPrintState. DPrintState* operator ->() { return State; } }; FCriticalSection DPrintAccess::Mutex; DPrintState* DPrintAccess::State; ////////////////////////////////////////////////////////////// // // Namespace DebugPrint. // // This contains all the various versions of the DPrint routine. // ////////////////////////////////////////////////////////////// namespace DebugPrint { // DPrint. Invoke all the callbacks, and store the message. void DPrint(const FString& fs) { DPrintAccess state; for (DPrintCallback cb : state->Callbacks) { cb(fs); } if (state->Collect) { state->Messages.Emplace(fs); } } // Alternative interface to the dispatcher. void DPrint(const char* msg) { DPrint(FString(msg)); } // Alternative interface to the dispatcher. void DPrint(const char* msg, size_t len) { DPrint(FString(len, (const UTF8CHAR*)msg)); } } // namespace DebugPrint ////////////////////////////////////////////////////////////// // // Namespace DebugPrintControl. // // Configuration and control for the DPrint routine. // ////////////////////////////////////////////////////////////// namespace DebugPrintControl { // Register a callback. Check for duplication. void RegisterCallback(DPrintCallback cb) { DPrintAccess state; for (DPrintCallback old : state->Callbacks) { if (cb == old) return; } state->Callbacks.Add(cb); } // Set the collection bit in the global state. void EnableCollection() { DPrintAccess state; state->Collect = true; } // Get the array of collected messages, if any. TArray GetStored() { DPrintAccess state; TArray result = std::move(state->Messages); state->Messages.Empty(); return result; } }; ////////////////////////////////////////////////////////////// // // ConsoleOutput // // Storing the text that goes in the unreal console. // ////////////////////////////////////////////////////////////// void FlxConsoleOutput::Truncate() { int lines = 50; int csize = Content.Len(); int total = 0; for (int i = csize - 1; i >= 0; i--) { if (Content[i] == '\n') { total += 1; if (total == lines) { Content = Content.RightChop(i + 1); return; } } } } bool FlxConsoleOutput::MaybeAppendNewline() { int csize = Content.Len(); if ((csize > 0) && (Content[csize - 1] != '\n')) { Content += TEXT("\n"); return true; } else { return false; } } bool FlxConsoleOutput::MaybeAppendText(const FString& text) { if (!text.IsEmpty()) { Content += text; return true; } else { return false; } } void FlxConsoleOutput::Append(const FString& text) { bool modified = MaybeAppendText(text); if (modified) { Dirty = true; Truncate(); } } void FlxConsoleOutput::AppendLine(const FString& text) { bool modified = MaybeAppendNewline(); modified |= MaybeAppendText(text); modified |= MaybeAppendNewline(); if (modified) { Dirty = true; Truncate(); } }