Bootloader installer 2.0 (C++ port)

This commit is contained in:
paravis committed 2026-11-07 20:41:09 +03:00
commit c538f115f1
15 files changed
+1569

No files matched your search

+415
View File
@@ -0,0 +1,415 @@
#include "app.h"
#include "util.h"
#include <windowsx.h>
#include <algorithm>
#include <string>
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 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<App*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
switch (message) {
case WM_CREATE: {
auto* create = reinterpret_cast<CREATESTRUCTW*>(lParam);
app = reinterpret_cast<App*>(create->lpCreateParams);
SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(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<LPARAM>(icon));
SendMessageW(hwnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(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];
FillRectColor(memory, x - 1, y - 1, bw + 2, bh + 2,
ColorFromHex(CLR_BORDER));
FillRectColor(memory, x, y, bw, bh, buttonBg[i]);
DrawTextRect(memory, kButtonNames[i], fontButton, x, y, bw, bh,
ColorFromHex(CLR_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);
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 (busy) {
downButton = -1;
InvalidateRect(hwnd, nullptr, FALSE);
return;
}
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<wchar_t>(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<int>(message.wParam);
}