mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-29 07:52:50 -05:00
displays: add configurator for niri, Hyprland, and MangoWC
- Configure position, VRR, orientation, resolution, refresh rate - Split Display section into Configuration, Gamma, and Widgets - MangoWC omits VRR because it doesnt have per-display VRR - HDR configuration not present for Hyprland
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
pragma ComponentBehavior
|
||||
|
||||
import QtCore
|
||||
import QtQuick
|
||||
@@ -23,6 +23,8 @@ Singleton {
|
||||
property var windows: []
|
||||
property var displayScales: ({})
|
||||
|
||||
property var _realOutputs: ({})
|
||||
|
||||
property bool inOverview: false
|
||||
|
||||
property int currentKeyboardLayoutIndex: 0
|
||||
@@ -214,12 +216,12 @@ Singleton {
|
||||
const ws = workspaces[w.workspace_id];
|
||||
if (!ws) {
|
||||
return {
|
||||
window: w,
|
||||
outputX: 999999,
|
||||
outputY: 999999,
|
||||
wsIdx: 999999,
|
||||
col: 999999,
|
||||
row: 999999
|
||||
"window": w,
|
||||
"outputX": 999999,
|
||||
"outputY": 999999,
|
||||
"wsIdx": 999999,
|
||||
"col": 999999,
|
||||
"row": 999999
|
||||
};
|
||||
}
|
||||
|
||||
@@ -232,12 +234,12 @@ Singleton {
|
||||
const row = (pos && pos.length >= 2) ? pos[1] : 999999;
|
||||
|
||||
return {
|
||||
window: w,
|
||||
outputX: outputX,
|
||||
outputY: outputY,
|
||||
wsIdx: ws.idx,
|
||||
col: col,
|
||||
row: row
|
||||
"window": w,
|
||||
"outputX": outputX,
|
||||
"outputY": outputY,
|
||||
"wsIdx": ws.idx,
|
||||
"col": col,
|
||||
"row": row
|
||||
};
|
||||
});
|
||||
|
||||
@@ -598,7 +600,7 @@ Singleton {
|
||||
const editor = Quickshell.env("DMS_SCREENSHOT_EDITOR");
|
||||
const command = editor === "satty" ? ["satty", "-f", data.path] : ["swappy", "-f", data.path];
|
||||
Quickshell.execDetached({
|
||||
command: command
|
||||
"command": command
|
||||
});
|
||||
pendingScreenshotPath = "";
|
||||
}
|
||||
@@ -993,35 +995,35 @@ Singleton {
|
||||
const gaps = typeof SettingsData !== "undefined" ? Math.max(4, (SettingsData.barConfigs[0]?.spacing ?? 4)) : 4;
|
||||
|
||||
const dmsWarning = `// ! DO NOT EDIT !
|
||||
// ! AUTO-GENERATED BY DMS !
|
||||
// ! CHANGES WILL BE OVERWRITTEN !
|
||||
// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE !
|
||||
// ! AUTO-GENERATED BY DMS !
|
||||
// ! CHANGES WILL BE OVERWRITTEN !
|
||||
// ! PLACE YOUR CUSTOM CONFIGURATION ELSEWHERE !
|
||||
|
||||
`;
|
||||
`;
|
||||
|
||||
const configContent = dmsWarning + `layout {
|
||||
gaps ${gaps}
|
||||
gaps ${gaps}
|
||||
|
||||
border {
|
||||
border {
|
||||
width 2
|
||||
}
|
||||
}
|
||||
|
||||
focus-ring {
|
||||
focus-ring {
|
||||
width 2
|
||||
}
|
||||
}
|
||||
window-rule {
|
||||
geometry-corner-radius ${cornerRadius}
|
||||
clip-to-geometry true
|
||||
tiled-state true
|
||||
draw-border-with-background false
|
||||
}`;
|
||||
}
|
||||
}
|
||||
window-rule {
|
||||
geometry-corner-radius ${cornerRadius}
|
||||
clip-to-geometry true
|
||||
tiled-state true
|
||||
draw-border-with-background false
|
||||
}`;
|
||||
|
||||
const alttabContent = dmsWarning + `recent-windows {
|
||||
highlight {
|
||||
highlight {
|
||||
corner-radius ${cornerRadius}
|
||||
}
|
||||
}`;
|
||||
}
|
||||
}`;
|
||||
|
||||
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
||||
const niriDmsDir = configDir + "/niri/dms";
|
||||
@@ -1054,6 +1056,141 @@ window-rule {
|
||||
writeBlurruleProcess.running = true;
|
||||
}
|
||||
|
||||
function updateOutputPosition(outputName, x, y) {
|
||||
if (!outputs || !outputs[outputName])
|
||||
return;
|
||||
const updatedOutputs = {};
|
||||
for (const name in outputs) {
|
||||
const output = outputs[name];
|
||||
if (name === outputName && output.logical) {
|
||||
updatedOutputs[name] = JSON.parse(JSON.stringify(output));
|
||||
updatedOutputs[name].logical.x = x;
|
||||
updatedOutputs[name].logical.y = y;
|
||||
} else {
|
||||
updatedOutputs[name] = output;
|
||||
}
|
||||
}
|
||||
outputs = updatedOutputs;
|
||||
}
|
||||
|
||||
function applyOutputConfig(outputName, config, callback) {
|
||||
if (!CompositorService.isNiri || !outputName) {
|
||||
if (callback)
|
||||
callback(false, "Invalid config");
|
||||
return;
|
||||
}
|
||||
|
||||
const commands = [];
|
||||
|
||||
if (config.position !== undefined) {
|
||||
commands.push(`niri msg output "${outputName}" position ${config.position.x} ${config.position.y}`);
|
||||
}
|
||||
|
||||
if (config.mode !== undefined) {
|
||||
commands.push(`niri msg output "${outputName}" mode ${config.mode}`);
|
||||
}
|
||||
|
||||
if (config.vrr !== undefined) {
|
||||
commands.push(`niri msg output "${outputName}" vrr ${config.vrr ? "on" : "off"}`);
|
||||
}
|
||||
|
||||
if (config.scale !== undefined) {
|
||||
commands.push(`niri msg output "${outputName}" scale ${config.scale}`);
|
||||
}
|
||||
|
||||
if (config.transform !== undefined) {
|
||||
commands.push(`niri msg output "${outputName}" transform "${config.transform}"`);
|
||||
}
|
||||
|
||||
if (commands.length === 0) {
|
||||
if (callback)
|
||||
callback(true, "No changes");
|
||||
return;
|
||||
}
|
||||
|
||||
const fullCommand = commands.join(" && ");
|
||||
Proc.runCommand("niri-output-config", ["sh", "-c", fullCommand], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("NiriService: Failed to apply output config:", output);
|
||||
if (callback)
|
||||
callback(false, output);
|
||||
return;
|
||||
}
|
||||
console.info("NiriService: Applied output config for", outputName);
|
||||
fetchOutputs();
|
||||
if (callback)
|
||||
callback(true, "Success");
|
||||
});
|
||||
}
|
||||
|
||||
function getOutputIdentifier(output, outputName) {
|
||||
if (SettingsData.displayNameMode === "model" && output.make && output.model) {
|
||||
const serial = output.serial || "Unknown";
|
||||
return output.make + " " + output.model + " " + serial;
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
function generateOutputsConfig(outputsData) {
|
||||
const data = outputsData || outputs;
|
||||
if (!data || Object.keys(data).length === 0)
|
||||
return;
|
||||
let kdlContent = `// Auto-generated by DMS - do not edit manually\n\n`;
|
||||
|
||||
for (const outputName in data) {
|
||||
const output = data[outputName];
|
||||
const identifier = getOutputIdentifier(output, outputName);
|
||||
kdlContent += `output "${identifier}" {\n`;
|
||||
|
||||
if (output.current_mode !== undefined && output.modes && output.modes[output.current_mode]) {
|
||||
const mode = output.modes[output.current_mode];
|
||||
kdlContent += ` mode "${mode.width}x${mode.height}@${(mode.refresh_rate / 1000).toFixed(3)}"\n`;
|
||||
}
|
||||
|
||||
if (output.logical) {
|
||||
if (output.logical.scale && output.logical.scale !== 1.0) {
|
||||
kdlContent += ` scale ${output.logical.scale}\n`;
|
||||
}
|
||||
|
||||
if (output.logical.transform && output.logical.transform !== "Normal") {
|
||||
const transformMap = {
|
||||
"Normal": "normal",
|
||||
"90": "90",
|
||||
"180": "180",
|
||||
"270": "270",
|
||||
"Flipped": "flipped",
|
||||
"Flipped90": "flipped-90",
|
||||
"Flipped180": "flipped-180",
|
||||
"Flipped270": "flipped-270"
|
||||
};
|
||||
kdlContent += ` transform "${transformMap[output.logical.transform] || "normal"}"\n`;
|
||||
}
|
||||
|
||||
if (output.logical.x !== undefined && output.logical.y !== undefined) {
|
||||
kdlContent += ` position x=${output.logical.x} y=${output.logical.y}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (output.vrr_enabled) {
|
||||
kdlContent += ` variable-refresh-rate\n`;
|
||||
}
|
||||
|
||||
kdlContent += `}\n\n`;
|
||||
}
|
||||
|
||||
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
||||
const niriDmsDir = configDir + "/niri/dms";
|
||||
const outputsPath = niriDmsDir + "/outputs.kdl";
|
||||
|
||||
Proc.runCommand("niri-write-outputs", ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${kdlContent}EOF`], (output, exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
console.warn("NiriService: Failed to write outputs config:", output);
|
||||
return;
|
||||
}
|
||||
console.info("NiriService: Generated outputs config at", outputsPath);
|
||||
});
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function screenshot(): string {
|
||||
if (!CompositorService.isNiri) {
|
||||
|
||||
Reference in New Issue
Block a user