Events
Purpose
Events delivers notifications through persistent registrations. A bus owns callbacks and channel identity; it does not create a thread, choose simulation timing or automatically own the objects borrowed by those callbacks.
Interface
Types below are in CE::Events unless another namespace is shown.
Types and operations
API and purpose |
|---|
|
|
|
|
|
|
Delivery and cleanup choices
Choice |
Purpose and details |
|---|---|
Immediate delivery |
Invoke on each dispatch caller, in registration order; concurrent callers may overlap. Immediate delivery. |
Platform / simulation delivery |
Queue owned payloads through a saved owner endpoint; never execute inline. Queued adapters; dispatch. |
Worker delivery |
Give one listener stream serial FIFO execution over shared group capacity. Worker streams. |
Unregister / unregister-and-wait / close |
Invalidate new invocation, optionally settle entered callbacks, or close all acceptance. Removal and shutdown. |
Usage
Register on the bus selected by the producer, choose the callback’s execution owner, and retain the registration for explicit cleanup. Use the Context bus for runtime observations or an application-owned bus for an isolated domain.
---
config:
theme: base
themeVariables:
fontFamily: system-ui, sans-serif
fontSize: 14px
flowchart:
htmlLabels: true
curve: linear
nodeSpacing: 22
rankSpacing: 38
padding: 12
---
flowchart LR
accTitle: Events · practical delivery choices
accDescr: Local names omit Events::; ambiguous nested namespaces remain qualified. Arrows point toward dependencies. Application roles are rounded; named API operations have rounded corners. Dashed arrows mark optional choices.
k0(["Application<br/>listener setup"]):::app
k1["EventBus"]:::focal
k2["EventBus::<br/>Delivery"]:::focal
k3("platform_delivery()"):::focal
k4("simulation_delivery()"):::focal
k5("worker_delivery()"):::focal
k6["Runtime::PlatformDispatcher::<br/>Submission"]:::neighbor
k7["Runtime::SimulationDispatcher::<br/>Submission"]:::neighbor
k8["Workers::<br/>Group"]:::neighbor
k9["EventBus::<br/>ErrorHandler"]:::focal
k0 --> k1
k1 --> k2
k0 -.-> k3
k0 -.-> k4
k0 -.-> k5
k3 --> k6
k4 --> k7
k5 --> k8
k3 --> k2
k4 --> k2
k5 --> k2
k0 --> k9
classDef focal fill:#e7f3ef,stroke:#387d6b,color:#153d33,stroke-width:1.2px
classDef neighbor fill:#f0f3f8,stroke:#8c9bb1,color:#35445b,stroke-width:1px
classDef app fill:#fff2df,stroke:#c09452,color:#674515,stroke-dasharray:5 3
classDef system fill:#eaf0fc,stroke:#617fab,color:#263f69,stroke-width:1.2px
linkStyle default stroke:#7e8fa7,stroke-width:1.2px
Local names omit Events:: where unambiguous. Explore this diagram and its other views →
Explore the immediate-only path, application registrations, or engine producers and delivery dependencies.
Choose delivery and participation
Need |
Choose |
Responsibility |
|---|---|---|
Observe immediately on the publishing owner |
No delivery adapter |
The callback runs synchronously; overlapping publishers may enter it concurrently. |
Change platform/graphics-owned state |
Save a platform submission endpoint and supply an independently owned, nonthrowing error sink. |
|
Change simulation-owned gameplay or UI |
Save a simulation endpoint; callback entry occurs at a scheduled simulation boundary. |
|
Process observations serially away from owners |
Keep physical worker capacity alive; copies of one adapter share one FIFO stream. |
|
Apply required sizing or recurring runtime work |
View/service attachment |
Use the owner’s |
A bus subscription has no replay. Read the producer’s retained snapshot or use its seeded watch when current state matters. The full observation/participation contract and queued-delivery example cover setup, failure handling and teardown.
Uses
Dependency |
Where it belongs |
|---|---|
|
The bus identifies typed subscriptions by channel name and payload type. |
Optional queued transport and its required error authority. The immediate bus itself owns no dispatcher or worker pool. |
|
|
Retained by the optional owner-delivery adapters. Dispatch. |
Shared CPU capacity used by the optional worker-delivery adapter. Workers. |
|
Identify bus observations and failures; diagnostics does not settle callbacks. Diagnostics. |
Used by
Consumer |
How it connects |
|---|---|
Runtime connects supported producers to its Context bus at session start. View/lifecycle/service facts are also published there. |
|
Native GLFW Display |
Publish raw framebuffer resize through the default |
Publish executed upload outcomes and committed exact-key catalogue changes through configured weak routes. |
|
Stage terminal playback outcomes and explicitly publish them on its designated owner. |
|
Application producers/listeners |
Define their own channels and bus boundaries; own callback targets and explicit registration removal. |
Engine events
The following are the public typed channels supplied by Engine and its modules.
Names omit the leading CE::. Register on the producer’s bus, not merely on a
channel with the same spelling. Unless stated otherwise, Context-connected
producers below publish through engine.events(); the default bus is a separate
instance. Payloads describe observations, not authority to change the producing
system.
Window, view and input observations
Channel and payload |
Producer and meaning |
|---|---|
Native GLFW, on platform via |
|
|
Runtime, on platform: one sampled main-window close intent with owned context/session/source/intent IDs. No window pointer or shutdown decision capability. Close bridge. |
|
Runtime’s platform selection: previous/current logical size, pixels and optional native focus. Read |
|
Runtime’s simulation selection before a dependent tick: previous/current copied size pairs. Required sizing uses |
|
Input’s platform owner after complete polls and explicit deinitialization: current/previous convenience-slot state. Deinitialization does not mean physical disconnection; destruction closes the source silently. Subscribe then read |
Session and shutdown observations
All six channels run their immediate observers on platform and carry an owned
lifecycle selection. runtime.lifecycle().snapshot() retains current/terminal
state after runtime destruction. Milestone delivery does not replay to a late
subscriber.
Channel and payload |
When it is observed |
|---|---|
Successful owner/game initialization, before the first update. Simulation is still gated during immediate Ready fanout; do not synchronously wait for it. |
|
Shutdown commits, before simulation joins; partial startup can reach this milestone without Ready. |
|
Simulation/mailbox has stopped and applicable game quiescence has been attempted, before accepted CPU work settles. |
|
Ordered cleanup was attempted and the terminal outcome/counters were retained. Failed cleanup does not prove successful native resource release. |
|
An ordinary request was admitted Pending. Only the installed host policy receives the decision capability. |
|
|
An ordinary request was cancelled, with its identity and reason. Forced supersession does not fabricate cancellation. |
Read the lifecycle milestones and negotiable shutdown protocol for their ordering and retained request state.
Runtime service observations
Channel and payload |
Producer and meaning |
|---|---|
|
A real platform service pass, including its reason and pass identity. Maintenance does not pump native input. |
|
Simulation selected input/view and an attempted step’s timing before the game hook. This does not prove that update completed. |
Use required service attachments for work that must finish before dependent runtime operations.
Asset, graphics and audio observations
Channel and payload |
Producer, setup and meaning |
|---|---|
|
Loader’s serialized loading owner: one aggregate outcome per executed attempt, with counts and an owned failure. Configure |
|
Catalogue drainer, potentially a producer thread: an exact-key publish/replace or reset, with generations and loss metadata. Each asset/key type pair is a distinct channel. Runtime routes provider catalogues; standalone sources opt in. Read a snapshot or seeded watch to reconcile current keys. Catalogue changes. |
|
Graphics owner: actual native availability/invalidation, with a historical snapshot and retained observation token. Runtime configures renderer routing. The token retains no native objects. Graphics domains. |
Designated audio publication owner: a logical voice generation’s terminal reason. The application configures a weak publisher and explicitly pumps staged records, optionally through runtime maintenance. Counters expose bounded-record loss. Playback outcomes. |
No public event family currently reports FontService catalogue/configuration changes, monitor hotplug,
full physical-device topology, or OS suspend/resume. Input’s ordered Input::Events::Record stream is
read from ticks/polls, separately from these bus channels. Runtime pressure and
shutdown-policy transport channels are implementation details.
Contracts
Registration persists until explicit removal or bus destruction; dropping its identifier does not unregister. Immediate callbacks run in registration order on each dispatch caller, and overlapping dispatches can overlap listeners. Typed channels require the exact copy-constructible payload type.
Unregister prevents fresh invocation but a callback already entered may finish.
Use unregister_and_wait() before destroying a borrowed target, without holding
a lock that callback needs. Waiting from that same listener rejects. Queued
payloads own their copied values; pointees remain governed by the payload type.
Lifecycle
Construct an owned bus or deliberately use the default. Retain registration IDs
for cleanup. Stop producers, invalidate registrations and settle entered
callbacks before destroying borrowed targets. close() invalidates and rejects
new work but does not wait for running callbacks. An independent queued error
sink must survive pending-ticket destruction even after its listener is removed.
Execution
Immediate delivery is synchronous on dispatch’s caller. Queued delivery owns
the work without invoking it inline, preserving each listener’s FIFO stream.
Platform/simulation adapters follow those owners; copies of one worker-delivery
adapter share a serial stream. Independent listeners/targets have no global
completion order. Immediate dispatch() failures propagate and stop its remaining
listeners; notify() isolates failures and attempts later listeners. Queued
failures reach the registration-owned error sink.
Integration
An application-owned bus can notify simulation-owned listeners synchronously:
CE::Events::EventBus bus;
const CE::Events::EventChannel<int> score_changed{"score-changed"};
int displayed_score = 0;
auto registration = bus.register_listener(score_changed, [&](const int& value) {
displayed_score = value;
});
bus.dispatch(score_changed, 10);
static_cast<void>(bus.unregister_and_wait(registration));
Here the caller supplies synchronization by performing all operations on one owner. Choose a delivery adapter before registering when producers use other threads.
Limits and further reading
Channel names/RTTI form an in-process identity, not a serialization protocol. The default bus does not provide automatic callback lifetime or UI ownership. Read event delivery and failure contracts, thread dispatch and the bus interface.