Tracks the "current context" of each screen for a placement engine: the global current virtual desktop, per-output desktop overrides (Plasma 6.7 "switch desktops independently per screen", #648), the sticky-desktop pin (KWin "virtualdesktopsonlyonprimary" model), and the current activity.
More...
#include <phosphor-engine/include/PhosphorEngine/ScreenContextTracker.h>
Tracks the "current context" of each screen for a placement engine: the global current virtual desktop, per-output desktop overrides (Plasma 6.7 "switch desktops independently per screen", #648), the sticky-desktop pin (KWin "virtualdesktopsonlyonprimary" model), and the current activity.
Resolves a screen's owning PlacementStateKey via currentKeyForScreen() with the precedence: sticky-pin override > per-output desktop > global desktop; activity is always the current activity.
Extracted verbatim (in behaviour) from AutotileEngine so the snap engine can share it. Plain value/helper type — no QObject, no signals; engines read it synchronously when resolving keys.
◆ ScreenContextTracker()
| PhosphorEngine::ScreenContextTracker::ScreenContextTracker |
( |
| ) |
|
|
default |
◆ activityContextEverSet()
| bool PhosphorEngine::ScreenContextTracker::activityContextEverSet |
( |
| ) |
const |
|
inlinenoexcept |
◆ clearCurrentDesktopForScreen()
| void PhosphorEngine::ScreenContextTracker::clearCurrentDesktopForScreen |
( |
const QString & |
screenId | ) |
|
|
inline |
Drop a screen's per-output desktop entry, falling it back to the global.
No production caller, and deliberately so. Turning Plasma's per-output desktop setting OFF does not strand these entries: the effect's desktopChanged handler fans a global switch out to EVERY output, so the pushes keep arriving and simply become uniform. There is no "per-output
mode ended" event to hook, and none is needed. A screen that genuinely goes away is handled by removeScreen / removeScreensIf / pruneDesktop.
Wiring a caller here would drop an entry that currentKeyForScreen's contract says must survive while its screen exists.
◆ currentActivity()
| const QString & PhosphorEngine::ScreenContextTracker::currentActivity |
( |
| ) |
const |
|
inlinenoexcept |
◆ currentDesktop()
| int PhosphorEngine::ScreenContextTracker::currentDesktop |
( |
| ) |
const |
|
inlinenoexcept |
◆ currentKeyForScreen()
| PlacementStateKey PhosphorEngine::ScreenContextTracker::currentKeyForScreen |
( |
const QString & |
screenId | ) |
const |
Construct the owning key for a screen in the current context.
Precedence (highest first):
- sticky-pin override — a CORRECTNESS constraint: sticky on-all-desktops windows must keep their state on the desktop where they live;
- per-output virtual desktop (#648) — the normal per-screen input;
- the global current desktop — fallback. The activity dimension is always the current activity.
Tier 3 is a STARTUP SEED, not a live input, and reading it as one is the mistake to avoid here. Under per-output desktops the daemon pushes the global desktop once during startup and every later change arrives through setCurrentDesktopForScreen, unconditionally, for every screen — so a screen that reaches tier 3 after startup is resolving against a value frozen at login. That is why a per-output entry must never be dropped while its screen still exists: the fallback below it does not track anything, and every output falling through to it lands on one shared desktop.
◆ desktopContextEverSet()
| bool PhosphorEngine::ScreenContextTracker::desktopContextEverSet |
( |
| ) |
const |
|
inlinenoexcept |
◆ hasStickyPin()
| bool PhosphorEngine::ScreenContextTracker::hasStickyPin |
( |
const QString & |
screenId | ) |
const |
|
inline |
◆ pruneDesktop()
| void PhosphorEngine::ScreenContextTracker::pruneDesktop |
( |
int |
removedDesktop | ) |
|
Drop entries from both per-screen maps whose DESKTOP value equals removedDesktop (a virtual desktop was destroyed / renumbered).
◆ releaseScreenOwnership()
| void PhosphorEngine::ScreenContextTracker::releaseScreenOwnership |
( |
const QString & |
screenId | ) |
|
|
inline |
Drop only the ENGINE-OWNED half for a screen leaving this engine's mode set, keeping the per-output desktop.
Operationally this clears the sticky pin and nothing else — the name says what the CALLER is doing rather than what the call does, because the point of it is the half deliberately left alone. Read it as clearStickyPin with a mandatory rationale attached.
The sticky pin is this engine's own bookkeeping and is meaningless once the engine stops managing the screen. The per-output desktop is not: it is compositor truth about which desktop the screen is showing, true whoever manages it, and the engine cannot reconstruct it.
Dropping it made screenDesktop() silently fall back to the GLOBAL desktop — which the daemon sets exactly once at startup and never updates, because every later change arrives through the per-output screenDesktopChanged path. So a screen that left an engine's set and came back keyed EVERY context to the startup desktop until its next per-output change: on the scrolling engine that merged every virtual desktop's strip into one, and windows from other desktops rode along in its batches.
The pin is NOT rebuilt on re-entry, and the consequence is worth knowing before changing either half. Both engines' updateStickyScreenPins iterate their OWN managed-screen set, and their only callers are the daemon's two context-switch handlers — so an all-sticky screen that leaves the mode and returns with no intervening desktop or activity switch comes back unpinned, resolves to its per-output desktop, and finds no state there. Its pinned-desktop state is orphaned until a prune reaps it.
Preserving the pin here instead would trade that for the mirror case: the unpin arm iterates the same managed-screen set, so a pin kept for a screen OUTSIDE the mode goes stale if its windows stop being sticky. That case needs one extra state change to reach, which is why it is the narrower of the two, but the migration path for a stale pin is not traced.
◆ removeScreen()
| void PhosphorEngine::ScreenContextTracker::removeScreen |
( |
const QString & |
screenId | ) |
|
|
inline |
Drop both per-screen maps' entries for a screen leaving the engine's set.
For an OUTPUT that is going away. A screen that merely leaves this engine's mode set must use releaseScreenOwnership instead — see its doc for why dropping the per-output desktop there is a correctness bug.
No production caller today: real output removal goes through removeScreensIf, which every engine drives from its own pruneStatesForRemovedScreen, and orphaned virtual-screen ids are swept the same way. Kept as the single-screen form of that operation and exercised by the tracker's tests. Reach for removeScreensIf first.
◆ removeScreensIf()
| void PhosphorEngine::ScreenContextTracker::removeScreensIf |
( |
const std::function< bool(const QString &)> & |
pred | ) |
|
Drop entries from both per-screen maps whose SCREEN key matches pred (e.g.
an orphaned virtual-screen id that no longer exists).
◆ screenDesktop()
| int PhosphorEngine::ScreenContextTracker::screenDesktop |
( |
const QString & |
screenId | ) |
const |
|
inline |
The screen's PER-OUTPUT desktop: its own entry if set, else the global current desktop.
NOT the effective desktop, and the difference matters: this deliberately ignores the sticky pin, which currentKeyForScreen ranks ABOVE both of these. On a pinned screen the two disagree, and the key the engine actually resolves is the one currentKeyForScreen returns. Ask this only when you specifically want the per-output value; ask currentKeyForScreen when you want the desktop a lookup will use.
Kept excluding the pin on purpose — releaseScreenOwnership's contract is written in terms of this falling back to the global value, and the tracker's tests pin that behaviour.
◆ setCurrentActivity()
| ContextChange PhosphorEngine::ScreenContextTracker::setCurrentActivity |
( |
const QString & |
activity | ) |
|
◆ setCurrentDesktop()
| ContextChange PhosphorEngine::ScreenContextTracker::setCurrentDesktop |
( |
int |
desktop | ) |
|
◆ setCurrentDesktopForScreen()
| ContextChange PhosphorEngine::ScreenContextTracker::setCurrentDesktopForScreen |
( |
const QString & |
screenId, |
|
|
int |
desktop |
|
) |
| |
◆ setStickyPin()
| void PhosphorEngine::ScreenContextTracker::setStickyPin |
( |
const QString & |
screenId, |
|
|
int |
desktop |
|
) |
| |
|
inline |
Pin a screen to a desktop, outranking both the per-output and the global value in currentKeyForScreen.
Rejects desktop < 1, symmetric with setCurrentDesktop and setCurrentDesktopForScreen — and the guard matters MORE here, because a poisoned pin outranks both of the values those two protect. Unreachable from the in-tree callers, which pass a desktop off a live state key.
◆ stickyPinnedDesktop()
| int PhosphorEngine::ScreenContextTracker::stickyPinnedDesktop |
( |
const QString & |
screenId | ) |
const |
|
inline |
The sticky-pin desktop for a screen, or 0 when it is not pinned.
Non-consuming, unlike takeStickyPin. 0 is unambiguous as "not pinned" because setStickyPin rejects anything below 1.
◆ takeStickyPin()
| int PhosphorEngine::ScreenContextTracker::takeStickyPin |
( |
const QString & |
screenId | ) |
|
|
inline |
Remove and return the sticky-pin desktop for a screen (default-constructed int == 0 when absent, mirroring QHash::take).
The documentation for this class was generated from the following file: