Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
WindowPlacement.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
8#include <QHash>
9#include <QJsonArray>
10#include <QJsonObject>
11#include <QLatin1String>
12#include <QRect>
13#include <QString>
14#include <QStringList>
15
16#include <utility>
17
18namespace PhosphorEngine {
19
32{
33 QString state;
34 QStringList zoneIds;
35 int order = -1;
36
37 bool operator==(const EngineSlot& o) const
38 {
39 return state == o.state && zoneIds == o.zoneIds && order == o.order;
40 }
41 bool operator!=(const EngineSlot& o) const
42 {
43 return !(*this == o);
44 }
45 bool isEmpty() const
46 {
47 return state.isEmpty();
48 }
49};
50
68{
69 // ── Identity (the two store keys) ──
70 QString windowId;
71 QString appId;
72
73 // ── Context (the universal restore + disabled-context gate) ──
74 QString screenId;
85 QString activity;
87
88 // ── Per-engine managed state, keyed by engineId() ──
89 QHash<QString, EngineSlot> engines;
90
91 // ── Shared free/float geometry, keyed by screenId ──
92 QHash<QString, QRect> freeGeometryByScreen;
93
94 // ── Recency (most-recent-wins ordering; stamped by the store) ──
95 quint64 sequence = 0;
96
97 bool isValid() const
98 {
99 return !windowId.isEmpty() && !appId.isEmpty();
100 }
101
104 EngineSlot slotFor(const QString& engineId) const
105 {
106 return engines.value(engineId);
107 }
108
111 QRect freeGeometryFor(const QString& screenId) const
112 {
113 return freeGeometryByScreen.value(screenId);
114 }
115
122 {
123 QString best;
124 for (auto it = freeGeometryByScreen.constBegin(); it != freeGeometryByScreen.constEnd(); ++it) {
125 if (it.value().isValid() && (best.isEmpty() || it.key() < best)) {
126 best = it.key();
127 }
128 }
129 return best;
130 }
131
136 QRect anyFreeGeometry() const
137 {
139 }
140
156 {
157 if (anyFreeGeometry().isValid()) {
158 return true;
159 }
160 for (auto it = engines.constBegin(); it != engines.constEnd(); ++it) {
161 const EngineSlot& s = it.value();
162 if (s.state == stateSnapped() || s.state == stateTiled()) {
163 return true;
164 }
165 if (!s.zoneIds.isEmpty() || s.order >= 0) {
166 return true;
167 }
168 }
169 return false;
170 }
171
178 bool sameContentAs(const WindowPlacement& o) const
179 {
180 return windowId == o.windowId && appId == o.appId && screenId == o.screenId
181 && virtualDesktop == o.virtualDesktop && activity == o.activity && kind == o.kind && engines == o.engines
183 }
184
190 static QLatin1String snapEngineId()
191 {
192 return QLatin1String("snap");
193 }
194 static QLatin1String autotileEngineId()
195 {
196 return QLatin1String("autotile");
197 }
198 static QLatin1String scrollingEngineId()
199 {
200 return QLatin1String("scrolling");
201 }
202
210 static QLatin1String stateFree()
211 {
212 return QLatin1String("free");
213 }
214 static QLatin1String stateFloating()
215 {
216 return QLatin1String("floating");
217 }
218 static QLatin1String stateSnapped()
219 {
220 return QLatin1String("snapped");
221 }
222 static QLatin1String stateTiled()
223 {
224 return QLatin1String("tiled");
225 }
237 static QLatin1String stateReleased()
238 {
239 return QLatin1String("released");
240 }
241
242 QJsonObject toJson() const
243 {
244 QJsonObject obj;
245 obj[QLatin1String("windowId")] = windowId;
246 obj[QLatin1String("screen")] = screenId;
247 obj[QLatin1String("desktop")] = virtualDesktop;
248 if (!activity.isEmpty()) {
249 obj[QLatin1String("activity")] = activity;
250 }
251 obj[QLatin1String("kind")] = static_cast<int>(kind);
252
253 QJsonObject eng;
254 for (auto it = engines.constBegin(); it != engines.constEnd(); ++it) {
255 const EngineSlot& s = it.value();
256 if (s.isEmpty()) {
257 continue;
258 }
259 QJsonObject so;
260 so[QLatin1String("state")] = s.state;
261 if (!s.zoneIds.isEmpty()) {
262 QJsonArray z;
263 for (const QString& id : s.zoneIds) {
264 z.append(id);
265 }
266 so[QLatin1String("zoneIds")] = z;
267 }
268 if (s.order >= 0) {
269 so[QLatin1String("order")] = s.order;
270 }
271 eng[it.key()] = so;
272 }
273 if (!eng.isEmpty()) {
274 obj[QLatin1String("engines")] = eng;
275 }
276
277 QJsonObject fg;
278 for (auto it = freeGeometryByScreen.constBegin(); it != freeGeometryByScreen.constEnd(); ++it) {
279 const QRect& g = it.value();
280 if (!g.isValid()) {
281 continue;
282 }
283 QJsonObject go;
284 go[QLatin1String("x")] = g.x();
285 go[QLatin1String("y")] = g.y();
286 go[QLatin1String("w")] = g.width();
287 go[QLatin1String("h")] = g.height();
288 fg[it.key()] = go;
289 }
290 if (!fg.isEmpty()) {
291 obj[QLatin1String("freeGeo")] = fg;
292 }
293
294 obj[QLatin1String("seq")] = static_cast<double>(sequence);
295 return obj;
296 }
297
298 static WindowPlacement fromJson(const QString& appId, const QJsonObject& obj)
299 {
301 p.appId = appId;
302 p.windowId = obj.value(QLatin1String("windowId")).toString();
303 p.screenId = obj.value(QLatin1String("screen")).toString();
304 p.virtualDesktop = obj.value(QLatin1String("desktop")).toInt();
305 p.activity = obj.value(QLatin1String("activity")).toString();
306 p.kind = clampWindowKindFromWire(obj.value(QLatin1String("kind")).toInt());
307
308 const QJsonObject eng = obj.value(QLatin1String("engines")).toObject();
309 for (auto it = eng.constBegin(); it != eng.constEnd(); ++it) {
310 const QJsonObject so = it.value().toObject();
311 EngineSlot s;
312 s.state = so.value(QLatin1String("state")).toString();
313 for (const QJsonValue& v : so.value(QLatin1String("zoneIds")).toArray()) {
314 const QString z = v.toString();
315 if (!z.isEmpty()) {
316 s.zoneIds.append(z);
317 }
318 }
319 s.order = so.value(QLatin1String("order")).toInt(-1);
320 if (!s.isEmpty()) {
321 p.engines.insert(it.key(), s);
322 }
323 }
324
325 const QJsonObject fg = obj.value(QLatin1String("freeGeo")).toObject();
326 for (auto it = fg.constBegin(); it != fg.constEnd(); ++it) {
327 const QJsonObject go = it.value().toObject();
328 const QRect g(go.value(QLatin1String("x")).toInt(), go.value(QLatin1String("y")).toInt(),
329 go.value(QLatin1String("w")).toInt(), go.value(QLatin1String("h")).toInt());
330 if (g.isValid()) {
331 p.freeGeometryByScreen.insert(it.key(), g);
332 }
333 }
334
335 p.sequence = static_cast<quint64>(obj.value(QLatin1String("seq")).toDouble());
336 return p;
337 }
338};
339
402inline bool hasStableAppIdFor(const QString& appId, const QString& windowId)
403{
404 return !appId.isEmpty() && appId != windowId;
405}
406
422inline bool recordContextMatchesLive(const WindowPlacement& p, int liveDesktop, const QString& liveActivity)
423{
424 if (p.virtualDesktop != 0 && liveDesktop != 0 && p.virtualDesktop != liveDesktop) {
425 return false;
426 }
427 return p.activity.isEmpty() || liveActivity.isEmpty() || p.activity == liveActivity;
428}
429
430template<typename IsEngineMode>
431bool pendingCrossScreenManagedRestore(const WindowPlacement& p, QLatin1String engineId, QLatin1String managedState,
432 const QString& openingScreenId, IsEngineMode&& isEngineMode)
433{
434 if (p.slotFor(engineId).state != managedState) {
435 return false;
436 }
437 if (p.screenId.isEmpty() || p.screenId == openingScreenId) {
438 return false; // same screen (or unscreened): the opening context's engine owns it
439 }
440 return isEngineMode(p.screenId, p.virtualDesktop, p.activity);
441}
442
447template<typename IsSnappingMode>
448bool pendingCrossScreenSnapRestore(const WindowPlacement& p, const QString& openingScreenId,
449 IsSnappingMode&& isSnappingMode)
450{
452 openingScreenId, std::forward<IsSnappingMode>(isSnappingMode));
453}
454
455} // namespace PhosphorEngine
Definition EngineTypes.h:14
bool pendingCrossScreenSnapRestore(const WindowPlacement &p, const QString &openingScreenId, IsSnappingMode &&isSnappingMode)
The snap-engine specialization of pendingCrossScreenManagedRestore — the original three-way gate (Sna...
Definition WindowPlacement.h:448
bool recordContextMatchesLive(const WindowPlacement &p, int liveDesktop, const QString &liveActivity)
Whether a record's own (desktop, activity) context is compatible with the live context an engine woul...
Definition WindowPlacement.h:422
WindowKind clampWindowKindFromWire(int wire)
Clamp an integer wire value to a valid WindowKind.
Definition EngineTypes.h:82
bool hasStableAppIdFor(const QString &appId, const QString &windowId)
Shared cross-engine ownership predicate over a placement record: does this record carry engineId's sl...
Definition WindowPlacement.h:402
WindowKind
Coarse structural classification for the snap-restore consume gate.
Definition EngineTypes.h:66
bool pendingCrossScreenManagedRestore(const WindowPlacement &p, QLatin1String engineId, QLatin1String managedState, const QString &openingScreenId, IsEngineMode &&isEngineMode)
Definition WindowPlacement.h:431
One engine's view of a window: which managed slot it occupies (or that it is floating / unmanaged) in...
Definition WindowPlacement.h:32
bool isEmpty() const
Definition WindowPlacement.h:45
int order
autotile slot — tile index within the screen; -1 for snap
Definition WindowPlacement.h:35
bool operator==(const EngineSlot &o) const
Definition WindowPlacement.h:37
bool operator!=(const EngineSlot &o) const
Definition WindowPlacement.h:41
QStringList zoneIds
snap slot — zone UUIDs (first is primary); empty for autotile
Definition WindowPlacement.h:34
QString state
engine-defined token: snap "snapped"/"floating"; autotile "tiled"/"floating" ("free" retired)
Definition WindowPlacement.h:33
One window's single, authoritative placement record — the unit of the unified, engine-agnostic restor...
Definition WindowPlacement.h:68
EngineSlot slotFor(const QString &engineId) const
The slot for engineId, or a default (empty) slot if this engine has no recorded state for the window.
Definition WindowPlacement.h:104
static QLatin1String snapEngineId()
Built-in engine-slot ids.
Definition WindowPlacement.h:190
static QLatin1String stateFree()
Common state-token vocabulary.
Definition WindowPlacement.h:210
QRect freeGeometryFor(const QString &screenId) const
The shared free/float geometry for screenId, or an invalid rect if none has been captured on that scr...
Definition WindowPlacement.h:111
static QLatin1String autotileEngineId()
Definition WindowPlacement.h:194
static QLatin1String scrollingEngineId()
Definition WindowPlacement.h:198
WindowKind kind
Definition WindowPlacement.h:86
bool isValid() const
Definition WindowPlacement.h:97
static QLatin1String stateTiled()
Definition WindowPlacement.h:222
bool sameContentAs(const WindowPlacement &o) const
Content equality IGNORING sequence (the store stamps a fresh sequence on every record(),...
Definition WindowPlacement.h:178
static QLatin1String stateReleased()
An engine that KNOWINGLY GAVE THE WINDOW UP (a cross-mode handoff) leaves this instead of its managed...
Definition WindowPlacement.h:237
int virtualDesktop
0 = all-desktops / sticky / unknown.
Definition WindowPlacement.h:84
QString activity
empty = all-activities / unknown
Definition WindowPlacement.h:85
static QLatin1String stateFloating()
Definition WindowPlacement.h:214
QRect anyFreeGeometry() const
Any captured free/float geometry (the deterministic pick of anyFreeGeometryScreenId()),...
Definition WindowPlacement.h:136
QJsonObject toJson() const
Definition WindowPlacement.h:242
QString anyFreeGeometryScreenId() const
The screenId of the deterministic cross-screen fallback pick: the lexicographically-smallest screenId...
Definition WindowPlacement.h:121
static QLatin1String stateSnapped()
Definition WindowPlacement.h:218
static WindowPlacement fromJson(const QString &appId, const QJsonObject &obj)
Definition WindowPlacement.h:298
QString screenId
last managed-context screen
Definition WindowPlacement.h:74
quint64 sequence
Definition WindowPlacement.h:95
QHash< QString, EngineSlot > engines
Definition WindowPlacement.h:89
QString appId
window class; FIFO key for close/reopen (uuid changes)
Definition WindowPlacement.h:71
QHash< QString, QRect > freeGeometryByScreen
Definition WindowPlacement.h:92
QString windowId
full appId|uuid; exact key for daemon-restart (uuid stable)
Definition WindowPlacement.h:70
bool hasRestorableContent() const
Whether this record carries anything worth restoring.
Definition WindowPlacement.h:155