Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
AssignmentEntry.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 <QList>
10#include <QString>
11#include <QVariantMap>
12#include <QtGlobal>
13
14#include <optional>
15
16namespace PhosphorZones {
17
22{
23 QString screenId; // Stable EDID-based identifier (or connector name fallback)
24 int virtualDesktop = 0; // 0 = all desktops
25 QString activity; // Empty = all activities
26
27 bool operator==(const LayoutAssignmentKey& other) const
28 {
29 return screenId == other.screenId && virtualDesktop == other.virtualDesktop && activity == other.activity;
30 }
31
46 static LayoutAssignmentKey fromGroupName(const QString& groupName, const QString& prefix)
47 {
49 if (!groupName.startsWith(prefix))
50 return result;
51 QString remainder = groupName.mid(prefix.size());
52 if (remainder.isEmpty())
53 return result;
54
55 int actIdx = remainder.indexOf(QLatin1String(":Activity:"));
56 if (actIdx >= 0) {
57 const QString activity = remainder.mid(actIdx + 10);
58 if (!activity.isEmpty())
59 result.activity = activity;
60 remainder = remainder.left(actIdx);
61 }
62 int deskIdx = remainder.indexOf(QLatin1String(":Desktop:"));
63 if (deskIdx >= 0) {
64 bool ok = false;
65 int desktop = remainder.mid(deskIdx + 9).toInt(&ok);
66 if (ok && desktop > 0)
67 result.virtualDesktop = desktop;
68 remainder = remainder.left(deskIdx);
69 }
70 result.screenId = remainder;
71 return result;
72 }
73};
74
75inline size_t qHash(const LayoutAssignmentKey& key, size_t seed = 0)
76{
77 seed = ::qHash(key.screenId, seed);
78 seed = ::qHash(key.virtualDesktop, seed);
79 seed = ::qHash(key.activity, seed);
80 return seed;
81}
82
91{
100 enum Mode {
113 Scrolling = 2
114 };
116 QString snappingLayout; // UUID string of manual layout
117 QString tilingAlgorithm; // e.g. "dwindle", "wide", "tall"
118
119 QString activeLayoutId() const
120 {
121 if (mode == Autotile) {
122 // Autotile mode always produces a non-empty id so the cascade
123 // visitors in LayoutRegistry accept it (they reject on
124 // activeLayoutId().isEmpty()). For a mode-only entry (empty
125 // tilingAlgorithm — what the KCM writes for "autotile, use
126 // default algorithm"), makeAutotileId returns the bare
127 // prefix @c "autotile:"; downstream callers use
128 // @ref PhosphorLayout::LayoutId::isAutotile to detect mode
129 // and @ref PhosphorLayout::LayoutId::extractAlgorithmId to
130 // get the algorithm (empty = engine default).
132 }
133 return snappingLayout;
134 }
135 bool isValid() const
136 {
137 return !snappingLayout.isEmpty() || !tilingAlgorithm.isEmpty();
138 }
139 bool operator==(const AssignmentEntry& other) const
140 {
141 return mode == other.mode && snappingLayout == other.snappingLayout && tilingAlgorithm == other.tilingAlgorithm;
142 }
143
149 static AssignmentEntry fromLayoutId(const QString& layoutId, const AssignmentEntry& existing)
150 {
151 AssignmentEntry entry = existing;
153 entry.mode = Autotile;
155 } else {
156 entry.mode = Snapping;
157 entry.snappingLayout = layoutId;
158 }
159 return entry;
160 }
162 static AssignmentEntry fromLayoutId(const QString& layoutId)
163 {
164 AssignmentEntry entry;
166 entry.mode = Autotile;
168 } else {
169 entry.mode = Snapping;
170 entry.snappingLayout = layoutId;
171 }
172 return entry;
173 }
174};
175
188{
189 std::optional<int> zonePadding;
190 std::optional<int> outerGap;
191 std::optional<bool> usePerSideOuterGap;
192 std::optional<int> outerGapTop;
193 std::optional<int> outerGapBottom;
194 std::optional<int> outerGapLeft;
195 std::optional<int> outerGapRight;
196
197 bool isEmpty() const
198 {
200 && !outerGapRight;
201 }
202};
203
220{
221 std::optional<QString> shaderId;
222 QVariantMap shaderParams;
223 std::optional<int> style;
224
225 bool isEmpty() const
226 {
227 return !shaderId && !style;
228 }
229};
230
242{
243 switch (mode) {
245 return QStringLiteral("snapping");
247 return QStringLiteral("autotile");
249 return QStringLiteral("scrolling");
250 }
251 // Switch is exhaustive over `Mode`. A `static_cast<Mode>(99)` reaching
252 // this line means either (a) a future Mode enum value was added without
253 // a case here — the real guard against that is Qt's -Wswitch diagnostic
254 // at the missing-case site (NOTE: this project's CMake does NOT
255 // promote it to -Werror, so it's a warning at build time, not an
256 // error), or (b) callers fabricated an out-of-range value via cast.
257 // `Q_UNREACHABLE_RETURN` expands to `[[unreachable]] + return`
258 // on modern compilers, so the sentinel below carries through release
259 // builds even when the optimizer assumes the function never reaches
260 // here.
261 //
262 // The sentinel `"invalid"` is rejected by the `DisableEngine`
263 // descriptor's closed-vocabulary validator
264 // (`engineModeOptions().contains(...)`), so a malformed disable rule
265 // fails load loudly. NOTE: `SetEngineMode`'s validator only checks
266 // `hasNonEmptyString` (open-vocabulary by design — see
267 // `libs/phosphor-window-rules/src/ruleaction.cpp:225-238`), so a
268 // malformed assignment rule survives load but is silently coerced
269 // back to Snapping at consumption via
270 // `entryFromRuleMatchActions → modeFromWireString → nullopt`. The
271 // sentinel makes the corruption visible to operators inspecting
272 // windowrules.json by eye, but is not a load-time gate for the
273 // assignment path.
274 Q_UNREACHABLE_RETURN(QStringLiteral("invalid"));
275}
276
285inline std::optional<AssignmentEntry::Mode> modeFromWireString(const QString& wire)
286{
287 if (wire == QLatin1String("snapping")) {
289 }
290 if (wire == QLatin1String("autotile")) {
292 }
293 if (wire == QLatin1String("scrolling")) {
295 }
296 return std::nullopt;
297}
298
309inline QList<AssignmentEntry::Mode> allModes()
310{
312}
313
314} // namespace PhosphorZones
QString makeAutotileId(const QString &algorithmId)
Definition LayoutId.h:50
bool isAutotile(const QString &id)
Definition LayoutId.h:28
QString extractAlgorithmId(const QString &id)
Extract the algorithm id portion from an autotile preview id.
Definition LayoutId.h:38
Definition IWindowTrackingService.h:23
size_t qHash(const LayoutAssignmentKey &key, size_t seed=0)
Definition AssignmentEntry.h:75
std::optional< AssignmentEntry::Mode > modeFromWireString(const QString &wire)
Inverse of modeToWireString.
Definition AssignmentEntry.h:285
QString modeToWireString(AssignmentEntry::Mode mode)
Canonical wire-string for an AssignmentEntry::Mode.
Definition AssignmentEntry.h:241
QList< AssignmentEntry::Mode > allModes()
Iteration order for every AssignmentEntry::Mode value.
Definition AssignmentEntry.h:309
Explicit per-context assignment entry storing both mode fields.
Definition AssignmentEntry.h:91
QString snappingLayout
Definition AssignmentEntry.h:116
static AssignmentEntry fromLayoutId(const QString &layoutId)
Create a fresh AssignmentEntry from a layoutId string.
Definition AssignmentEntry.h:162
static AssignmentEntry fromLayoutId(const QString &layoutId, const AssignmentEntry &existing)
Update an existing AssignmentEntry from a layoutId, preserving the "other" field.
Definition AssignmentEntry.h:149
QString activeLayoutId() const
Definition AssignmentEntry.h:119
Mode
Per-context engine selection.
Definition AssignmentEntry.h:100
@ Scrolling
Reserved engine slot for a future scrolling-workspace engine.
Definition AssignmentEntry.h:113
@ Snapping
Definition AssignmentEntry.h:101
@ Autotile
Definition AssignmentEntry.h:102
bool operator==(const AssignmentEntry &other) const
Definition AssignmentEntry.h:139
bool isValid() const
Definition AssignmentEntry.h:135
QString tilingAlgorithm
Definition AssignmentEntry.h:117
Mode mode
Definition AssignmentEntry.h:115
Per-context gap override resolved from window rules.
Definition AssignmentEntry.h:188
std::optional< int > outerGapLeft
Definition AssignmentEntry.h:194
std::optional< int > outerGapBottom
Definition AssignmentEntry.h:193
std::optional< int > zonePadding
Definition AssignmentEntry.h:189
std::optional< int > outerGapTop
Definition AssignmentEntry.h:192
bool isEmpty() const
Definition AssignmentEntry.h:197
std::optional< bool > usePerSideOuterGap
Definition AssignmentEntry.h:191
std::optional< int > outerGap
Definition AssignmentEntry.h:190
std::optional< int > outerGapRight
Definition AssignmentEntry.h:195
Per-context overlay-property overrides resolved from window-rule actions.
Definition AssignmentEntry.h:220
std::optional< int > style
Definition AssignmentEntry.h:223
QVariantMap shaderParams
Definition AssignmentEntry.h:222
std::optional< QString > shaderId
Definition AssignmentEntry.h:221
bool isEmpty() const
Definition AssignmentEntry.h:225
Key for layout assignment (screen + desktop + activity)
Definition AssignmentEntry.h:22
static LayoutAssignmentKey fromGroupName(const QString &groupName, const QString &prefix)
Parse a "<prefix><screenId>[:Desktop:N][:Activity:uuid]" group name.
Definition AssignmentEntry.h:46
bool operator==(const LayoutAssignmentKey &other) const
Definition AssignmentEntry.h:27
QString screenId
Definition AssignmentEntry.h:23
int virtualDesktop
Definition AssignmentEntry.h:24
QString activity
Definition AssignmentEntry.h:25