Jamie Reece Wilson
ca2c0462ab
[*] NT: Fix missing CoTaskMemFree leak on startup [*] Fix ConsoleStd restart bug
850 lines
24 KiB
C++
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 |