Keep MangoWM workspace apps on their own monitor (#3832)

port 2.0

(cherry picked from commit 203b1369b3)
This commit is contained in:
Tomás
2026-10-21 11:24:35 -04:00
committed by bbedward
parent f63d8bf4fb
commit 12e54e0225
5 changed files with 29 additions and 6 deletions
+1 -1
View File
@@ -439,7 +439,7 @@ function mangoActiveWorkspace(raw, screenName) {
function mangoWindowsOnWorkspace(windows, workspace) {
// mangoTags are 1-based; workspace.id is 0-based.
return windows.filter(win => win && (win.mangoTags || []).includes(workspace.id + 1));
return windows.filter(win => win && win.mangoMonitor === workspace.output && (win.mangoTags || []).includes(workspace.id + 1));
}
function i3Key(ws) {
+1 -1
View File
@@ -468,7 +468,7 @@ Singleton {
const yKey = y.niriWindowId !== undefined ? y.niriWindowId : y.mangoWindowId;
if (xKey === undefined || yKey === undefined)
return false;
if (xKey !== yKey || x.niriWorkspaceId !== y.niriWorkspaceId || x.appId !== y.appId || x.title !== y.title || !!x.activated !== !!y.activated || !!x.fullscreen !== !!y.fullscreen || !!x.maximized !== !!y.maximized || !!x.minimized !== !!y.minimized)
if (xKey !== yKey || x.niriWorkspaceId !== y.niriWorkspaceId || x.mangoMonitor !== y.mangoMonitor || x.appId !== y.appId || x.title !== y.title || !!x.activated !== !!y.activated || !!x.fullscreen !== !!y.fullscreen || !!x.maximized !== !!y.maximized || !!x.minimized !== !!y.minimized)
return false;
}
return true;
+2 -2
View File
@@ -36,8 +36,8 @@
}
},
"windows": [
{ "appId": "alacritty", "mangoTags": [3] },
{ "appId": "firefox", "mangoTags": [2, 3] },
{ "appId": "alacritty", "mangoMonitor": "DP-1", "mangoTags": [3] },
{ "appId": "firefox", "mangoMonitor": "DP-1", "mangoTags": [2, 3] },
{ "appId": "untagged" }
]
},
+8
View File
@@ -20,6 +20,14 @@ const aqueousService = source("../Services/AqueousService.qml");
const model = vm.createContext({});
vm.runInContext(source("../Common/WindowModel.js").replace(/^\.pragma.*$/m, ""), model);
test("mango monitor changes invalidate cached toplevels", () => {
const context = vm.createContext({});
lift(compositorService, "_toplevelListEquivalent", context);
const window = { mangoWindowId: 1, appId: "app1", mangoMonitor: "DP-1", mangoTags: [1] };
assert.equal(context._toplevelListEquivalent([window], [{ ...window }]), true);
assert.equal(context._toplevelListEquivalent([window], [{ ...window, mangoMonitor: "HDMI-A-1" }]), false);
});
function niriState(scenario) {
const s = fixture("niri")[scenario];
const toplevels = s.toplevels.map(t => ({ ...t }));
+17 -2
View File
@@ -291,8 +291,23 @@ test("mango scroll targets, active tag and windows", () => {
assert.deepEqual(plain(model.mangoActiveWorkspace(dp1, "DP-1")), { id: 2, idx: 3, name: "", output: "DP-1", active: true, placeholder: false });
assert.equal(model.mangoActiveWorkspace(mango("available", "HDMI-A-1"), "HDMI-A-1").id, 0);
assert.equal(model.mangoActiveWorkspace(mango("available", "DP-2"), "DP-2"), null);
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(dp1.windows, { id: 2 }), "appId"), ["alacritty", "firefox"]);
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(dp1.windows, { id: 1 }), "appId"), ["firefox"]);
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(dp1.windows, { id: 2, output: "DP-1" }), "appId"), ["alacritty", "firefox"]);
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(dp1.windows, { id: 1, output: "DP-1" }), "appId"), ["firefox"]);
});
test("mango workspace apps follow their output when windows share a tag", () => {
const windows = [
{ appId: "app1", mangoMonitor: "DP-1", mangoTags: [1] },
{ appId: "app2", mangoMonitor: "HDMI-A-1", mangoTags: [1] }
];
const dp1 = model.mangoRecord(0, {}, "DP-1");
const hdmi = model.mangoRecord(0, {}, "HDMI-A-1");
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(windows, dp1), "appId"), ["app1"]);
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(windows, hdmi), "appId"), ["app2"]);
windows[0].mangoMonitor = "HDMI-A-1";
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(windows, dp1), "appId"), []);
assert.deepEqual(pick(model.mangoWindowsOnWorkspace(windows, hdmi), "appId"), ["app1", "app2"]);
});
test("i3 lists order numbered before named and strip the number prefix", () => {