blob: fdf806ac2a49ef457fdf73e7ad17cf93085ea0f1 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
#pragma once
#include <unbox/kernel/frames.hpp>
#include <unbox/kernel/hooks.hpp> // ExtensionId
#include <functional>
#include <unordered_map>
#include <vector>
// The kernel's per-frame animation driver. Holds the set of live per-frame
// callbacks registered via Host::request_frames; the kernel's output frame
// handler drains them (in order, reentrancy-safe) BEFORE the ui substrate ticks
// and the scene commits, and — while the set is non-empty — keeps requesting
// output frames so animations advance even when the scene is idle.
//
// Error-isolated: a throwing callback is caught at the boundary and the owning
// extension is disabled via the injected sink (same contract as hooks / file
// watches), never the session.
//
// Single wl_event_loop thread throughout; no internal locking.
namespace unbox::kernel {
class FrameDriver final : public detail::FrameRegistry {
public:
using Token = detail::FrameRegistry::Token;
// `disable` disables the owning extension when its callback throws (injected
// by the kernel, same as the bus/substrate/file-watch isolation sink).
explicit FrameDriver(std::function<void(ExtensionId)> disable);
~FrameDriver() override = default;
FrameDriver(const FrameDriver&) = delete;
auto operator=(const FrameDriver&) -> FrameDriver& = delete;
// Register `on_frame` (fired each frame with dt seconds, error-isolated to
// `who`). Returns a FrameRequest RAII handle; destroying it removes the
// callback. Always active (the driver itself has no loop dependency — the
// caller decides whether a handle is inert when there is no output).
[[nodiscard]] auto add(std::function<void(double)> on_frame, ExtensionId who) -> FrameRequest;
// True if at least one callback is live (the kernel keeps scheduling frames
// while this holds).
[[nodiscard]] auto has_requests() const noexcept -> bool { return !entries_.empty(); }
// Fire every live callback once with `dt_seconds`. Reentrancy-safe: a
// callback may add or remove requests (including its own) mid-drain — a
// request added during the drain first fires NEXT frame, one removed during
// the drain does not fire again this drain.
void drain(double dt_seconds);
// detail::FrameRegistry — stop the callback with this token (handle dtor).
void remove_frame_request(Token token) noexcept override;
private:
struct Entry {
std::function<void(double)> on_frame;
ExtensionId who{};
};
std::function<void(ExtensionId)> disable_;
Token next_token_ = 0;
std::unordered_map<Token, Entry> entries_;
};
} // namespace unbox::kernel
|