upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/gui.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui.cpp')
-rw-r--r--src/gui.cpp155
1 files changed, 145 insertions, 10 deletions
diff --git a/src/gui.cpp b/src/gui.cpp
index 89a7ab6..2a7f350 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -11,6 +11,11 @@
11#include <ctime> 11#include <ctime>
12#include <cstdio> 12#include <cstdio>
13#include <sstream> 13#include <sstream>
14#ifdef _WIN32
15#include <shellapi.h> // ShellExecuteA, to open the website in the browser
16#else
17#include <unistd.h>
18#endif
14#include <iomanip> 19#include <iomanip>
15#include <atomic> 20#include <atomic>
16#include <mutex> 21#include <mutex>
@@ -172,7 +177,11 @@ static bool PathIsInsideDataDir(const std::string& strPath)
172 177
173static void SetDefaultBackupPath() 178static void SetDefaultBackupPath()
174{ 179{
175 std::string path = DefaultBackupDir() + "/wallet-backup-" + BackupTimeSuffix() + ".dat"; 180 // Match the extension to the live backend: BackupWallet copies wallet.sqlite
181 // under the SQLite backend and wallet.dat under Berkeley DB, so a ".dat"
182 // default name would put SQLite contents in a misleadingly-named file.
183 const char* pszExt = WalletSQLiteRuntimeActive() ? ".sqlite" : ".dat";
184 std::string path = DefaultBackupDir() + "/wallet-backup-" + BackupTimeSuffix() + pszExt;
176 strncpy(g_backupPath, path.c_str(), sizeof(g_backupPath)-1); 185 strncpy(g_backupPath, path.c_str(), sizeof(g_backupPath)-1);
177 g_backupPath[sizeof(g_backupPath)-1] = '\0'; 186 g_backupPath[sizeof(g_backupPath)-1] = '\0';
178} 187}
@@ -1254,11 +1263,11 @@ static void DrawWalletSafetyDialog()
1254 } 1263 }
1255 ImGui::Text("Phrase-backed spendable balance: %s BTF", 1264 ImGui::Text("Phrase-backed spendable balance: %s BTF",
1256 FmtMoney(g_recoveryAudit.nRecoverableCredit).c_str()); 1265 FmtMoney(g_recoveryAudit.nRecoverableCredit).c_str());
1257 ImGui::Text("Wallet.dat-only spendable balance: %s BTF", 1266 ImGui::Text("Not phrase-backed spendable balance: %s BTF",
1258 FmtMoney(g_recoveryAudit.nLegacyCredit).c_str()); 1267 FmtMoney(g_recoveryAudit.nLegacyCredit).c_str());
1259 ImGui::Text("Phrase-backed immature mining rewards: %s BTF", 1268 ImGui::Text("Phrase-backed immature mining rewards: %s BTF",
1260 FmtMoney(g_recoveryAudit.nRecoverableImmatureCredit).c_str()); 1269 FmtMoney(g_recoveryAudit.nRecoverableImmatureCredit).c_str());
1261 ImGui::Text("Wallet.dat-only immature mining rewards: %s BTF", 1270 ImGui::Text("Not phrase-backed immature mining rewards: %s BTF",
1262 FmtMoney(g_recoveryAudit.nLegacyImmatureCredit).c_str()); 1271 FmtMoney(g_recoveryAudit.nLegacyImmatureCredit).c_str());
1263 if (!g_recoveryAudit.fDeriveComplete) 1272 if (!g_recoveryAudit.fDeriveComplete)
1264 { 1273 {
@@ -1272,8 +1281,8 @@ static void DrawWalletSafetyDialog()
1272 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f)); 1281 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f));
1273 ImGui::TextWrapped( 1282 ImGui::TextWrapped(
1274 "Some coins are on keys the phrase does not reproduce. " 1283 "Some coins are on keys the phrase does not reproduce. "
1275 "Keep wallet.dat backups until that balance has been moved to a " 1284 "Keep a file backup of this wallet until that balance has been "
1276 "phrase-backed address."); 1285 "moved to a phrase-backed address.");
1277 ImGui::PopStyleColor(); 1286 ImGui::PopStyleColor();
1278 } 1287 }
1279 else if (HaveHDSeed() && 1288 else if (HaveHDSeed() &&
@@ -1369,16 +1378,38 @@ static void DrawDiagnosticsDialog()
1369 ImGui::End(); 1378 ImGui::End();
1370} 1379}
1371 1380
1381static void OpenURL(const char* pszUrl)
1382{
1383#ifdef _WIN32
1384 ShellExecuteA(NULL, "open", pszUrl, NULL, NULL, SW_SHOWNORMAL);
1385#else
1386 std::string cmd = std::string("xdg-open '") + pszUrl + "' >/dev/null 2>&1 &";
1387 int r = system(cmd.c_str());
1388 (void)r;
1389#endif
1390}
1391
1372static void DrawAboutDialog() 1392static void DrawAboutDialog()
1373{ 1393{
1374 if (!g_showAbout) return; 1394 if (!g_showAbout) return;
1375 ImGui::SetNextWindowSize(ImVec2(380.0f, 170.0f), ImGuiCond_Always); 1395 ImGui::SetNextWindowSize(ImVec2(380.0f, 205.0f), ImGuiCond_Always);
1376 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), 1396 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1377 ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 1397 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
1378 if (ImGui::Begin("About Bitflash", &g_showAbout, 1398 if (ImGui::Begin("About Bitflash", &g_showAbout,
1379 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) 1399 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse))
1380 { 1400 {
1381 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Bitflash BTF v1.1.0"); 1401 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f),
1402 "Bitflash BTF v" BITFLASH_VERSION_STRING);
1403 ImGui::Spacing();
1404 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.40f, 0.70f, 1.0f, 1.0f));
1405 ImGui::TextUnformatted("https://bitflash.network");
1406 ImGui::PopStyleColor();
1407 if (ImGui::IsItemHovered())
1408 {
1409 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
1410 if (ImGui::IsMouseClicked(0))
1411 OpenURL("https://bitflash.network");
1412 }
1382 ImGui::Spacing(); 1413 ImGui::Spacing();
1383 ImGui::TextWrapped( 1414 ImGui::TextWrapped(
1384 "CPU-only cryptocurrency. RandomX proof of work. " 1415 "CPU-only cryptocurrency. RandomX proof of work. "
@@ -1400,6 +1431,98 @@ static void DrawAboutDialog()
1400static bool g_showBackendWizard = false; 1431static bool g_showBackendWizard = false;
1401static std::string g_wizardStatus; 1432static std::string g_wizardStatus;
1402static bool g_wizardConverted = false; 1433static bool g_wizardConverted = false;
1434static int g_restartArgc = 0;
1435static char** g_restartArgv = NULL;
1436
1437#ifdef _WIN32
1438// Quote one argument so CommandLineToArgvW parses it back to exactly this
1439// string: only backslashes that immediately precede a double quote (including
1440// the closing one) are doubled, so ordinary Windows paths are not mangled.
1441static std::string QuoteArgWin(const std::string& a)
1442{
1443 if (!a.empty() && a.find_first_of(" \t\"") == std::string::npos)
1444 return a;
1445 std::string r = "\"";
1446 size_t bs = 0;
1447 for (size_t i = 0; i < a.size(); i++)
1448 {
1449 if (a[i] == '\\') { bs++; continue; }
1450 if (a[i] == '"') { r.append(bs * 2 + 1, '\\'); r += '"'; bs = 0; continue; }
1451 r.append(bs, '\\'); bs = 0; r += a[i];
1452 }
1453 r.append(bs * 2, '\\');
1454 r += "\"";
1455 return r;
1456}
1457#endif
1458
1459// Relaunch Bitflash with the same arguments and datadir, then ask this instance
1460// to exit -- but only once the relaunch has actually started, so a failure never
1461// leaves the user with no node running. The new instance waits (-restartwait)
1462// before it opens the network port and the block index, so this one is gone
1463// first and they never fight over the same files.
1464static void RestartApplication()
1465{
1466 bool fLaunched = false;
1467#ifdef _WIN32
1468 char exePath[MAX_PATH];
1469 if (GetModuleFileNameA(NULL, exePath, MAX_PATH) == 0)
1470 return;
1471 std::string strCmd = QuoteArgWin(exePath);
1472 bool fHasDataDir = false;
1473 for (int i = 1; i < g_restartArgc; i++)
1474 {
1475 std::string a = g_restartArgv[i] ? g_restartArgv[i] : "";
1476 if (a.find("-datadir=") == 0 || a.find("/datadir=") == 0)
1477 fHasDataDir = true;
1478 strCmd += " " + QuoteArgWin(a);
1479 }
1480 if (!fHasDataDir)
1481 strCmd += " " + QuoteArgWin("-datadir=" + GetAppDir());
1482 strCmd += " -restartwait";
1483
1484 std::vector<char> cmdBuf(strCmd.begin(), strCmd.end());
1485 cmdBuf.push_back('\0');
1486 STARTUPINFOA si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
1487 PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi));
1488 if (CreateProcessA(NULL, &cmdBuf[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
1489 {
1490 CloseHandle(pi.hProcess);
1491 CloseHandle(pi.hThread);
1492 fLaunched = true;
1493 }
1494#else
1495 pid_t pid = fork();
1496 if (pid == 0)
1497 {
1498 // Child: wait for the parent to release the port and files, then re-exec.
1499 // /proc/self/exe is the reliable absolute path; argv[0] may be relative
1500 // or a bare name that execv() cannot resolve.
1501 sleep(2);
1502 char exePath[4096];
1503 ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
1504 if (len > 0)
1505 {
1506 exePath[len] = '\0';
1507 execv(exePath, g_restartArgv);
1508 }
1509 if (g_restartArgv)
1510 execv(g_restartArgv[0], g_restartArgv);
1511 _exit(127);
1512 }
1513 fLaunched = (pid > 0);
1514#endif
1515
1516 if (fLaunched)
1517 {
1518 fShutdown = true;
1519 if (glfwGetCurrentContext())
1520 glfwSetWindowShouldClose(glfwGetCurrentContext(), true);
1521 }
1522 else
1523 g_wizardStatus = "Could not relaunch automatically -- please restart "
1524 "Bitflash yourself to switch to the SQLite wallet.";
1525}
1403 1526
1404static void DrawBackendWizardDialog() 1527static void DrawBackendWizardDialog()
1405{ 1528{
@@ -1416,11 +1539,19 @@ static void DrawBackendWizardDialog()
1416 ImGui::TextWrapped("%s", g_wizardStatus.c_str()); 1539 ImGui::TextWrapped("%s", g_wizardStatus.c_str());
1417 ImGui::Spacing(); 1540 ImGui::Spacing();
1418 ImGui::TextWrapped( 1541 ImGui::TextWrapped(
1419 "Restart Bitflash to start using the SQLite wallet. Your Berkeley " 1542 "The switch takes effect when Bitflash restarts. Until then it is "
1420 "DB wallet.dat is kept untouched as a fallback."); 1543 "still running on the old Berkeley DB wallet. Your wallet.dat is "
1544 "kept untouched as a fallback.");
1421 ImGui::Spacing(); 1545 ImGui::Spacing();
1422 if (ImGui::Button("Close", ImVec2(120.0f, 0.0f))) 1546 if (ImGui::Button("Restart Now", ImVec2(140.0f, 0.0f)))
1547 RestartApplication();
1548 ImGui::SameLine();
1549 if (ImGui::Button("Later", ImVec2(120.0f, 0.0f)))
1423 g_showBackendWizard = false; 1550 g_showBackendWizard = false;
1551 ImGui::Spacing();
1552 ImGui::TextDisabled(
1553 "If you keep mining before restarting, new coins go to the old "
1554 "wallet until you switch.");
1424 } 1555 }
1425 else 1556 else
1426 { 1557 {
@@ -1535,6 +1666,10 @@ int RunGUI(int argc, char* argv[])
1535 g_mineRadio = nMineMode; 1666 g_mineRadio = nMineMode;
1536 strncpy(g_participantPool, strParticipantPool.c_str(), sizeof(g_participantPool)-1); 1667 strncpy(g_participantPool, strParticipantPool.c_str(), sizeof(g_participantPool)-1);
1537 1668
1669 // Kept for the wizard's "Restart Now": relaunch with the same command line.
1670 g_restartArgc = argc;
1671 g_restartArgv = argv;
1672
1538 // First run on a Berkeley DB wallet with no recorded choice yet: offer to 1673 // First run on a Berkeley DB wallet with no recorded choice yet: offer to
1539 // convert to SQLite. Never when already on SQLite, once wallet.sqlite exists, 1674 // convert to SQLite. Never when already on SQLite, once wallet.sqlite exists,
1540 // or once a choice has been recorded in the backend marker. 1675 // or once a choice has been recorded in the backend marker.