AuroraRuntime/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.cpp
Jamie Reece Wilson ca2c0462ab [*] Continue to refactor allocations of synchronization primitive away
[*] NT: Fix missing CoTaskMemFree leak on startup
[*] Fix ConsoleStd restart bug
2023-06-28 10:33:12 +01:00

850 lines
24 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: ConsoleWxWidgets.cpp
Date: 2021-6-8
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "ConsoleWxWidgets.hpp"
#include <Source/Console/ConsoleFIO/ConsoleFIO.hpp>
#if defined(_AUHAS_WXWIDGETS)
#if defined(AURORA_PLATFORM_WIN32)
#include <Uxtheme.h>
#include <Source/Extensions/Win32/DarkTheme.hpp>
#endif
#include <wx/wxprec.h>
#include <wx/wx.h>
#include <wx/wxprec.h>
#include <wx/wx.h>
#include <wx/richtext/richtextctrl.h>
class ConsoleFrame;
static AuList<Aurora::Console::ConsoleMessage> gPendingLines;
static AuThreadPrimitives::Mutex gMutex;
static bool gWxConsoleReady;
static bool gConsoleStarted = false;
static ConsoleFrame *gWxFrame;
class WxSplitterLine : public wxWindow
{
public:
WxSplitterLine() {}
WxSplitterLine(wxSize splitter, AuOptional<wxColor> color, wxWindow *parent, wxWindowID winid) : wxWindow(parent, winid), _color(color)
{
SetMinSize(splitter);
}
void OnPaint(wxPaintEvent &paint)
{
wxPaintDC re(this);
re.SetBrush(_color.value_or(this->GetParent()->GetBackgroundColour()));
re.SetBackground(_color.value_or(this->GetParent()->GetBackgroundColour()));
re.Clear();
}
private:
DECLARE_EVENT_TABLE()
AuOptional<wxColor> _color;
};
wxBEGIN_EVENT_TABLE(WxSplitterLine, wxPanel)
EVT_PAINT(WxSplitterLine::OnPaint)
wxEND_EVENT_TABLE()
class ConsoleApp : public wxApp
{
public:
~ConsoleApp();
virtual bool OnInit();
};
IMPLEMENT_APP_NO_MAIN(ConsoleApp);
ConsoleApp::~ConsoleApp()
{
}
class ConsoleFrame : public wxFrame
{
public:
ConsoleFrame(const wxString &title, const wxPoint &pos, const wxSize &size);
~ConsoleFrame()
{
AuThreadPrimitives::MutexUnique_t re(gMutex.get());
gWxConsoleReady = false;
}
void WriteLine(const Aurora::Console::ConsoleMessage &msg);
private:
void OnHello(wxCommandEvent &event);
void OnExit(wxCommandEvent &event);
void OnAbout(wxCommandEvent &event);
void OnBugWrite(wxCommandEvent &event);
void OnIssueWrite(wxCommandEvent &event);
void OpenLogDir(wxCommandEvent &event);
void OnInit(wxWindowCreateEvent &event);
void OnShow(wxWindowCreateEvent &event);
void OnCmd(wxCommandEvent &event);
void OnPump(wxTimerEvent& event);
WxSplitterLine *NewSplitter(wxSize splitter, wxColor color);
WxSplitterLine *NewSplitter(wxSize splitter);
wxDECLARE_EVENT_TABLE();
wxRichTextCtrl *richtextbox_;
wxTextCtrl *commandbox_;
wxTimer timer_;
};
enum
{
ID_Hello = 1,
ID_BLACKBOX,
ID_WRITE_CARD,
ID_WRITE_BUG,
ID_OPEN_LOG_DIR,
ID_OPENBIN
};
wxBEGIN_EVENT_TABLE(ConsoleFrame, wxFrame)
EVT_MENU(ID_Hello, ConsoleFrame::OnHello)
EVT_MENU(wxID_EXIT, ConsoleFrame::OnExit)
EVT_MENU(wxID_ABOUT, ConsoleFrame::OnAbout)
EVT_MENU(ID_WRITE_CARD, ConsoleFrame::OnIssueWrite)
EVT_MENU(ID_WRITE_BUG, ConsoleFrame::OnBugWrite)
EVT_MENU(ID_OPEN_LOG_DIR, ConsoleFrame::OpenLogDir)
EVT_WINDOW_CREATE(ConsoleFrame::OnInit)
wxEND_EVENT_TABLE()
bool ConsoleApp::OnInit()
{
//gRuntimeConfig.console.
ConsoleFrame *frame = _new ConsoleFrame("Aurora SDK Sample", wxPoint(50, 50), wxSize(1080, 550));
frame->Show(true);
gWxFrame = frame;
return true;
}
void ConsoleFrame::OnCmd(wxCommandEvent &event)
{
// im lazy, i know, bite me
auto textbox = reinterpret_cast<wxTextCtrl *>(this);
AuString line = textbox->GetLineText(0);
if (line.empty())
{
return;
}
textbox->Clear();
Aurora::Console::DispatchRawLine(line);
}
WxSplitterLine *ConsoleFrame::NewSplitter(wxSize splitter, wxColor color)
{
return new WxSplitterLine(splitter, color, this, -1);
}
WxSplitterLine *ConsoleFrame::NewSplitter(wxSize splitter)
{
return new WxSplitterLine(splitter, {}, this, -1);
}
void ConsoleFrame::WriteLine(const Aurora::Console::ConsoleMessage &string)
{
static AuArray<const wxColour, static_cast<size_t>(Aurora::Console::EAnsiColor::eEnumCount)> ColorMap
{
*wxRED, // kRed,
*wxRED, // kBoldRed,
*wxGREEN, // kGreen,
*wxGREEN, // kBoldGreen,
*wxYELLOW, // kYellow,
*wxYELLOW, // kBoldYellow,
*wxBLUE, // kBlue,
*wxBLUE, // kBoldBlue,
*wxRED, // kMagenta,
*wxRED, // kBoldMagenta,
*wxBLUE, // kCyan,
*wxBLUE, // kBoldCyan,
*wxWHITE, // kReset
};
static AuArray<bool, static_cast<size_t>(Aurora::Console::EAnsiColor::eEnumCount)> HahaBald
{
false, // kRed,
true, // kBoldRed,
false, // kGreen,
true, // kBoldGreen,
false, // kYellow,
true, // kBoldYellow,
false, // kBlue,
true, // kBoldBlue,
false, // kMagenta,
true, // kBoldMagenta,
false, // kCyan,
true, // kBoldCyan,
false, // kReset
};
auto writeLine = string.ToSimplified();
bool useColor = true;// DarkModeEnabled();
auto color = useColor ? ColorMap[static_cast<int>(string.color)] : wxColor(0, 0, 0);
auto bold = useColor ? HahaBald[static_cast<int>(string.color)] : false;
if (richtextbox_->GetNumberOfLines() > 1000)
richtextbox_->Clear();
//richtextbox_->BeginParagraphSpacing(0, 20);
auto pos = richtextbox_->GetCaretPosition();
auto selPos = richtextbox_->GetLastPosition() - 1;
richtextbox_->SetCaretPosition(selPos);
richtextbox_->BeginTextColour(color);
if (bold)
richtextbox_->BeginBold();
richtextbox_->WriteText(wxString::FromUTF8(writeLine));
richtextbox_->Newline();
if (bold)
richtextbox_->EndBold();
richtextbox_->EndTextColour();
if ((pos == -1) || // true once
(pos == selPos) || // most likely
(abs(pos - selPos) < 5)) // ux
{
auto newPos = richtextbox_->GetLastPosition() - 1;
richtextbox_->ScrollIntoView(newPos, WXK_DOWN);
richtextbox_->SetCaretPosition(newPos);
}
else
{
richtextbox_->SetCaretPosition(pos);
}
//richtextbox_->Disable
//richtextbox_->EndParagraphSpacing();
}
#include <wx/gbsizer.h>
ConsoleFrame::ConsoleFrame(const wxString &title, const wxPoint &pos, const wxSize &size)
: wxFrame(NULL, wxID_ANY, title, pos, size)
{
#if defined(AURORA_PLATFORM_WIN32)
bool useWin32DarkHack = Aurora::Extensions::Win32::g_darkModeSupported;
#else
bool useWin32DarkHack = false;
#endif
// _ _ _ _ _ _ _
// (_) | | | | | | | | | | | |
// __ ____ _ _ __ ___ _ __ _ _ __ ___ ___ ___ __ _| |__ ___ __ _ __| | | | | | | | | |
// \ \ /\ / / _` | '__| / __| '__| | '_ ` _ \ / _ \/ __| / _` | '_ \ / _ \/ _` |/ _` | | | | | | | | |
// \ V V / (_| | | | (__| | | | | | | | | __/\__ \ | (_| | | | | __/ (_| | (_| | |_| |_| |_| |_|
// \_/\_/ \__,_|_| \___|_| |_|_| |_| |_|\___||___/ \__,_|_| |_|\___|\__,_|\__,_| (_) (_) (_) (_)
//
//
// I dont even care if this function leaks on failure. sucks to be you, i dont care.
// valgrind can fail when your system is running low on resources at start up
static const auto kEnableDark = (useWin32DarkHack) ||
(Aurora::Build::kCurrentPlatform != Aurora::Build::EPlatform::ePlatformWin32);
static const auto kIsBgColorDefault = !kEnableDark;
static const auto kBackgroundColor = kIsBgColorDefault ? this->GetBackgroundColour() : wxColour(38, 38, 38);
static const auto kTextBoxColor = kIsBgColorDefault ? this->GetForegroundColour() : wxColour(255, 255, 255);
static const auto kTextBoxOutter = wxColour(0x15, 0x15, 0x15);
static const auto kTextBoxInner = wxColour(0x38, 0x38, 0x38);
static const auto kHeaderPadUpDown = 14;
static const auto kHeaderPadLeftRight = 15;
static const auto kTextboxWin32PadInner = 2;
static const auto kTextboxWin32PadOutter = 1;
static const auto kTitleContentSplitterDepth = 2;
static const auto kTextboxHeight = 20;
static const auto kTextBoxHandlePad = kEnableDark;
static const auto kTextBoxHandleColor = kEnableDark;
static const auto kDrawOwnCheckboxes = kEnableDark;
// 1080, 550
this->SetClientSize(this->FromDIP(wxSize(903, 434)));
auto menuBar_ = _new wxMenuBar();
if (!menuBar_) return;
{
auto menuFile = _new wxMenu();
if (!menuFile) return;
menuFile->Append(wxID_SAVE, "Export text log");
menuFile->AppendSeparator();
menuFile->Append(ID_OPEN_LOG_DIR, "Open log directory");
menuBar_->Append(menuFile, "&Logs");
}
{
auto menuHelp = _new wxMenu();
if (!menuHelp) return;
menuHelp->Append(ID_BLACKBOX, "&Report application state to the black box");
#if defined(STAGING) || defined(DEBUG)
menuHelp->Append(ID_WRITE_CARD, "&[Internal] Write a bug report");
#endif
menuHelp->Append(ID_WRITE_BUG, "&[Public] Write a bug report");
menuBar_->Append(menuHelp, "&Bugs");
}
if (useWin32DarkHack)
{
this->Connect(wxEVT_SHOW, wxWindowCreateEventHandler(ConsoleFrame::OnShow));
}
auto sizer = _new wxBoxSizer(wxVERTICAL);
if (!sizer) return;
auto font = this->GetFont();
font.SetPointSize(9);
font.MakeBold();
auto font2 = this->GetFont();
font2.SetPointSize(9);
auto addTextbox = [&](wxBoxSizer *parent, int minx, int miny, bool alt = false) -> wxTextCtrl *
{
auto filterBarContainer = _new wxBoxSizer(wxVERTICAL);
if (!filterBarContainer) return {};
auto filterBar = _new wxBoxSizer(wxHORIZONTAL);
if (!filterBar) return{}; // TODO: a leak idc about
auto filterBox = _new wxTextCtrl(this, -1, wxEmptyString,
wxDefaultPosition, alt ? wxDefaultSize : wxSize(400, 20),
(kTextBoxHandlePad ? wxBORDER_NONE : 0) | wxTE_PROCESS_ENTER | wxEXPAND);
if (!filterBox) return{}; // TODO: a leak idc about
filterBox->SetMinSize({minx, miny});
if (kTextBoxHandlePad)
{
filterBarContainer->Add(NewSplitter(wxSize(kTextboxWin32PadOutter, kTextboxWin32PadOutter), kTextBoxOutter), 0, wxEXPAND);
filterBarContainer->Add(NewSplitter(wxSize(kTextboxWin32PadInner, kTextboxWin32PadInner), kTextBoxInner), 0, wxEXPAND);
}
filterBarContainer->Add(filterBox, wxSizerFlags().Proportion(1).Expand());
if (kTextBoxHandleColor)
{
filterBox->SetBackgroundColour(kBackgroundColor);
filterBox->SetForegroundColour(kTextBoxColor);
}
if (kTextBoxHandlePad)
{
filterBarContainer->Add(NewSplitter(wxSize(kTextboxWin32PadInner, kTextboxWin32PadInner), kTextBoxInner), 0, wxEXPAND);
filterBarContainer->Add(NewSplitter(wxSize(kTextboxWin32PadOutter, kTextboxWin32PadOutter), kTextBoxOutter), 0, wxEXPAND);
}
if (kTextBoxHandlePad)
{
filterBar->Add(NewSplitter(wxSize(kTextboxWin32PadOutter, kTextboxWin32PadOutter), kTextBoxOutter) , 0, wxEXPAND | wxTOP | wxBOTTOM, 1);
filterBar->Add(NewSplitter(wxSize(kTextboxWin32PadInner, kTextboxWin32PadInner), kTextBoxInner), 0, wxEXPAND | wxTOP | wxBOTTOM, 1);
}
filterBar->Add(filterBarContainer, wxSizerFlags().Proportion(1).Expand());
if (kTextBoxHandlePad)
{
filterBar->Add(NewSplitter(wxSize(kTextboxWin32PadInner, kTextboxWin32PadInner), kTextBoxInner), 0, wxEXPAND | wxTOP | wxBOTTOM, 1);
filterBar->Add(NewSplitter(wxSize(kTextboxWin32PadOutter, kTextboxWin32PadOutter), kTextBoxOutter), 0, wxEXPAND | wxTOP | wxBOTTOM, 1);
}
parent->Add(filterBar, wxSizerFlags().Expand());
return filterBox;
};
auto header = _new wxBoxSizer(wxHORIZONTAL);
auto addHeaderPanel = [&](wxBoxSizer *parent, wxBoxSizer *inner, bool up, bool down, bool left, bool right, int top, int sides)
{
auto filterBar = _new wxBoxSizer(wxVERTICAL);
if (!filterBar) return;
auto innerBar = _new wxBoxSizer(wxHORIZONTAL);
if (!innerBar) return;
// PAD FILTER REGION UP
if (up)
{
filterBar->Add(NewSplitter(wxSize(top, top)), wxSizerFlags().Expand());
}
{
// PAD FILTER REGION LEFT
if (left)
{
innerBar->Add(NewSplitter(wxSize(sides, sides)), wxSizerFlags().Expand());
}
innerBar->Add(inner, wxSizerFlags().Expand());
// PAD FILTER REGION RIGHT
if (right)
{
innerBar->Add(NewSplitter(wxSize(sides, sides)), wxSizerFlags().Expand());
}
}
filterBar->Add(innerBar, wxSizerFlags().Expand());
// PAD FILTER REGION DOWN
if (down)
{
filterBar->Add(NewSplitter(wxSize(top, top)), wxSizerFlags().Expand());
}
parent->Add(filterBar, wxSizerFlags().Expand());
};
this->SetBackgroundColour(kBackgroundColor);
// HEADER PANEL 1# filter
{
auto filterBarContainer = _new wxBoxSizer(wxVERTICAL);
if (!filterBarContainer) return;
auto filterLabel = _new wxStaticText(this, -1, "FILTER");
if (!filterLabel) return;
filterLabel->SetFont(font);
filterLabel->SetBackgroundColour(kBackgroundColor);
filterLabel->SetForegroundColour(kTextBoxColor);
filterBarContainer->Add(filterLabel);
filterBarContainer->Add(NewSplitter(wxSize(kTitleContentSplitterDepth, kTitleContentSplitterDepth)), wxSizerFlags().Expand());
addTextbox(filterBarContainer, 400, kTextboxHeight);
addHeaderPanel(header, filterBarContainer, true, true, true, false, kHeaderPadUpDown, kHeaderPadLeftRight);
}
// Align right
header->AddStretchSpacer();
// Draw a little bar
//header->Add(NewSplitter(wxSize(1, 1), wxColour(0x38, 0x38, 0x38)), 0, wxEXPAND);
// HEADER PANEL 2# levels
{
AuList<AuString> chkName = {"Debug", "Warn", "Info", "Game"};
auto checkboxes = _new wxBoxSizer(wxHORIZONTAL);
if (!checkboxes) return;
auto checkboxesContainer = _new wxBoxSizer(wxVERTICAL);
if (!checkboxes) return;
auto checkboxLabel = _new wxStaticText(this, -1, "LEVEL");
if (!checkboxLabel) return;
checkboxLabel->SetFont(font);
checkboxLabel->SetBackgroundColour(kBackgroundColor);
checkboxLabel->SetForegroundColour(kTextBoxColor);
for (int i = 0; i < chkName.size(); i++)
{
const auto &name = chkName[i];
if (kDrawOwnCheckboxes)
{
if (i != 0)
{
checkboxes->Add(NewSplitter(wxSize(6, 6)), wxSizerFlags().Expand());
}
auto label = _new wxStaticText(this, -1, name.c_str());
label->SetBackgroundColour(kBackgroundColor);
label->SetForegroundColour(kTextBoxColor);
label->SetFont(font2);
checkboxes->Add(label);
checkboxes->Add(NewSplitter(wxSize(2, 2)), wxSizerFlags().Expand());
}
auto checkbox1 = new wxCheckBox(this, -1, kDrawOwnCheckboxes ? "" : name.c_str(), wxDefaultPosition, wxDefaultSize);
checkbox1->SetFont(font2);
checkbox1->SetValue(true);
checkboxes->Add(NewSplitter(wxSize(2, 2)), wxSizerFlags().Expand());
checkboxes->Add(checkbox1, wxSizerFlags(0).CenterVertical().GetFlags());
checkboxes->Add(NewSplitter(wxSize(2, 2)), wxSizerFlags().Expand());
}
checkboxesContainer->Add(checkboxLabel);
checkboxesContainer->Add(NewSplitter(wxSize(kTitleContentSplitterDepth, kTitleContentSplitterDepth)), wxSizerFlags().Expand());
checkboxesContainer->Add(checkboxes);
addHeaderPanel(header, checkboxesContainer, true, true, false, true, kHeaderPadUpDown, kHeaderPadLeftRight);
}
sizer->Add(header, 0, wxEXPAND);
// Shadow down
if (/*useWin32DarkHack*/ true)
{
sizer->Add(NewSplitter(wxSize(kTextboxWin32PadInner, kTextboxWin32PadInner), kTextBoxInner), 0, wxEXPAND);
sizer->Add(NewSplitter(wxSize(kTextboxWin32PadOutter, kTextboxWin32PadOutter), kTextBoxOutter), 0, wxEXPAND);
}
// Rich text box
{
richtextbox_ = _new wxRichTextCtrl(this, -1, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
(kTextBoxHandlePad ? wxBORDER_NONE : 0) | wxTE_MULTILINE | wxRE_READONLY | wxRE_CENTER_CARET);
if (!richtextbox_) return;
if (useWin32DarkHack)
{
richtextbox_->SetBackgroundColour(kBackgroundColor);
richtextbox_->SetForegroundColour(kTextBoxColor);
this->Connect(wxEVT_SHOW, wxWindowCreateEventHandler(ConsoleFrame::OnShow));
}
sizer->Add(richtextbox_, 1, wxEXPAND);
}
// Shadow down
if (/*useWin32DarkHack*/ true)
{
sizer->Add(NewSplitter(wxSize(kTextboxWin32PadInner, kTextboxWin32PadInner), kTextBoxInner), 0, wxEXPAND);
sizer->Add(NewSplitter(wxSize(kTextboxWin32PadOutter, kTextboxWin32PadOutter), kTextBoxOutter), 0, wxEXPAND);
}
// Command Box
{
commandbox_ = addTextbox(sizer, 0, kTextboxHeight, true);
commandbox_->SetHint("Type a command here");
commandbox_->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(ConsoleFrame::OnCmd));
}
SetSizer(sizer);
SetMenuBar(menuBar_);
// SetAutoLayout(true);
timer_.Bind(wxEVT_TIMER, &ConsoleFrame::OnPump, this);
timer_.Start(100);
gWxConsoleReady = true;
}
void ConsoleFrame::OnInit(wxWindowCreateEvent &event)
{
OnShow(event);
}
#if defined(AURORA_PLATFORM_WIN32)
void ConsoleFrame::OnShow(wxWindowCreateEvent &event)
{
if (!Aurora::Extensions::Win32::g_darkModeSupported) return;
auto handle = event.GetWindow()->GetHWND();
Aurora::Extensions::Win32::AllowDarkModeForWindow(handle, true);
if (Aurora::Extensions::Win32::_FlushMenuThemes)
Aurora::Extensions::Win32::_FlushMenuThemes();
SetWindowTheme(handle, L"DarkMode_Explorer", NULL);
SendMessageW(handle, WM_THEMECHANGED, 0, 0);
Aurora::Extensions::Win32::RefreshTitleBarThemeColor(handle);
UpdateWindow(handle);
}
#else
void ConsoleFrame::OnShow(wxWindowCreateEvent &event)
{}
static bool DarkModeEnabled()
{
return Aurora::Build::EPlatform == Aurora::Build::EPlatform::kPlatformLinux;
}
#endif
void ConsoleFrame::OnExit(wxCommandEvent &event)
{
Close(true);
}
void ConsoleFrame::OnAbout(wxCommandEvent &event)
{
}
void ConsoleFrame::OnHello(wxCommandEvent &event)
{
AuLogGame("nani?!");
}
void ConsoleFrame::OnBugWrite(wxCommandEvent &event)
{
Aurora::Processes::OpenUri(gRuntimeConfig.console.supportPublic);
}
void ConsoleFrame::OnIssueWrite(wxCommandEvent &event)
{
Aurora::Processes::OpenUri(gRuntimeConfig.console.supportInternal);
}
void ConsoleFrame::OpenLogDir(wxCommandEvent &event)
{
Aurora::Processes::OpenFile(Aurora::Console::ConsoleFIO::GetLogDirectory());
}
namespace Aurora::Console::ConsoleWxWidgets
{
static void WxWidgetsPump();
}
void ConsoleFrame::OnPump(wxTimerEvent &event)
{
Aurora::Console::ConsoleWxWidgets::WxWidgetsPump();
}
namespace Aurora::Console::ConsoleWxWidgets
{
static wxInitializer *gInitializer;
static bool UseWxConsole()
{
if (!gRuntimeConfig.console.enableConsole)
{
return false;
}
if (!gRuntimeConfig.console.enableWxWidgets)
{
return false;
}
#if defined(AURORA_PLATFORM_WIN32)
if (GetConsoleWindow())
{
if (!gRuntimeConfig.console.forceToolKitWindow)
{
return false;
}
}
#endif
return true;
}
static bool WxWidgetsInit()
{
wxApp::SetInstance(_new ConsoleApp());
#if defined(AURORA_PLATFORM_WIN32) || true
std::wstring auroraw(L"Aurora");
static wxChar *s = auroraw.data();
static int count = 1;
#endif
gInitializer = new wxInitializer(count, &s);
if (!gInitializer->IsOk())
{
#if wxUSE_LOG
delete wxLog::SetActiveTarget(NULL);
#endif
}
try
{
if (!wxTheApp->CallOnInit())
{
return false;
}
}
catch (...)
{
wxTheApp->OnUnhandledException();
return false;
}
return true;
}
void WxWidgetsRequestExit()
{
if (!gWxConsoleReady)
{
gMutex.reset();
return;
}
gMutex->Lock();
gWxConsoleReady = false;
gMutex->Unlock();
auto window = wxTheApp->GetTopWindow();
if (!window)
{
gMutex.reset();
return;
}
window->GetEventHandler()->CallAfter([]()
{
wxTheApp->Exit();
gMutex.reset();
});
}
static AuThreads::ThreadUnique_t gWxWidgetsThread;
static void WxWidgetsThreadMain()
{
if (!WxWidgetsInit())
{
return;
}
try
{
wxTheApp->OnRun();
}
catch (...)
{
wxTheApp->OnUnhandledException();
}
}
void Init()
{
if (!UseWxConsole())
{
return;
}
Start();
}
class ConsoleMessageSubscriber : public Aurora::Console::Hooks::IConsoleSubscriber
{
public:
void OnMessage(const ConsoleMessage &string) override;
};
void ConsoleMessageSubscriber::OnMessage(const ConsoleMessage &string)
{
AU_LOCK_GUARD(gMutex);
gPendingLines.push_back(string);
}
static ConsoleMessageSubscriber gConsoleMessageSubscriber;
void Start()
{
if (AuExchange(gConsoleStarted, true)) return;
Aurora::Console::Hooks::AddSubscription(AuUnsafeRaiiToShared(&gConsoleMessageSubscriber));
gWxWidgetsThread = AuThreads::ThreadUnique(AuThreads::ThreadInfo(
AuMakeShared<AuThreads::IThreadVectorsFunctional>(AuThreads::IThreadVectorsFunctional::OnEntry_t(std::bind(WxWidgetsThreadMain)),
AuThreads::IThreadVectorsFunctional::OnExit_t{}),
"WxWidgets"
));
if (!gWxWidgetsThread) return;
gWxWidgetsThread->Run();
}
static void WxWidgetsPump()
{
// called from within WxWidget main loop, do not worry about locking is ready
// we _know_ the wxwidget app is referenceable
if (!wxTheApp) return;
if (!gWxConsoleReady) return;
AuList<Console::ConsoleMessage> lines;
{
AU_LOCK_GUARD(gMutex);
lines = AuExchange(gPendingLines, {});
}
for (const auto& line : lines)
{
gWxFrame->WriteLine(line);
}
}
void Pump()
{
}
void Exit()
{
WxWidgetsRequestExit();
Aurora::Console::Hooks::RemoveSubscription(AuUnsafeRaiiToShared(&gConsoleMessageSubscriber));
gWxWidgetsThread.reset();
auto wxHandle = wxTheApp;
if (wxHandle)
{
delete wxHandle;
}
gPendingLines.clear();
gConsoleStarted = false;
}
}
#else
namespace Aurora::Console::ConsoleWxWidgets
{
void Init()
{
}
void Pump()
{
}
void Exit()
{
}
void Start()
{
}
}
#endif