Bootloader installer 2.0 (C++ port)
This commit is contained in:
commit
c538f115f1
15 files changed
+1569
No files matched your search
+415
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user