Frozen-snapshot façade over the per-screen mode + desktop + activity + disable/lock cascade. Replaces hand-stitched cascade chains across the daemon's navigation/start/osd paths and the three D-Bus adaptors (SnapAdaptor, WindowDragAdaptor, WindowTrackingAdaptor) with one resolver call. The KWin effect does not consume this library (effect-side state has no disable/lock cascade). OverlayService is the remaining unmigrated consumer in the daemon, still on the legacy inline
isContextDisabled(...)cascade. See thesrc/daemon/daemon.hcontextResolver()docstring.
Every gating site in the Phosphor stack used to inline the same five-call cascade: read the screen's mode, read the current virtual desktop, read the current activity, ask the disable list whether that tuple is off, ask the lock store whether that tuple is locked. Each call crossed a different state source, and the inline shape let a user's virtual-desktop switch mid-handler decouple the mode from the gate.
This library names that cascade as a single primitive. ContextHandle freezes (screenId, virtualDesktop, activity, mode) at construction. The resolver answers gate questions against that frozen tuple instead of re-crossing the workspace on every read. The cascade order (monitor > desktop > activity) and the desktop = 0 means "pinned across all desktops" sentinel match the historical contextDisabledReason policy verbatim so the migration is behaviour-preserving.
The lib owns:
ContextHandle carries the frozen tuple between call sites without re-resolution.IContextResolver exposes disabledReason(), isLocked(), isDisabled(), isGated(). Every gate question is one method call.IWorkspaceState, IModeProvider, and IContextGateSource are three small interfaces that consumers wire into their own state sources. The daemon ships concrete adapters forwarding to VirtualDesktopManager / ActivityManager / ScreenModeRouter / ISettings.ContextResolver borrows three adapter pointers, composes the cascade, and owns no state of its own.| Type | Purpose |
|---|---|
PhosphorContext::ContextHandle | Frozen (screenId, virtualDesktop, activity, mode) snapshot passed to gate calls. |
PhosphorContext::DisabledReason | Why a context is disabled: MonitorDisabled, DesktopDisabled, ActivityDisabled, or NotDisabled. |
PhosphorContext::IContextResolver | The façade: handleFor(), globalHandle(), handleForMode(), handleForPersisted(), disabledReason(), isLocked(), isGated(). |
PhosphorContext::ContextResolver | Concrete IContextResolver over the three adapter interfaces. |
PhosphorContext::IWorkspaceState | Adapter answering "what desktop / activity is the user on?" |
PhosphorContext::IModeProvider | Adapter answering "what mode is this screen?" |
PhosphorContext::IContextGateSource | Adapter answering "is this `(mode, screen, desktop, activity)` disabled / locked?" |
#include <PhosphorContext/ContextResolver.h>
using namespace PhosphorContext;
IContextResolver *resolver = /* injected — see ContextResolverWiring */;
// Snapshot once; every downstream gate sees the same tuple.
const ContextHandle ctx = resolver->handleFor(screenId);
if (resolver->isGated(ctx)) {
return; // disabled or locked — silently no-op
}
// Override the mode axis when a caller already knows which mode to query
// (e.g. autotile-only shortcut handlers). The desktop/activity axes
// stay frozen at the original snapshot.
const ContextHandle autotileCtx =
resolver->handleForMode(screenId, AssignmentEntry::Autotile);
const auto reason = resolver->disabledReason(autotileCtx);handleFor() captures (desktop, activity, mode) once and threads the handle through every downstream gate. The raw workspace readers (currentVirtualDesktop(), currentActivity()) re-cross the workspace state every call and are documented as such. Use the handle's field when consistency with a prior snapshot matters.globalHandle() returns a handle whose screenId is empty, and the mode is delegated to IModeProvider::modeFor("") per the documented adapter contract. Empty desktop (<= 0) and empty activity short-circuit inside the daemon's IContextGateSource adapter so a sentinel-axis handle never matches a per-axis disable entry. Empty screenId is handled upstream of the adapter by ScreenIdentity::variantsFor, which returns an empty list for the empty input, so Settings::isMonitorDisabled then finds no candidate to match against.ContextResolver qFatals on a null adapter at construction so wiring mistakes are loud.ScreenModeRouter::engineFor(screenId) continues to dispatch placement engines. The resolver only consumes IModeProvider::modeFor() because the gate primitives are Mode-scoped.Qt6::CoreAssignmentEntry::Mode is the mode wire type carried in ContextHandle (public link).No Qt6::Gui, no QObjects, no QML, no D-Bus.
AssignmentEntry::Mode and the wire vocabulary the handle carries.