diff options
Diffstat (limited to 'src/gui.cpp')
| -rw-r--r-- | src/gui.cpp | 701 |
1 files changed, 424 insertions, 277 deletions
diff --git a/src/gui.cpp b/src/gui.cpp index 2210296..a1a0698 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | // Bitflash GUI — ImGui + GLFW + OpenGL3 | 1 | // Bitflash GUI -- ImGui + GLFW + OpenGL3 |
| 2 | // Same source on Linux and Windows. No platform ifdefs. | 2 | // Same source on Linux and Windows. No platform ifdefs. |
| 3 | 3 | ||
| 4 | #include "imgui/imgui.h" | 4 | #include "imgui/imgui.h" |
| @@ -25,6 +25,7 @@ extern std::string strParticipantPool; | |||
| 25 | extern CAddress addrLocalHost; | 25 | extern CAddress addrLocalHost; |
| 26 | 26 | ||
| 27 | void MainFrameRepaint(); | 27 | void MainFrameRepaint(); |
| 28 | int GetDiscoveredPeerCount(); | ||
| 28 | string DateTimeStr(int64 nTime); | 29 | string DateTimeStr(int64 nTime); |
| 29 | bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew); | 30 | bool SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew); |
| 30 | int64 GetBalance(); | 31 | int64 GetBalance(); |
| @@ -34,15 +35,20 @@ void ThreadBitcoinMiner(void*); | |||
| 34 | void ThreadRPCServer(void*); | 35 | void ThreadRPCServer(void*); |
| 35 | 36 | ||
| 36 | // --------------------------------------------------------------------------- | 37 | // --------------------------------------------------------------------------- |
| 37 | // State | 38 | // Layout constants |
| 39 | // --------------------------------------------------------------------------- | ||
| 40 | static const float STATUS_BAR_H = 22.0f; | ||
| 41 | |||
| 42 | // --------------------------------------------------------------------------- | ||
| 43 | // GUI state | ||
| 38 | // --------------------------------------------------------------------------- | 44 | // --------------------------------------------------------------------------- |
| 39 | static std::string g_myAddress; | 45 | static std::string g_myAddress; |
| 40 | static int64 g_balance = 0; | 46 | static int64 g_balance = 0; |
| 41 | static int64 g_lastWalletRefresh = 0; | 47 | static int64 g_lastWalletRefresh = 0; |
| 42 | static bool g_showSend = false; | 48 | static bool g_showSend = false; |
| 43 | static bool g_showOptions = false; | 49 | static bool g_showOptions = false; |
| 44 | static bool g_showAbout = false; | 50 | static bool g_showAbout = false; |
| 45 | static bool g_needRefresh = true; | 51 | static bool g_needRefresh = true; |
| 46 | 52 | ||
| 47 | static char g_sendAddr[128] = {}; | 53 | static char g_sendAddr[128] = {}; |
| 48 | static char g_sendAmount[32] = {}; | 54 | static char g_sendAmount[32] = {}; |
| @@ -73,36 +79,34 @@ static std::string FmtMoney(int64 n) | |||
| 73 | static std::string FmtAge(int64 createdAt) | 79 | static std::string FmtAge(int64 createdAt) |
| 74 | { | 80 | { |
| 75 | int64 age = GetTime() - createdAt; | 81 | int64 age = GetTime() - createdAt; |
| 76 | if (age < 0) | 82 | if (age < 0) age = 0; |
| 77 | age = 0; | 83 | if (age < 60) return strprintf("%llds", (long long)age); |
| 78 | if (age < 60) | 84 | if (age < 3600) return strprintf("%lldm", (long long)(age / 60)); |
| 79 | return strprintf("%llds", (long long)age); | 85 | return strprintf("%lldh", (long long)(age / 3600)); |
| 80 | if (age < 3600) | ||
| 81 | return strprintf("%lldm", (long long)(age / 60)); | ||
| 82 | return strprintf("%lldh", (long long)(age / 3600)); | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | static ImVec4 ParticipantStatusColor(const std::string& status) | 88 | static ImVec4 StatusColor(const std::string& s) |
| 86 | { | 89 | { |
| 87 | if (status.find("failed") != std::string::npos || | 90 | if (s.find("failed") != std::string::npos || |
| 88 | status.find("rejected") != std::string::npos || | 91 | s.find("rejected") != std::string::npos || |
| 89 | status.find("stopped") != std::string::npos) | 92 | s.find("stopped") != std::string::npos) |
| 90 | return ImVec4(1.0f, 0.38f, 0.38f, 1.0f); | 93 | return ImVec4(1.0f, 0.38f, 0.38f, 1.0f); // red |
| 91 | if (status.find("hashing") != std::string::npos || | 94 | if (s.find("hashing") != std::string::npos || |
| 92 | status.find("accepted") != std::string::npos || | 95 | s.find("accepted") != std::string::npos || |
| 93 | status.find("connected") != std::string::npos) | 96 | s.find("connected")!= std::string::npos) |
| 94 | return ImVec4(0.35f, 1.0f, 0.45f, 1.0f); | 97 | return ImVec4(0.35f, 1.0f, 0.45f, 1.0f); // green |
| 95 | if (status.find("asking") != std::string::npos || | 98 | if (s.find("asking") != std::string::npos || |
| 96 | status.find("authoriz") != std::string::npos || | 99 | s.find("authoriz") != std::string::npos || |
| 97 | status.find("subscrib") != std::string::npos) | 100 | s.find("subscrib") != std::string::npos) |
| 98 | return ImVec4(1.0f, 0.75f, 0.25f, 1.0f); | 101 | return ImVec4(1.0f, 0.75f, 0.25f, 1.0f); // amber |
| 99 | return ImVec4(0.55f, 0.80f, 1.0f, 1.0f); | 102 | return ImVec4(0.55f, 0.80f, 1.0f, 1.0f); // blue-grey |
| 100 | } | 103 | } |
| 101 | 104 | ||
| 102 | static std::string PoolLabel(const BtfPoolAnnouncement& ann) | 105 | static std::string PoolLabel(const BtfPoolAnnouncement& ann) |
| 103 | { | 106 | { |
| 104 | std::ostringstream o; | 107 | std::ostringstream o; |
| 105 | o << ann.poolName << " - " << std::fixed << std::setprecision(2) << ann.feePercent << "%"; | 108 | o << ann.poolName << " - " |
| 109 | << std::fixed << std::setprecision(2) << ann.feePercent << "%"; | ||
| 106 | if (ann.connectedMiners > 0) | 110 | if (ann.connectedMiners > 0) |
| 107 | o << " - " << ann.connectedMiners << " miners"; | 111 | o << " - " << ann.connectedMiners << " miners"; |
| 108 | return o.str(); | 112 | return o.str(); |
| @@ -118,19 +122,47 @@ static void RefreshWallet() | |||
| 118 | g_txRows.clear(); | 122 | g_txRows.clear(); |
| 119 | TRY_CRITICAL_BLOCK(cs_mapWallet) | 123 | TRY_CRITICAL_BLOCK(cs_mapWallet) |
| 120 | { | 124 | { |
| 121 | std::vector<std::pair<int64,uint256>> vs; | 125 | // Sort: unconfirmed first (depth 0), then by depth ascending (least |
| 122 | for (auto& kv : mapWallet) vs.push_back({kv.second.GetTxTime(), kv.first}); | 126 | // deep = most recent). We can't use nTimeReceived because a node |
| 123 | std::sort(vs.rbegin(), vs.rend()); | 127 | // resync re-stamps all txs with the same time. Depth is always correct. |
| 128 | std::vector<std::pair<int,uint256>> vs; | ||
| 129 | for (auto& kv : mapWallet) | ||
| 130 | vs.push_back({kv.second.GetDepthInMainChain(), kv.second.GetHash()}); | ||
| 131 | std::sort(vs.begin(), vs.end(), | ||
| 132 | [](const std::pair<int,uint256>& a, const std::pair<int,uint256>& b) { | ||
| 133 | return a.first < b.first; // 0 (unconfirmed) first, then shallowest | ||
| 134 | }); | ||
| 124 | for (auto& sv : vs) { | 135 | for (auto& sv : vs) { |
| 125 | auto mi = mapWallet.find(sv.second); | 136 | auto mi = mapWallet.find(sv.second); |
| 126 | if (mi == mapWallet.end()) continue; | 137 | if (mi == mapWallet.end()) continue; |
| 127 | CWalletTx& wtx = mi->second; | 138 | CWalletTx& wtx = mi->second; |
| 128 | int64 net = wtx.GetCredit() - wtx.GetDebit(); | 139 | int depth = wtx.GetDepthInMainChain(); |
| 129 | int depth = wtx.GetDepthInMainChain(); | 140 | int toMature = wtx.IsCoinBase() ? wtx.GetBlocksToMaturity() : 0; |
| 141 | // An orphaned coinbase has hashBlock set but depth=0 (block not in main chain). | ||
| 142 | // A genuinely unconfirmed tx has hashBlock==0. | ||
| 143 | bool isOrphaned = wtx.IsCoinBase() && wtx.hashBlock != 0 && depth == 0; | ||
| 144 | int64 net; | ||
| 130 | std::string desc; | 145 | std::string desc; |
| 131 | if (wtx.IsCoinBase()) desc = depth<1 ? "Generated (unconfirmed)" : "Generated"; | 146 | if (wtx.IsCoinBase()) { |
| 132 | else if (wtx.GetDebit() > 0) desc = "Sent"; | 147 | if (isOrphaned) { |
| 133 | else desc = "Received"; | 148 | net = 0; |
| 149 | desc = "Generated (orphaned -- block not in main chain)"; | ||
| 150 | } else if (toMature > 0) { | ||
| 151 | net = wtx.GetValueOut(); | ||
| 152 | char buf[64]; | ||
| 153 | snprintf(buf, sizeof(buf), "Generated (matures in %d blocks)", toMature); | ||
| 154 | desc = buf; | ||
| 155 | } else { | ||
| 156 | net = wtx.GetCredit() - wtx.GetDebit(); | ||
| 157 | desc = depth < 1 ? "Generated (unconfirmed)" : "Generated"; | ||
| 158 | } | ||
| 159 | } else if (wtx.GetDebit() > 0) { | ||
| 160 | net = wtx.GetCredit() - wtx.GetDebit(); | ||
| 161 | desc = "Sent"; | ||
| 162 | } else { | ||
| 163 | net = wtx.GetCredit() - wtx.GetDebit(); | ||
| 164 | desc = "Received"; | ||
| 165 | } | ||
| 134 | g_txRows.push_back({DateTimeStr(wtx.GetTxTime()), desc, net, depth}); | 166 | g_txRows.push_back({DateTimeStr(wtx.GetTxTime()), desc, net, depth}); |
| 135 | } | 167 | } |
| 136 | } | 168 | } |
| @@ -149,18 +181,103 @@ string DateTimeStr(int64 nTime) | |||
| 149 | } | 181 | } |
| 150 | 182 | ||
| 151 | // --------------------------------------------------------------------------- | 183 | // --------------------------------------------------------------------------- |
| 184 | // Status bar -- separate fullscreen-width overlay pinned to bottom edge. | ||
| 185 | // Drawn last so it always sits on top of everything else. | ||
| 186 | // --------------------------------------------------------------------------- | ||
| 187 | static void DrawStatusBar() | ||
| 188 | { | ||
| 189 | ImGuiIO& io = ImGui::GetIO(); | ||
| 190 | ImGui::SetNextWindowPos(ImVec2(0.0f, io.DisplaySize.y - STATUS_BAR_H)); | ||
| 191 | ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, STATUS_BAR_H)); | ||
| 192 | ImGui::SetNextWindowBgAlpha(1.0f); | ||
| 193 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 3.0f)); | ||
| 194 | ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); | ||
| 195 | ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); | ||
| 196 | ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.13f, 0.13f, 0.13f, 1.0f)); | ||
| 197 | ImGui::Begin("##statusbar", nullptr, | ||
| 198 | ImGuiWindowFlags_NoTitleBar | | ||
| 199 | ImGuiWindowFlags_NoResize | | ||
| 200 | ImGuiWindowFlags_NoMove | | ||
| 201 | ImGuiWindowFlags_NoScrollbar | | ||
| 202 | ImGuiWindowFlags_NoScrollWithMouse| | ||
| 203 | ImGuiWindowFlags_NoSavedSettings | | ||
| 204 | ImGuiWindowFlags_NoBringToFrontOnFocus); | ||
| 205 | ImGui::PopStyleVar(3); | ||
| 206 | ImGui::PopStyleColor(); | ||
| 207 | |||
| 208 | int connected = (int)vNodes.size(); | ||
| 209 | int discovered = GetDiscoveredPeerCount(); | ||
| 210 | |||
| 211 | if (nMineMode == MINE_PARTICIPANT) { | ||
| 212 | uint64 sent = 0, accepted = 0; | ||
| 213 | double hashRate = 0.0; | ||
| 214 | std::string st = GetParticipantMiningStatus(); | ||
| 215 | GetParticipantMiningStats(sent, accepted, hashRate); | ||
| 216 | if (sent > 0 || accepted > 0 || hashRate > 0.0) | ||
| 217 | ImGui::TextDisabled( | ||
| 218 | "Connected peers: %d Detected peers: %d Height: %d" | ||
| 219 | " Shares: %llu sent / %llu accepted %.2f H/s", | ||
| 220 | connected, discovered, nBestHeight, | ||
| 221 | (unsigned long long)sent, (unsigned long long)accepted, hashRate); | ||
| 222 | else if (!st.empty()) | ||
| 223 | ImGui::TextDisabled( | ||
| 224 | "Connected peers: %d Detected peers: %d Height: %d %s", | ||
| 225 | connected, discovered, nBestHeight, st.c_str()); | ||
| 226 | else | ||
| 227 | ImGui::TextDisabled( | ||
| 228 | "Connected peers: %d Detected peers: %d Height: %d", | ||
| 229 | connected, discovered, nBestHeight); | ||
| 230 | } else if (nMineMode == MINE_OPERATOR) { | ||
| 231 | int miners = 0, blocksFound = 0; | ||
| 232 | uint64 roundShares = 0; | ||
| 233 | double totalHashRate = 0.0; | ||
| 234 | GetPoolOperatorStats(miners, blocksFound, roundShares, totalHashRate); | ||
| 235 | ImGui::TextDisabled( | ||
| 236 | "Connected peers: %d Detected peers: %d Height: %d" | ||
| 237 | " Miners: %d Round shares: %llu Blocks found: %d %.2f H/s", | ||
| 238 | connected, discovered, nBestHeight, | ||
| 239 | miners, (unsigned long long)roundShares, blocksFound, totalHashRate); | ||
| 240 | } else { | ||
| 241 | ImGui::TextDisabled( | ||
| 242 | "Connected peers: %d Detected peers: %d Height: %d", | ||
| 243 | connected, discovered, nBestHeight); | ||
| 244 | } | ||
| 245 | |||
| 246 | ImGui::End(); | ||
| 247 | } | ||
| 248 | |||
| 249 | // --------------------------------------------------------------------------- | ||
| 152 | // Main window | 250 | // Main window |
| 251 | // | ||
| 252 | // Layout: | ||
| 253 | // [menu bar] -- ImGui built-in menu | ||
| 254 | // [header strip] -- address, balance, send button, mine status | ||
| 255 | // [separator] | ||
| 256 | // [telemetry strip] -- mode-specific live stats (never scrolls) | ||
| 257 | // [separator] | ||
| 258 | // [scrollable content child] -- transactions + mode panels | ||
| 259 | // | ||
| 260 | // The scrollable child ends at the top of the status bar so content never | ||
| 261 | // hides behind it. The child is explicitly sized to fill the remaining space. | ||
| 153 | // --------------------------------------------------------------------------- | 262 | // --------------------------------------------------------------------------- |
| 154 | static void DrawMainWindow() | 263 | static void DrawMainWindow() |
| 155 | { | 264 | { |
| 156 | ImGuiIO& io = ImGui::GetIO(); | 265 | ImGuiIO& io = ImGui::GetIO(); |
| 157 | ImGui::SetNextWindowPos(ImVec2(0,0)); | 266 | |
| 158 | ImGui::SetNextWindowSize(io.DisplaySize); | 267 | // Full display height minus the status bar at the bottom. |
| 159 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12,12)); | 268 | const float winH = io.DisplaySize.y - STATUS_BAR_H; |
| 269 | |||
| 270 | ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); | ||
| 271 | ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, winH)); | ||
| 272 | ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12.0f, 12.0f)); | ||
| 160 | ImGui::Begin("##main", nullptr, | 273 | ImGui::Begin("##main", nullptr, |
| 161 | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | | 274 | ImGuiWindowFlags_NoTitleBar | |
| 162 | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_MenuBar | | 275 | ImGuiWindowFlags_NoResize | |
| 163 | ImGuiWindowFlags_NoFocusOnAppearing); | 276 | ImGuiWindowFlags_NoMove | |
| 277 | ImGuiWindowFlags_NoScrollbar | | ||
| 278 | ImGuiWindowFlags_NoScrollWithMouse | | ||
| 279 | ImGuiWindowFlags_MenuBar | | ||
| 280 | ImGuiWindowFlags_NoBringToFrontOnFocus); | ||
| 164 | ImGui::SetWindowFontScale(0.92f); | 281 | ImGui::SetWindowFontScale(0.92f); |
| 165 | ImGui::PopStyleVar(); | 282 | ImGui::PopStyleVar(); |
| 166 | 283 | ||
| @@ -170,179 +287,209 @@ static void DrawMainWindow() | |||
| 170 | if (ImGui::MenuItem("Exit")) glfwSetWindowShouldClose(glfwGetCurrentContext(), true); | 287 | if (ImGui::MenuItem("Exit")) glfwSetWindowShouldClose(glfwGetCurrentContext(), true); |
| 171 | ImGui::EndMenu(); | 288 | ImGui::EndMenu(); |
| 172 | } | 289 | } |
| 173 | if (ImGui::MenuItem("Options")) g_showOptions = true; | 290 | if (ImGui::MenuItem("Options")) g_showOptions = true; |
| 174 | if (ImGui::MenuItem("About")) g_showAbout = true; | 291 | if (ImGui::MenuItem("About")) g_showAbout = true; |
| 175 | ImGui::EndMenuBar(); | 292 | ImGui::EndMenuBar(); |
| 176 | } | 293 | } |
| 177 | 294 | ||
| 178 | // ---- Address bar ---- | 295 | // ---- Header strip (never scrolls) ---- |
| 179 | ImGui::Spacing(); | 296 | ImGui::Spacing(); |
| 180 | ImGui::TextDisabled("Address:"); | 297 | ImGui::TextDisabled("Address:"); |
| 181 | ImGui::SameLine(); | 298 | ImGui::SameLine(); |
| 182 | ImGui::SetNextItemWidth(-80); | 299 | ImGui::SetNextItemWidth(-80.0f); |
| 183 | ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f,0.14f,0.14f,1)); | 300 | ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 1.0f)); |
| 184 | ImGui::InputText("##addr", (char*)g_myAddress.c_str(), g_myAddress.size()+1, | 301 | ImGui::InputText("##addr", (char*)g_myAddress.c_str(), g_myAddress.size()+1, |
| 185 | ImGuiInputTextFlags_ReadOnly); | 302 | ImGuiInputTextFlags_ReadOnly); |
| 186 | ImGui::PopStyleColor(); | 303 | ImGui::PopStyleColor(); |
| 187 | ImGui::SameLine(); | 304 | ImGui::SameLine(); |
| 188 | if (ImGui::Button("Copy##a")) ImGui::SetClipboardText(g_myAddress.c_str()); | 305 | if (ImGui::Button("Copy##a")) ImGui::SetClipboardText(g_myAddress.c_str()); |
| 189 | 306 | ||
| 190 | // ---- Balance on its own line ---- | ||
| 191 | ImGui::TextDisabled("Balance:"); | 307 | ImGui::TextDisabled("Balance:"); |
| 192 | ImGui::SameLine(); | 308 | ImGui::SameLine(); |
| 193 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.35f,1.0f,0.35f,1.0f)); | 309 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.35f, 1.0f, 0.35f, 1.0f)); |
| 194 | ImGui::Text("%s BTF", FmtMoney(g_balance).c_str()); | 310 | ImGui::Text("%s BTF", FmtMoney(g_balance).c_str()); |
| 195 | ImGui::PopStyleColor(); | 311 | ImGui::PopStyleColor(); |
| 196 | 312 | ||
| 197 | // ---- Toolbar ---- | ||
| 198 | ImGui::Spacing(); | 313 | ImGui::Spacing(); |
| 199 | ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f,0.40f,0.15f,1)); | 314 | ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.40f, 0.15f, 1.0f)); |
| 200 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20f,0.55f,0.20f,1)); | 315 | ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.20f, 0.55f, 0.20f, 1.0f)); |
| 201 | if (ImGui::Button(" Send Coins ")) g_showSend = true; | 316 | if (ImGui::Button(" Send Coins ")) g_showSend = true; |
| 202 | ImGui::PopStyleColor(2); | 317 | ImGui::PopStyleColor(2); |
| 203 | 318 | ||
| 204 | // Mining status indicator | 319 | ImGui::SameLine(0.0f, 20.0f); |
| 205 | ImGui::SameLine(0, 20); | ||
| 206 | if (fGenerateBitcoins) { | 320 | if (fGenerateBitcoins) { |
| 207 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f,0.6f,0.0f,1.0f)); | 321 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.6f, 0.0f, 1.0f)); |
| 208 | const char* modeLabel = nMineMode==MINE_OPERATOR ? "Mining (operator)" : | 322 | const char* modeLabel = |
| 209 | nMineMode==MINE_PARTICIPANT ? "Mining (pool)" : "Mining"; | 323 | nMineMode == MINE_OPERATOR ? "Mining (operator)" : |
| 210 | ImGui::Text("⬤ %s", modeLabel); | 324 | nMineMode == MINE_PARTICIPANT ? "Mining (pool)" : |
| 325 | nMineMode == MINE_RELAY ? "Relay" : "Mining"; | ||
| 326 | ImGui::Text("* %s", modeLabel); | ||
| 211 | ImGui::PopStyleColor(); | 327 | ImGui::PopStyleColor(); |
| 212 | if (nMineMode == MINE_PARTICIPANT) { | 328 | if (nMineMode == MINE_PARTICIPANT) { |
| 213 | std::string mineStatus = GetParticipantMiningStatus(); | 329 | std::string st = GetParticipantMiningStatus(); |
| 214 | if (!mineStatus.empty()) { | 330 | if (!st.empty()) { |
| 215 | ImGui::SameLine(0, 12); | 331 | ImGui::SameLine(0.0f, 12.0f); |
| 216 | ImGui::PushStyleColor(ImGuiCol_Text, ParticipantStatusColor(mineStatus)); | 332 | ImGui::PushStyleColor(ImGuiCol_Text, StatusColor(st)); |
| 217 | ImGui::Text("[%s]", mineStatus.c_str()); | 333 | ImGui::Text("[%s]", st.c_str()); |
| 218 | ImGui::PopStyleColor(); | 334 | ImGui::PopStyleColor(); |
| 219 | } | 335 | } |
| 220 | } | 336 | } |
| 221 | } else { | 337 | } else { |
| 222 | ImGui::TextDisabled("○ Not mining"); | 338 | ImGui::TextDisabled("Not mining"); |
| 223 | } | 339 | } |
| 224 | 340 | ||
| 225 | ImGui::Spacing(); | 341 | ImGui::Spacing(); |
| 226 | ImGui::Separator(); | 342 | ImGui::Separator(); |
| 227 | ImGui::Spacing(); | ||
| 228 | 343 | ||
| 229 | // ---- Live telemetry strip ---- | 344 | // ---- Telemetry strip (never scrolls) ---- |
| 230 | { | 345 | { |
| 231 | const char* modeName = nMineMode==MINE_OPERATOR ? "Operator" : | 346 | ImGui::Spacing(); |
| 232 | nMineMode==MINE_PARTICIPANT ? "Participant" : "Solo"; | 347 | const char* modeName = |
| 233 | int64 age = g_lastWalletRefresh > 0 ? (GetTime() - g_lastWalletRefresh) : -1; | 348 | nMineMode == MINE_OPERATOR ? "Operator" : |
| 234 | if (age < 0) age = 0; | 349 | nMineMode == MINE_PARTICIPANT ? "Participant" : |
| 350 | nMineMode == MINE_RELAY ? "Relay" : "Solo"; | ||
| 351 | int64 age = g_lastWalletRefresh > 0 ? GetTime() - g_lastWalletRefresh : 0; | ||
| 235 | 352 | ||
| 236 | ImGui::Text("Mode: %s", modeName); | 353 | ImGui::Text("Mode: %s", modeName); |
| 237 | ImGui::SameLine(0, 16); | 354 | ImGui::SameLine(0.0f, 16.0f); |
| 238 | ImGui::Text("Wallet refresh: %llds ago", (long long)age); | 355 | ImGui::TextDisabled("Wallet refreshed %llds ago", (long long)age); |
| 239 | 356 | ||
| 240 | if (nMineMode == MINE_PARTICIPANT) { | 357 | if (nMineMode == MINE_PARTICIPANT) { |
| 241 | uint64 sent = 0, accepted = 0; | 358 | uint64 sent = 0, accepted = 0; |
| 242 | double hashRate = 0.0; | 359 | double hashRate = 0.0; |
| 243 | std::string mineStatus = GetParticipantMiningStatus(); | 360 | std::string st = GetParticipantMiningStatus(); |
| 244 | GetParticipantMiningStats(sent, accepted, hashRate); | 361 | GetParticipantMiningStats(sent, accepted, hashRate); |
| 245 | if (sent > 0 || accepted > 0 || hashRate > 0.0) { | 362 | if (sent > 0 || accepted > 0 || hashRate > 0.0) { |
| 246 | double acceptPct = sent ? (100.0 * (double)accepted / (double)sent) : 0.0; | 363 | double pct = sent ? (100.0 * (double)accepted / (double)sent) : 0.0; |
| 247 | ImGui::TextDisabled("Your mining: sent %llu, accepted %llu (%.1f%%), %.2f H/s", | 364 | ImGui::TextDisabled( |
| 248 | (unsigned long long)sent, | 365 | "Sent %llu shares, accepted %llu (%.1f%%), %.2f H/s", |
| 249 | (unsigned long long)accepted, | 366 | (unsigned long long)sent, (unsigned long long)accepted, pct, hashRate); |
| 250 | acceptPct, | 367 | } else if (!st.empty()) { |
| 251 | hashRate); | 368 | ImGui::TextDisabled("Status: %s", st.c_str()); |
| 252 | } else if (!mineStatus.empty()) { | ||
| 253 | ImGui::TextDisabled("Your mining: %s", mineStatus.c_str()); | ||
| 254 | } else { | 369 | } else { |
| 255 | ImGui::TextDisabled("Your mining: asking pool for work"); | 370 | ImGui::TextDisabled("Waiting for pool work..."); |
| 256 | } | 371 | } |
| 257 | } else if (nMineMode == MINE_OPERATOR) { | 372 | } else if (nMineMode == MINE_OPERATOR) { |
| 258 | int miners = 0, blocksFound = 0; | 373 | int miners = 0, blocksFound = 0; |
| 259 | uint64 roundShares = 0; | 374 | uint64 roundShares = 0; |
| 260 | GetPoolOperatorStats(miners, blocksFound, roundShares); | 375 | double totalHashRate = 0.0; |
| 376 | GetPoolOperatorStats(miners, blocksFound, roundShares, totalHashRate); | ||
| 261 | if (miners > 0 || blocksFound > 0 || roundShares > 0) | 377 | if (miners > 0 || blocksFound > 0 || roundShares > 0) |
| 262 | ImGui::TextDisabled("Pool runtime: %d authorized miners, %llu round shares, %d blocks found", miners, (unsigned long long)roundShares, blocksFound); | 378 | ImGui::TextDisabled( |
| 379 | "Authorized miners: %d Round shares: %llu Blocks found: %d %.2f H/s", | ||
| 380 | miners, (unsigned long long)roundShares, blocksFound, totalHashRate); | ||
| 263 | else | 381 | else |
| 264 | ImGui::TextDisabled("Pool runtime: waiting for miners"); | 382 | ImGui::TextDisabled("Pool server running, waiting for miners"); |
| 383 | } else if (nMineMode == MINE_RELAY) { | ||
| 384 | ImGui::TextDisabled("Relay mode: syncing, not mining"); | ||
| 265 | } else { | 385 | } else { |
| 266 | ImGui::TextDisabled("Solo runtime: local wallet mining telemetry"); | 386 | ImGui::TextDisabled("Solo mining"); |
| 267 | } | 387 | } |
| 268 | ImGui::Spacing(); | 388 | ImGui::Spacing(); |
| 269 | ImGui::Separator(); | 389 | ImGui::Separator(); |
| 270 | ImGui::Spacing(); | 390 | ImGui::Spacing(); |
| 271 | } | 391 | } |
| 272 | 392 | ||
| 393 | // ---- Scrollable content child ---- | ||
| 394 | // Fills all remaining height in the window (cursor Y to bottom of window). | ||
| 395 | // Content that overflows scrolls; nothing leaks past the window edge. | ||
| 396 | // Size the scrollable child to fill exactly the remaining window height. | ||
| 397 | // GetContentRegionAvail() already accounts for padding/borders. | ||
| 398 | const float availH = ImGui::GetContentRegionAvail().y; | ||
| 399 | ImGui::BeginChild("##scroll", ImVec2(0.0f, availH), false, 0); | ||
| 400 | |||
| 273 | // ---- Transaction list ---- | 401 | // ---- Transaction list ---- |
| 274 | float listHeight = io.DisplaySize.y * 0.26f; | 402 | float txH = io.DisplaySize.y * 0.26f; |
| 275 | if (listHeight < 190.0f) listHeight = 190.0f; | 403 | if (txH < 180.0f) txH = 180.0f; |
| 276 | if (listHeight > 260.0f) listHeight = 260.0f; | 404 | if (txH > 260.0f) txH = 260.0f; |
| 405 | |||
| 277 | if (ImGui::BeginTable("txlist", 4, | 406 | if (ImGui::BeginTable("txlist", 4, |
| 278 | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | | 407 | ImGuiTableFlags_Borders | |
| 279 | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingStretchProp, | 408 | ImGuiTableFlags_RowBg | |
| 280 | ImVec2(0, listHeight))) | 409 | ImGuiTableFlags_ScrollY | |
| 410 | ImGuiTableFlags_SizingStretchProp, | ||
| 411 | ImVec2(0.0f, txH))) | ||
| 281 | { | 412 | { |
| 282 | ImGui::TableSetupScrollFreeze(0, 1); | 413 | ImGui::TableSetupScrollFreeze(0, 1); |
| 283 | ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 110); | 414 | ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 110.0f); |
| 284 | ImGui::TableSetupColumn("Date", ImGuiTableColumnFlags_WidthFixed, 130); | 415 | ImGui::TableSetupColumn("Date", ImGuiTableColumnFlags_WidthFixed, 130.0f); |
| 285 | ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch); | 416 | ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch); |
| 286 | ImGui::TableSetupColumn("Amount", ImGuiTableColumnFlags_WidthFixed, 140); | 417 | ImGui::TableSetupColumn("Amount", ImGuiTableColumnFlags_WidthFixed, 140.0f); |
| 287 | ImGui::TableHeadersRow(); | 418 | ImGui::TableHeadersRow(); |
| 288 | 419 | ||
| 289 | for (auto& row : g_txRows) { | 420 | for (auto& row : g_txRows) { |
| 290 | ImGui::TableNextRow(); | 421 | ImGui::TableNextRow(); |
| 291 | ImGui::TableSetColumnIndex(0); | 422 | ImGui::TableSetColumnIndex(0); |
| 292 | if (row.depth < 1) | 423 | bool isImmatureCoinbase = (row.desc.find("matures in") != std::string::npos); |
| 293 | ImGui::TextColored(ImVec4(1,0.6f,0,1), "Unconfirmed"); | 424 | bool isOrphaned = (row.desc.find("orphaned") != std::string::npos); |
| 425 | if (isOrphaned) | ||
| 426 | ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "Orphaned"); | ||
| 427 | else if (row.depth < 1) | ||
| 428 | ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Unconfirmed"); | ||
| 429 | else if (isImmatureCoinbase) | ||
| 430 | ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.25f, 1.0f), "Immature"); | ||
| 431 | else if (row.depth < 6) | ||
| 432 | ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.3f, 1.0f), "%d confirm%s", | ||
| 433 | row.depth, row.depth == 1 ? "" : "s"); | ||
| 294 | else | 434 | else |
| 295 | ImGui::Text("%d blocks deep", row.depth); | 435 | ImGui::TextColored(ImVec4(0.5f, 0.9f, 0.5f, 1.0f), "Confirmed"); |
| 296 | ImGui::TableSetColumnIndex(1); | 436 | ImGui::TableSetColumnIndex(1); |
| 297 | ImGui::TextDisabled("%s", row.date.c_str()); | 437 | ImGui::TextDisabled("%s", row.date.c_str()); |
| 298 | ImGui::TableSetColumnIndex(2); | 438 | ImGui::TableSetColumnIndex(2); |
| 299 | ImGui::TextUnformatted(row.desc.c_str()); | 439 | ImGui::TextUnformatted(row.desc.c_str()); |
| 300 | ImGui::TableSetColumnIndex(3); | 440 | ImGui::TableSetColumnIndex(3); |
| 301 | if (row.amount >= 0) | 441 | if (row.amount >= 0) |
| 302 | ImGui::TextColored(ImVec4(0.35f,1,0.35f,1), "+%s BTF", FmtMoney(row.amount).c_str()); | 442 | ImGui::TextColored(ImVec4(0.35f, 1.0f, 0.35f, 1.0f), |
| 443 | "+%s BTF", FmtMoney(row.amount).c_str()); | ||
| 303 | else | 444 | else |
| 304 | ImGui::TextColored(ImVec4(1,0.4f,0.4f,1), "%s BTF", FmtMoney(row.amount).c_str()); | 445 | ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), |
| 446 | "%s BTF", FmtMoney(row.amount).c_str()); | ||
| 305 | } | 447 | } |
| 306 | ImGui::EndTable(); | 448 | ImGui::EndTable(); |
| 307 | } | 449 | } |
| 308 | 450 | ||
| 451 | // ---- Mode panels ---- | ||
| 309 | std::vector<BtfPoolAnnouncement> pools; | 452 | std::vector<BtfPoolAnnouncement> pools; |
| 310 | BtfGetPoolAnnouncements(pools); | 453 | BtfGetPoolAnnouncements(pools); |
| 311 | 454 | ||
| 312 | if (nMineMode == MINE_PARTICIPANT) { | 455 | if (nMineMode == MINE_PARTICIPANT) { |
| 313 | ImGui::Spacing(); | 456 | ImGui::Spacing(); |
| 314 | ImGui::SeparatorText("Participant Mining"); | 457 | ImGui::SeparatorText("Participant Mining"); |
| 315 | ImGui::Text("Selected pool:"); | 458 | |
| 459 | ImGui::TextDisabled("Selected pool:"); | ||
| 460 | ImGui::SameLine(); | ||
| 316 | if (strParticipantPool.empty()) | 461 | if (strParticipantPool.empty()) |
| 317 | ImGui::TextDisabled("No pool selected yet"); | 462 | ImGui::TextDisabled("none"); |
| 318 | else | 463 | else |
| 319 | ImGui::TextWrapped("%s", strParticipantPool.c_str()); | 464 | ImGui::TextWrapped("%s", strParticipantPool.c_str()); |
| 320 | 465 | ||
| 321 | const BtfPoolAnnouncement* selected = NULL; | 466 | const BtfPoolAnnouncement* sel = nullptr; |
| 322 | for (size_t i = 0; i < pools.size(); i++) | 467 | for (size_t i = 0; i < pools.size(); i++) |
| 323 | if (pools[i].btfAddress == strParticipantPool) { selected = &pools[i]; break; } | 468 | if (pools[i].btfAddress == strParticipantPool) { sel = &pools[i]; break; } |
| 324 | 469 | ||
| 325 | if (selected) { | 470 | if (sel) { |
| 326 | ImGui::Text("Pool name: %s", selected->poolName.c_str()); | 471 | ImGui::Text("Pool: %s Fee: %.2f%% Miners: %d Blocks: %d %.2f H/s", |
| 327 | ImGui::Text("Fee: %.2f%%", selected->feePercent); | 472 | sel->poolName.c_str(), sel->feePercent, |
| 328 | ImGui::Text("Live stats: %d miners, %d blocks", selected->connectedMiners, selected->blocksFound); | 473 | sel->connectedMiners, sel->blocksFound, sel->hashRate); |
| 329 | if (!selected->dashboardUrl.empty()) | 474 | if (!sel->dashboardUrl.empty()) |
| 330 | ImGui::TextWrapped("Dashboard: %s", selected->dashboardUrl.c_str()); | 475 | ImGui::TextDisabled("Dashboard: %s", sel->dashboardUrl.c_str()); |
| 331 | } else if (!strParticipantPool.empty()) { | 476 | } else if (!strParticipantPool.empty()) { |
| 332 | ImGui::TextDisabled("No fresh announcement found for this pool yet"); | 477 | ImGui::TextDisabled("No live announcement for this pool yet."); |
| 333 | } | 478 | } |
| 334 | 479 | ||
| 335 | if (!pools.empty()) { | 480 | if (!pools.empty()) { |
| 336 | ImGui::Spacing(); | 481 | ImGui::Spacing(); |
| 337 | ImGui::SeparatorText("Live Pools"); | 482 | ImGui::SeparatorText("Live Pools"); |
| 338 | if (ImGui::BeginTable("livepools", 5, | 483 | if (ImGui::BeginTable("livepools", 5, |
| 339 | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) | 484 | ImGuiTableFlags_Borders | |
| 485 | ImGuiTableFlags_RowBg | | ||
| 486 | ImGuiTableFlags_SizingStretchProp)) | ||
| 340 | { | 487 | { |
| 341 | ImGui::TableSetupColumn("Pool", ImGuiTableColumnFlags_WidthStretch); | 488 | ImGui::TableSetupColumn("Pool", ImGuiTableColumnFlags_WidthStretch); |
| 342 | ImGui::TableSetupColumn("Fee", ImGuiTableColumnFlags_WidthFixed, 70); | 489 | ImGui::TableSetupColumn("Fee", ImGuiTableColumnFlags_WidthFixed, 60.0f); |
| 343 | ImGui::TableSetupColumn("Stats", ImGuiTableColumnFlags_WidthFixed, 160); | 490 | ImGui::TableSetupColumn("Stats", ImGuiTableColumnFlags_WidthFixed, 150.0f); |
| 344 | ImGui::TableSetupColumn("Dashboard", ImGuiTableColumnFlags_WidthStretch); | 491 | ImGui::TableSetupColumn("Dashboard", ImGuiTableColumnFlags_WidthStretch); |
| 345 | ImGui::TableSetupColumn("Use", ImGuiTableColumnFlags_WidthFixed, 60); | 492 | ImGui::TableSetupColumn("Use", ImGuiTableColumnFlags_WidthFixed, 50.0f); |
| 346 | ImGui::TableHeadersRow(); | 493 | ImGui::TableHeadersRow(); |
| 347 | for (size_t i = 0; i < pools.size(); i++) { | 494 | for (size_t i = 0; i < pools.size(); i++) { |
| 348 | const BtfPoolAnnouncement& ann = pools[i]; | 495 | const BtfPoolAnnouncement& ann = pools[i]; |
| @@ -353,15 +500,19 @@ static void DrawMainWindow() | |||
| 353 | ImGui::TableSetColumnIndex(1); | 500 | ImGui::TableSetColumnIndex(1); |
| 354 | ImGui::Text("%.2f%%", ann.feePercent); | 501 | ImGui::Text("%.2f%%", ann.feePercent); |
| 355 | ImGui::TableSetColumnIndex(2); | 502 | ImGui::TableSetColumnIndex(2); |
| 356 | ImGui::Text("%d miners, %d blocks, %s old", ann.connectedMiners, ann.blocksFound, FmtAge(ann.createdAt).c_str()); | 503 | ImGui::Text("%d miners %d blocks %.2f H/s %s", |
| 504 | ann.connectedMiners, ann.blocksFound, ann.hashRate, | ||
| 505 | FmtAge(ann.createdAt).c_str()); | ||
| 357 | ImGui::TableSetColumnIndex(3); | 506 | ImGui::TableSetColumnIndex(3); |
| 358 | if (!ann.dashboardUrl.empty()) | 507 | if (!ann.dashboardUrl.empty()) |
| 359 | ImGui::TextWrapped("%s", ann.dashboardUrl.c_str()); | 508 | ImGui::TextWrapped("%s", ann.dashboardUrl.c_str()); |
| 360 | else | 509 | else |
| 361 | ImGui::TextDisabled("-"); | 510 | ImGui::TextDisabled("-"); |
| 362 | ImGui::TableSetColumnIndex(4); | 511 | ImGui::TableSetColumnIndex(4); |
| 363 | if (ImGui::SmallButton((std::string("Use##") + ann.btfAddress).c_str())) { | 512 | std::string btnId = "Use##" + ann.btfAddress; |
| 364 | strncpy(g_participantPool, ann.btfAddress.c_str(), sizeof(g_participantPool)-1); | 513 | if (ImGui::SmallButton(btnId.c_str())) { |
| 514 | strncpy(g_participantPool, ann.btfAddress.c_str(), | ||
| 515 | sizeof(g_participantPool)-1); | ||
| 365 | strParticipantPool = ann.btfAddress; | 516 | strParticipantPool = ann.btfAddress; |
| 366 | CWalletDB().WriteSetting("strParticipantPool", strParticipantPool); | 517 | CWalletDB().WriteSetting("strParticipantPool", strParticipantPool); |
| 367 | g_needRefresh = true; | 518 | g_needRefresh = true; |
| @@ -375,55 +526,60 @@ static void DrawMainWindow() | |||
| 375 | if (nMineMode == MINE_OPERATOR) { | 526 | if (nMineMode == MINE_OPERATOR) { |
| 376 | ImGui::Spacing(); | 527 | ImGui::Spacing(); |
| 377 | ImGui::SeparatorText("Operator Overview"); | 528 | ImGui::SeparatorText("Operator Overview"); |
| 378 | ImGui::Text("Pool .btf address:"); | 529 | |
| 379 | std::string opAddr = BtfLocalAddress(); | 530 | std::string opAddr = BtfLocalAddress(); |
| 531 | ImGui::TextDisabled("Pool .btf address:"); | ||
| 532 | ImGui::SameLine(); | ||
| 380 | if (opAddr.empty()) | 533 | if (opAddr.empty()) |
| 381 | ImGui::TextDisabled("Identity not ready yet"); | 534 | ImGui::TextDisabled("identity not ready"); |
| 382 | else | 535 | else |
| 383 | ImGui::TextWrapped("%s", opAddr.c_str()); | 536 | ImGui::TextWrapped("%s", opAddr.c_str()); |
| 384 | 537 | ||
| 538 | // Pending payouts | ||
| 385 | std::vector<PendingPayoutView> owed; | 539 | std::vector<PendingPayoutView> owed; |
| 386 | GetPendingPayouts(owed); | 540 | GetPendingPayouts(owed); |
| 387 | ImGui::Spacing(); | 541 | ImGui::Spacing(); |
| 388 | ImGui::SeparatorText("Pending Pool Payouts (you owe miners)"); | 542 | ImGui::SeparatorText("Pending Payouts (owed to miners)"); |
| 389 | if (owed.empty()) { | 543 | if (owed.empty()) { |
| 390 | ImGui::TextDisabled("No pending pool payouts"); | 544 | ImGui::TextDisabled("No pending payouts"); |
| 391 | } else if (ImGui::BeginTable("owedpayouts", 4, | 545 | } else if (ImGui::BeginTable("owedpayouts", 4, |
| 392 | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) | 546 | ImGuiTableFlags_Borders | |
| 547 | ImGuiTableFlags_RowBg | | ||
| 548 | ImGuiTableFlags_SizingStretchProp)) | ||
| 393 | { | 549 | { |
| 394 | ImGui::TableSetupColumn("Matures At", ImGuiTableColumnFlags_WidthFixed, 100); | 550 | ImGui::TableSetupColumn("Matures At", ImGuiTableColumnFlags_WidthFixed, 90.0f); |
| 395 | ImGui::TableSetupColumn("Blocks Left", ImGuiTableColumnFlags_WidthFixed, 90); | 551 | ImGui::TableSetupColumn("Blocks left", ImGuiTableColumnFlags_WidthFixed, 80.0f); |
| 396 | ImGui::TableSetupColumn("Recipients", ImGuiTableColumnFlags_WidthFixed, 90); | 552 | ImGui::TableSetupColumn("Recipients", ImGuiTableColumnFlags_WidthFixed, 80.0f); |
| 397 | ImGui::TableSetupColumn("Total", ImGuiTableColumnFlags_WidthStretch); | 553 | ImGui::TableSetupColumn("Total", ImGuiTableColumnFlags_WidthStretch); |
| 398 | ImGui::TableHeadersRow(); | 554 | ImGui::TableHeadersRow(); |
| 399 | for (const PendingPayoutView& row : owed) { | 555 | for (const PendingPayoutView& row : owed) { |
| 400 | int remaining = std::max(0, row.matureAtHeight - nBestHeight); | 556 | int left = std::max(0, row.matureAtHeight - nBestHeight); |
| 401 | ImGui::TableNextRow(); | 557 | ImGui::TableNextRow(); |
| 402 | ImGui::TableSetColumnIndex(0); | 558 | ImGui::TableSetColumnIndex(0); ImGui::Text("%d", row.matureAtHeight); |
| 403 | ImGui::Text("%d", row.matureAtHeight); | 559 | ImGui::TableSetColumnIndex(1); ImGui::Text("%d", left); |
| 404 | ImGui::TableSetColumnIndex(1); | 560 | ImGui::TableSetColumnIndex(2); ImGui::Text("%d", row.recipients); |
| 405 | ImGui::Text("%d", remaining); | 561 | ImGui::TableSetColumnIndex(3); ImGui::Text("%s BTF", FmtMoney(row.totalAmount).c_str()); |
| 406 | ImGui::TableSetColumnIndex(2); | ||
| 407 | ImGui::Text("%d", row.recipients); | ||
| 408 | ImGui::TableSetColumnIndex(3); | ||
| 409 | ImGui::Text("%s BTF", FmtMoney(row.totalAmount).c_str()); | ||
| 410 | } | 562 | } |
| 411 | ImGui::EndTable(); | 563 | ImGui::EndTable(); |
| 412 | } | 564 | } |
| 413 | 565 | ||
| 566 | // Workers | ||
| 414 | std::vector<PoolWorkerStatView> workers; | 567 | std::vector<PoolWorkerStatView> workers; |
| 415 | GetPoolWorkerStats(workers); | 568 | GetPoolWorkerStats(workers); |
| 416 | ImGui::Spacing(); | 569 | ImGui::Spacing(); |
| 417 | ImGui::SeparatorText("Current Round Workers"); | 570 | ImGui::SeparatorText("Current Round Workers"); |
| 418 | if (workers.empty()) { | 571 | if (workers.empty()) { |
| 419 | ImGui::TextDisabled("No active workers in this round"); | 572 | ImGui::TextDisabled("No active workers this round"); |
| 420 | } else if (ImGui::BeginTable("workers", 4, | 573 | } else if (ImGui::BeginTable("workers", 5, |
| 421 | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) | 574 | ImGuiTableFlags_Borders | |
| 575 | ImGuiTableFlags_RowBg | | ||
| 576 | ImGuiTableFlags_SizingStretchProp)) | ||
| 422 | { | 577 | { |
| 423 | ImGui::TableSetupColumn("Payout Addr", ImGuiTableColumnFlags_WidthStretch); | 578 | ImGui::TableSetupColumn("Payout Address", ImGuiTableColumnFlags_WidthStretch); |
| 424 | ImGui::TableSetupColumn("Worker", ImGuiTableColumnFlags_WidthStretch); | 579 | ImGui::TableSetupColumn("Worker", ImGuiTableColumnFlags_WidthStretch); |
| 425 | ImGui::TableSetupColumn("Round Shares", ImGuiTableColumnFlags_WidthFixed, 90); | 580 | ImGui::TableSetupColumn("Hashrate", ImGuiTableColumnFlags_WidthFixed, 90.0f); |
| 426 | ImGui::TableSetupColumn("Last Seen", ImGuiTableColumnFlags_WidthFixed, 100); | 581 | ImGui::TableSetupColumn("Round shares", ImGuiTableColumnFlags_WidthFixed, 90.0f); |
| 582 | ImGui::TableSetupColumn("Last seen", ImGuiTableColumnFlags_WidthFixed, 90.0f); | ||
| 427 | ImGui::TableHeadersRow(); | 583 | ImGui::TableHeadersRow(); |
| 428 | for (const PoolWorkerStatView& row : workers) { | 584 | for (const PoolWorkerStatView& row : workers) { |
| 429 | ImGui::TableNextRow(); | 585 | ImGui::TableNextRow(); |
| @@ -432,53 +588,19 @@ static void DrawMainWindow() | |||
| 432 | ImGui::TableSetColumnIndex(1); | 588 | ImGui::TableSetColumnIndex(1); |
| 433 | ImGui::TextDisabled("%s", row.worker.empty() ? "worker" : row.worker.c_str()); | 589 | ImGui::TextDisabled("%s", row.worker.empty() ? "worker" : row.worker.c_str()); |
| 434 | ImGui::TableSetColumnIndex(2); | 590 | ImGui::TableSetColumnIndex(2); |
| 435 | ImGui::Text("%llu", (unsigned long long)row.roundShares); | 591 | ImGui::Text("%.2f H/s", row.hashRate); |
| 436 | ImGui::TableSetColumnIndex(3); | 592 | ImGui::TableSetColumnIndex(3); |
| 437 | ImGui::TextDisabled("%s", FmtAge(row.lastSeen).c_str()); | 593 | ImGui::Text("%llu", (unsigned long long)row.roundShares); |
| 594 | ImGui::TableSetColumnIndex(4); | ||
| 595 | ImGui::TextDisabled("%s ago", FmtAge(row.lastSeen).c_str()); | ||
| 438 | } | 596 | } |
| 439 | ImGui::EndTable(); | 597 | ImGui::EndTable(); |
| 440 | } | 598 | } |
| 441 | } | 599 | } |
| 442 | 600 | ||
| 443 | // ---- Status bar ---- | 601 | ImGui::EndChild(); // ##scroll |
| 444 | { | ||
| 445 | ImGui::Spacing(); | ||
| 446 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,2)); | ||
| 447 | |||
| 448 | int peers = (int)vNodes.size(); | ||
| 449 | if (nMineMode == MINE_PARTICIPANT) { | ||
| 450 | uint64 sent = 0, accepted = 0; | ||
| 451 | double hashRate = 0.0; | ||
| 452 | std::string mineStatus = GetParticipantMiningStatus(); | ||
| 453 | GetParticipantMiningStats(sent, accepted, hashRate); | ||
| 454 | if (sent > 0 || accepted > 0 || hashRate > 0.0) | ||
| 455 | ImGui::TextDisabled("Peers %d height %d shares sent %llu accepted %llu hash rate %.2f H/s", | ||
| 456 | peers, | ||
| 457 | nBestHeight, | ||
| 458 | (unsigned long long)sent, | ||
| 459 | (unsigned long long)accepted, | ||
| 460 | hashRate); | ||
| 461 | else if (!mineStatus.empty()) | ||
| 462 | ImGui::TextDisabled("Peers %d height %d %s", peers, nBestHeight, mineStatus.c_str()); | ||
| 463 | else | ||
| 464 | ImGui::TextDisabled("Peers %d height %d asking pool for work", peers, nBestHeight); | ||
| 465 | } else if (nMineMode == MINE_OPERATOR) { | ||
| 466 | int miners = 0, blocksFound = 0; | ||
| 467 | uint64 roundShares = 0; | ||
| 468 | GetPoolOperatorStats(miners, blocksFound, roundShares); | ||
| 469 | if (miners > 0 || blocksFound > 0 || roundShares > 0) | ||
| 470 | ImGui::TextDisabled("Peers %d height %d authorized miners %d round shares %llu blocks found %d", | ||
| 471 | peers, nBestHeight, miners, (unsigned long long)roundShares, blocksFound); | ||
| 472 | else | ||
| 473 | ImGui::TextDisabled("Peers %d height %d waiting for miners", peers, nBestHeight); | ||
| 474 | } else { | ||
| 475 | ImGui::TextDisabled("Peers %d height %d", peers, nBestHeight); | ||
| 476 | } | ||
| 477 | ImGui::PopStyleVar(); | ||
| 478 | } | ||
| 479 | |||
| 480 | ImGui::SetWindowFontScale(1.0f); | 602 | ImGui::SetWindowFontScale(1.0f); |
| 481 | ImGui::End(); | 603 | ImGui::End(); // ##main |
| 482 | } | 604 | } |
| 483 | 605 | ||
| 484 | // --------------------------------------------------------------------------- | 606 | // --------------------------------------------------------------------------- |
| @@ -487,42 +609,53 @@ static void DrawMainWindow() | |||
| 487 | static void DrawSendDialog() | 609 | static void DrawSendDialog() |
| 488 | { | 610 | { |
| 489 | if (!g_showSend) return; | 611 | if (!g_showSend) return; |
| 490 | ImGui::SetNextWindowSize(ImVec2(500, 180), ImGuiCond_Always); | 612 | ImGui::SetNextWindowSize(ImVec2(500.0f, 180.0f), ImGuiCond_Always); |
| 491 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(.5f,.5f)); | 613 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), |
| 614 | ImGuiCond_Always, ImVec2(0.5f, 0.5f)); | ||
| 492 | if (ImGui::Begin("Send Coins", &g_showSend, | 615 | if (ImGui::Begin("Send Coins", &g_showSend, |
| 493 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) | 616 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) |
| 494 | { | 617 | { |
| 495 | ImGui::Text("Recipient address:"); | 618 | ImGui::Text("Recipient address:"); |
| 496 | ImGui::SetNextItemWidth(-1); | 619 | ImGui::SetNextItemWidth(-1.0f); |
| 497 | ImGui::InputText("##sa", g_sendAddr, sizeof(g_sendAddr)); | 620 | ImGui::InputText("##sa", g_sendAddr, sizeof(g_sendAddr)); |
| 498 | 621 | ||
| 499 | ImGui::Text("Amount (BTF):"); | 622 | ImGui::Text("Amount (BTF):"); |
| 500 | ImGui::SetNextItemWidth(180); | 623 | ImGui::SetNextItemWidth(180.0f); |
| 501 | ImGui::InputText("##sm", g_sendAmount, sizeof(g_sendAmount)); | 624 | ImGui::InputText("##sm", g_sendAmount, sizeof(g_sendAmount)); |
| 502 | 625 | ||
| 503 | if (!g_sendStatus.empty()) { | 626 | if (!g_sendStatus.empty()) { |
| 504 | ImGui::Spacing(); | 627 | ImGui::Spacing(); |
| 505 | ImGui::TextColored(ImVec4(1,.4f,.4f,1), "%s", g_sendStatus.c_str()); | 628 | ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), |
| 629 | "%s", g_sendStatus.c_str()); | ||
| 506 | } | 630 | } |
| 507 | 631 | ||
| 508 | ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); | 632 | ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); |
| 509 | if (ImGui::Button("Send", ImVec2(90,0))) { | 633 | if (ImGui::Button("Send", ImVec2(90.0f, 0.0f))) { |
| 510 | uint160 h; int64 nv = 0; | 634 | uint160 h; int64 nv = 0; |
| 511 | if (!AddressToHash160(g_sendAddr, h)) g_sendStatus = "Invalid address."; | 635 | if (!AddressToHash160(g_sendAddr, h)) |
| 512 | else if (!ParseMoney(g_sendAmount,nv)||nv<=0) g_sendStatus = "Invalid amount."; | 636 | g_sendStatus = "Invalid address."; |
| 637 | else if (!ParseMoney(g_sendAmount, nv) || nv <= 0) | ||
| 638 | g_sendStatus = "Invalid amount."; | ||
| 513 | else { | 639 | else { |
| 514 | CScript s; s << OP_DUP << OP_HASH160 << h << OP_EQUALVERIFY << OP_CHECKSIG; | 640 | CScript sc; |
| 641 | sc << OP_DUP << OP_HASH160 << h << OP_EQUALVERIFY << OP_CHECKSIG; | ||
| 515 | CWalletTx wtx; | 642 | CWalletTx wtx; |
| 516 | if (SendMoney(s, nv, wtx)) { | 643 | if (SendMoney(sc, nv, wtx)) { |
| 517 | g_showSend=false; g_sendStatus=""; | 644 | g_showSend = false; |
| 518 | memset(g_sendAddr,0,sizeof(g_sendAddr)); | 645 | g_sendStatus = ""; |
| 519 | memset(g_sendAmount,0,sizeof(g_sendAmount)); | 646 | memset(g_sendAddr, 0, sizeof(g_sendAddr)); |
| 647 | memset(g_sendAmount, 0, sizeof(g_sendAmount)); | ||
| 520 | g_needRefresh = true; | 648 | g_needRefresh = true; |
| 521 | } else g_sendStatus = "Transaction failed."; | 649 | } else { |
| 650 | g_sendStatus = "Transaction failed."; | ||
| 651 | } | ||
| 522 | } | 652 | } |
| 523 | } | 653 | } |
| 524 | ImGui::SameLine(); | 654 | ImGui::SameLine(); |
| 525 | if (ImGui::Button("Cancel", ImVec2(90,0))) { g_showSend=false; g_sendStatus=""; } | 655 | if (ImGui::Button("Cancel", ImVec2(90.0f, 0.0f))) { |
| 656 | g_showSend = false; | ||
| 657 | g_sendStatus = ""; | ||
| 658 | } | ||
| 526 | } | 659 | } |
| 527 | ImGui::End(); | 660 | ImGui::End(); |
| 528 | } | 661 | } |
| @@ -534,7 +667,6 @@ static void DrawOptionsDialog() | |||
| 534 | { | 667 | { |
| 535 | if (!g_showOptions) return; | 668 | if (!g_showOptions) return; |
| 536 | 669 | ||
| 537 | // Always sync from current globals when dialog opens | ||
| 538 | static bool wasOpen = false; | 670 | static bool wasOpen = false; |
| 539 | if (!wasOpen) { | 671 | if (!wasOpen) { |
| 540 | g_mineRadio = nMineMode; | 672 | g_mineRadio = nMineMode; |
| @@ -544,42 +676,46 @@ static void DrawOptionsDialog() | |||
| 544 | strncpy(g_poolDash, strPoolDashboardUrl.c_str(), sizeof(g_poolDash)-1); | 676 | strncpy(g_poolDash, strPoolDashboardUrl.c_str(), sizeof(g_poolDash)-1); |
| 545 | } | 677 | } |
| 546 | wasOpen = true; | 678 | wasOpen = true; |
| 547 | ImGui::SetNextWindowSize(ImVec2(500, 310), ImGuiCond_Always); | 679 | |
| 548 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(.5f,.5f)); | 680 | ImGui::SetNextWindowSize(ImVec2(500.0f, 335.0f), ImGuiCond_Always); |
| 681 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), | ||
| 682 | ImGuiCond_Always, ImVec2(0.5f, 0.5f)); | ||
| 549 | if (ImGui::Begin("Options", &g_showOptions, | 683 | if (ImGui::Begin("Options", &g_showOptions, |
| 550 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) | 684 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) |
| 551 | { | 685 | { |
| 552 | // Transaction fee | 686 | // Transaction fee |
| 553 | ImGui::SeparatorText("Transaction Fee"); | 687 | ImGui::SeparatorText("Transaction Fee"); |
| 554 | static char feeStr[32] = {}; | 688 | static char feeStr[32] = {}; |
| 555 | if (feeStr[0]=='\0') snprintf(feeStr,sizeof(feeStr),"%s",FmtMoney(nTransactionFee).c_str()); | 689 | if (feeStr[0] == '\0') |
| 556 | ImGui::SetNextItemWidth(180); | 690 | snprintf(feeStr, sizeof(feeStr), "%s", FmtMoney(nTransactionFee).c_str()); |
| 691 | ImGui::SetNextItemWidth(180.0f); | ||
| 557 | ImGui::InputText("BTF per transaction##fee", feeStr, sizeof(feeStr)); | 692 | ImGui::InputText("BTF per transaction##fee", feeStr, sizeof(feeStr)); |
| 558 | 693 | ||
| 559 | // Mining | 694 | // Mining mode |
| 560 | ImGui::SeparatorText("Mining Mode"); | 695 | ImGui::SeparatorText("Mining Mode"); |
| 561 | ImGui::RadioButton("Solo — rewards go to your wallet", &g_mineRadio, MINE_SOLO); | 696 | ImGui::RadioButton("Relay -- just run the node, don't mine", &g_mineRadio, MINE_RELAY); |
| 562 | ImGui::RadioButton("Operator — run a pool for other miners", &g_mineRadio, MINE_OPERATOR); | 697 | ImGui::RadioButton("Solo -- rewards go to your wallet", &g_mineRadio, MINE_SOLO); |
| 563 | ImGui::RadioButton("Participant — mine to someone's pool", &g_mineRadio, MINE_PARTICIPANT); | 698 | ImGui::RadioButton("Operator -- run a pool for other miners", &g_mineRadio, MINE_OPERATOR); |
| 699 | ImGui::RadioButton("Participant -- mine to someone's pool", &g_mineRadio, MINE_PARTICIPANT); | ||
| 564 | 700 | ||
| 565 | if (g_mineRadio == MINE_OPERATOR) { | 701 | if (g_mineRadio == MINE_OPERATOR) { |
| 566 | ImGui::Spacing(); | 702 | ImGui::Spacing(); |
| 567 | ImGui::SeparatorText("Pool Announcement"); | 703 | ImGui::SeparatorText("Pool Announcement"); |
| 568 | ImGui::Text("Pool name:"); | 704 | ImGui::Text("Pool name:"); |
| 569 | ImGui::SetNextItemWidth(-1); | 705 | ImGui::SetNextItemWidth(-1.0f); |
| 570 | ImGui::InputText("##poolname", g_poolName, sizeof(g_poolName)); | 706 | ImGui::InputText("##poolname", g_poolName, sizeof(g_poolName)); |
| 571 | ImGui::Text("Fee %%:"); | 707 | ImGui::Text("Fee %%:"); |
| 572 | ImGui::SetNextItemWidth(120); | 708 | ImGui::SetNextItemWidth(120.0f); |
| 573 | ImGui::InputText("##poolfee", g_poolFee, sizeof(g_poolFee)); | 709 | ImGui::InputText("##poolfee", g_poolFee, sizeof(g_poolFee)); |
| 574 | ImGui::Text("Dashboard URL:"); | 710 | ImGui::Text("Dashboard URL:"); |
| 575 | ImGui::SetNextItemWidth(-1); | 711 | ImGui::SetNextItemWidth(-1.0f); |
| 576 | ImGui::InputText("##pooldash", g_poolDash, sizeof(g_poolDash)); | 712 | ImGui::InputText("##pooldash", g_poolDash, sizeof(g_poolDash)); |
| 577 | } | 713 | } |
| 578 | 714 | ||
| 579 | if (g_mineRadio == MINE_PARTICIPANT) { | 715 | if (g_mineRadio == MINE_PARTICIPANT) { |
| 580 | ImGui::Spacing(); | 716 | ImGui::Spacing(); |
| 581 | ImGui::SeparatorText("Pool Selection"); | 717 | ImGui::SeparatorText("Pool Selection"); |
| 582 | ImGui::TextDisabled("Pick from live discovered pools. Manual entry is disabled here."); | 718 | ImGui::TextDisabled("Pick from live discovered pools."); |
| 583 | 719 | ||
| 584 | std::vector<BtfPoolAnnouncement> pools; | 720 | std::vector<BtfPoolAnnouncement> pools; |
| 585 | BtfGetPoolAnnouncements(pools); | 721 | BtfGetPoolAnnouncements(pools); |
| @@ -591,46 +727,49 @@ static void DrawOptionsDialog() | |||
| 591 | break; | 727 | break; |
| 592 | } | 728 | } |
| 593 | } | 729 | } |
| 594 | if (ImGui::BeginCombo("Discovered pools", current.c_str())) { | 730 | if (ImGui::BeginCombo("Discovered pools##combo", current.c_str())) { |
| 595 | for (size_t i = 0; i < pools.size(); i++) { | 731 | for (size_t i = 0; i < pools.size(); i++) { |
| 596 | const BtfPoolAnnouncement& ann = pools[i]; | 732 | const BtfPoolAnnouncement& ann = pools[i]; |
| 597 | std::string label = PoolLabel(ann); | 733 | bool selected = (ann.btfAddress == g_participantPool); |
| 598 | bool selected = ann.btfAddress == g_participantPool; | 734 | if (ImGui::Selectable(PoolLabel(ann).c_str(), selected)) |
| 599 | if (ImGui::Selectable(label.c_str(), selected)) { | 735 | strncpy(g_participantPool, ann.btfAddress.c_str(), |
| 600 | strncpy(g_participantPool, ann.btfAddress.c_str(), sizeof(g_participantPool)-1); | 736 | sizeof(g_participantPool)-1); |
| 601 | } | ||
| 602 | } | 737 | } |
| 603 | ImGui::EndCombo(); | 738 | ImGui::EndCombo(); |
| 604 | } | 739 | } |
| 605 | } else { | 740 | } else { |
| 606 | ImGui::TextDisabled("No live pools discovered yet"); | 741 | ImGui::TextDisabled("No live pools discovered yet."); |
| 607 | } | 742 | } |
| 608 | } | 743 | } |
| 609 | 744 | ||
| 610 | ImGui::Spacing(); | ||
| 611 | const char* genLabel = fGenerateBitcoins ? "Stop Mining" : "Start Mining"; | ||
| 612 | if (ImGui::Button(genLabel, ImVec2(130,0))) { | ||
| 613 | fGenerateBitcoins = fGenerateBitcoins ? 0 : 1; | ||
| 614 | if (fGenerateBitcoins) _beginthread(ThreadBitcoinMiner, 0, NULL); | ||
| 615 | CWalletDB().WriteSetting("fGenerateBitcoins", fGenerateBitcoins); | ||
| 616 | } | ||
| 617 | |||
| 618 | ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); | 745 | ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); |
| 619 | if (ImGui::Button("OK", ImVec2(90,0))) { | 746 | if (ImGui::Button("OK", ImVec2(90.0f, 0.0f))) { |
| 620 | int64 fee=0; | 747 | int64 fee = 0; |
| 621 | if (ParseMoney(feeStr,fee)) { nTransactionFee=fee; CWalletDB().WriteSetting("nTransactionFee",nTransactionFee); } | 748 | if (ParseMoney(feeStr, fee)) { |
| 622 | 749 | nTransactionFee = fee; | |
| 623 | bool changed = (g_mineRadio != nMineMode); | 750 | CWalletDB().WriteSetting("nTransactionFee", nTransactionFee); |
| 624 | nMineMode = g_mineRadio; | 751 | } |
| 625 | strParticipantPool = g_participantPool; | 752 | |
| 626 | strPoolName = g_poolName[0] ? g_poolName : "Bitflash Pool"; | 753 | bool poolChanged = |
| 627 | strPoolDashboardUrl = g_poolDash; | 754 | nMineMode != g_mineRadio || |
| 628 | dPoolFeePercent = atof(g_poolFee); | 755 | strPoolName != (g_poolName[0] ? g_poolName : "Bitflash Pool") || |
| 629 | CWalletDB().WriteSetting("nMineMode", nMineMode); | 756 | strPoolDashboardUrl != g_poolDash || |
| 630 | CWalletDB().WriteSetting("strParticipantPool", strParticipantPool); | 757 | dPoolFeePercent != atof(g_poolFee); |
| 631 | CWalletDB().WriteSetting("strPoolName", strPoolName); | 758 | |
| 632 | CWalletDB().WriteSetting("strPoolDashboardUrl", strPoolDashboardUrl); | 759 | nMineMode = g_mineRadio; |
| 633 | CWalletDB().WriteSetting("dPoolFeePercent", dPoolFeePercent); | 760 | strParticipantPool = g_participantPool; |
| 761 | strPoolName = g_poolName[0] ? g_poolName : "Bitflash Pool"; | ||
| 762 | strPoolDashboardUrl = g_poolDash; | ||
| 763 | dPoolFeePercent = atof(g_poolFee); | ||
| 764 | |||
| 765 | if (nMineMode == MINE_OPERATOR && poolChanged) gAnnounceNow = true; | ||
| 766 | |||
| 767 | if (nMineMode == MINE_RELAY) { | ||
| 768 | fGenerateBitcoins = 0; | ||
| 769 | } else { | ||
| 770 | fGenerateBitcoins = 1; | ||
| 771 | if (!vfThreadRunning[3]) _beginthread(ThreadBitcoinMiner, 0, NULL); | ||
| 772 | } | ||
| 634 | 773 | ||
| 635 | if (nMineMode == MINE_OPERATOR) { | 774 | if (nMineMode == MINE_OPERATOR) { |
| 636 | if (!gPoolServerRunning) { | 775 | if (!gPoolServerRunning) { |
| @@ -645,7 +784,7 @@ static void DrawOptionsDialog() | |||
| 645 | g_showOptions = false; | 784 | g_showOptions = false; |
| 646 | } | 785 | } |
| 647 | ImGui::SameLine(); | 786 | ImGui::SameLine(); |
| 648 | if (ImGui::Button("Cancel", ImVec2(90,0))) g_showOptions = false; | 787 | if (ImGui::Button("Cancel", ImVec2(90.0f, 0.0f))) g_showOptions = false; |
| 649 | } | 788 | } |
| 650 | if (!g_showOptions) wasOpen = false; | 789 | if (!g_showOptions) wasOpen = false; |
| 651 | ImGui::End(); | 790 | ImGui::End(); |
| @@ -657,18 +796,21 @@ static void DrawOptionsDialog() | |||
| 657 | static void DrawAboutDialog() | 796 | static void DrawAboutDialog() |
| 658 | { | 797 | { |
| 659 | if (!g_showAbout) return; | 798 | if (!g_showAbout) return; |
| 660 | ImGui::SetNextWindowSize(ImVec2(380, 170), ImGuiCond_Always); | 799 | ImGui::SetNextWindowSize(ImVec2(380.0f, 170.0f), ImGuiCond_Always); |
| 661 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Always, ImVec2(.5f,.5f)); | 800 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), |
| 801 | ImGuiCond_Always, ImVec2(0.5f, 0.5f)); | ||
| 662 | if (ImGui::Begin("About Bitflash", &g_showAbout, | 802 | if (ImGui::Begin("About Bitflash", &g_showAbout, |
| 663 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) | 803 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) |
| 664 | { | 804 | { |
| 665 | ImGui::TextColored(ImVec4(1,.6f,0,1), "Bitflash BTF v1.1.0"); | 805 | ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Bitflash BTF v1.1.0"); |
| 666 | ImGui::Spacing(); | 806 | ImGui::Spacing(); |
| 667 | ImGui::TextWrapped("CPU-only cryptocurrency. RandomX proof of work. Anonymous .btf addressing over Nostr. No premine."); | 807 | ImGui::TextWrapped( |
| 808 | "CPU-only cryptocurrency. RandomX proof of work. " | ||
| 809 | "Anonymous .btf addressing over Nostr. No premine."); | ||
| 668 | ImGui::Spacing(); | 810 | ImGui::Spacing(); |
| 669 | ImGui::TextDisabled("Based on Bitcoin 0.1.0 (Satoshi Nakamoto, 2009)"); | 811 | ImGui::TextDisabled("Based on Bitcoin 0.1.0 (Satoshi Nakamoto, 2009)"); |
| 670 | ImGui::Spacing(); | 812 | ImGui::Spacing(); |
| 671 | if (ImGui::Button("OK", ImVec2(90,0))) g_showAbout = false; | 813 | if (ImGui::Button("OK", ImVec2(90.0f, 0.0f))) g_showAbout = false; |
| 672 | } | 814 | } |
| 673 | ImGui::End(); | 815 | ImGui::End(); |
| 674 | } | 816 | } |
| @@ -705,34 +847,33 @@ int RunGUI(int argc, char* argv[]) | |||
| 705 | ImGuiIO& io = ImGui::GetIO(); | 847 | ImGuiIO& io = ImGui::GetIO(); |
| 706 | io.IniFilename = nullptr; | 848 | io.IniFilename = nullptr; |
| 707 | 849 | ||
| 708 | // Load Roboto at 16px — clean, readable on all platforms | ||
| 709 | io.Fonts->AddFontFromMemoryCompressedTTF( | 850 | io.Fonts->AddFontFromMemoryCompressedTTF( |
| 710 | RobotoMedium_compressed_data, RobotoMedium_compressed_size, 16.0f); | 851 | RobotoMedium_compressed_data, RobotoMedium_compressed_size, 14.0f); |
| 711 | 852 | ||
| 712 | ImGui::StyleColorsDark(); | 853 | ImGui::StyleColorsDark(); |
| 713 | ImGuiStyle& s = ImGui::GetStyle(); | 854 | ImGuiStyle& st = ImGui::GetStyle(); |
| 714 | s.WindowRounding = s.FrameRounding = s.PopupRounding = 4.0f; | 855 | st.WindowRounding = st.FrameRounding = st.PopupRounding = 4.0f; |
| 715 | s.ItemSpacing = ImVec2(10, 7); | 856 | st.ItemSpacing = ImVec2(10.0f, 7.0f); |
| 716 | s.FramePadding = ImVec2(8, 5); | 857 | st.FramePadding = ImVec2(8.0f, 5.0f); |
| 717 | s.WindowPadding = ImVec2(12, 12); | 858 | st.WindowPadding = ImVec2(12.0f, 12.0f); |
| 718 | // Orange accent | 859 | // Orange accent |
| 719 | s.Colors[ImGuiCol_TitleBgActive] = ImVec4(.80f,.45f,.00f,1); | 860 | st.Colors[ImGuiCol_TitleBgActive] = ImVec4(.80f, .45f, .00f, 1.0f); |
| 720 | s.Colors[ImGuiCol_CheckMark] = ImVec4(1.0f,.60f,.00f,1); | 861 | st.Colors[ImGuiCol_CheckMark] = ImVec4(1.0f, .60f, .00f, 1.0f); |
| 721 | s.Colors[ImGuiCol_SliderGrab] = ImVec4(.80f,.45f,.00f,1); | 862 | st.Colors[ImGuiCol_SliderGrab] = ImVec4(.80f, .45f, .00f, 1.0f); |
| 722 | s.Colors[ImGuiCol_Button] = ImVec4(.25f,.25f,.25f,1); | 863 | st.Colors[ImGuiCol_Button] = ImVec4(.25f, .25f, .25f, 1.0f); |
| 723 | s.Colors[ImGuiCol_ButtonHovered] = ImVec4(.80f,.45f,.00f,1); | 864 | st.Colors[ImGuiCol_ButtonHovered] = ImVec4(.80f, .45f, .00f, 1.0f); |
| 724 | s.Colors[ImGuiCol_ButtonActive] = ImVec4(.60f,.30f,.00f,1); | 865 | st.Colors[ImGuiCol_ButtonActive] = ImVec4(.60f, .30f, .00f, 1.0f); |
| 725 | s.Colors[ImGuiCol_Header] = ImVec4(.80f,.45f,.00f,.40f); | 866 | st.Colors[ImGuiCol_Header] = ImVec4(.80f, .45f, .00f, .40f); |
| 726 | s.Colors[ImGuiCol_HeaderHovered] = ImVec4(.80f,.45f,.20f,.80f); | 867 | st.Colors[ImGuiCol_HeaderHovered] = ImVec4(.80f, .45f, .20f, .80f); |
| 727 | s.Colors[ImGuiCol_Tab] = ImVec4(.20f,.20f,.20f,1); | 868 | st.Colors[ImGuiCol_Tab] = ImVec4(.20f, .20f, .20f, 1.0f); |
| 728 | s.Colors[ImGuiCol_TabHovered] = ImVec4(.80f,.45f,.00f,1); | 869 | st.Colors[ImGuiCol_TabHovered] = ImVec4(.80f, .45f, .00f, 1.0f); |
| 729 | s.Colors[ImGuiCol_TabActive] = ImVec4(.80f,.45f,.00f,1); | 870 | st.Colors[ImGuiCol_TabActive] = ImVec4(.80f, .45f, .00f, 1.0f); |
| 730 | s.Colors[ImGuiCol_SeparatorHovered]=ImVec4(.80f,.45f,.00f,1); | 871 | st.Colors[ImGuiCol_SeparatorHovered]= ImVec4(.80f, .45f, .00f, 1.0f); |
| 731 | s.Colors[ImGuiCol_FrameBg] = ImVec4(.14f,.14f,.14f,1); | 872 | st.Colors[ImGuiCol_FrameBg] = ImVec4(.14f, .14f, .14f, 1.0f); |
| 732 | s.Colors[ImGuiCol_FrameBgHovered] = ImVec4(.20f,.20f,.20f,1); | 873 | st.Colors[ImGuiCol_FrameBgHovered] = ImVec4(.20f, .20f, .20f, 1.0f); |
| 733 | s.Colors[ImGuiCol_TableHeaderBg] = ImVec4(.18f,.18f,.18f,1); | 874 | st.Colors[ImGuiCol_TableHeaderBg] = ImVec4(.18f, .18f, .18f, 1.0f); |
| 734 | s.Colors[ImGuiCol_TableRowBg] = ImVec4(.11f,.11f,.11f,1); | 875 | st.Colors[ImGuiCol_TableRowBg] = ImVec4(.11f, .11f, .11f, 1.0f); |
| 735 | s.Colors[ImGuiCol_TableRowBgAlt] = ImVec4(.14f,.14f,.14f,1); | 876 | st.Colors[ImGuiCol_TableRowBgAlt] = ImVec4(.14f, .14f, .14f, 1.0f); |
| 736 | 877 | ||
| 737 | ImGui_ImplGlfw_InitForOpenGL(window, true); | 878 | ImGui_ImplGlfw_InitForOpenGL(window, true); |
| 738 | ImGui_ImplOpenGL3_Init("#version 330"); | 879 | ImGui_ImplOpenGL3_Init("#version 330"); |
| @@ -744,22 +885,28 @@ int RunGUI(int argc, char* argv[]) | |||
| 744 | while (!glfwWindowShouldClose(window) && !fShutdown) | 885 | while (!glfwWindowShouldClose(window) && !fShutdown) |
| 745 | { | 886 | { |
| 746 | glfwPollEvents(); | 887 | glfwPollEvents(); |
| 747 | if (g_needRefresh || (frame % 120 == 0)) { RefreshWallet(); g_needRefresh = false; } | 888 | if (g_needRefresh || (frame % 120 == 0)) { |
| 889 | RefreshWallet(); | ||
| 890 | g_needRefresh = false; | ||
| 891 | } | ||
| 748 | frame++; | 892 | frame++; |
| 749 | 893 | ||
| 750 | ImGui_ImplOpenGL3_NewFrame(); | 894 | ImGui_ImplOpenGL3_NewFrame(); |
| 751 | ImGui_ImplGlfw_NewFrame(); | 895 | ImGui_ImplGlfw_NewFrame(); |
| 752 | ImGui::NewFrame(); | 896 | ImGui::NewFrame(); |
| 753 | 897 | ||
| 898 | // Draw order matters: status bar last so it is always on top. | ||
| 754 | DrawMainWindow(); | 899 | DrawMainWindow(); |
| 755 | DrawSendDialog(); | 900 | DrawSendDialog(); |
| 756 | DrawOptionsDialog(); | 901 | DrawOptionsDialog(); |
| 757 | DrawAboutDialog(); | 902 | DrawAboutDialog(); |
| 903 | DrawStatusBar(); | ||
| 758 | 904 | ||
| 759 | ImGui::Render(); | 905 | ImGui::Render(); |
| 760 | int w, h; glfwGetFramebufferSize(window, &w, &h); | 906 | int w, h; |
| 907 | glfwGetFramebufferSize(window, &w, &h); | ||
| 761 | glViewport(0, 0, w, h); | 908 | glViewport(0, 0, w, h); |
| 762 | glClearColor(.08f,.08f,.08f,1); | 909 | glClearColor(.08f, .08f, .08f, 1.0f); |
| 763 | glClear(GL_COLOR_BUFFER_BIT); | 910 | glClear(GL_COLOR_BUFFER_BIT); |
| 764 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | 911 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); |
| 765 | glfwSwapBuffers(window); | 912 | glfwSwapBuffers(window); |