Display and window ownership
The display owns its windows and monitor inventory. Applications borrow window
pointers and monitor references; copying a CE::Platform::Monitor retains its value, not its
native monitor or display owner. The neutral contracts are
CE::Platform::DisplaySystem,
CE::Platform::Window and
CE::Platform::Monitor.
Access native display/window state, create/mutate windows and perform teardown on the platform owner. Window size getters do not synchronize cross-thread access. Simulation and render workers receive copied logical/framebuffer dimensions through the runtime handoff, rather than querying a live window. Graphics context binding, presentation and resource domains belong to the selected graphics owner.
Native GLFW inventory and selection
CE::Platform::GLFW::DisplaySystem shares GLFW initialization lifetime with other live displays and owns every window it creates. Its windows are destroyed before its GLFW lifetime share is released; the final display terminates GLFW. Input adapters detach while their window is live, and graphics resources/context users finish before window/display destruction. The native input lifetime also requires the original Windows notification window to outlive its initialized adapter.
Construction captures available monitor video modes once, with the primary monitor
first. Construction fails without a primary video mode or monitor inventory.
monitors(), primary_monitor() and monitor_count() describe that fixed inventory;
the returned references remain display-owned. IDs are generated inventory identities,
not native handles or persistent physical-monitor identifiers. Copies from the same
inventory retain their IDs; unknown IDs fail content_scale() and window creation.
There is no runtime monitor hotplug refresh or native-handle rebinding. Monitor topology must remain stable while using this inventory: removal can invalidate its native handles, and video-mode changes do not update captured dimensions. Hotplug support needs an explicit refresh, identity and existing-window rebinding contract before consumers rely on it.
create_window() returns a borrowed pointer owned until display destruction;
callers do not delete it. Dimensions are positive logical coordinates. Invalid
monitor IDs/modes, allocation failures and failed native creation propagate.
Omitting dimensions uses the captured monitor size; omitting or passing an empty
title uses a generated title. Successful creation does not make the window active.
active_window() is initially nullptr. activate_window() accepts the first owned
window and repeated selection of that window. It rejects a foreign window or replacing
the selected one. Selection does not make a graphics context current or establish
renderer/resource-domain switching.
Multiple active rendering windows and selection switching are
deferred extensions.
Sizes, modes and observation
Observation |
Units and freshness |
|---|---|
|
Captured video-mode dimensions; native GLFW does not refresh them. |
|
Cached logical window coordinates, updated by native size callbacks and explicit resize/mode queries. |
|
Cached drawable pixels, independently updated by framebuffer callbacks and explicit queries. Zero dimensions can describe an unavailable drawable. |
|
Current GLFW X/Y monitor scale for an inventory ID; this leaves its dimensions unchanged. |
Pump native events on platform before relying on new callback observations. Logical
and framebuffer dimensions can differ; use valid positive sizes for pixel mapping
and avoid division/render submission while the drawable is unavailable.
CE::Platform::Dimensions<int> describes logical dimensions, while
CE::Platform::FramebufferSize describes drawable pixels.
Choose the initial mode or a supported transition using the Native GLFW behavior below. Mode names alone do not imply a desktop-sized borderless window.
Mode |
Native GLFW consequence |
|---|---|
|
Restore saved windowed placement and decorations. |
|
Use saved windowed placement without decorations; retain its extent. |
|
Attach the selected monitor at its captured dimensions and queried refresh rate. |
resize() requests positive logical dimensions and queries the resulting logical
and pixel sizes. The platform may accept different dimensions. set_mode() restores
saved windowed placement for CE::Platform::WindowMode::NORMAL, uses it without decorations for BORDERLESS,
and attaches the chosen monitor at its captured dimensions for FULLSCREEN. Native
fullscreen switching uses the monitor’s queried refresh rate. Mode changes then
query the resulting sizes. Borderless mode preserves the saved windowed extent;
it does not itself select a desktop-sized extent.
mode() reflects the selected mode and can already have changed when a transition
reports a listener failure.
A changed framebuffer size publishes CE::Platform::WindowResized on the canonical typed
CE::Platform::window_resized_event channel. Native GLFW first dispatches the legacy named
"window-resized" notification, then the typed channel, using the same saved size
observation. A logical size change alone does not establish a framebuffer-size
event. Nested resize delivery can interleave these channels; each payload keeps its
own observation even if another listener changes the window again. Queued callbacks
do not have a completion order across channels.
The event contains copied pixel dimensions and a borrowed window pointer. Queued delivery does not extend window lifetime or permit dereferencing it on another owner. Deferred consumers retain the dimensions and use their normal runtime and registration lifetime. The event-delivery contract defines channel identity, payload ownership and optional owner delivery.
Native callback failures follow the
failure-reporting contract. Explicit resize/mode operations
consume earlier callback failures and can propagate listener failures after native
state/dimensions change. A synchronous legacy-dispatch failure prevents the typed
offer for that observation; typed listener failures use the same native callback
boundary. Earlier deliveries and native state changes are not rolled back. should_close()
also consumes deferred callback failure before reading the close flag. Cached size
getters do not consume that failure.
Committed window and simulation views
Use engine.window_state() for the runtime’s copied platform selection and
engine.simulation_view() for the dimensions selected by simulation. These
sources, declared in <cheryl/core/engine/view-state.h>, avoid borrowing a live
window across owners. Their synchronized snapshot() results can be retained;
checked source/session identities and revisions distinguish new selections.
initialized is false before the first selection and closed becomes true
when runtime closes the source. Closure does not fabricate a resize or focus event.
WindowState contains logical size, framebuffer pixels, observation time and
optional native focus. Window::focused() defaults to unknown; Native GLFW
supplies the sampled native value. Input keyboard-routing focus is a separate
concept. The first selection establishes a baseline. Later logical, pixel or
supported focus changes advance the window revision.
flowchart TB
accTitle: Native window state and the view used by a tick
accDescr: Platform samples the final native window state and applies the renderer viewport. A copied selection crosses to simulation, which commits a SimulationView before required UI sizing and the matching tick.
window["Native window on platform"] -->|sample after callbacks| selected["WindowState"]
selected -->|required pixel view| renderer["Renderer viewport"]
selected -->|copied handoff| simulation["SimulationView"]
simulation -->|required seeded watch| ui["UI sizing"]
ui --> tick["Matching game update"]
Simulation selects copied dimensions before each actual update and carries that
selection in TickContext. Its view revision changes only for a new size pair,
not a focus-only window change. window_revision records the originating window
selection when that view commits; it need not equal the latest focus-only
revision. Required view callbacks finish before dependent work. The renderer
uses a required platform watch for the initial viewport and later pixel changes;
UI sessions can follow simulation view.
For optional observation, register CE::Runtime::window_state_changed_event or
simulation_view_changed_event on the Context bus. Both payloads own previous/
current values. A bus subscription has no seed. A source’s watch() instead
requires its initialized owner, invokes a protected seed=true callback before
returning, and returns an owned ViewAttachment. A required application failure
propagates and prevents dependent work; it is not hidden as an optional listener
failure. Observation versus participation
explains these choices and cleanup.
The legacy WindowResized producer and its named-then-typed failure ordering
remain as described above. Runtime samples the final live state rather than
using a potentially older nested raw payload. A listener that mutates the native
window is sampled again at a later safe platform pass; runtime does not recursively
drain view changes, and it avoids rendering with a stale selected native view.
Native callback freshness is still poll-gated: a full input backlog can delay
new close, resize or focus observations. Maintenance phases add no extra GLFW pump.
Native close intent
Native GLFW implements supports_close_intent() and
consume_close_intent(). Consumption observes and clears its sticky close flag,
so rejecting one close request does not poison later polling. A later native
close can create a fresh request. Other window adapters default to unsupported
and keep the boolean should_close() contract.
Runtime publishes an owned CE::Platform::WindowCloseRequested through
window_close_requested_event, with context/session, window-source and intent
identity plus observation time. It carries no window pointer or decision
capability. The runtime’s required main-window bridge enters the
shutdown request protocol, even
if optional observation fails. Native input backlog can delay observing the
intent; host endpoint requests remain independently serviced.
With no policy, close requests retain the ordinary accept-and-shutdown behavior.
An installed negotiable policy requires a consumable window. Explicit
NativeClosePolicy::Force allows host-only negotiation with a boolean adapter
while retaining unconditional native close. Native callback failures still use
the failure boundary above and can force runtime shutdown.
Scale and UI capabilities
A monitor scale query neither establishes per-window scale nor reports scale-change events. A window can move between monitors independently of its original snapshot. Use copied logical/framebuffer sizes for current pixel mapping and an explicit font scale within the adapter’s supported scope.
hide_cursor() selects hidden or normal cursor visibility. Pointer capture, locked
relative motion, cursor shapes, clipboard and IME need their own capability contracts.
The current UI capability boundaries
describe what adapters expose and what further consumer requirements must settle.