// SPDX-License-Identifier: LGPL-2.1-or-later #pragma once #include #include #include #include #include struct ReviewQuery { UINT message; WPARAM index; DWORD_PTR answer = 0; bool complete = false; }; inline void CALLBACK review_query_completed(HWND, UINT, ULONG_PTR data, LRESULT result) { auto &reply = *reinterpret_cast(data); reply.answer = result; reply.complete = true; } // Only scalar messages may use this path. The capture STA owns one batch; // late callbacks retain their storage until all replies arrive or it exits. inline bool review_query_many(HWND window, std::vector &requests, uint64_t deadline) { static thread_local std::unique_ptr> pending; auto pump = [deadline] { MSG message{}; while (GetTickCount64() < deadline && PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&message); DispatchMessageW(&message); } }; if (requests.empty()) return GetTickCount64() < deadline; auto complete = [](const auto &items) { return std::all_of(items.begin(), items.end(), [](const auto &item) { return item.complete; }); }; pump(); if (GetTickCount64() >= deadline) return false; if (pending && !complete(*pending)) return false; pending = std::make_unique>(requests); for (auto &item : *pending) { if (!SendMessageCallbackW(window, item.message, item.index, 0, review_query_completed, reinterpret_cast(&item))) { // Keep any requests already sent alive, including during failure. item.complete = true; item.answer = static_cast(-1); } } while (!complete(*pending)) { auto now = GetTickCount64(); if (now >= deadline) return false; MsgWaitForMultipleObjectsEx(0, nullptr, static_cast(deadline-now), QS_ALLINPUT, MWMO_INPUTAVAILABLE); pump(); } if (GetTickCount64() >= deadline) return false; requests = *pending; pending.reset(); return std::none_of(requests.begin(), requests.end(), [](const auto &item) { return item.answer == static_cast(-1); }); }