Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
Manager.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
7#include "VirtualScreen.h"
8#include "phosphorscreenscore_export.h"
9
10#include <QHash>
11#include <QMap>
12#include <QObject>
13#include <QPointer>
14#include <QSet>
15#include <QTimer>
16#include <QVector>
17
18class QScreen;
19class QWindow;
20
21namespace Phosphor::Screens {
22
23class IConfigStore;
24class IPanelSource;
25class IScreenProvider;
26
57
74class PHOSPHORSCREENSCORE_EXPORT ScreenManager : public QObject
75{
76 Q_OBJECT
77public:
79
80 explicit ScreenManager(ScreenManagerConfig cfg = ScreenManagerConfig{}, QObject* parent = nullptr);
81 ~ScreenManager() override;
82
91 void start();
92
94 void stop();
95
96 // ─── Physical screen queries ─────────────────────────────────────────
97 //
98 // screens() / primaryScreen() / screenByName() reflect the screen
99 // provider's LIVE output set. physicalScreenFor() and the
100 // screenGeometry() / screenAvailableGeometry() resolvers below instead
101 // read the TRACKED snapshot refreshed on each lifecycle signal — the two
102 // agree except transiently, inside a lifecycle slot mid-resync. Prefer
103 // the tracked resolvers for geometry work so a query stays consistent
104 // with the available-geometry cache.
105
106 QVector<PhysicalScreen> screens() const;
108 PhysicalScreen screenByName(const QString& name) const;
109
116 {
117 return m_cfg.maxVirtualScreensPerPhysical;
118 }
119
132 QRect actualAvailableGeometry(const PhysicalScreen& screen) const;
133
147 QRect actualAvailableGeometry(QScreen* screen) const;
148
158
159 // ─── Virtual screen management ───────────────────────────────────────
160
165 bool setVirtualScreenConfig(const QString& physicalScreenId, const VirtualScreenConfig& config);
166
170 void refreshVirtualConfigs(const QHash<QString, VirtualScreenConfig>& configs);
171
172 VirtualScreenConfig virtualScreenConfig(const QString& physicalScreenId) const;
173 bool hasVirtualScreens(const QString& physicalScreenId) const;
174
175 // ─── Effective-screen queries ────────────────────────────────────────
176
177 QStringList effectiveScreenIds() const;
178 QStringList virtualScreenIdsFor(const QString& physicalScreenId) const;
179
180 QRect screenGeometry(const QString& screenId) const;
181 QRect screenAvailableGeometry(const QString& screenId) const;
182
185 PhysicalScreen physicalScreenFor(const QString& screenId) const;
186 VirtualScreenDef::PhysicalEdges physicalEdgesFor(const QString& screenId) const;
187
188 QString virtualScreenAt(const QPoint& globalPos, const QString& physicalScreenId) const;
189 QString effectiveScreenAt(const QPoint& globalPos) const;
190
191 // ─── Panel re-query control ──────────────────────────────────────────
192
199
200 // ─── Compositor-reported available geometry ──────────────────────────
201
222 void setCompositorAvailableGeometry(const QString& screenName, const QRect& available);
223
224Q_SIGNALS:
225 void screenAdded(const PhysicalScreen& screen);
226 void screenRemoved(const PhysicalScreen& screen);
228 void availableGeometryChanged(const PhysicalScreen& screen, const QRect& availableGeometry);
229
235
240
243 void virtualScreensChanged(const QString& physicalScreenId);
244
249 void virtualScreenRegionsChanged(const QString& physicalScreenId);
250
259 void screenIdentifierChanged(const QString& oldId, const QString& newId);
260
261private Q_SLOTS:
262 void onProviderScreenAdded(const PhysicalScreen& screen);
263 void onProviderScreenRemoved(const PhysicalScreen& screen);
264 void onProviderScreenGeometryChanged(const PhysicalScreen& screen);
265
266private:
267 // Rebuild m_trackedScreens from the provider's current output set.
268 // The provider is the single source of truth — every lifecycle slot
269 // resyncs through this rather than mutating the vector incrementally.
270 void syncTrackedScreens();
271
272 // Tracked-screen lookups. Return a copy of the matching tracked screen,
273 // or an invalid PhysicalScreen when nothing matches. By value rather
274 // than a pointer-into-m_trackedScreens so a caller cannot dangle it
275 // across a syncTrackedScreens that re-assigns the vector.
276 PhysicalScreen trackedScreenByName(const QString& name) const;
277 PhysicalScreen trackedScreenFor(const QString& screenId) const;
278
279 // Geometry sensor (layer-shell) lifecycle. Keyed by connector name.
280 void createGeometrySensor(const PhysicalScreen& screen);
281 void destroyGeometrySensor(const QString& screenName);
282 void onSensorGeometryChanged(const QString& screenName);
283
284 // Reads sensor + IPanelSource offsets for @p screen, updates
285 // m_availableGeometryCache, fires availableGeometryChanged on diff.
286 void calculateAvailableGeometry(const PhysicalScreen& screen);
287
288 void onPanelOffsetsChanged(QScreen* screen);
289 void onPanelRequeryCompleted();
290
291 void onConfigStoreChanged();
292
293 Config m_cfg;
294
295 // The effective screen provider — either the injected one or a
296 // QtScreenProvider the manager constructed (parented to `this`) when
297 // the config left it null.
298 IScreenProvider* m_screenProvider = nullptr;
299
300 bool m_running = false;
301 bool m_panelGeometryReadyEmitted = false;
302
303 QVector<PhysicalScreen> m_trackedScreens;
304
305 // Layer-shell sensor windows (one per screen, keyed by connector name)
306 // — track live available area size from the compositor.
307 QHash<QString, QPointer<QWindow>> m_geometrySensors;
308
309 // Per-screen-name available-geometry cache (was file-static in
310 // pre-extraction class — instance state now to avoid singleton coupling).
311 //
312 // NOTE on the `mutable` accessor caches below (this one, the
313 // effective-ID cache, and the virtual-geometry cache): they're
314 // populated lazily by nominally-const accessors and not protected by
315 // any synchronisation. The class contract is GUI-thread-only (see
316 // class-level doc and ScreenIdentity.h for the same constraint) —
317 // callers on worker threads will race these caches. Don't add locks
318 // here without also re-auditing every const accessor to make sure
319 // the mutation isn't leaked via returned QString&/QRect& references.
320 mutable QHash<QString, QRect> m_availableGeometryCache;
321
322 // Authoritative per-screen available geometry pushed by the compositor
323 // bridge (the KWin effect's clientArea/MaximizeArea query). Keyed by
324 // connector name. When an entry exists for a screen it overrides the
325 // sensor/D-Bus heuristic entirely in calculateAvailableGeometry — see
326 // setCompositorAvailableGeometry. Cleared per-screen on screen removal
327 // (destroyGeometrySensor) and wholesale on stop().
328 QHash<QString, QRect> m_compositorAvailableGeometry;
329
330 // Virtual screen configurations, keyed by physical screen ID.
331 QHash<QString, VirtualScreenConfig> m_virtualConfigs;
332
333 mutable QStringList m_cachedEffectiveScreenIds;
334 mutable bool m_effectiveScreenIdsDirty = true;
335
336 mutable QHash<QString, QRect> m_virtualGeometryCache;
337
338 // Warn-once set for screenGeometry() virtual-screen cache misses. A stale
339 // "physId/vs:N" id that survives a VS-config change gets queried
340 // repeatedly (per cursor move, per snap commit) — warn once per id rather
341 // than flooding the journal. Pruned by invalidateVirtualGeometryCache and
342 // cleared on a successful resolve so a genuinely-new miss after a
343 // reconfigure still surfaces.
344 mutable QSet<QString> m_warnedVirtualGeometryMisses;
345
346 void invalidateVirtualGeometryCache(const QString& physicalScreenId = {}) const;
347 void rebuildVirtualGeometryCache(const QString& physicalScreenId) const;
348
349 QString virtualScreenAtWithScreen(const QPoint& globalPos, const QString& physicalScreenId,
350 const PhysicalScreen& screen) const;
351
357 void propagateIdentifierDrift(const QHash<QString, QString>& oldIds);
358};
359
360} // namespace Phosphor::Screens
Pluggable persistence for virtual-screen configurations.
Definition IConfigStore.h:37
Pluggable producer of panel-reservation offsets per screen.
Definition IPanelSource.h:33
Pluggable source of the connected-output set and its lifecycle.
Definition IScreenProvider.h:33
Centralized screen-topology service.
Definition Manager.h:75
ScreenManager(ScreenManagerConfig cfg=ScreenManagerConfig{}, QObject *parent=nullptr)
VirtualScreenConfig virtualScreenConfig(const QString &physicalScreenId) const
void screenRemoved(const PhysicalScreen &screen)
void delayedPanelRequeryCompleted()
Fired when a delayed panel re-query (scheduleDelayedPanelRequery) has settled.
void screenIdentifierChanged(const QString &oldId, const QString &newId)
Emitted when a connected screen's identifier flips — e.g.
QRect actualAvailableGeometry(QScreen *screen) const
Bridge overload for consumers that still hold a live QScreen*.
QStringList effectiveScreenIds() const
void screenGeometryChanged(const PhysicalScreen &screen)
PhysicalScreen physicalScreenFor(const QString &screenId) const
The physical output a (physical or virtual) screen ID resolves to.
void virtualScreenRegionsChanged(const QString &physicalScreenId)
VS regions changed but the VS ID set is unchanged (swap, rotate, boundary resize).
void screenAdded(const PhysicalScreen &screen)
void scheduleDelayedPanelRequery(int delayMs)
Schedule a one-shot panel re-query after delayMs.
void stop()
Tear down everything start() set up. Idempotent.
PhysicalScreen screenByName(const QString &name) const
bool isPanelGeometryReady() const
Has the panel source produced its first reading?
void refreshVirtualConfigs(const QHash< QString, VirtualScreenConfig > &configs)
Diff configs against the current cache and apply the delta.
QVector< PhysicalScreen > screens() const
bool setVirtualScreenConfig(const QString &physicalScreenId, const VirtualScreenConfig &config)
Apply a single VS subdivision config.
QRect actualAvailableGeometry(const PhysicalScreen &screen) const
Per-screen panel-aware available geometry.
PhysicalScreen primaryScreen() const
int maxVirtualScreensPerPhysical() const
Maximum number of virtual screens per physical monitor this manager will admit.
Definition Manager.h:115
bool hasVirtualScreens(const QString &physicalScreenId) const
QRect screenGeometry(const QString &screenId) const
void start()
Begin tracking screens.
QRect screenAvailableGeometry(const QString &screenId) const
void setCompositorAvailableGeometry(const QString &screenName, const QRect &available)
Record the authoritative per-screen available geometry as reported by the compositor.
QString effectiveScreenAt(const QPoint &globalPos) const
void virtualScreensChanged(const QString &physicalScreenId)
VS topology changed (added, removed, renamed).
void availableGeometryChanged(const PhysicalScreen &screen, const QRect &availableGeometry)
QString virtualScreenAt(const QPoint &globalPos, const QString &physicalScreenId) const
QStringList virtualScreenIdsFor(const QString &physicalScreenId) const
VirtualScreenDef::PhysicalEdges physicalEdgesFor(const QString &screenId) const
void panelGeometryReady()
Fired once when isPanelGeometryReady transitions to true.
Definition IWindowTrackingService.h:26
A physical output as ScreenManager sees it — decoupled from QScreen.
Definition PhysicalScreen.h:37
Construction-time wiring for ScreenManager.
Definition Manager.h:50
IScreenProvider * screenProvider
Definition Manager.h:51
IConfigStore * configStore
Definition Manager.h:53
bool useGeometrySensors
Definition Manager.h:54
int maxVirtualScreensPerPhysical
Definition Manager.h:55
IPanelSource * panelSource
Definition Manager.h:52
Configuration for how a physical screen is subdivided into virtual screens.
Definition VirtualScreen.h:124
Check which edges of this virtual screen are at the physical screen boundary (vs internal edges share...
Definition VirtualScreen.h:103