src/internal/xinput_route_probe.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef DETOURMODKIT_INTERNAL_XINPUT_ROUTE_PROBE_HPP | ||
| 2 | #define DETOURMODKIT_INTERNAL_XINPUT_ROUTE_PROBE_HPP | ||
| 3 | |||
| 4 | #include <cstdint> | ||
| 5 | |||
| 6 | namespace DetourModKit::detail | ||
| 7 | { | ||
| 8 | /** @brief The required export whose scoped call supplies a receipt. */ | ||
| 9 | enum class XInputRouteMember : std::uint8_t | ||
| 10 | { | ||
| 11 | Primary, | ||
| 12 | Ex | ||
| 13 | }; | ||
| 14 | |||
| 15 | /** @brief Exact identity of one export invocation under an interception snapshot. */ | ||
| 16 | struct XInputRouteProbeIdentity | ||
| 17 | { | ||
| 18 | std::uint64_t owner{0}; | ||
| 19 | std::uint64_t hook_epoch{0}; | ||
| 20 | XInputRouteMember member{XInputRouteMember::Primary}; | ||
| 21 | std::uint32_t user_index{0}; | ||
| 22 | std::uintptr_t state_address{0}; | ||
| 23 | }; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * @brief Reserves receipt TLS and opens probe admission. Call under the interception control lock. | ||
| 27 | * @return false when TLS reservation fails. Existing leases retain their index across a reopen. | ||
| 28 | */ | ||
| 29 | [[nodiscard]] bool reserve_xinput_route_probe() noexcept; | ||
| 30 | |||
| 31 | /** @brief Closes probe admission for XInput teardown. */ | ||
| 32 | void close_xinput_route_probes() noexcept; | ||
| 33 | |||
| 34 | /** @brief Returns the number of leases across upstream calls and final snapshot publication. */ | ||
| 35 | [[nodiscard]] std::uint64_t xinput_route_probe_inflight() noexcept; | ||
| 36 | |||
| 37 | /** | ||
| 38 | * @brief Returns receipt TLS after closed admission and zero probe leases. | ||
| 39 | * @details Call under the interception control lock after every callback that reads receipt TLS drains. | ||
| 40 | * @return false while admission is open or any lease remains. A refused release retains the index. | ||
| 41 | */ | ||
| 42 | [[nodiscard]] bool release_xinput_route_probe_if_idle() noexcept; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * @brief Records this thread's exact scoped export invocation without an application lock or C++ allocation. | ||
| 46 | * @details A receipt proves this invocation only. The identity must match its private token and native thread. | ||
| 47 | */ | ||
| 48 | void xinput_route_probe_record(const XInputRouteProbeIdentity &identity) noexcept; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * @brief Owns permanent receipt storage and a counted upstream-call lease. | ||
| 52 | * @details Construct before the export call and retain through final snapshot publication. Nested probes restore | ||
| 53 | * prior numeric TLS tokens in reverse order. Keep the object on its construction thread. | ||
| 54 | */ | ||
| 55 | class XInputRouteProbe | ||
| 56 | { | ||
| 57 | public: | ||
| 58 | /** @brief Opens one probe. Refuse the export call when admitted() is false. */ | ||
| 59 | explicit XInputRouteProbe(const XInputRouteProbeIdentity &identity) noexcept; | ||
| 60 | /** @brief Restores an unfinished token, clears its receipt slot, and releases the complete-call lease. */ | ||
| 61 | ~XInputRouteProbe() noexcept; | ||
| 62 | |||
| 63 | XInputRouteProbe(const XInputRouteProbe &) = delete; | ||
| 64 | XInputRouteProbe &operator=(const XInputRouteProbe &) = delete; | ||
| 65 | XInputRouteProbe(XInputRouteProbe &&) = delete; | ||
| 66 | XInputRouteProbe &operator=(XInputRouteProbe &&) = delete; | ||
| 67 | |||
| 68 | /** @brief Reports whether the lease and its TLS token were acquired. */ | ||
| 69 | 5179 | [[nodiscard]] bool admitted() const noexcept { return m_admitted; } | |
| 70 | |||
| 71 | /** | ||
| 72 | * @brief Restores prior TLS and returns whether this exact invocation supplied a receipt. | ||
| 73 | * @details The lease stays counted until destruction. A repeated call returns the cached verdict. | ||
| 74 | * @return false after an entry failure, unmatched receipt, token mismatch, or failed TLS restore. | ||
| 75 | */ | ||
| 76 | [[nodiscard]] bool finish() noexcept; | ||
| 77 | |||
| 78 | private: | ||
| 79 | std::uintptr_t m_token{0}; | ||
| 80 | std::uintptr_t m_previous_token{0}; | ||
| 81 | std::uint32_t m_index{0}; | ||
| 82 | std::uint32_t m_slot{0}; | ||
| 83 | bool m_admitted{false}; | ||
| 84 | bool m_finished{false}; | ||
| 85 | bool m_receipt{false}; | ||
| 86 | }; | ||
| 87 | |||
| 88 | #if defined(DMK_ENABLE_TEST_SEAMS) | ||
| 89 | /** @brief Forces TLS reservation, probe entry, or token restore failures for deterministic proofs. */ | ||
| 90 | void set_xinput_route_probe_failures_for_test(bool reservation, bool entry, bool restore) noexcept; | ||
| 91 | /** @brief Returns the private receipt TLS index, or TLS_OUT_OF_INDEXES. */ | ||
| 92 | [[nodiscard]] std::uint32_t xinput_route_probe_tls_index_for_test() noexcept; | ||
| 93 | /** @brief Returns this thread's numeric receipt token, or zero without an index. */ | ||
| 94 | [[nodiscard]] std::uintptr_t xinput_route_probe_token_for_test() noexcept; | ||
| 95 | /** @brief Replaces this thread's token to verify nonce and unrelated-call refusal. */ | ||
| 96 | [[nodiscard]] bool set_xinput_route_probe_token_for_test(std::uintptr_t token) noexcept; | ||
| 97 | #endif | ||
| 98 | } // namespace DetourModKit::detail | ||
| 99 | |||
| 100 | #endif // DETOURMODKIT_INTERNAL_XINPUT_ROUTE_PROBE_HPP | ||
| 101 |