61 lines
2.1 KiB
C++
61 lines
2.1 KiB
C++
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
#pragma once
|
|
#include <windows.h>
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
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<ReviewQuery *>(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<ReviewQuery> &requests, uint64_t deadline) {
|
|
static thread_local std::unique_ptr<std::vector<ReviewQuery>> 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<std::vector<ReviewQuery>>(requests);
|
|
for (auto &item : *pending) {
|
|
if (!SendMessageCallbackW(window, item.message, item.index, 0, review_query_completed,
|
|
reinterpret_cast<ULONG_PTR>(&item))) {
|
|
// Keep any requests already sent alive, including during failure.
|
|
item.complete = true;
|
|
item.answer = static_cast<DWORD_PTR>(-1);
|
|
}
|
|
}
|
|
while (!complete(*pending)) {
|
|
auto now = GetTickCount64();
|
|
if (now >= deadline) return false;
|
|
MsgWaitForMultipleObjectsEx(0, nullptr, static_cast<DWORD>(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<DWORD_PTR>(-1);
|
|
});
|
|
}
|