Files
Polyhedron/projects/PolyhedronGui/launcher_gui.cpp
T
Efim Beshmenev ffd46f89e3 Neural data cache
2026-07-12 14:25:18 +03:00

908 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <algorithm>
#include <cerrno>
#include <cstdint>
#include <cwchar>
#include <cwctype>
#include <filesystem>
#include <limits>
#include <string>
#include <thread>
#include <vector>
namespace fs = std::filesystem;
namespace {
constexpr UINT WM_APPEND_LOG = WM_APP + 1;
constexpr UINT WM_PROCESS_DONE = WM_APP + 2;
constexpr int LOG_TEXT_LIMIT = 8 * 1024 * 1024;
constexpr int LOG_TRIM_KEEP = 6 * 1024 * 1024;
constexpr int LOG_APPEND_MARGIN = 32768;
constexpr int TOPOLOGY_COUNT = 59;
constexpr int ID_CUDA_CHAINS = 1001;
constexpr int ID_CUDA_ITERS = 1002;
constexpr int ID_MINUTES = 1003;
constexpr int ID_TOPOLOGY_FROM = 1004;
constexpr int ID_TOPOLOGY_TO = 1005;
constexpr int ID_CHECKPOINT_SECONDS = 1006;
constexpr int ID_SEARCH_MODE = 1007;
constexpr int ID_START = 2001;
constexpr int ID_STOP = 2002;
enum class SearchState {
idle,
running,
stopping
};
HWND g_main_window = nullptr;
HWND g_log = nullptr;
HWND g_status = nullptr;
HWND g_start = nullptr;
HWND g_stop = nullptr;
HWND g_cuda_chains = nullptr;
HWND g_cuda_iters = nullptr;
HWND g_minutes = nullptr;
HWND g_topology_from = nullptr;
HWND g_topology_to = nullptr;
HWND g_checkpoint_seconds = nullptr;
HWND g_search_mode = nullptr;
std::vector<HWND> g_setting_controls;
HFONT g_ui_font = nullptr;
HFONT g_title_font = nullptr;
HFONT g_mono_font = nullptr;
fs::path g_root;
fs::path g_stop_file;
PROCESS_INFORMATION g_process{};
SearchState g_search_state = SearchState::idle;
bool g_close_after_stop = false;
std::wstring quote_arg(const std::wstring& value) {
if (value.empty()) {
return L"\"\"";
}
if (value.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
return value;
}
std::wstring result;
result.push_back(L'"');
size_t backslash_count = 0;
for (const wchar_t ch : value) {
if (ch == L'\\') {
++backslash_count;
continue;
}
if (ch == L'"') {
result.append(backslash_count * 2 + 1, L'\\');
result.push_back(L'"');
} else {
result.append(backslash_count, L'\\');
result.push_back(ch);
}
backslash_count = 0;
}
result.append(backslash_count * 2, L'\\');
result.push_back(L'"');
return result;
}
std::wstring join_args(const std::vector<std::wstring>& args) {
std::wstring result;
for (const std::wstring& arg : args) {
if (!result.empty()) {
result.push_back(L' ');
}
result += quote_arg(arg);
}
return result;
}
std::wstring get_text(HWND hwnd) {
const int length = GetWindowTextLengthW(hwnd);
std::vector<wchar_t> buffer(static_cast<size_t>(length) + 1, L'\0');
GetWindowTextW(hwnd, buffer.data(), static_cast<int>(buffer.size()));
return std::wstring(buffer.data());
}
int make_automatic_seed() {
LARGE_INTEGER counter{};
QueryPerformanceCounter(&counter);
std::uint64_t value = static_cast<std::uint64_t>(counter.QuadPart);
value ^= static_cast<std::uint64_t>(GetTickCount64()) << 21;
value ^= static_cast<std::uint64_t>(GetCurrentProcessId()) << 37;
value ^= value >> 30;
value *= 0xbf58476d1ce4e5b9ULL;
value ^= value >> 27;
return 1 + static_cast<int>(value % 2147483646ULL);
}
std::wstring now_time() {
SYSTEMTIME st{};
GetLocalTime(&st);
wchar_t buffer[32]{};
swprintf_s(buffer, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
return buffer;
}
std::wstring win32_error_message(DWORD error) {
wchar_t* message = nullptr;
const DWORD length = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
error,
0,
reinterpret_cast<wchar_t*>(&message),
0,
nullptr);
if (length == 0 || message == nullptr) {
return L"код " + std::to_wstring(error);
}
std::wstring result(message, length);
LocalFree(message);
while (!result.empty() && (result.back() == L'\r' || result.back() == L'\n' || result.back() == L' ')) {
result.pop_back();
}
return result + L" (код " + std::to_wstring(error) + L")";
}
void post_log(const std::wstring& text) {
auto* payload = new std::wstring(text);
const HWND window = g_main_window;
if (window == nullptr ||
!PostMessageW(window, WM_APPEND_LOG, 0, reinterpret_cast<LPARAM>(payload))) {
delete payload;
}
}
std::wstring bytes_to_wide(const char* data, int length) {
if (length <= 0) {
return L"";
}
int needed = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, data, length, nullptr, 0);
UINT codepage = CP_UTF8;
DWORD flags = MB_ERR_INVALID_CHARS;
if (needed <= 0) {
codepage = CP_ACP;
flags = 0;
needed = MultiByteToWideChar(codepage, flags, data, length, nullptr, 0);
}
if (needed <= 0) {
return L"";
}
std::wstring result(static_cast<size_t>(needed), L'\0');
MultiByteToWideChar(codepage, flags, data, length, result.data(), needed);
return result;
}
void append_log_direct(const std::wstring& text) {
if (g_log == nullptr) {
return;
}
const std::wstring line = L"[" + now_time() + L"] " + text + L"\r\n";
const int length = GetWindowTextLengthW(g_log);
if (length + static_cast<int>(line.size()) > LOG_TEXT_LIMIT - LOG_APPEND_MARGIN) {
const int remove_until = std::max(0, length - LOG_TRIM_KEEP);
if (remove_until > 0) {
SendMessageW(g_log, WM_SETREDRAW, FALSE, 0);
SendMessageW(g_log, EM_SETSEL, 0, remove_until);
SendMessageW(
g_log,
EM_REPLACESEL,
FALSE,
reinterpret_cast<LPARAM>(L"[... старые строки журнала удалены ...]\r\n"));
SendMessageW(g_log, WM_SETREDRAW, TRUE, 0);
InvalidateRect(g_log, nullptr, TRUE);
}
}
const int end = GetWindowTextLengthW(g_log);
SendMessageW(g_log, EM_SETSEL, end, end);
SendMessageW(g_log, EM_REPLACESEL, FALSE, reinterpret_cast<LPARAM>(line.c_str()));
SendMessageW(g_log, EM_SCROLLCARET, 0, 0);
}
void set_search_state(SearchState state, const std::wstring& status) {
g_search_state = state;
const BOOL settings_enabled = state == SearchState::idle ? TRUE : FALSE;
for (HWND control : g_setting_controls) {
EnableWindow(control, settings_enabled);
}
EnableWindow(g_start, state == SearchState::idle ? TRUE : FALSE);
EnableWindow(g_stop, state == SearchState::running ? TRUE : FALSE);
if (g_status != nullptr) {
SetWindowTextW(g_status, status.c_str());
}
}
fs::path find_root() {
std::vector<wchar_t> buffer(32768, L'\0');
const DWORD length = GetModuleFileNameW(
nullptr,
buffer.data(),
static_cast<DWORD>(buffer.size()));
fs::path candidate = fs::path(std::wstring(buffer.data(), length)).parent_path();
while (!candidate.empty()) {
if (fs::exists(candidate / L"CMakeLists.txt") &&
fs::exists(candidate / L"data" / L"topologies.txt")) {
return candidate;
}
const fs::path parent = candidate.parent_path();
if (parent == candidate) {
break;
}
candidate = parent;
}
return fs::path(std::wstring(buffer.data(), length)).parent_path();
}
fs::path main_exe() {
const fs::path msbuild =
g_root / L"build" / L"msbuild" / L"bin" / L"x64" / L"Release" / L"Szilassi.exe";
if (fs::exists(msbuild)) {
return msbuild;
}
return g_root / L"build" / L"vs2026" / L"Release" / L"neighborly_main.exe";
}
void close_process_handles() {
if (g_process.hProcess != nullptr) {
CloseHandle(g_process.hProcess);
g_process.hProcess = nullptr;
}
if (g_process.hThread != nullptr) {
CloseHandle(g_process.hThread);
g_process.hThread = nullptr;
}
g_process.dwProcessId = 0;
g_process.dwThreadId = 0;
}
bool parse_integer_setting(
HWND control,
const wchar_t* name,
int minimum,
int maximum,
int& result
) {
const std::wstring text = get_text(control);
const wchar_t* first = text.c_str();
while (*first != L'\0' && iswspace(*first)) {
++first;
}
errno = 0;
wchar_t* end = nullptr;
const long long value = wcstoll(first, &end, 10);
while (end != nullptr && *end != L'\0' && iswspace(*end)) {
++end;
}
if (first == end || end == nullptr || *end != L'\0' || errno == ERANGE ||
value < minimum || value > maximum) {
const std::wstring message =
std::wstring(L"Поле «") + name + L"» должно содержать целое число от " +
std::to_wstring(minimum) + L" до " + std::to_wstring(maximum) + L".";
MessageBoxW(g_main_window, message.c_str(), L"Некорректные настройки", MB_OK | MB_ICONWARNING);
SetFocus(control);
SendMessageW(control, EM_SETSEL, 0, -1);
return false;
}
result = static_cast<int>(value);
return true;
}
fs::path make_stop_path() {
SYSTEMTIME st{};
GetLocalTime(&st);
wchar_t name[128]{};
swprintf_s(
name,
L"cuda_stop_%04d%02d%02d_%02d%02d%02d_%03d_pid%lu.flag",
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
st.wMilliseconds,
static_cast<unsigned long>(GetCurrentProcessId()));
return g_root / L"runtime" / L"reports" / name;
}
bool start_process(
const std::wstring& title,
const fs::path& exe,
const std::vector<std::wstring>& args,
const fs::path& stop_file
) {
if (g_search_state != SearchState::idle) {
return false;
}
if (!fs::exists(exe)) {
const std::wstring message =
L"Не найден исполняемый файл:\n" + exe.wstring() +
L"\n\nСоберите preset vs2026-release через CMake или отдельно "
L"projects\\Szilassi\\Szilassi.vcxproj в конфигурации Release|x64.";
MessageBoxW(g_main_window, message.c_str(), L"Файл не найден", MB_OK | MB_ICONWARNING);
return false;
}
std::error_code directory_error;
fs::create_directories(stop_file.parent_path(), directory_error);
fs::create_directories(g_root / L"results" / L"search", directory_error);
std::error_code remove_error;
fs::remove(stop_file, remove_error);
if (remove_error || fs::exists(stop_file)) {
const std::wstring message =
L"Не удалось подготовить stop-файл:\n" + stop_file.wstring();
MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR);
return false;
}
SECURITY_ATTRIBUTES sa{};
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
HANDLE read_pipe = nullptr;
HANDLE write_pipe = nullptr;
if (!CreatePipe(&read_pipe, &write_pipe, &sa, 0)) {
const DWORD error = GetLastError();
const std::wstring message =
L"Не удалось создать канал вывода:\n" + win32_error_message(error);
MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR);
return false;
}
if (!SetHandleInformation(read_pipe, HANDLE_FLAG_INHERIT, 0)) {
const DWORD error = GetLastError();
CloseHandle(read_pipe);
CloseHandle(write_pipe);
const std::wstring message =
L"Не удалось настроить канал вывода:\n" + win32_error_message(error);
MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR);
return false;
}
std::wstring command = quote_arg(exe.wstring());
const std::wstring argument_string = join_args(args);
if (!argument_string.empty()) {
command += L" " + argument_string;
}
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESTDHANDLES;
startup.hStdOutput = write_pipe;
startup.hStdError = write_pipe;
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
close_process_handles();
PROCESS_INFORMATION process{};
std::vector<wchar_t> mutable_command(command.begin(), command.end());
mutable_command.push_back(L'\0');
append_log_direct(L"=== " + title + L" ===");
append_log_direct(command);
const BOOL created = CreateProcessW(
nullptr,
mutable_command.data(),
nullptr,
nullptr,
TRUE,
CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS,
nullptr,
g_root.c_str(),
&startup,
&process);
const DWORD create_error = created ? ERROR_SUCCESS : GetLastError();
CloseHandle(write_pipe);
if (!created) {
CloseHandle(read_pipe);
const std::wstring message =
L"Не удалось запустить поиск:\n" + win32_error_message(create_error);
MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR);
return false;
}
g_process = process;
g_stop_file = stop_file;
g_close_after_stop = false;
set_search_state(SearchState::running, L"Поиск выполняется");
std::thread([read_pipe, process]() {
char buffer[4096];
DWORD bytes_read = 0;
while (ReadFile(read_pipe, buffer, sizeof(buffer), &bytes_read, nullptr) && bytes_read > 0) {
post_log(bytes_to_wide(buffer, static_cast<int>(bytes_read)));
}
CloseHandle(read_pipe);
WaitForSingleObject(process.hProcess, INFINITE);
DWORD exit_code = 0;
GetExitCodeProcess(process.hProcess, &exit_code);
const HWND window = g_main_window;
if (window != nullptr) {
PostMessageW(window, WM_PROCESS_DONE, static_cast<WPARAM>(exit_code), 0);
}
}).detach();
return true;
}
bool create_stop_file() {
if (g_stop_file.empty()) {
return false;
}
HANDLE file = CreateFileW(
g_stop_file.c_str(),
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr);
if (file == INVALID_HANDLE_VALUE) {
const DWORD error = GetLastError();
const std::wstring message =
L"Не удалось запросить штатную остановку:\n" + win32_error_message(error) +
L"\n\nПоиск продолжает работать; данные не были принудительно прерваны.";
MessageBoxW(g_main_window, message.c_str(), L"Ошибка остановки", MB_OK | MB_ICONERROR);
append_log_direct(message);
return false;
}
static constexpr char marker[] = "stop\n";
DWORD written = 0;
WriteFile(file, marker, static_cast<DWORD>(sizeof(marker) - 1), &written, nullptr);
FlushFileBuffers(file);
CloseHandle(file);
return true;
}
void request_stop(bool close_after_stop) {
if (g_search_state == SearchState::idle) {
if (close_after_stop) {
DestroyWindow(g_main_window);
}
return;
}
if (g_search_state == SearchState::stopping) {
g_close_after_stop = g_close_after_stop || close_after_stop;
if (g_close_after_stop) {
SetWindowTextW(g_status, L"Останавливается, сохраняет checkpoint и закроется...");
}
return;
}
if (!create_stop_file()) {
return;
}
g_close_after_stop = close_after_stop;
append_log_direct(L"Запрошена штатная остановка. Ожидаю завершения CUDA-пакета и сохранения checkpoint...");
set_search_state(
SearchState::stopping,
close_after_stop
? L"Останавливается, сохраняет checkpoint и закроется..."
: L"Останавливается и сохраняет checkpoint...");
}
HWND make_label(HWND parent, const std::wstring& text, int x, int y, int width, int height) {
HWND control = CreateWindowExW(
0,
L"STATIC",
text.c_str(),
WS_CHILD | WS_VISIBLE,
x,
y,
width,
height,
parent,
nullptr,
nullptr,
nullptr);
SendMessageW(control, WM_SETFONT, reinterpret_cast<WPARAM>(g_ui_font), TRUE);
return control;
}
HWND make_number_edit(
HWND parent,
int id,
const std::wstring& text,
int x,
int y,
int width,
int height
) {
HWND control = CreateWindowExW(
WS_EX_CLIENTEDGE,
L"EDIT",
text.c_str(),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_NUMBER,
x,
y,
width,
height,
parent,
reinterpret_cast<HMENU>(static_cast<INT_PTR>(id)),
nullptr,
nullptr);
SendMessageW(control, WM_SETFONT, reinterpret_cast<WPARAM>(g_ui_font), TRUE);
SendMessageW(control, EM_SETLIMITTEXT, 10, 0);
g_setting_controls.push_back(control);
return control;
}
HWND make_button(
HWND parent,
int id,
const std::wstring& text,
int x,
int y,
int width,
int height
) {
HWND control = CreateWindowExW(
0,
L"BUTTON",
text.c_str(),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
x,
y,
width,
height,
parent,
reinterpret_cast<HMENU>(static_cast<INT_PTR>(id)),
nullptr,
nullptr);
SendMessageW(control, WM_SETFONT, reinterpret_cast<WPARAM>(g_ui_font), TRUE);
return control;
}
HWND make_search_mode_combo(HWND parent, int x, int y, int width, int height) {
HWND control = CreateWindowExW(
0,
L"COMBOBOX",
L"",
WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST,
x,
y,
width,
height,
parent,
reinterpret_cast<HMENU>(static_cast<INT_PTR>(ID_SEARCH_MODE)),
nullptr,
nullptr);
SendMessageW(control, WM_SETFONT, reinterpret_cast<WPARAM>(g_ui_font), TRUE);
SendMessageW(control, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Обычный"));
SendMessageW(control, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Проработка худших"));
SendMessageW(control, CB_SETCURSEL, 0, 0);
g_setting_controls.push_back(control);
return control;
}
void create_controls(HWND window) {
g_title_font = CreateFontW(
24, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH, L"Segoe UI");
g_ui_font = CreateFontW(
17, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
DEFAULT_PITCH, L"Segoe UI");
g_mono_font = CreateFontW(
15, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY,
FIXED_PITCH, L"Consolas");
HWND title = make_label(window, L"Szilassi — CUDA-поиск", 18, 14, 430, 32);
SendMessageW(title, WM_SETFONT, reinterpret_cast<WPARAM>(g_title_font), TRUE);
make_label(window, L"Папка проекта: " + g_root.wstring(), 20, 50, 980, 24);
make_label(window, L"Цепочек (0 = авто)", 22, 86, 138, 22);
g_cuda_chains = make_number_edit(window, ID_CUDA_CHAINS, L"0", 22, 110, 120, 27);
make_label(window, L"Итераций / пакет", 160, 86, 150, 22);
g_cuda_iters = make_number_edit(window, ID_CUDA_ITERS, L"64", 160, 110, 135, 27);
make_label(window, L"Минут", 313, 86, 85, 22);
g_minutes = make_number_edit(window, ID_MINUTES, L"480", 313, 110, 85, 27);
make_label(window, L"Топологии: от", 416, 86, 125, 22);
g_topology_from = make_number_edit(window, ID_TOPOLOGY_FROM, L"0", 416, 110, 70, 27);
make_label(window, L"до", 504, 86, 45, 22);
g_topology_to = make_number_edit(window, ID_TOPOLOGY_TO, L"58", 504, 110, 70, 27);
make_label(window, L"Checkpoint, сек", 592, 86, 135, 22);
g_checkpoint_seconds =
make_number_edit(window, ID_CHECKPOINT_SECONDS, L"30", 592, 110, 125, 27);
make_label(window, L"Режим поиска", 735, 86, 245, 22);
g_search_mode = make_search_mode_combo(window, 735, 110, 245, 180);
make_label(
window,
L"CUDA-устройство выбирается автоматически. Поиск: FP32; финальная проверка кандидатов: DD.",
22,
149,
960,
24);
g_start = make_button(window, ID_START, L"Запустить поиск", 22, 184, 220, 38);
g_stop = make_button(window, ID_STOP, L"Остановить поиск", 254, 184, 220, 38);
g_status = make_label(window, L"Готово", 22, 238, 960, 24);
g_log = CreateWindowExW(
WS_EX_CLIENTEDGE,
L"EDIT",
L"",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
18,
270,
982,
400,
window,
nullptr,
nullptr,
nullptr);
SendMessageW(g_log, EM_SETLIMITTEXT, static_cast<WPARAM>(LOG_TEXT_LIMIT), 0);
SendMessageW(g_log, WM_SETFONT, reinterpret_cast<WPARAM>(g_mono_font), TRUE);
set_search_state(SearchState::idle, L"Готово");
append_log_direct(L"Интерфейс готов. Существующие checkpoint будут подхвачены автоматически.");
append_log_direct(L"Исполняемый файл поиска: " + main_exe().wstring());
}
void resize_controls(HWND window) {
if (g_log == nullptr) {
return;
}
RECT client{};
GetClientRect(window, &client);
const int width = client.right - client.left;
const int height = client.bottom - client.top;
MoveWindow(g_log, 18, 270, std::max(360, width - 36), std::max(140, height - 290), TRUE);
}
void start_search() {
if (g_search_state != SearchState::idle) {
return;
}
int cuda_chains = 0;
int cuda_iters = 0;
int minutes = 0;
int topology_from = 0;
int topology_to = 0;
int checkpoint_seconds = 0;
if (!parse_integer_setting(g_cuda_chains, L"GPU-цепочек", 0, 1048576, cuda_chains) ||
!parse_integer_setting(g_cuda_iters, L"Итераций / пакет", 1, 64, cuda_iters) ||
!parse_integer_setting(g_minutes, L"Минут", 0, 525600, minutes) ||
!parse_integer_setting(g_topology_from, L"Топологии: от", 0, TOPOLOGY_COUNT - 1, topology_from) ||
!parse_integer_setting(g_topology_to, L"Топологии: до", 0, TOPOLOGY_COUNT - 1, topology_to) ||
!parse_integer_setting(
g_checkpoint_seconds,
L"Checkpoint, сек",
5,
3600,
checkpoint_seconds)) {
return;
}
if (topology_from > topology_to) {
MessageBoxW(
g_main_window,
L"Начало диапазона топологий не может быть больше конца.",
L"Некорректный диапазон",
MB_OK | MB_ICONWARNING);
SetFocus(g_topology_from);
return;
}
const int seed = make_automatic_seed();
append_log_direct(L"Seed запуска: " + std::to_wstring(seed));
const bool prioritize_worst =
SendMessageW(g_search_mode, CB_GETCURSEL, 0, 0) == 1;
append_log_direct(
prioritize_worst
? L"Режим поиска: проработка худших вариантов."
: L"Режим поиска: обычный.");
const fs::path stop_path = make_stop_path();
const std::wstring title =
L"CUDA-поиск, топологии " + std::to_wstring(topology_from) +
L"" + std::to_wstring(topology_to);
std::vector<std::wstring> args{
L"--global-search",
L"--cuda",
L"--seed", std::to_wstring(seed),
L"--cuda-chains", std::to_wstring(cuda_chains),
L"--cuda-iters", std::to_wstring(cuda_iters),
L"--minutes", std::to_wstring(minutes),
L"--topology-from", std::to_wstring(topology_from),
L"--topology-to", std::to_wstring(topology_to),
L"--checkpoint-seconds", std::to_wstring(checkpoint_seconds),
L"--stop-file", stop_path.wstring(),
L"--global-dir", L"results\\search"
};
if (prioritize_worst) {
args.push_back(L"--prioritize-worst");
}
start_process(
title,
main_exe(),
args,
stop_path);
}
void handle_command(int id) {
switch (id) {
case ID_START:
start_search();
break;
case ID_STOP:
request_stop(false);
break;
}
}
LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
switch (message) {
case WM_CREATE:
g_main_window = window;
create_controls(window);
return 0;
case WM_SIZE:
resize_controls(window);
return 0;
case WM_COMMAND:
if (HIWORD(wparam) == BN_CLICKED) {
handle_command(LOWORD(wparam));
}
return 0;
case WM_APPEND_LOG: {
auto* text = reinterpret_cast<std::wstring*>(lparam);
append_log_direct(*text);
delete text;
return 0;
}
case WM_PROCESS_DONE: {
const DWORD exit_code = static_cast<DWORD>(wparam);
const bool stopped_by_user = g_search_state == SearchState::stopping;
append_log_direct(L"Процесс завершён, код выхода: " + std::to_wstring(exit_code));
close_process_handles();
if (!g_stop_file.empty()) {
if (exit_code == 0) {
std::error_code remove_error;
fs::remove(g_stop_file, remove_error);
}
g_stop_file.clear();
}
if (exit_code == 0 && stopped_by_user) {
set_search_state(SearchState::idle, L"Поиск остановлен, checkpoint сохранён");
} else if (exit_code == 0) {
set_search_state(SearchState::idle, L"Поиск завершён");
} else {
set_search_state(
SearchState::idle,
L"Поиск завершился с ошибкой, код " + std::to_wstring(exit_code));
}
if (g_close_after_stop) {
g_close_after_stop = false;
DestroyWindow(window);
}
return 0;
}
case WM_QUERYENDSESSION:
if (g_search_state != SearchState::idle) {
request_stop(true);
}
return TRUE;
case WM_CLOSE:
if (g_search_state == SearchState::running) {
const int answer = MessageBoxW(
window,
L"Поиск ещё выполняется. Штатно остановить его, сохранить checkpoint и закрыть окно?",
L"Поиск выполняется",
MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2);
if (answer == IDYES) {
request_stop(true);
}
return 0;
}
if (g_search_state == SearchState::stopping) {
g_close_after_stop = true;
SetWindowTextW(g_status, L"Останавливается, сохраняет checkpoint и закроется...");
return 0;
}
DestroyWindow(window);
return 0;
case WM_DESTROY:
g_main_window = nullptr;
if (g_search_state == SearchState::idle) {
close_process_handles();
}
if (g_ui_font != nullptr) {
DeleteObject(g_ui_font);
g_ui_font = nullptr;
}
if (g_title_font != nullptr) {
DeleteObject(g_title_font);
g_title_font = nullptr;
}
if (g_mono_font != nullptr) {
DeleteObject(g_mono_font);
g_mono_font = nullptr;
}
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(window, message, wparam, lparam);
}
} // namespace
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int show_command) {
g_root = find_root();
WNDCLASSW window_class{};
window_class.lpfnWndProc = window_proc;
window_class.hInstance = instance;
window_class.lpszClassName = L"PolyhedronGuiWindow";
window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
window_class.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
RegisterClassW(&window_class);
HWND window = CreateWindowExW(
0,
window_class.lpszClassName,
L"Szilassi — CUDA-поиск",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
1040,
740,
nullptr,
nullptr,
instance,
nullptr);
if (window == nullptr) {
return static_cast<int>(GetLastError());
}
ShowWindow(window, show_command);
UpdateWindow(window);
MSG message{};
while (GetMessageW(&message, nullptr, 0, 0)) {
TranslateMessage(&message);
DispatchMessageW(&message);
}
return static_cast<int>(message.wParam);
}