mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 15:24:00 -04:00
- new blur variants, glow, wobble, motion blur and groupbar support - new inputs support for mouse, touchpad and keyboard settings - greeter, cheatsheet and scroll capture dispatch in Lua - scrolling workspace screenshot focus fix - fix scrolling screenshots from being cut-off on fractional-scale displays - workspaces are keyed by address on the beta branch and upcoming stable release where named workspaces have no id - bind flags, per-device binds, new window-rule keys and effects - outputs default to preferred/auto/auto and gate HDR/VRR on hardwareDetails - new dms setup/installer systemd session support on hyprland-session.target
223 lines
14 KiB
JavaScript
223 lines
14 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import vm from "node:vm";
|
|
import test from "node:test";
|
|
|
|
const here = new URL("./", import.meta.url);
|
|
const source = path => readFileSync(new URL(path, here), "utf8");
|
|
const fixture = name => JSON.parse(readFileSync(new URL(`fixtures/windows/${name}.json`, here), "utf8"));
|
|
const Position = { Top: 0, Bottom: 1, Left: 2, Right: 3 };
|
|
const positions = ["Top", "Bottom", "Left", "Right"];
|
|
|
|
function lift(text, name, context) {
|
|
const match = text.match(new RegExp(`^ function ${name}\\([^\\n]*\\) \\{\\n[\\s\\S]*?^ \\}`, "m"));
|
|
assert.ok(match, `function ${name} not found`);
|
|
vm.runInContext(match[0], context);
|
|
}
|
|
|
|
const compositorService = source("../Services/CompositorService.qml");
|
|
const aqueousService = source("../Services/AqueousService.qml");
|
|
const model = vm.createContext({});
|
|
vm.runInContext(source("../Common/WindowModel.js").replace(/^\.pragma.*$/m, ""), model);
|
|
|
|
function niriState(scenario) {
|
|
const s = fixture("niri")[scenario];
|
|
const toplevels = s.toplevels.map(t => ({ ...t }));
|
|
const window = id => s.windows.find(w => w.id === id);
|
|
const sorted = s.sortedToplevels.map(st => ({ niriWindowId: st.niriWindowId, niriWorkspaceId: st.niriWorkspaceId, appId: window(st.niriWindowId).app_id, title: window(st.niriWindowId).title, sourceToplevel: st.toplevel === undefined ? undefined : toplevels[st.toplevel] }));
|
|
return { ...s, toplevels, sorted, screens: fixture("niri").screens };
|
|
}
|
|
|
|
function filterCurrentDisplay(list, name) {
|
|
return list.filter(t => !t.screens || t.screens.includes(name));
|
|
}
|
|
|
|
function facade(compositor, globals) {
|
|
const context = vm.createContext({
|
|
compositor,
|
|
WindowModel: model,
|
|
_edgePositions: { top: Position.Top, bottom: Position.Bottom, left: Position.Left, right: Position.Right },
|
|
sortedToplevels: [],
|
|
ToplevelManager: { activeToplevel: null, toplevels: { values: [] } },
|
|
NiriService: {},
|
|
Hyprland: {},
|
|
MangoService: {},
|
|
AqueousService: {},
|
|
filterCurrentDisplay,
|
|
...globals
|
|
});
|
|
for (const name of ["_windowAlive", "_fallbackActiveWindow", "_retainedWindow", "activeWindowForScreen", "windowPid", "windowOnActiveWorkspace", "windowsHideBar", "windowsOverlapDock", "maximizedWindowOnScreen", "specialWorkspaceName"])
|
|
lift(compositorService, name, context);
|
|
return context;
|
|
}
|
|
|
|
function presentable(toplevel) {
|
|
return !!toplevel && !!(toplevel.title || toplevel.appId);
|
|
}
|
|
|
|
function focusedAppNiri(state, screenName, includeLastFocused, activeToplevel, previous) {
|
|
const context = facade("niri", {
|
|
sortedToplevels: state.sorted,
|
|
NiriService: { windows: state.windows, allWorkspaces: state.allWorkspaces, currentOutput: state.currentOutput, lastFocusedWindowId: state.lastFocusedWindowId },
|
|
ToplevelManager: { activeToplevel, toplevels: { values: state.toplevels } }
|
|
});
|
|
const activeWindow = context.activeWindowForScreen(screenName, previous, includeLastFocused);
|
|
const index = state.toplevels.indexOf(activeWindow);
|
|
return { activeWindow: activeWindow === null ? null : index >= 0 ? index : "foreign", onActiveWorkspace: presentable(activeWindow) && context.windowOnActiveWorkspace(screenName ?? "", activeWindow, includeLastFocused), pid: context.windowPid(activeWindow) };
|
|
}
|
|
|
|
function barHideNiri(state, screenName, position) {
|
|
const context = facade("niri", { NiriService: { windows: state.windows, allWorkspaces: state.allWorkspaces } });
|
|
const screen = state.screens[screenName];
|
|
return context.windowsHideBar(screenName, Position[position], 48 + 4, screen?.width ?? 0, screen?.height ?? 0);
|
|
}
|
|
|
|
function dockOverlapNiri(state, screenName, position) {
|
|
const context = facade("niri", { NiriService: { windows: state.windows, allWorkspaces: state.allWorkspaces } });
|
|
const screen = state.screens[screenName];
|
|
return context.windowsOverlapDock(screenName, Position[position], 60, screen?.width ?? 0, screen?.height ?? 0);
|
|
}
|
|
|
|
function hyprlandState() {
|
|
const s = fixture("hyprland");
|
|
const waylands = {};
|
|
const wayland = id => id === null ? null : (waylands[id] ??= { id, appId: id, title: id });
|
|
return { focusedWorkspace: s.focusedWorkspace, toplevels: s.toplevels.map(t => ({ ...t, wayland: wayland(t.wayland) })), wayland: id => wayland(id) };
|
|
}
|
|
|
|
function focusedAppHyprland(state, activeWindow, focusedWorkspace) {
|
|
const context = facade("hyprland", { Hyprland: { toplevels: { values: state.toplevels }, focusedWorkspace } });
|
|
return { onActiveWorkspace: presentable(activeWindow) && context.windowOnActiveWorkspace("DP-1", activeWindow, false), pid: context.windowPid(activeWindow) };
|
|
}
|
|
|
|
function specialWorkspaceName(state, waylandToplevel) {
|
|
return facade("hyprland", { Hyprland: { toplevels: { values: state.toplevels } } }).specialWorkspaceName(waylandToplevel);
|
|
}
|
|
|
|
function mangoPid(windows, sortedWindow) {
|
|
const activeWindow = { appId: "app", title: "t" };
|
|
const context = facade("mango", { sortedToplevels: sortedWindow ? [{ ...sortedWindow, sourceToplevel: activeWindow }] : [], MangoService: { windows } });
|
|
return context.windowPid(activeWindow);
|
|
}
|
|
|
|
function mangoOverlap(s, screenName, position) {
|
|
return facade("mango", { MangoService: { windows: s.windows, outputs: s.outputs } }).windowsOverlapDock(screenName, Position[position], 60, 1920, 1080);
|
|
}
|
|
|
|
function mangoMaximized(s, screenName) {
|
|
return facade("mango", { MangoService: { windows: s.windows, outputs: s.outputs } }).maximizedWindowOnScreen(screenName);
|
|
}
|
|
|
|
function aqueousActiveWindow(focusedWindow, screenName) {
|
|
const activeWindow = facade("aqueous", { AqueousService: { available: true, focusedWindow } }).activeWindowForScreen(screenName, null, false);
|
|
return activeWindow === null ? null : activeWindow.id;
|
|
}
|
|
|
|
function aqueousOverlap(s, screenName, position) {
|
|
const context = vm.createContext({ outputs: s.outputs, windows: s.windows, SettingsData: { Position } });
|
|
lift(aqueousService, "overlapsDock", context);
|
|
return context.overlapsDock(screenName, Position[position], 60, 1920, 1080);
|
|
}
|
|
|
|
test("niri active window: focused window on the bar's screen wins, foreign active toplevels keep an alive previous window", () => {
|
|
const s = niriState("twoScreens");
|
|
assert.deepEqual(focusedAppNiri(s, "DP-1", false, null, null), { activeWindow: 0, onActiveWorkspace: true, pid: 100 });
|
|
assert.deepEqual(focusedAppNiri(s, "HDMI-A-1", false, null, null), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppNiri(s, null, false, null, null), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppNiri(s, "HDMI-A-1", false, s.toplevels[1], null), { activeWindow: 1, onActiveWorkspace: true, pid: 102 });
|
|
assert.deepEqual(focusedAppNiri(s, "DP-1", false, s.toplevels[2], null), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppNiri(s, "DP-1", false, s.toplevels[2], s.toplevels[0]), { activeWindow: 0, onActiveWorkspace: true, pid: 100 });
|
|
assert.deepEqual(focusedAppNiri(s, "HDMI-A-1", false, null, s.toplevels[1]), { activeWindow: 1, onActiveWorkspace: true, pid: 102 });
|
|
assert.deepEqual(focusedAppNiri(s, "HDMI-A-1", false, null, { appId: "dead", title: "dead" }), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppNiri(s, "DP-3", false, s.toplevels[3], null), { activeWindow: 3, onActiveWorkspace: true, pid: 0 });
|
|
});
|
|
|
|
test("niri last-focused fallback applies only while the popout is open, ghosts on empty workspaces are dropped", () => {
|
|
const unfocused = niriState("unfocused");
|
|
assert.deepEqual(focusedAppNiri(unfocused, "DP-1", false, null, null), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppNiri(unfocused, "DP-1", true, null, null), { activeWindow: 1, onActiveWorkspace: true, pid: 0 });
|
|
const sorted = niriState("unfocusedSorted");
|
|
assert.deepEqual(focusedAppNiri(sorted, "DP-1", true, null, null), { activeWindow: 2, onActiveWorkspace: true, pid: 101 });
|
|
assert.deepEqual(focusedAppNiri(sorted, "HDMI-A-1", true, null, null), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
const empty = niriState("emptyWorkspace");
|
|
assert.deepEqual(focusedAppNiri(empty, "DP-1", true, null, null), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppNiri(empty, "DP-1", false, null, empty.toplevels[0]), { activeWindow: null, onActiveWorkspace: false, pid: 0 });
|
|
const noActive = niriState("noActiveWorkspace");
|
|
assert.deepEqual(focusedAppNiri(noActive, "DP-3", false, null, null), { activeWindow: 0, onActiveWorkspace: true, pid: 104 });
|
|
});
|
|
|
|
test("niri edges: the bar hides for any tiled window, the dock only when geometry crosses its edge", () => {
|
|
const two = niriState("twoScreens");
|
|
assert.deepEqual(positions.map(p => barHideNiri(two, "DP-1", p)), [true, true, true, true]);
|
|
assert.deepEqual(positions.map(p => dockOverlapNiri(two, "DP-1", p)), [true, true, true, true]);
|
|
assert.deepEqual(positions.map(p => barHideNiri(two, "HDMI-A-1", p)), [true, true, true, true]);
|
|
assert.deepEqual(positions.map(p => dockOverlapNiri(two, "HDMI-A-1", p)), [false, false, false, false]);
|
|
assert.deepEqual(positions.map(p => barHideNiri(two, "DP-3", p)), [false, false, false, false]);
|
|
const floating = niriState("floatingAtEdge");
|
|
assert.deepEqual(positions.map(p => barHideNiri(floating, "DP-1", p)), [false, true, false, false]);
|
|
assert.deepEqual(positions.map(p => dockOverlapNiri(floating, "DP-1", p)), [false, true, false, false]);
|
|
const tiledNoGeometry = niriState("tiledNoGeometry");
|
|
assert.deepEqual([barHideNiri(tiledNoGeometry, "DP-1", "Top"), dockOverlapNiri(tiledNoGeometry, "DP-1", "Top")], [true, true]);
|
|
const floatingNoGeometry = niriState("floatingNoGeometry");
|
|
assert.deepEqual([barHideNiri(floatingNoGeometry, "DP-1", "Top"), dockOverlapNiri(floatingNoGeometry, "DP-1", "Top")], [false, false]);
|
|
assert.equal(barHideNiri(niriState("emptyWorkspace"), "DP-1", "Top"), false);
|
|
});
|
|
|
|
test("hyprland: workspace membership against the focused workspace, pid from the ipc object, special workspace names", () => {
|
|
const hypr = hyprlandState();
|
|
assert.deepEqual(focusedAppHyprland(hypr, hypr.wayland("w1"), hypr.focusedWorkspace), { onActiveWorkspace: true, pid: 501 });
|
|
assert.deepEqual(focusedAppHyprland(hypr, hypr.wayland("w3"), hypr.focusedWorkspace), { onActiveWorkspace: false, pid: 503 });
|
|
assert.deepEqual(focusedAppHyprland(hypr, hypr.wayland("w4"), hypr.focusedWorkspace), { onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppHyprland(hypr, hypr.wayland("w5"), hypr.focusedWorkspace), { onActiveWorkspace: false, pid: 0 });
|
|
assert.deepEqual(focusedAppHyprland(hypr, hypr.wayland("w1"), null), { onActiveWorkspace: false, pid: 501 });
|
|
assert.deepEqual(["w1", "w2", "w4", "w5"].map(id => specialWorkspaceName(hypr, hypr.wayland(id))), ["", "scratch", "notes", ""]);
|
|
assert.equal(specialWorkspaceName(hypr, null), "");
|
|
});
|
|
|
|
test("hyprland main: named workspaces all report id -1, so active-workspace membership compares addresses", () => {
|
|
const music = { id: -1, address: "music", name: "music" };
|
|
const chat = { id: -1, address: "chat", name: "chat" };
|
|
const win = { id: "w", appId: "w", title: "w" };
|
|
const state = { toplevels: [{ wayland: win, workspace: music, lastIpcObject: { pid: 7 } }] };
|
|
assert.deepEqual([chat, music].map(focused => focusedAppHyprland(state, win, focused).onActiveWorkspace), [false, true]);
|
|
});
|
|
|
|
test("mango: compositor visibility covers global windows and overview without counting hidden clients", () => {
|
|
const windows = [
|
|
{ id: 1, monitor: "DP-1", tags: [2], is_visible: true },
|
|
{ id: 2, monitor: "DP-1", tags: [1], is_visible: false },
|
|
{ id: 3, monitor: "DP-2", tags: [1], is_visible: true },
|
|
{ id: 4, monitor: "DP-1", tags: [1], is_visible: true, is_minimized: true },
|
|
{ id: 5, monitor: "DP-1", tags: [1] }
|
|
];
|
|
const visible = activeTags => Array.from(model.mangoVisibleWindows(windows, { activeTags }, "DP-1"), win => win.id);
|
|
assert.deepEqual(visible([1]), [1, 5]);
|
|
assert.deepEqual(visible([0]), [1]);
|
|
});
|
|
|
|
test("mango: pid by window id, overlap and maximized only count visible windows on the output's active tags", () => {
|
|
const m = fixture("mango");
|
|
assert.equal(mangoPid(m.twoOutputs.windows, { mangoWindowId: 1 }), 201);
|
|
assert.equal(mangoPid(m.twoOutputs.windows, { mangoWindowId: 99 }), 0);
|
|
assert.equal(mangoPid(m.twoOutputs.windows, null), 0);
|
|
assert.deepEqual(positions.map(p => mangoOverlap(m.twoOutputs, "DP-1", p)), [true, false, true, true]);
|
|
assert.deepEqual(positions.map(p => mangoOverlap(m.twoOutputs, "DP-2", p)), [false, false, false, false]);
|
|
assert.equal(mangoOverlap(m.twoOutputs, "DP-9", "Top"), false);
|
|
assert.equal(mangoMaximized(m.twoOutputs, "DP-1"), false);
|
|
assert.equal(mangoMaximized(m.twoOutputs, "DP-2"), true);
|
|
assert.equal(mangoMaximized(m.maximizedActive, "DP-1"), true);
|
|
assert.deepEqual(positions.map(p => mangoOverlap(m.maximizedActive, "DP-1", p)), [true, true, true, true]);
|
|
});
|
|
|
|
test("aqueous: focused window must sit on the bar's screen, overlap ignores hidden and minimized windows", () => {
|
|
const a = fixture("aqueous");
|
|
assert.equal(aqueousActiveWindow(a.focusedWindow, "DP-1"), "a1");
|
|
assert.equal(aqueousActiveWindow(a.focusedWindow, "HDMI-A-1"), null);
|
|
assert.equal(aqueousActiveWindow(a.focusedWindow, null), "a1");
|
|
assert.equal(aqueousActiveWindow(a.focusedWindow, ""), null);
|
|
assert.equal(aqueousActiveWindow(null, "DP-1"), null);
|
|
assert.deepEqual(positions.map(p => aqueousOverlap(a, "DP-1", p)), [false, true, false, false]);
|
|
assert.deepEqual(positions.map(p => aqueousOverlap(a, "HDMI-A-1", p)), [true, false, true, false]);
|
|
assert.equal(aqueousOverlap(a, "DP-9", "Top"), false);
|
|
});
|