mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
refactor(Xray): Update Xray & standalone to frame transitions
- Fix dbar autohide with Xray options that could have blocked bar area content - Fixes #2729
This commit is contained in:
@@ -22,6 +22,7 @@ Singleton {
|
||||
property bool isLabwc: false
|
||||
property string compositor: "unknown"
|
||||
property bool compositorDetected: false
|
||||
readonly property bool frameCompositorLayoutReady: (!isNiri || NiriService.frameLayoutReady) && (!isHyprland || HyprlandService.frameLayoutReady)
|
||||
readonly property bool useHyprlandFocusGrab: isHyprland && Quickshell.env("DMS_HYPRLAND_EXCLUSIVE_FOCUS") !== "1"
|
||||
|
||||
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
|
||||
@@ -612,7 +613,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function frameConfiguredForScreen(screenOrName) {
|
||||
if (!SettingsData.frameEnabled)
|
||||
if (!FrameTransitionState.effectiveFrameEnabled)
|
||||
return false;
|
||||
const screen = _screenForName(screenOrName);
|
||||
if (!screen || !SettingsData.isScreenInPreferences(screen, SettingsData.frameScreenPreferences))
|
||||
@@ -627,7 +628,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function usesConnectedFrameChromeForScreen(screenOrName) {
|
||||
return SettingsData.connectedFrameModeActive && frameWindowVisibleForScreen(screenOrName);
|
||||
return FrameTransitionState.effectiveConnectedFrameModeActive && frameWindowVisibleForScreen(screenOrName);
|
||||
}
|
||||
|
||||
function framePeerSurfacesUseOverlayForScreen(screenOrName) {
|
||||
|
||||
@@ -25,6 +25,22 @@ Singleton {
|
||||
property bool luaConfigStatusReady: false
|
||||
property bool luaConfigStatusLoading: false
|
||||
property string luaConfigFormat: ""
|
||||
property bool layoutGenerationPending: false
|
||||
property bool layoutGenerationRunning: false
|
||||
property int _layoutRequestRevision: 0
|
||||
property int _layoutAppliedRevision: 0
|
||||
property int _frameTransitionRevision: 0
|
||||
readonly property bool frameLayoutReady: _layoutAppliedRevision >= _frameTransitionRevision
|
||||
|
||||
DeferredAction {
|
||||
id: layoutGenerationAction
|
||||
onTriggered: root.doGenerateLayoutConfig()
|
||||
}
|
||||
|
||||
onLuaConfigStatusLoadingChanged: {
|
||||
if (!luaConfigStatusLoading && layoutGenerationPending)
|
||||
layoutGenerationAction.schedule();
|
||||
}
|
||||
|
||||
onLuaConfigActiveChanged: {
|
||||
if (luaConfigActive)
|
||||
@@ -237,18 +253,39 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
function reloadConfig() {
|
||||
function reloadConfig(callback) {
|
||||
Proc.runCommand("hyprctl-reload", ["hyprctl", "reload"], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("hyprctl reload failed:", output);
|
||||
if (callback)
|
||||
callback(exitCode === 0);
|
||||
});
|
||||
}
|
||||
|
||||
function generateLayoutConfig() {
|
||||
function generateLayoutConfig(frameTransition) {
|
||||
if (!CompositorService.isHyprland)
|
||||
return;
|
||||
if (!canWriteLuaConfig("layout"))
|
||||
_layoutRequestRevision++;
|
||||
if (frameTransition === true)
|
||||
_frameTransitionRevision = _layoutRequestRevision;
|
||||
layoutGenerationPending = true;
|
||||
layoutGenerationAction.schedule();
|
||||
}
|
||||
|
||||
function doGenerateLayoutConfig() {
|
||||
if (layoutGenerationRunning)
|
||||
return;
|
||||
layoutGenerationPending = false;
|
||||
const requestRevision = _layoutRequestRevision;
|
||||
if (!canWriteLuaConfig("layout")) {
|
||||
if (luaConfigStatusLoading || !luaConfigStatusReady) {
|
||||
layoutGenerationPending = true;
|
||||
return;
|
||||
}
|
||||
_layoutAppliedRevision = Math.max(_layoutAppliedRevision, requestRevision);
|
||||
return;
|
||||
}
|
||||
layoutGenerationRunning = true;
|
||||
|
||||
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
|
||||
const defaultGaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
|
||||
@@ -262,10 +299,9 @@ Singleton {
|
||||
const barFrameXrayEnabled = typeof SettingsData === "undefined" || SettingsData.hyprlandLayoutBarXrayEnabled;
|
||||
const frameEnabled = typeof SettingsData !== "undefined" && SettingsData.frameEnabled;
|
||||
const frameConnectedMode = frameEnabled && SettingsData.frameMode === "connected";
|
||||
|
||||
// Hyprland's `xray = false` is still in early development, so unlike niri we never emit it (unset already
|
||||
// samples real content) - we only force xray=true
|
||||
const barFrameTargetNamespace = !frameEnabled ? "dms:bar" : (frameConnectedMode ? null : "dms:frame");
|
||||
// Hyprland `xray = false` is still early-development; unset already samples real content, so only force xray=true
|
||||
// Standalone Bar Xray is safe only while every active bar reserves its strip
|
||||
const barFrameTargetNamespace = !frameEnabled ? (SettingsData.standaloneBarXrayAvailable ? "dms:bar" : null) : (frameConnectedMode ? null : "dms:frame");
|
||||
|
||||
let content = `-- Auto-generated by DMS — do not edit manually
|
||||
|
||||
@@ -302,10 +338,21 @@ hl.layer_rule({
|
||||
Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
log.warn("Failed to write layout config:", output);
|
||||
// Best-effort ack so a failed write can't wedge frame transitions
|
||||
_layoutAppliedRevision = Math.max(_layoutAppliedRevision, requestRevision);
|
||||
layoutGenerationRunning = false;
|
||||
if (layoutGenerationPending)
|
||||
layoutGenerationAction.schedule();
|
||||
return;
|
||||
}
|
||||
log.info("Generated layout config at", layoutPath);
|
||||
reloadConfig();
|
||||
reloadConfig(success => {
|
||||
// Advance even on failure — proceed degraded rather than wedge the transition
|
||||
_layoutAppliedRevision = Math.max(_layoutAppliedRevision, requestRevision);
|
||||
layoutGenerationRunning = false;
|
||||
if (layoutGenerationPending)
|
||||
layoutGenerationAction.schedule();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ Singleton {
|
||||
property bool suppressNextConfigToast: false
|
||||
property bool matugenSuppression: false
|
||||
property bool configGenerationPending: false
|
||||
property int _layoutRequestRevision: 0
|
||||
property int _layoutAppliedRevision: 0
|
||||
property int _frameTransitionRevision: 0
|
||||
property int _awaitingLayoutReloadRevision: 0
|
||||
property string _lastGeneratedAlttabContent: ""
|
||||
readonly property bool frameLayoutReady: _layoutAppliedRevision >= _frameTransitionRevision
|
||||
|
||||
readonly property string screenshotsDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.PicturesLocation)) + "/Screenshots"
|
||||
property string pendingScreenshotPath: ""
|
||||
@@ -72,9 +78,8 @@ Singleton {
|
||||
onTriggered: root.matugenSuppression = false
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: configGenerationDebounce
|
||||
interval: 100
|
||||
DeferredAction {
|
||||
id: configGenerationAction
|
||||
onTriggered: root.doGenerateNiriLayoutConfig()
|
||||
}
|
||||
|
||||
@@ -117,13 +122,20 @@ Singleton {
|
||||
id: writeConfigProcess
|
||||
property string configContent: ""
|
||||
property string configPath: ""
|
||||
property int requestRevision: 0
|
||||
|
||||
onExited: exitCode => {
|
||||
if (exitCode === 0) {
|
||||
log.info("Generated layout config at", configPath);
|
||||
return;
|
||||
} else {
|
||||
if (root._awaitingLayoutReloadRevision === requestRevision)
|
||||
root._awaitingLayoutReloadRevision = 0;
|
||||
// Best-effort ack so a failed write can't wedge frame transitions
|
||||
root._layoutAppliedRevision = Math.max(root._layoutAppliedRevision, requestRevision);
|
||||
log.warn("Failed to write layout config, exit code:", exitCode);
|
||||
}
|
||||
log.warn("Failed to write layout config, exit code:", exitCode);
|
||||
if (root.configGenerationPending && root._awaitingLayoutReloadRevision <= root._layoutAppliedRevision)
|
||||
configGenerationAction.schedule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,12 +571,25 @@ Singleton {
|
||||
function handleConfigLoaded(data) {
|
||||
if (data.failed) {
|
||||
validateProcess.running = true;
|
||||
// Best-effort ack: a rejected reload must not wedge frame transitions
|
||||
if (_awaitingLayoutReloadRevision > _layoutAppliedRevision) {
|
||||
_layoutAppliedRevision = _awaitingLayoutReloadRevision;
|
||||
_awaitingLayoutReloadRevision = 0;
|
||||
}
|
||||
if (configGenerationPending)
|
||||
configGenerationAction.schedule();
|
||||
return;
|
||||
}
|
||||
|
||||
configValidationOutput = "";
|
||||
ToastService.dismissCategory("niri-config");
|
||||
fetchOutputs();
|
||||
if (_awaitingLayoutReloadRevision > _layoutAppliedRevision) {
|
||||
_layoutAppliedRevision = _awaitingLayoutReloadRevision;
|
||||
_awaitingLayoutReloadRevision = 0;
|
||||
}
|
||||
if (configGenerationPending)
|
||||
configGenerationAction.schedule();
|
||||
configReloaded();
|
||||
|
||||
if (hasInitialConnection && !suppressConfigToast && !suppressNextConfigToast && !matugenSuppression) {
|
||||
@@ -1069,15 +1094,21 @@ Singleton {
|
||||
return _matchAndEnrichToplevels(toplevels, windows.filter(nw => outputWorkspaceIds.has(nw.workspace_id)));
|
||||
}
|
||||
|
||||
function generateNiriLayoutConfig() {
|
||||
if (!CompositorService.isNiri || configGenerationPending)
|
||||
function generateNiriLayoutConfig(frameTransition) {
|
||||
if (!CompositorService.isNiri)
|
||||
return;
|
||||
_layoutRequestRevision++;
|
||||
if (frameTransition === true)
|
||||
_frameTransitionRevision = _layoutRequestRevision;
|
||||
suppressNextToast();
|
||||
configGenerationPending = true;
|
||||
configGenerationDebounce.restart();
|
||||
configGenerationAction.schedule();
|
||||
}
|
||||
|
||||
function doGenerateNiriLayoutConfig() {
|
||||
if (writeConfigProcess.running || _awaitingLayoutReloadRevision > _layoutAppliedRevision)
|
||||
return;
|
||||
configGenerationPending = false;
|
||||
log.debug("Generating layout config...");
|
||||
|
||||
const defaultRadius = typeof SettingsData !== "undefined" ? SettingsData.cornerRadius : 12;
|
||||
@@ -1091,19 +1122,14 @@ Singleton {
|
||||
const barFrameXrayEnabled = typeof SettingsData === "undefined" || SettingsData.niriLayoutBarXrayEnabled;
|
||||
const frameEnabled = typeof SettingsData !== "undefined" && SettingsData.frameEnabled;
|
||||
const frameConnectedMode = frameEnabled && SettingsData.frameMode === "connected";
|
||||
|
||||
// The one wallpaper-flush surface to force xray=true on: bar (Frame off) or Frame's Separate-mode border
|
||||
const barFrameTargetNamespace = !frameEnabled ? "dms:bar" : (frameConnectedMode ? null : "dms:frame");
|
||||
// Standalone Bar Xray is safe only while every active bar reserves its strip
|
||||
const barFrameTargetNamespace = !frameEnabled ? (SettingsData.standaloneBarXrayAvailable ? "dms:bar" : null) : (frameConnectedMode ? null : "dms:frame");
|
||||
|
||||
let xrayRules = "";
|
||||
if (!xrayEnabled) {
|
||||
let excludes = "";
|
||||
if (frameEnabled)
|
||||
excludes += `
|
||||
exclude namespace="^dms:bar$"`;
|
||||
xrayRules += `
|
||||
layer-rule {
|
||||
match namespace=".*"` + excludes + `
|
||||
match namespace=".*"
|
||||
background-effect {
|
||||
xray false
|
||||
}
|
||||
@@ -1157,13 +1183,18 @@ Singleton {
|
||||
|
||||
writeConfigProcess.configContent = configContent;
|
||||
writeConfigProcess.configPath = configPath;
|
||||
writeConfigProcess.requestRevision = _layoutRequestRevision;
|
||||
writeConfigProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${configPath}" << 'EOF'\n${configContent}\nEOF`];
|
||||
_awaitingLayoutReloadRevision = Math.max(_awaitingLayoutReloadRevision, _layoutRequestRevision);
|
||||
writeConfigProcess.running = true;
|
||||
|
||||
writeAlttabProcess.alttabContent = alttabContent;
|
||||
writeAlttabProcess.alttabPath = alttabPath;
|
||||
writeAlttabProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${alttabPath}" << 'EOF'\n${alttabContent}\nEOF`];
|
||||
writeAlttabProcess.running = true;
|
||||
if (_lastGeneratedAlttabContent !== alttabContent) {
|
||||
_lastGeneratedAlttabContent = alttabContent;
|
||||
writeAlttabProcess.alttabContent = alttabContent;
|
||||
writeAlttabProcess.alttabPath = alttabPath;
|
||||
writeAlttabProcess.command = ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${alttabPath}" << 'EOF'\n${alttabContent}\nEOF`];
|
||||
writeAlttabProcess.running = true;
|
||||
}
|
||||
|
||||
for (const name of ["outputs", "binds", "cursor", "windowrules", "colors", "alttab", "layout"]) {
|
||||
const path = niriDmsDir + "/" + name + ".kdl";
|
||||
@@ -1173,7 +1204,6 @@ Singleton {
|
||||
});
|
||||
}
|
||||
|
||||
configGenerationPending = false;
|
||||
}
|
||||
|
||||
function generateNiriBlurrule() {
|
||||
|
||||
Reference in New Issue
Block a user