// Igneum Miner for Windows: the window around the engine. One C++ file, built on the PC by BUILD-APP.bat // (MSVC, the WebView2 SDK from NuGet, static loader). It starts igneum-app.exe --wrapper next to it, reads // "URL ..." / "STATE {...}" / "EXIT" from its stdout, shows the URL in a WebView2 control, keeps a tray icon with the // state, and on quit writes "quit" to the engine's stdin and waits for EXIT (miners stop first, then the node). // Closing the window hides it to the tray; the miner keeps running. If the WebView2 runtime is missing the dashboard // opens in the default browser instead and the window says so. 4 October 2026. // // Untested on a real PC at the time of writing (written on a Mac): BUILD-APP.bat is the first run. #define WIN32_LEAN_AND_MEAN #ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #include #include #include #include #include #include #include #include "WebView2.h" #include "version.h" using namespace Microsoft::WRL; #define WM_ENGINE_LINE (WM_APP + 1) #define WM_TRAY (WM_APP + 2) #define ID_TRAY_OPEN 1001 #define ID_TRAY_PAUSE 1002 #define ID_TRAY_QUIT 1003 #define ID_QUIT_TIMER 7 #define IDI_APP 1 static HWND g_hwnd = nullptr; static HANDLE g_engine = nullptr, g_engineIn = nullptr, g_engineOut = nullptr; static ComPtr g_controller; static ComPtr g_webview; static std::wstring g_url, g_status = L"starting the engine"; static bool g_paused = false, g_quitting = false, g_exited = false, g_webviewOk = false, g_hintShown = false; static NOTIFYICONDATAW g_nid = {}; static std::wstring g_trayTitle = L"Igneum Miner"; static ULONGLONG g_quitStarted = 0; static std::wstring widen(const std::string& s) { if (s.empty()) return L""; int n = MultiByteToWideChar(CP_UTF8, 0, s.data(), (int)s.size(), nullptr, 0); std::wstring w(n, 0); MultiByteToWideChar(CP_UTF8, 0, s.data(), (int)s.size(), &w[0], n); return w; } static std::wstring exeDir() { wchar_t buf[MAX_PATH]; GetModuleFileNameW(nullptr, buf, MAX_PATH); std::wstring p(buf); size_t i = p.find_last_of(L"\\/"); return i == std::wstring::npos ? L"." : p.substr(0, i); } static void sendEngine(const char* line) { if (!g_engineIn) return; DWORD w = 0; WriteFile(g_engineIn, line, (DWORD)strlen(line), &w, nullptr); WriteFile(g_engineIn, "\n", 1, &w, nullptr); } static void setTray(const std::wstring& tip) { g_trayTitle = tip; wcsncpy_s(g_nid.szTip, tip.c_str(), _TRUNCATE); Shell_NotifyIconW(NIM_MODIFY, &g_nid); } static void repaintStatus() { InvalidateRect(g_hwnd, nullptr, TRUE); } // A tiny JSON number/string/bool reader for the STATE line (flat object, known keys). static std::string jsonField(const std::string& j, const char* key) { std::string k = std::string("\"") + key + "\":"; size_t i = j.find(k); if (i == std::string::npos) return ""; i += k.size(); while (i < j.size() && j[i] == ' ') i++; if (i < j.size() && j[i] == '"') { size_t e = j.find('"', i + 1); return e == std::string::npos ? "" : j.substr(i + 1, e - i - 1); } size_t e = i; while (e < j.size() && j[e] != ',' && j[e] != '}') e++; return j.substr(i, e - i); } static void applyState(const std::string& j) { std::string mining = jsonField(j, "mining"), node = jsonField(j, "node"), phase = jsonField(j, "phase"); g_paused = jsonField(j, "paused") == "true"; double hash = atof(jsonField(j, "hash_total").c_str()); std::string blocks = jsonField(j, "blocks"), accepted = jsonField(j, "accepted_total"); wchar_t tip[128]; if (jsonField(j, "quitting") == "true") swprintf_s(tip, L"Igneum Miner: stopping"); else if (phase != "dashboard") swprintf_s(tip, L"Igneum Miner: set up"); else if (mining == "mining") swprintf_s(tip, L"Igneum Miner: %.1f MH/s, %S blocks found, node %S", hash, accepted.c_str(), node.c_str()); else if (mining == "paused") swprintf_s(tip, L"Igneum Miner: paused, node %S", node.c_str()); else swprintf_s(tip, L"Igneum Miner: %S, node %S (%S blocks)", mining.c_str(), node.c_str(), blocks.c_str()); setTray(tip); } static void openInBrowser(const std::wstring& url) { ShellExecuteW(nullptr, L"open", url.c_str(), nullptr, nullptr, SW_SHOWNORMAL); } static void navigate() { if (g_webview && !g_url.empty()) g_webview->Navigate((g_url + L"?host=windows").c_str()); } static void initWebView() { std::wstring data = L""; wchar_t* local = nullptr; size_t len = 0; if (_wdupenv_s(&local, &len, L"LOCALAPPDATA") == 0 && local) { data = std::wstring(local) + L"\\igneum\\webview2"; free(local); } HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(nullptr, data.empty() ? nullptr : data.c_str(), nullptr, Callback([](HRESULT result, ICoreWebView2Environment* env) -> HRESULT { if (FAILED(result) || !env) { g_status = L"The WebView2 runtime is not installed. The dashboard opens in your browser; install the runtime from microsoft.com for the app window."; repaintStatus(); if (!g_url.empty()) { openInBrowser(g_url); g_hintShown = true; } return S_OK; } env->CreateCoreWebView2Controller(g_hwnd, Callback([](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT { if (FAILED(result) || !controller) { g_status = L"The app window could not start WebView2. The dashboard opens in your browser."; repaintStatus(); if (!g_url.empty()) { openInBrowser(g_url); g_hintShown = true; } return S_OK; } g_controller = controller; g_controller->get_CoreWebView2(&g_webview); g_webviewOk = true; ComPtr settings; if (g_webview && SUCCEEDED(g_webview->get_Settings(&settings)) && settings) { settings->put_AreDefaultContextMenusEnabled(FALSE); settings->put_IsStatusBarEnabled(FALSE); settings->put_AreDevToolsEnabled(FALSE); } // links off 127.0.0.1 open in the default browser g_webview->add_NewWindowRequested(Callback([](ICoreWebView2*, ICoreWebView2NewWindowRequestedEventArgs* args) -> HRESULT { LPWSTR uri = nullptr; if (SUCCEEDED(args->get_Uri(&uri)) && uri) { openInBrowser(uri); CoTaskMemFree(uri); } args->put_Handled(TRUE); return S_OK; }).Get(), nullptr); g_webview->add_NavigationStarting(Callback([](ICoreWebView2*, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT { LPWSTR uri = nullptr; if (SUCCEEDED(args->get_Uri(&uri)) && uri) { std::wstring u(uri); CoTaskMemFree(uri); if (u.rfind(L"http", 0) == 0 && u.find(L"127.0.0.1") == std::wstring::npos) { args->put_Cancel(TRUE); openInBrowser(u); } } return S_OK; }).Get(), nullptr); RECT rc; GetClientRect(g_hwnd, &rc); g_controller->put_Bounds(rc); COREWEBVIEW2_COLOR dark = { 255, 12, 12, 14 }; ComPtr c2; if (SUCCEEDED(g_controller.As(&c2)) && c2) c2->put_DefaultBackgroundColor(dark); g_status = L""; repaintStatus(); navigate(); return S_OK; }).Get()); return S_OK; }).Get()); if (FAILED(hr)) { g_status = L"The WebView2 runtime is not installed. The dashboard opens in your browser; install the runtime from microsoft.com for the app window."; repaintStatus(); if (!g_url.empty()) { openInBrowser(g_url); g_hintShown = true; } } } static bool startEngine() { SECURITY_ATTRIBUTES sa = { sizeof(sa), nullptr, TRUE }; HANDLE outR, outW, inR, inW; if (!CreatePipe(&outR, &outW, &sa, 0) || !CreatePipe(&inR, &inW, &sa, 0)) return false; SetHandleInformation(outR, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(inW, HANDLE_FLAG_INHERIT, 0); STARTUPINFOW si = { sizeof(si) }; si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = outW; si.hStdError = GetStdHandle(STD_ERROR_HANDLE); si.hStdInput = inR; PROCESS_INFORMATION pi = {}; std::wstring exe = exeDir() + L"\\igneum-app.exe"; std::wstring cmd = L"\"" + exe + L"\" --wrapper"; std::vector buf(cmd.begin(), cmd.end()); buf.push_back(0); BOOL ok = CreateProcessW(exe.c_str(), buf.data(), nullptr, nullptr, TRUE, CREATE_NO_WINDOW, nullptr, exeDir().c_str(), &si, &pi); CloseHandle(outW); CloseHandle(inR); if (!ok) { CloseHandle(outR); CloseHandle(inW); return false; } CloseHandle(pi.hThread); g_engine = pi.hProcess; g_engineIn = inW; g_engineOut = outR; std::thread([] { std::string acc; char chunk[4096]; DWORD n; while (ReadFile(g_engineOut, chunk, sizeof(chunk), &n, nullptr) && n > 0) { acc.append(chunk, n); size_t nl; while ((nl = acc.find('\n')) != std::string::npos) { std::string line = acc.substr(0, nl); acc.erase(0, nl + 1); if (!line.empty() && line.back() == '\r') line.pop_back(); PostMessageW(g_hwnd, WM_ENGINE_LINE, 0, (LPARAM) new std::string(line)); } } PostMessageW(g_hwnd, WM_ENGINE_LINE, 1, 0); }).detach(); return true; } static void showTrayMenu() { HMENU m = CreatePopupMenu(); AppendMenuW(m, MF_STRING, ID_TRAY_OPEN, L"Open Igneum Miner"); AppendMenuW(m, MF_STRING, ID_TRAY_PAUSE, g_paused ? L"Resume mining" : L"Pause mining"); AppendMenuW(m, MF_SEPARATOR, 0, nullptr); AppendMenuW(m, MF_STRING | MF_GRAYED, 0, g_trayTitle.c_str()); AppendMenuW(m, MF_SEPARATOR, 0, nullptr); AppendMenuW(m, MF_STRING, ID_TRAY_QUIT, L"Quit Igneum Miner"); POINT pt; GetCursorPos(&pt); SetForegroundWindow(g_hwnd); TrackPopupMenu(m, TPM_RIGHTBUTTON | TPM_BOTTOMALIGN, pt.x, pt.y, 0, g_hwnd, nullptr); DestroyMenu(m); } static void beginQuit() { if (g_quitting) return; g_quitting = true; g_quitStarted = GetTickCount64(); g_status = L"Stopping: the miner first, then the node"; setTray(L"Igneum Miner: stopping"); if (g_webview) g_webview->ExecuteScript(L"document.getElementById('pill-text').textContent='stopping'", nullptr); if (g_engine && !g_exited) { sendEngine("quit"); SetTimer(g_hwnd, ID_QUIT_TIMER, 500, nullptr); } else { DestroyWindow(g_hwnd); } } static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_SIZE: if (g_controller) { RECT rc; GetClientRect(hwnd, &rc); g_controller->put_Bounds(rc); } return 0; case WM_ENGINE_LINE: { if (wp == 1) { g_exited = true; if (g_quitting) { DestroyWindow(hwnd); return 0; } g_status = L"The engine stopped. Close this window and open Igneum Miner again."; setTray(L"Igneum Miner: stopped"); repaintStatus(); return 0; } std::string* line = (std::string*)lp; if (line->rfind("URL ", 0) == 0) { g_url = widen(line->substr(4)); if (g_webviewOk) navigate(); else if (!g_webview && g_status.find(L"WebView2") != std::wstring::npos && !g_hintShown) { openInBrowser(g_url); g_hintShown = true; } } else if (line->rfind("STATE ", 0) == 0) { applyState(line->substr(6)); } else if (line->rfind("FATAL ", 0) == 0) { g_status = widen(line->substr(6)); repaintStatus(); MessageBoxW(hwnd, g_status.c_str(), L"Igneum Miner", MB_OK | MB_ICONERROR); } else if (*line == "EXIT") { g_exited = true; if (g_quitting) DestroyWindow(hwnd); } delete line; return 0; } case WM_TIMER: if (wp == ID_QUIT_TIMER) { DWORD code = 0; bool gone = !g_engine || (GetExitCodeProcess(g_engine, &code) && code != STILL_ACTIVE); if (gone || g_exited || GetTickCount64() - g_quitStarted > 45000) { if (!gone && g_engine) TerminateProcess(g_engine, 1); KillTimer(hwnd, ID_QUIT_TIMER); DestroyWindow(hwnd); } } return 0; case WM_TRAY: if (lp == WM_LBUTTONUP || lp == WM_LBUTTONDBLCLK) { ShowWindow(hwnd, SW_SHOW); SetForegroundWindow(hwnd); } else if (lp == WM_RBUTTONUP || lp == WM_CONTEXTMENU) showTrayMenu(); return 0; case WM_COMMAND: switch (LOWORD(wp)) { case ID_TRAY_OPEN: ShowWindow(hwnd, SW_SHOW); SetForegroundWindow(hwnd); return 0; case ID_TRAY_PAUSE: sendEngine(g_paused ? "resume" : "pause"); return 0; case ID_TRAY_QUIT: beginQuit(); return 0; } return 0; case WM_CLOSE: // hide to the tray; the miner keeps running ShowWindow(hwnd, SW_HIDE); if (!g_hintShown) { g_nid.uFlags |= NIF_INFO; wcscpy_s(g_nid.szInfoTitle, L"Igneum Miner keeps mining"); wcscpy_s(g_nid.szInfo, L"The window is in the tray. Right-click the icon to pause or quit."); g_nid.dwInfoFlags = NIIF_INFO; Shell_NotifyIconW(NIM_MODIFY, &g_nid); g_nid.uFlags &= ~NIF_INFO; g_hintShown = true; } return 0; case WM_PAINT: { PAINTSTRUCT ps; HDC dc = BeginPaint(hwnd, &ps); RECT rc; GetClientRect(hwnd, &rc); HBRUSH bg = CreateSolidBrush(RGB(12, 12, 14)); FillRect(dc, &rc, bg); DeleteObject(bg); if (!g_status.empty()) { SetBkMode(dc, TRANSPARENT); SetTextColor(dc, RGB(154, 154, 158)); HFONT f = CreateFontW(-15, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, L"Segoe UI"); HFONT old = (HFONT)SelectObject(dc, f); RECT tr = rc; tr.left += 40; tr.right -= 40; DrawTextW(dc, g_status.c_str(), -1, &tr, DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_NOPREFIX | (g_webviewOk ? DT_BOTTOM : DT_VCENTER)); SelectObject(dc, old); DeleteObject(f); } EndPaint(hwnd, &ps); return 0; } case WM_DESTROY: Shell_NotifyIconW(NIM_DELETE, &g_nid); PostQuitMessage(0); return 0; } return DefWindowProcW(hwnd, msg, wp, lp); } // --version and --help print one line and exit, for the CI smoke run and support scripts. A windows-subsystem exe has // no console of its own: the text goes to the inherited stdout when there is one (a pipe or a file), else to the // parent's console. static bool handleCliFlags() { int argc = 0; LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (!argv) return false; std::wstring text; for (int i = 1; i < argc; i++) { std::wstring a = argv[i]; if (a == L"--version" || a == L"-V") text = L"Igneum Miner " IGNEUM_HOST_VERSION_STR L" (window host)\r\n"; else if (a == L"--help" || a == L"-h" || a == L"/?") text = L"Igneum Miner " IGNEUM_HOST_VERSION_STR L" (window host)\r\n" L"Usage: \"Igneum Miner.exe\" [--version | --help]\r\n" L"Without flags it starts igneum-app.exe --wrapper next to it and shows the dashboard in a WebView2 window.\r\n"; } LocalFree(argv); if (text.empty()) return false; HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); if (out == nullptr || out == INVALID_HANDLE_VALUE) { if (AttachConsole(ATTACH_PARENT_PROCESS)) out = GetStdHandle(STD_OUTPUT_HANDLE); } if (out && out != INVALID_HANDLE_VALUE) { int n = WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (int)text.size(), nullptr, 0, nullptr, nullptr); std::string utf8(n, 0); WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (int)text.size(), &utf8[0], n, nullptr, nullptr); DWORD written = 0; WriteFile(out, utf8.data(), (DWORD)utf8.size(), &written, nullptr); } return true; } int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE, PWSTR, int) { if (handleCliFlags()) return 0; HANDLE once = CreateMutexW(nullptr, TRUE, L"Local\\IgneumMinerWindow"); if (GetLastError() == ERROR_ALREADY_EXISTS) { HWND other = FindWindowW(L"IgneumMinerWindow", nullptr); if (other) { ShowWindow(other, SW_SHOW); SetForegroundWindow(other); } return 0; } CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); WNDCLASSW wc = {}; wc.lpfnWndProc = WndProc; wc.hInstance = hInst; wc.lpszClassName = L"IgneumMinerWindow"; wc.hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_APP)); wc.hCursor = LoadCursorW(nullptr, IDC_ARROW); wc.hbrBackground = CreateSolidBrush(RGB(12, 12, 14)); RegisterClassW(&wc); int w = 1120, h = 820; int sx = (GetSystemMetrics(SM_CXSCREEN) - w) / 2, sy = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; g_hwnd = CreateWindowExW(0, wc.lpszClassName, L"Igneum Miner", WS_OVERLAPPEDWINDOW, sx, sy, w, h, nullptr, nullptr, hInst, nullptr); ShowWindow(g_hwnd, SW_SHOW); g_nid.cbSize = sizeof(g_nid); g_nid.hWnd = g_hwnd; g_nid.uID = 1; g_nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; g_nid.uCallbackMessage = WM_TRAY; g_nid.hIcon = (HICON)LoadImageW(hInst, MAKEINTRESOURCEW(IDI_APP), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); wcscpy_s(g_nid.szTip, L"Igneum Miner: starting"); Shell_NotifyIconW(NIM_ADD, &g_nid); if (!startEngine()) { g_status = L"igneum-app.exe is missing next to this program. Run the installer again."; repaintStatus(); MessageBoxW(g_hwnd, g_status.c_str(), L"Igneum Miner", MB_OK | MB_ICONERROR); } initWebView(); MSG msg; while (GetMessageW(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessageW(&msg); } if (g_engine) { CloseHandle(g_engine); } CloseHandle(once); CoUninitialize(); return 0; }