Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
ScreenSurfaceRegistry.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 fuddlesworth
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
9#include <PhosphorLayer/phosphorlayer_export.h>
10
11#include <QHash>
12#include <QList>
13#include <QObject>
14#include <QPointer>
15#include <QScreen>
16#include <QSet>
17
18#include <functional>
19#include <type_traits>
20
21namespace PhosphorLayer {
22
23class Surface;
24
53template<typename SurfaceT = Surface>
55{
56 static_assert(std::is_base_of_v<Surface, SurfaceT>, "SurfaceT must derive from PhosphorLayer::Surface");
57
58public:
62 using Builder = std::function<SurfaceT*(QScreen*)>;
63
65 : m_factory(factory)
66 , m_screens(screens)
67 {
68 }
70 {
71 clear();
72 }
73
76
90 QList<SurfaceT*> createForAllScreens(Builder builder);
91
95 void syncToScreens(Builder builder);
96
98 void clear();
99
113 void adoptSurface(QScreen* screen, SurfaceT* surface);
114
115 SurfaceT* surfaceForScreen(QScreen* screen) const;
116 QList<SurfaceT*> surfaces() const;
117
118 SurfaceFactory* factory() const noexcept
119 {
120 return m_factory;
121 }
123 {
124 return m_screens;
125 }
126
127private:
128 SurfaceFactory* m_factory;
129 IScreenProvider* m_screens;
130 QHash<QScreen*, QPointer<SurfaceT>> m_entries;
131};
132
133// ── Template implementations ───────────────────────────────────────────
134
135template<typename SurfaceT>
137{
138 QList<SurfaceT*> out;
139 if (!m_screens) {
140 return out;
141 }
142 const auto list = m_screens->screens();
143 out.reserve(list.size());
144 for (QScreen* s : list) {
145 auto it = m_entries.find(s);
146 if (it != m_entries.end()) {
147 if (SurfaceT* live = it.value().data()) {
148 out.append(live);
149 continue;
150 }
151 // Stale QPointer (surface externally destroyed) — prune so the
152 // builder replaces it cleanly and surfaceForScreen() stops
153 // reporting null for a key that looks like it's present.
154 m_entries.erase(it);
155 }
156 SurfaceT* surface = builder ? builder(s) : nullptr;
157 if (surface) {
158 m_entries.insert(s, QPointer<SurfaceT>(surface));
159 out.append(surface);
160 }
161 }
162 return out;
163}
164
165template<typename SurfaceT>
167{
168 if (!m_screens) {
169 return;
170 }
171 const auto current = m_screens->screens();
172 const QSet<QScreen*> currentSet(current.begin(), current.end());
173
174 // Single pass: remove entries for (a) removed screens and (b) screens
175 // whose QPointer auto-nulled because the surface was destroyed
176 // externally. Without the null-sweep the map accumulates dangling
177 // entries under pathological consumer behaviour and surfaceForScreen()
178 // keeps reporting null for a key that looks present.
179 //
180 // Safety for deleteLater: QPointer auto-nulls on external destruction
181 // before the outer-parent's ~QObject finishes, and Qt coalesces
182 // DeferredDelete events, so calling deleteLater on a surface whose
183 // parent is also about to destroy it is a no-op rather than a
184 // double-free.
185 for (auto it = m_entries.begin(); it != m_entries.end();) {
186 if (!currentSet.contains(it.key())) {
187 if (SurfaceT* s = it.value().data()) {
188 s->deleteLater();
189 }
190 it = m_entries.erase(it);
191 } else if (!it.value().data()) {
192 it = m_entries.erase(it);
193 } else {
194 ++it;
195 }
196 }
197
198 for (QScreen* s : current) {
199 if (m_entries.contains(s) && m_entries.value(s).data()) {
200 continue;
201 }
202 SurfaceT* surface = builder ? builder(s) : nullptr;
203 if (surface) {
204 m_entries.insert(s, QPointer<SurfaceT>(surface));
205 }
206 }
207}
208
209template<typename SurfaceT>
211{
212 for (auto it = m_entries.begin(); it != m_entries.end(); ++it) {
213 if (SurfaceT* s = it.value().data()) {
214 s->deleteLater();
215 }
216 }
217 m_entries.clear();
218}
219
220template<typename SurfaceT>
221void ScreenSurfaceRegistry<SurfaceT>::adoptSurface(QScreen* screen, SurfaceT* surface)
222{
223 // Registry becomes the owner-of-record. Prior entries are scheduled for
224 // deleteLater so the QHash::insert below doesn't silently displace and
225 // leak. Re-adopting the same pointer is idempotent.
226 const auto existing = m_entries.constFind(screen);
227 if (existing != m_entries.constEnd()) {
228 if (SurfaceT* prior = existing.value().data()) {
229 if (prior != surface) {
230 prior->deleteLater();
231 }
232 }
233 }
234 m_entries.insert(screen, QPointer<SurfaceT>(surface));
235}
236
237template<typename SurfaceT>
239{
240 const auto it = m_entries.constFind(screen);
241 return (it != m_entries.constEnd()) ? it.value().data() : nullptr;
242}
243
244template<typename SurfaceT>
246{
247 QList<SurfaceT*> out;
248 out.reserve(m_entries.size());
249 for (auto it = m_entries.constBegin(); it != m_entries.constEnd(); ++it) {
250 if (SurfaceT* s = it.value().data()) {
251 out.append(s);
252 }
253 }
254 return out;
255}
256
257} // namespace PhosphorLayer
Source-of-truth interface for the available QScreen set.
Definition IScreenProvider.h:62
virtual QList< QScreen * > screens() const =0
Full list of screens the surfaces should be aware of.
Per-screen bookkeeping for surfaces with AllScreens affinity.
Definition ScreenSurfaceRegistry.h:55
~ScreenSurfaceRegistry()
Definition ScreenSurfaceRegistry.h:69
QList< SurfaceT * > surfaces() const
Definition ScreenSurfaceRegistry.h:245
SurfaceT * surfaceForScreen(QScreen *screen) const
Definition ScreenSurfaceRegistry.h:238
void adoptSurface(QScreen *screen, SurfaceT *surface)
Register an externally-created Surface under screen.
Definition ScreenSurfaceRegistry.h:221
void syncToScreens(Builder builder)
Diff-sync against the provider's current screen list.
Definition ScreenSurfaceRegistry.h:166
IScreenProvider * screenProvider() const noexcept
Definition ScreenSurfaceRegistry.h:122
std::function< SurfaceT *(QScreen *)> Builder
Per-screen builder.
Definition ScreenSurfaceRegistry.h:62
ScreenSurfaceRegistry(SurfaceFactory *factory, IScreenProvider *screens)
Definition ScreenSurfaceRegistry.h:64
SurfaceFactory * factory() const noexcept
Definition ScreenSurfaceRegistry.h:118
ScreenSurfaceRegistry & operator=(const ScreenSurfaceRegistry &)=delete
QList< SurfaceT * > createForAllScreens(Builder builder)
Create one Surface per screen reported by the provider.
Definition ScreenSurfaceRegistry.h:136
ScreenSurfaceRegistry(const ScreenSurfaceRegistry &)=delete
void clear()
Drop all tracked surfaces (deleteLater on each, clear map).
Definition ScreenSurfaceRegistry.h:210
Stateless constructor for Surfaces.
Definition SurfaceFactory.h:35
Definition SurfaceAnimator.h:26
constexpr const char * Surface
Definition LayerSurface.h:21