diff options
Diffstat (limited to 'src/gui.cpp')
| -rw-r--r-- | src/gui.cpp | 155 |
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 | ||
| 173 | static void SetDefaultBackupPath() | 178 | static 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 | ||
| 1381 | static 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 | |||
| 1372 | static void DrawAboutDialog() | 1392 | static 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() | |||
| 1400 | static bool g_showBackendWizard = false; | 1431 | static bool g_showBackendWizard = false; |
| 1401 | static std::string g_wizardStatus; | 1432 | static std::string g_wizardStatus; |
| 1402 | static bool g_wizardConverted = false; | 1433 | static bool g_wizardConverted = false; |
| 1434 | static int g_restartArgc = 0; | ||
| 1435 | static 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. | ||
| 1441 | static 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. | ||
| 1464 | static 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 | ||
| 1404 | static void DrawBackendWizardDialog() | 1527 | static 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. |