#include "app.h" #include "util.h" #include #include #include namespace { const wchar_t* kMainClass = L"ChangeBootLoaderCpp"; const wchar_t* kWindowTitle = L"Change BootLoader"; const DWORD CLR_BG = 0xF1F4F9; const DWORD CLR_HEAD = 0x1E2A44; const DWORD CLR_DIM = 0x6B7280; const DWORD CLR_BORDER = 0xBFC9D6; const DWORD CLR_HOVER = 0xE4EEFF; const DWORD CLR_PRESS = 0xCFE0FF; const DWORD CLR_TEXT = 0x14202E; const DWORD CLR_BUSY_BG = 0xE9ECF1; const DWORD CLR_BUSY_BORDER = 0xD5DAE2; const DWORD CLR_BUSY_TEXT = 0xA7AEBA; const DWORD CLR_ACTIVE_BG = 0xD8E4F8; const DWORD CLR_ACTIVE_BORDER = 0x9BB8E8; const DWORD CLR_ACTIVE_TEXT = 0x2E4A78; const wchar_t* kButtonNames[7] = { L"XorBoot", L"Bootmgr", L"Grub4Dos", L"Grub2", L"Bootmgr", L"Grub2", L"Xorboot" }; const int kButtonPos[7][4] = { { 14, 72, 170, 32 }, { 192, 72, 170, 32 }, { 14, 110, 170, 32 }, { 192, 110, 170, 32 }, { 14, 168, 110, 32 }, { 132, 168, 110, 32 }, { 250, 168, 110, 32 } }; enum class HitKind { None, Header, Title, Sub, Min, Close, Button }; struct HitResult { HitKind kind = HitKind::None; int index = -1; }; void FillRectColor(HDC dc, int x, int y, int w, int h, COLORREF color) { RECT rect{ x, y, x + w, y + h }; HBRUSH brush = CreateSolidBrush(color); FillRect(dc, &rect, brush); DeleteObject(brush); } void DrawTextRect(HDC dc, const std::wstring& text, HFONT font, int x, int y, int w, int h, COLORREF color, UINT flags) { HGDIOBJ oldFont = SelectObject(dc, font); int oldMode = SetBkMode(dc, TRANSPARENT); COLORREF oldColor = SetTextColor(dc, color); RECT rect{ x, y, x + w, y + h }; DrawTextW(dc, text.c_str(), -1, &rect, flags | DT_NOPREFIX | DT_SINGLELINE); SetTextColor(dc, oldColor); SetBkMode(dc, oldMode); SelectObject(dc, oldFont); } HitResult HitTest(POINT point) { auto inside = [&point](int x, int y, int w, int h) { return point.x >= x && point.y >= y && point.x < x + w && point.y < y + h; }; if (inside(306, 2, 30, 18)) { return { HitKind::Min, -1 }; } if (inside(338, 2, 30, 18)) { return { HitKind::Close, -1 }; } if (inside(16, 2, 300, 24)) { return { HitKind::Title, -1 }; } if (inside(18, 27, 300, 14)) { return { HitKind::Sub, -1 }; } if (inside(0, 0, 376, 48)) { return { HitKind::Header, -1 }; } for (int i = 0; i < 7; ++i) { int x = kButtonPos[i][0] - 1; int y = kButtonPos[i][1] - 1; int w = kButtonPos[i][2] + 2; int h = kButtonPos[i][3] + 2; if (inside(x, y, w, h)) { return { HitKind::Button, i }; } } return {}; } LRESULT CALLBACK MainProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { App* app = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_USERDATA)); switch (message) { case WM_CREATE: { auto* create = reinterpret_cast(lParam); app = reinterpret_cast(create->lpCreateParams); SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast(app)); if (app) { app->Init(hwnd); } return 0; } case WM_ERASEBKGND: return 1; case WM_PAINT: { PAINTSTRUCT paint{}; HDC dc = BeginPaint(hwnd, &paint); if (app) { app->Paint(dc); } EndPaint(hwnd, &paint); return 0; } case WM_TIMER: if (app && wParam == 1) { app->OnPoll(); } return 0; case WM_CLOSE: DebugLog(L"WM_CLOSE busy=%d", (app && app->busy) ? 1 : 0); if (app && app->busy) { return 0; } DestroyWindow(hwnd); return 0; case WM_DESTROY: if (app) { app->DestroyFonts(); } PostQuitMessage(0); return 0; } return DefWindowProcW(hwnd, message, wParam, lParam); } } // namespace bool App::Init(HWND window) { hwnd = window; HDC dc = GetDC(hwnd); int dpi = GetDeviceCaps(dc, LOGPIXELSY); ReleaseDC(hwnd, dc); if (dpi <= 0) { dpi = 96; } auto create = [dpi](int points, int weight, const wchar_t* face) -> HFONT { // AutoIt/Windows GUI font sizing truncates fractional pixel heights; // this matters for 8 pt fonts (10 px, not 11 px). int height = -(points * dpi / 72); return CreateFontW(height, 0, 0, 0, weight, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, face); }; fontTitle = create(15, FW_BOLD, L"Tahoma"); fontSub = create(9, FW_NORMAL, L"Tahoma"); fontMin = create(10, FW_NORMAL, L"Tahoma"); fontClose = create(9, FW_NORMAL, L"Tahoma"); fontSection = create(8, FW_BOLD, L"Tahoma"); fontButton = create(10, FW_NORMAL, L"Segoe UI Semibold"); HICON icon = LoadIconW(hInst, MAKEINTRESOURCEW(1)); if (icon) { SendMessageW(hwnd, WM_SETICON, ICON_BIG, reinterpret_cast(icon)); SendMessageW(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast(icon)); } return true; } void App::DestroyFonts() { for (HFONT* font : { &fontTitle, &fontSub, &fontMin, &fontClose, &fontSection, &fontButton }) { if (*font) { DeleteObject(*font); *font = nullptr; } } } void App::Paint(HDC dc) { RECT client{}; GetClientRect(hwnd, &client); int w = client.right; int h = client.bottom; if (w <= 0 || h <= 0) { return; } HDC memory = CreateCompatibleDC(dc); HBITMAP bitmap = CreateCompatibleBitmap(dc, w, h); HGDIOBJ oldBitmap = SelectObject(memory, bitmap); FillRectColor(memory, 0, 0, 376, 208, ColorFromHex(CLR_BG)); FillRectColor(memory, 0, 0, 376, 48, ColorFromHex(CLR_HEAD)); DrawTextRect(memory, L"Paravis Flash", fontTitle, 16, 2, 300, 24, RGB(255, 255, 255), DT_LEFT | DT_TOP); DrawTextRect(memory, L"Смена загрузчика", fontSub, 18, 27, 300, 14, ColorFromHex(0xC7D0E0), DT_LEFT | DT_TOP); FillRectColor(memory, 306, 2, 30, 18, minBg); DrawTextRect(memory, L"—", fontMin, 306, 2, 30, 18, ColorFromHex(0xC7D0E0), DT_CENTER | DT_VCENTER); FillRectColor(memory, 338, 2, 30, 18, closeBg); DrawTextRect(memory, L"✕", fontClose, 338, 2, 30, 18, ColorFromHex(0xC7D0E0), DT_CENTER | DT_VCENTER); DrawTextRect(memory, L"LEGACY BIOS", fontSection, 15, 56, 200, 14, ColorFromHex(CLR_DIM), DT_LEFT | DT_TOP); DrawTextRect(memory, L"UEFI", fontSection, 15, 152, 200, 14, ColorFromHex(CLR_DIM), DT_LEFT | DT_TOP); for (int i = 0; i < 7; ++i) { int x = kButtonPos[i][0]; int y = kButtonPos[i][1]; int bw = kButtonPos[i][2]; int bh = kButtonPos[i][3]; COLORREF background = buttonBg[i]; COLORREF border = ColorFromHex(CLR_BORDER); COLORREF text = ColorFromHex(CLR_TEXT); if (busy) { if (i == activeButton) { background = ColorFromHex(CLR_ACTIVE_BG); border = ColorFromHex(CLR_ACTIVE_BORDER); text = ColorFromHex(CLR_ACTIVE_TEXT); } else { background = ColorFromHex(CLR_BUSY_BG); border = ColorFromHex(CLR_BUSY_BORDER); text = ColorFromHex(CLR_BUSY_TEXT); } } FillRectColor(memory, x - 1, y - 1, bw + 2, bh + 2, border); FillRectColor(memory, x, y, bw, bh, background); DrawTextRect(memory, kButtonNames[i], fontButton, x, y, bw, bh, text, DT_CENTER | DT_VCENTER); } BitBlt(dc, 0, 0, w, h, memory, 0, 0, SRCCOPY); SelectObject(memory, oldBitmap); DeleteObject(bitmap); DeleteDC(memory); } void App::OnPoll() { if (modal) { return; } POINT screen{}; GetCursorPos(&screen); bool leftDown = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0; POINT client = screen; ScreenToClient(hwnd, &client); HitResult hit = HitTest(client); if (busy) { downButton = -1; for (int i = 0; i < 7; ++i) { buttonBg[i] = (i == activeButton) ? ColorFromHex(CLR_ACTIVE_BG) : ColorFromHex(CLR_BUSY_BG); } minBg = ColorFromHex(CLR_HEAD); closeBg = ColorFromHex(CLR_HEAD); InvalidateRect(hwnd, nullptr, FALSE); return; } COLORREF minColor = ColorFromHex(CLR_HEAD); COLORREF closeColor = ColorFromHex(CLR_HEAD); if (hit.kind == HitKind::Min) { minColor = leftDown ? ColorFromHex(0x3A4A6B) : ColorFromHex(0x2C3A57); } if (hit.kind == HitKind::Close) { closeColor = leftDown ? ColorFromHex(0xC0392B) : ColorFromHex(0xE04B3F); } minBg = minColor; closeBg = closeColor; if (hit.kind == HitKind::Min && leftDown) { minDown = true; } else if (minDown) { minDown = false; if (hit.kind == HitKind::Min && !leftDown) { ShowWindow(hwnd, SW_MINIMIZE); } } if (hit.kind == HitKind::Close && leftDown) { closeDown = true; } else if (closeDown) { closeDown = false; if (hit.kind == HitKind::Close && !leftDown && !busy) { DestroyWindow(hwnd); return; } } if ((hit.kind == HitKind::Header || hit.kind == HitKind::Title || hit.kind == HitKind::Sub) && leftDown && !dragging) { POINT mouse{}; RECT windowRect{}; GetCursorPos(&mouse); GetWindowRect(hwnd, &windowRect); dragging = true; dragX = mouse.x - windowRect.left; dragY = mouse.y - windowRect.top; } if (dragging) { if (!leftDown) { dragging = false; } else { POINT mouse{}; GetCursorPos(&mouse); SetWindowPos(hwnd, nullptr, mouse.x - dragX, mouse.y - dragY, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } for (int i = 0; i < 7; ++i) { COLORREF background = RGB(255, 255, 255); if (hit.kind == HitKind::Button && hit.index == i) { background = leftDown ? ColorFromHex(CLR_PRESS) : ColorFromHex(CLR_HOVER); } buttonBg[i] = background; } if (leftDown) { if (hit.kind == HitKind::Button) { downButton = hit.index; } else { downButton = -1; } } else if (downButton >= 0) { int index = downButton; downButton = -1; if (hit.kind == HitKind::Button && hit.index == index) { RunAction(index); } } InvalidateRect(hwnd, nullptr, FALSE); } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int) { App app; app.hInst = hInstance; std::wstring exeDir = GetExeDir(); if (exeDir.size() < 2 || exeDir[1] != L':') { MessageBoxW(nullptr, L"Не удалось определить диск установщика.", kWindowTitle, MB_ICONERROR | MB_OK); return 1; } wchar_t letter = exeDir[0]; if (letter >= L'a' && letter <= L'z') { letter = static_cast(letter - L'a' + L'A'); } app.disk = std::wstring(1, letter) + L":"; app.boot = app.disk + L"\\efi\\Paravis\\Boot"; app.bootice = app.boot + L"\\bootice.exe"; if (!FileExists(app.bootice) || !FileExists(app.boot + L"\\NOT.DEL")) { std::wstring text = L"Этот диск не является сборкой Paravis " L"(нет bootice.exe или NOT.DEL):\r\n" + app.disk; MessageBoxW(nullptr, text.c_str(), kWindowTitle, MB_ICONERROR | MB_OK); return 0; } WNDCLASSEXW windowClass{}; windowClass.cbSize = sizeof(windowClass); windowClass.lpfnWndProc = MainProc; windowClass.hInstance = hInstance; windowClass.hCursor = LoadCursorW(nullptr, IDC_ARROW); windowClass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(1)); windowClass.hIconSm = LoadIconW(hInstance, MAKEINTRESOURCEW(1)); windowClass.lpszClassName = kMainClass; if (!RegisterClassExW(&windowClass)) { return 1; } RECT workArea{}; SystemParametersInfoW(SPI_GETWORKAREA, 0, &workArea, 0); int workWidth = workArea.right - workArea.left; int workHeight = workArea.bottom - workArea.top; int x = workArea.left + (workWidth - app.width) / 2; int y = workArea.top + (workHeight - app.height) / 2; app.hwnd = CreateWindowExW(WS_EX_APPWINDOW, kMainClass, kWindowTitle, WS_POPUP | WS_CLIPCHILDREN, x, y, app.width, app.height, nullptr, nullptr, hInstance, &app); if (!app.hwnd) { return 1; } ShowWindow(app.hwnd, SW_SHOW); UpdateWindow(app.hwnd); SetForegroundWindow(app.hwnd); SetTimer(app.hwnd, 1, 40, nullptr); MSG message{}; while (GetMessageW(&message, nullptr, 0, 0) > 0) { TranslateMessage(&message); DispatchMessageW(&message); } return static_cast(message.wParam); }