#include "platform/windows/gui/slint_backend.h" #include #include #include #include #include #include #include namespace crossdesk { namespace { struct HandleCloser { void operator()(HANDLE handle) const noexcept { if (handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); } } }; using ScopedHandle = std::unique_ptr; std::string ErrorCode(DWORD code) { std::ostringstream text; text << "0x" << std::hex << std::uppercase << code; return text.str(); } std::string ProbeSlintRenderer(DWORD timeout_ms) { std::wstring executable(32768, L'\0'); const DWORD length = GetModuleFileNameW( nullptr, executable.data(), static_cast(executable.size())); if (length == 0 || length >= executable.size()) { return "Unable to locate renderer probe executable"; } executable.resize(length); // A job also cleans up the probe if the application is closed while the // graphics driver is stuck. Create it suspended so assignment cannot race. ScopedHandle job(CreateJobObjectW(nullptr, nullptr)); JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits = {}; limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; if (!job || !SetInformationJobObject(job.get(), JobObjectExtendedLimitInformation, &limits, sizeof(limits))) { return "Unable to create renderer probe job: " + ErrorCode(GetLastError()); } constexpr std::string_view argument = kSlintRendererProbeArgument; std::wstring command = L"\"" + executable + L"\" "; command.append(argument.begin(), argument.end()); STARTUPINFOW startup = {sizeof(startup)}; PROCESS_INFORMATION process = {}; if (!CreateProcessW(executable.c_str(), command.data(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW | CREATE_SUSPENDED, nullptr, nullptr, &startup, &process)) { return "Unable to start renderer probe: " + ErrorCode(GetLastError()); } ScopedHandle process_handle(process.hProcess); ScopedHandle thread_handle(process.hThread); if (!AssignProcessToJobObject(job.get(), process.hProcess) || ResumeThread(process.hThread) == static_cast(-1)) { const DWORD error = GetLastError(); TerminateProcess(process.hProcess, 1); return "Unable to run renderer probe: " + ErrorCode(error); } const DWORD wait = WaitForSingleObject(process.hProcess, timeout_ms); if (wait != WAIT_OBJECT_0) { // Closing the job kills only our probe, including any stuck driver thread. return wait == WAIT_TIMEOUT ? "Slint renderer probe timed out" : "Waiting for renderer probe failed: " + ErrorCode(GetLastError()); } DWORD exit_code = 0; if (!GetExitCodeProcess(process.hProcess, &exit_code)) { return "Unable to read renderer probe result: " + ErrorCode(GetLastError()); } return exit_code == 0 ? std::string{} : "Slint renderer probe exited with " + ErrorCode(exit_code); } } // namespace SlintBackendSelection ConfigureWindowsSlintBackend(uint32_t probe_timeout_ms) { if (const char* requested = std::getenv("SLINT_BACKEND"); requested && requested[0] != '\0') { return {true, requested, "Using explicitly requested Slint backend"}; } // Slint creates its own GL context. A successful SDL/WGL capability check // cannot validate it, and a Rust panic cannot be caught by C++ exceptions. // Exercise Slint in a separate process without touching GL in this process. const std::string error = ProbeSlintRenderer(probe_timeout_ms); if (error.empty()) { return {true, "default", "Slint renderer probe rendered successfully"}; } // _putenv_s updates the CRT and the OS environment read by slint_cpp.dll. // This is process-local so a later launch can retry hardware rendering. if (_putenv_s("SLINT_BACKEND", "winit-software") != 0) { return {false, {}, "Unable to select Slint software rendering: " + error}; } return {true, "winit-software", error}; } } // namespace crossdesk