442 lines
13 KiB
C++
442 lines
13 KiB
C++
#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 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<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];
|
|
|
|
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<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);
|
|
}
|