Per-screen layer-shell shell hosts, slot vocabulary, and animator-config wiring for multi-slot overlays.
Helper layer on top of `phosphor-layer`, `phosphor-surfaces`, and `phosphor-animation`. Given a consumer-provided surface factory, ShellHost owns the per-screen layer-shell shell lifecycle (create / destroy / rekey), the slot map keyed by consumer-chosen slot names, animator-driven slot hides, and per-role animator-config registration. The library knows nothing about zones, layouts, or specific content. Consumers wire their slot vocabulary through callbacks.
| Type | Purpose |
|---|---|
PhosphorOverlay::ShellHost | Owns the per-screen ShellState map, lifecycle methods, animator wiring. |
PhosphorOverlay::ShellState | Per-screen mechanism state: layer-shell surface, QQuickWindow, physical screen, slot map. |
PhosphorOverlay::SlotEntry | One slot entry: QPointer<QQuickItem> item + PhosphorLayer::Role role. |
PhosphorOverlay::makePerInstanceRole() | Build a per-instance Role by appending -{screenId}-{generation} to a base scope prefix. |
using namespace PhosphorOverlay;
ShellHost host;
host.setSurfaceAnimator(&animator); // from phosphor-animation
host.setSurfaceFactory([&](const QString& sid, QScreen* s) {
const auto role = makePerInstanceRole(MyShellBaseRole, sid, mgr.nextScopeGeneration());
return mgr.createSurface(/* role, qml url, screen, ... */);
});
host.setPostCreateCallback([&](const QString& sid, ShellState& state) {
// Look up content slot Items by their QML object names, wire QML
// signals, prime the RHI pipeline. Insert one SlotEntry per slot:
auto* item = qvariant_cast<QQuickItem*>(state.shellWindow()->property("osdSlotItem"));
state.slots.insert(QStringLiteral("osd"), SlotEntry{item, MyOsdRole});
});
host.setPreDestroyCallback([&](const QString& sid) {
// Drop any parallel consumer-side per-screen state so a stale signal
// firing during teardown doesn't dereference a dangling pointer.
});
// Lifecycle:
host.registerConfigForRole(MyOsdRole, buildOsdConfig());
host.ensureShell(screenId, physScreen);
host.syncSurfaceState(screenId, /*anyVisible=*/true, /*anyInputGrabbing=*/false);
host.hideSlot(screenId, QStringLiteral("osd"), [&]{ /* on hide-leg settle */ });
host.destroyShell(screenId);ShellState entries are heap-allocated, and the host stores QHash<QString, ShellState*>. Consumers cache the ShellState* returned by ensureShell / stateFor in parallel per-screen state and rely on the pointer staying valid across rehashes. QHash<QString, std::unique_ptr<ShellState>> would move the pointee but still rehash the bucket array, so consumer-cached references are stable only by the indirection. std::shared_ptr would add refcount overhead the lib does not need (there is one owner, the host, plus any number of borrowed observers). ShellState is non-copyable / non-movable to prevent accidental auto copy = host.stateFor(id) (note the missing &) from shallow-cloning the mechanism pointers and slot map.SurfaceFactory, PostCreateCallback, PreDestroyCallback) carry every consumer-specific decision: which role / qmlSource the surface uses, which slot QML object names to look up, which signal handlers to wire, which parallel content state to clear at teardown. The library owns surface lifecycle, and the consumer owns content.SlotEntry carries the PhosphorLayer::Role the slot animates as, so hideSlot can drive SurfaceAnimator::beginHide without the caller re-specifying the role at every dismiss / cancel call site.syncSurfaceState is mechanism-only.** Consumers compute anyVisible and anyInputGrabbing from their content slot visibility (e.g. modal vs non-modal classification), and the library decides surface mapping + Qt::WindowTransparentForInput toggling.ensureShell calls short-circuit until clearFailure(screenId) is called, typically on hot-plug. failureScreenIds() exposes the current set so consumers with their own id grammar (e.g. virtual- screen prefixes) can clear by prefix without the library learning the grammar.QtCore, QtQuickRole, Surface, wlr-layer-shell wire primitives.SurfaceAnimator that drives every overlay show / hide leg.QScreen).