224ab60395
Sqlite3 crash due to premature call to shutdown. calls to layout very rarely have any obvious effect, but sometimes under odd and idiosyncratic circumstances they do. Called shutdown after the destroy was executed, but destroy does not immediately destroy the windows, thus does not immediately finalize all compiled sql statements and close all database connections. You have to wait for all the windows to be destroyed. Again, a rare crash except under certain special circumstances.
203 lines
6.4 KiB
C++
203 lines
6.4 KiB
C++
#include "stdafx.h"
|
|
|
|
wxIMPLEMENT_APP(App);
|
|
|
|
App::App() : m_Config("wallet", "rho")
|
|
{
|
|
assert (singletonApp == nullptr);
|
|
singletonApp = this;
|
|
wxConfigBase::DontCreateOnDemand();
|
|
}
|
|
|
|
App::~App()
|
|
{
|
|
assert(singletonApp == this);
|
|
singletonApp = nullptr;
|
|
if (thl != nullptr)delete thl;
|
|
thl = nullptr;
|
|
}
|
|
|
|
bool App::OnInit()
|
|
{
|
|
if (wxApp::OnInit()) {
|
|
wxConfigBase* pConfig = &m_Config;
|
|
/* This causes the non volatile config data to be stored under rho\wallet
|
|
We will generally place data in the database, and if additional executables need their own data
|
|
in the config, they will create their own subkey under Computer\HKEY_CURRENT_USER\Software\rho */
|
|
/* pConfig corresponds to the Windows Registry entry
|
|
Computer\HKEY_CURRENT_USER\Software\rho\wallet
|
|
|
|
Contrary to wxWidgets documentation, the config data on windows is by default stored in
|
|
HKCU, HKEY_CURRENT_USER, not in HKLM, HKEY_LOCAL_MACHINE.
|
|
|
|
We probably should have placed per user data in an sqlite3 file in
|
|
wxStandardPaths::GetUserDataDir()
|
|
|
|
Data global to all users has to go in an sqlite3 file in wxStandardPaths::GetAppDocumentsDir()
|
|
or wxStandardPaths::GetLocalDataDir()
|
|
|
|
User local database will record the derivation of all secrets, and what wallets along the path
|
|
are logged in. The local machine database will hold the global consensus blockchain, which contains
|
|
no privacy sensitive information, and will also hold data global to all users on a particular
|
|
machine.
|
|
|
|
A wallet's secret can be stored in a file - we will eventually provide passwords for files,
|
|
but passwords provide a false sense of security, because if someone gets a copy of that file,
|
|
a sophisticated attacker can perform an offline brute force attack, thus a human memorable
|
|
password only provides protection against casual and opportunistic attackers.
|
|
If the file is insecure, password needs to impossible to remember, and stored somewhere secure..*/
|
|
Frame* frame = new Frame(pConfig->GetAppName());
|
|
frame->Show(true); //Frame, being top level unowned window, is owned by the one and only message pump
|
|
if (m_display_in_front && singletonFrame != nullptr && singletonFrame->m_pLogWindow != nullptr) singletonFrame->m_pLogWindow->GetFrame()->Raise();
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
int App::OnRun()
|
|
{
|
|
Bind(wxEVT_MENU, &App::OnError, this, myID_ERRORMESSAGE);
|
|
int exitcode = wxApp::OnRun();
|
|
//wxTheClipboard->Flush();
|
|
return exitcode ? exitcode : errorCode;
|
|
}
|
|
|
|
bool App::OnExceptionInMainLoop()
|
|
{
|
|
bool handled{ false };
|
|
wxString error;
|
|
try {
|
|
throw; // Rethrow the current exception.
|
|
}
|
|
catch (const FatalException& e) {
|
|
error = wsz_program + _wx(e.what());
|
|
if (!errorCode)errorCode = 10;
|
|
}
|
|
catch (const MyException& e) {
|
|
// If we handle an error at this level, the current action has been abruptly terminated,
|
|
// and we need to inform the user, but we are not going to terminate the program,
|
|
// nor set an error number for exit.
|
|
handled = true;
|
|
error = wsz_operation + _wx(e.what());
|
|
}
|
|
catch (const std::exception& e) {
|
|
error = wsz_program + _wx(e.what());
|
|
errorCode = 9;
|
|
}
|
|
catch (...) {
|
|
error = wsz_program + _wx(sz_unknown_error);
|
|
errorCode = 8;
|
|
}
|
|
wxLogError(wxT("%s"), error);
|
|
wxMessageDialog dlg(singletonFrame, error, wsz_error, wxICON_ERROR);
|
|
dlg.SetId(myID_ERRORMESSAGE);
|
|
dlg.ShowModal();
|
|
// returning false to exit the main loop and thus terminate the program.
|
|
return handled;
|
|
}
|
|
|
|
void App::OnInitCmdLine(wxCmdLineParser& parser)
|
|
{
|
|
parser.SetDesc(g_cmdLineDesc);
|
|
// must refuse '/' as parameter starter or cannot use "/path" style paths
|
|
parser.SetSwitchChars(wxT("-"));
|
|
//Command line parameters
|
|
parser.SetLogo(wsz_commandLineLogo);
|
|
parser.AddUsageText(wsz_usageText);
|
|
}
|
|
|
|
bool App::OnCmdLineParsed(wxCmdLineParser& parser)
|
|
{
|
|
for (const auto& arg : parser.GetArguments()) {
|
|
wxString optionName;
|
|
switch (arg.GetKind())
|
|
{
|
|
case wxCMD_LINE_SWITCH:
|
|
optionName = arg.GetShortName();
|
|
if (optionName == wxT("t")) {
|
|
m_unit_test = !arg.IsNegated();
|
|
}
|
|
else if (optionName == wxT("l")) {
|
|
m_display = !arg.IsNegated();
|
|
}
|
|
else if (optionName == wxT("d")) {
|
|
m_display |= m_display_in_front = !arg.IsNegated();
|
|
}
|
|
else if (optionName == wxT("f")) {
|
|
m_log_focus_events = !arg.IsNegated();
|
|
if (m_log_focus_events) {
|
|
Bind(
|
|
wxEVT_IDLE,
|
|
+[](wxIdleEvent& event) { //Since this function is only ever used once, never being unbound, using a lambda to avoid naming it.
|
|
static wxWindow* lastFocus = (wxWindow*)NULL;
|
|
//wxLogMessage(wxT("OnIdle"));
|
|
wxWindow* curFocus = ::wxWindow::FindFocus();
|
|
if (curFocus != lastFocus && curFocus)
|
|
{
|
|
lastFocus = curFocus;
|
|
wxString name{ "" };
|
|
do {
|
|
name = wxString(wxT("/")) + curFocus->GetClassInfo()->GetClassName() + wxT(":") + curFocus->GetName() + name;
|
|
} while (curFocus = curFocus->GetParent());
|
|
wxLogMessage(name);
|
|
}
|
|
event.Skip(); //Called so we can bind multiple tasks to idle, and they will all be handled.
|
|
}
|
|
);
|
|
}
|
|
}
|
|
else if (optionName == wxT("q")) {
|
|
m_quick_unit_test = !arg.IsNegated();
|
|
m_complete_unit_test = m_complete_unit_test && !m_quick_unit_test;
|
|
}
|
|
else if (optionName == wxT("c")) {
|
|
m_complete_unit_test = !arg.IsNegated();
|
|
m_quick_unit_test = m_quick_unit_test && !m_complete_unit_test;
|
|
}
|
|
break;
|
|
case wxCMD_LINE_OPTION:
|
|
assert(false);
|
|
/* switch (arg.GetType()) {
|
|
case wxCMD_LINE_VAL_NUMBER:
|
|
// do something with itarg->GetLongVal();
|
|
break;
|
|
case wxCMD_LINE_VAL_DOUBLE:
|
|
// do something with itarg->GetDoubleVal();
|
|
break;
|
|
case wxCMD_LINE_VAL_DATE:
|
|
// do something with itarg->GetDateVal();
|
|
break;
|
|
case wxCMD_LINE_VAL_STRING:
|
|
// do something with itarg->GetStrVal();
|
|
break;
|
|
}*/
|
|
break;
|
|
case wxCMD_LINE_PARAM:
|
|
m_params.push_back(arg.GetStrVal());
|
|
// This intended to support subcommand processing, but not handling subcommands yet
|
|
// g_cmdLineDesc has been set to disallow multiple arguments.
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void App::OnError(wxCommandEvent& event)
|
|
{
|
|
// We use this to display errors where throwing would cause problems, as in a destructor
|
|
// Instead we post an event to be handled in due course.
|
|
wxMessageDialog dlg(singletonFrame, event.GetString(), wsz_error, wxICON_ERROR);
|
|
dlg.SetId(myID_ERRORMESSAGE);
|
|
dlg.ShowModal();
|
|
}
|
|
|
|
int App::OnExit()
|
|
{ if (errorCode)wxLogDebug("%s", szError);
|
|
m_Config.Flush();
|
|
sqlite3_shutdown();
|
|
return 0;
|
|
}
|