mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 04:09:15 -04:00
feat(Hyprland): Introduce Lua support for Hyprland configurations
- Note: We do not convert your existing conf configs to lua. This update only reflects DMS defaults state - Updated README.md to reflect changes - Updated Keyboard shortcut support
This commit is contained in:
@@ -14,10 +14,10 @@ Singleton {
|
||||
|
||||
readonly property string configDir: Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation))
|
||||
readonly property string hyprDmsDir: configDir + "/hypr/dms"
|
||||
readonly property string outputsPath: hyprDmsDir + "/outputs.conf"
|
||||
readonly property string layoutPath: hyprDmsDir + "/layout.conf"
|
||||
readonly property string cursorPath: hyprDmsDir + "/cursor.conf"
|
||||
readonly property string windowrulesPath: hyprDmsDir + "/windowrules.conf"
|
||||
readonly property string outputsPath: hyprDmsDir + "/outputs.lua"
|
||||
readonly property string layoutPath: hyprDmsDir + "/layout.lua"
|
||||
readonly property string cursorPath: hyprDmsDir + "/cursor.lua"
|
||||
readonly property string windowrulesPath: hyprDmsDir + "/windowrules.lua"
|
||||
|
||||
property int _lastGapValue: -1
|
||||
|
||||
@@ -31,7 +31,7 @@ Singleton {
|
||||
function ensureWindowrulesConfig() {
|
||||
Proc.runCommand("hypr-ensure-windowrules", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && [ ! -f "${windowrulesPath}" ] && touch "${windowrulesPath}" || true`], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to ensure windowrules.conf:", output);
|
||||
log.warn("Failed to ensure windowrules.lua:", output);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -62,6 +62,18 @@ Singleton {
|
||||
return outputName;
|
||||
}
|
||||
|
||||
function luaQuoted(str) {
|
||||
return JSON.stringify(String(str ?? ""));
|
||||
}
|
||||
|
||||
function forceFlagValue(value) {
|
||||
if (value === true)
|
||||
return 1;
|
||||
if (value === false)
|
||||
return -1;
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
function generateOutputsConfig(outputsData, hyprlandSettings, callback) {
|
||||
if (!outputsData || Object.keys(outputsData).length === 0) {
|
||||
if (callback)
|
||||
@@ -70,8 +82,7 @@ Singleton {
|
||||
}
|
||||
|
||||
const settings = hyprlandSettings || SettingsData.hyprlandOutputSettings;
|
||||
let lines = ["# Auto-generated by DMS - do not edit manually", ""];
|
||||
let monitorv2Blocks = [];
|
||||
let lines = ["-- Auto-generated by DMS — do not edit manually", ""];
|
||||
|
||||
for (const outputName in outputsData) {
|
||||
const output = outputsData[outputName];
|
||||
@@ -82,7 +93,7 @@ Singleton {
|
||||
const outputSettings = settings[identifier] || {};
|
||||
|
||||
if (outputSettings.disabled) {
|
||||
lines.push("monitor = " + identifier + ", disable");
|
||||
lines.push(`hl.monitor({ output = ${luaQuoted(identifier)}, disabled = true })`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -98,68 +109,42 @@ Singleton {
|
||||
const position = x + "x" + y;
|
||||
const scale = output.logical?.scale ?? 1.0;
|
||||
|
||||
let monitorLine = "monitor = " + identifier + ", " + resolution + ", " + position + ", " + scale;
|
||||
const parts = [`output = ${luaQuoted(identifier)}`, `mode = ${luaQuoted(resolution)}`, `position = ${luaQuoted(position)}`, `scale = ${Number(scale)}`];
|
||||
|
||||
const transform = transformToHyprland(output.logical?.transform ?? "Normal");
|
||||
if (transform !== 0)
|
||||
monitorLine += ", transform, " + transform;
|
||||
parts.push(`transform = ${transform}`);
|
||||
|
||||
if (output.vrr_supported) {
|
||||
const vrrMode = outputSettings.vrrFullscreenOnly ? 2 : (output.vrr_enabled ? 1 : 0);
|
||||
monitorLine += ", vrr, " + vrrMode;
|
||||
parts.push(`vrr = ${vrrMode}`);
|
||||
}
|
||||
|
||||
if (output.mirror && output.mirror.length > 0)
|
||||
monitorLine += ", mirror, " + output.mirror;
|
||||
parts.push(`mirror = ${luaQuoted(output.mirror)}`);
|
||||
|
||||
if (outputSettings.bitdepth && outputSettings.bitdepth !== 8)
|
||||
monitorLine += ", bitdepth, " + outputSettings.bitdepth;
|
||||
parts.push(`bitdepth = ${Number(outputSettings.bitdepth)}`);
|
||||
|
||||
if (outputSettings.colorManagement && outputSettings.colorManagement !== "auto")
|
||||
monitorLine += ", cm, " + outputSettings.colorManagement;
|
||||
parts.push(`cm = ${luaQuoted(outputSettings.colorManagement)}`);
|
||||
|
||||
if (outputSettings.sdrBrightness !== undefined && outputSettings.sdrBrightness !== 1.0)
|
||||
monitorLine += ", sdrbrightness, " + outputSettings.sdrBrightness;
|
||||
parts.push(`sdrbrightness = ${Number(outputSettings.sdrBrightness)}`);
|
||||
|
||||
if (outputSettings.sdrSaturation !== undefined && outputSettings.sdrSaturation !== 1.0)
|
||||
monitorLine += ", sdrsaturation, " + outputSettings.sdrSaturation;
|
||||
parts.push(`sdrsaturation = ${Number(outputSettings.sdrSaturation)}`);
|
||||
|
||||
lines.push(monitorLine);
|
||||
if (outputSettings.supportsWideColor !== undefined)
|
||||
parts.push(`supports_wide_color = ${forceFlagValue(outputSettings.supportsWideColor)}`);
|
||||
|
||||
const needsMonitorv2 = outputSettings.supportsHdr || outputSettings.supportsWideColor || outputSettings.sdrMinLuminance !== undefined || outputSettings.sdrMaxLuminance !== undefined || outputSettings.minLuminance !== undefined || outputSettings.maxLuminance !== undefined || outputSettings.maxAvgLuminance !== undefined;
|
||||
if (outputSettings.supportsHdr !== undefined)
|
||||
parts.push(`supports_hdr = ${forceFlagValue(outputSettings.supportsHdr)}`);
|
||||
|
||||
if (needsMonitorv2) {
|
||||
let block = "monitorv2 {\n";
|
||||
block += " output = " + identifier + "\n";
|
||||
|
||||
if (outputSettings.supportsWideColor)
|
||||
block += " supports_wide_color = true\n";
|
||||
if (outputSettings.supportsHdr)
|
||||
block += " supports_hdr = true\n";
|
||||
if (outputSettings.sdrMinLuminance !== undefined)
|
||||
block += " sdr_min_luminance = " + outputSettings.sdrMinLuminance + "\n";
|
||||
if (outputSettings.sdrMaxLuminance !== undefined)
|
||||
block += " sdr_max_luminance = " + outputSettings.sdrMaxLuminance + "\n";
|
||||
if (outputSettings.minLuminance !== undefined)
|
||||
block += " min_luminance = " + outputSettings.minLuminance + "\n";
|
||||
if (outputSettings.maxLuminance !== undefined)
|
||||
block += " max_luminance = " + outputSettings.maxLuminance + "\n";
|
||||
if (outputSettings.maxAvgLuminance !== undefined)
|
||||
block += " max_avg_luminance = " + outputSettings.maxAvgLuminance + "\n";
|
||||
|
||||
block += "}";
|
||||
monitorv2Blocks.push(block);
|
||||
}
|
||||
}
|
||||
|
||||
if (monitorv2Blocks.length > 0) {
|
||||
lines.push("");
|
||||
for (const block of monitorv2Blocks)
|
||||
lines.push(block);
|
||||
lines.push("hl.monitor({ " + parts.join(", ") + " })");
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
const content = lines.join("\n");
|
||||
|
||||
Proc.runCommand("hypr-write-outputs", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
|
||||
@@ -196,17 +181,18 @@ Singleton {
|
||||
const gaps = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutGapsOverride >= 0) ? SettingsData.hyprlandLayoutGapsOverride : defaultGaps;
|
||||
const borderSize = (typeof SettingsData !== "undefined" && SettingsData.hyprlandLayoutBorderSize >= 0) ? SettingsData.hyprlandLayoutBorderSize : defaultBorderSize;
|
||||
|
||||
let content = `# Auto-generated by DMS - do not edit manually
|
||||
let content = `-- Auto-generated by DMS — do not edit manually
|
||||
|
||||
general {
|
||||
gaps_in = ${gaps}
|
||||
gaps_out = ${gaps}
|
||||
border_size = ${borderSize}
|
||||
}
|
||||
|
||||
decoration {
|
||||
rounding = ${cornerRadius}
|
||||
}
|
||||
hl.config({
|
||||
general = {
|
||||
gaps_in = ${gaps},
|
||||
gaps_out = ${gaps},
|
||||
border_size = ${borderSize},
|
||||
},
|
||||
decoration = {
|
||||
rounding = ${cornerRadius},
|
||||
},
|
||||
})
|
||||
`;
|
||||
|
||||
Proc.runCommand("hypr-write-layout", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && cat > "${layoutPath}" << 'EOF'\n${content}EOF`], (output, exitCode) => {
|
||||
@@ -271,7 +257,7 @@ decoration {
|
||||
|
||||
const settings = typeof SettingsData !== "undefined" ? SettingsData.cursorSettings : null;
|
||||
if (!settings) {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && printf '%s\\n' "-- Auto-generated by DMS — do not edit manually" "" > "${cursorPath}"`], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to write cursor config:", output);
|
||||
});
|
||||
@@ -289,32 +275,34 @@ decoration {
|
||||
const hasCursorSettings = hideOnKeyPress || hideOnTouch || inactiveTimeout > 0;
|
||||
|
||||
if (!hasTheme && !hasNonDefaultSize && !hasCursorSettings) {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && : > "${cursorPath}"`], (output, exitCode) => {
|
||||
Proc.runCommand("hypr-write-cursor", ["sh", "-c", `mkdir -p "${hyprDmsDir}" && printf '%s\\n' "-- Auto-generated by DMS — do not edit manually" "" > "${cursorPath}"`], (output, exitCode) => {
|
||||
if (exitCode !== 0)
|
||||
log.warn("Failed to write cursor config:", output);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let lines = ["# Auto-generated by DMS - do not edit manually", ""];
|
||||
let lines = ["-- Auto-generated by DMS — do not edit manually", ""];
|
||||
|
||||
if (hasTheme) {
|
||||
lines.push(`env = HYPRCURSOR_THEME,${themeName}`);
|
||||
lines.push(`env = XCURSOR_THEME,${themeName}`);
|
||||
lines.push(`hl.env("HYPRCURSOR_THEME", ${luaQuoted(themeName)})`);
|
||||
lines.push(`hl.env("XCURSOR_THEME", ${luaQuoted(themeName)})`);
|
||||
}
|
||||
lines.push(`env = HYPRCURSOR_SIZE,${size}`);
|
||||
lines.push(`env = XCURSOR_SIZE,${size}`);
|
||||
lines.push(`hl.env("HYPRCURSOR_SIZE", ${luaQuoted(String(size))})`);
|
||||
lines.push(`hl.env("XCURSOR_SIZE", ${luaQuoted(String(size))})`);
|
||||
|
||||
if (hasCursorSettings) {
|
||||
lines.push("");
|
||||
lines.push("cursor {");
|
||||
lines.push("hl.config({");
|
||||
lines.push("\tcursor = {");
|
||||
if (hideOnKeyPress)
|
||||
lines.push(" hide_on_key_press = true");
|
||||
lines.push("\t\thide_on_key_press = true,");
|
||||
if (hideOnTouch)
|
||||
lines.push(" hide_on_touch = true");
|
||||
lines.push("\t\thide_on_touch = true,");
|
||||
if (inactiveTimeout > 0)
|
||||
lines.push(` inactive_timeout = ${inactiveTimeout}`);
|
||||
lines.push("}");
|
||||
lines.push(`\t\tinactive_timeout = ${inactiveTimeout},`);
|
||||
lines.push("\t},");
|
||||
lines.push("})");
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
|
||||
@@ -7,6 +7,7 @@ import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import "../Common/ConfigIncludeResolve.js" as ConfigIncludeResolve
|
||||
import "../Common/KeybindActions.js" as Actions
|
||||
|
||||
Singleton {
|
||||
@@ -82,6 +83,7 @@ Singleton {
|
||||
case "niri":
|
||||
return compositorConfigDir + "/dms/binds.kdl";
|
||||
case "hyprland":
|
||||
return compositorConfigDir + "/dms/binds.lua";
|
||||
case "mangowc":
|
||||
return compositorConfigDir + "/dms/binds.conf";
|
||||
default:
|
||||
@@ -93,7 +95,7 @@ Singleton {
|
||||
case "niri":
|
||||
return compositorConfigDir + "/config.kdl";
|
||||
case "hyprland":
|
||||
return compositorConfigDir + "/hyprland.conf";
|
||||
return compositorConfigDir + "/hyprland.lua";
|
||||
case "mangowc":
|
||||
return compositorConfigDir + "/config.conf";
|
||||
default:
|
||||
@@ -247,8 +249,8 @@ Singleton {
|
||||
root.lastError = "";
|
||||
root.dmsBindsIncluded = true;
|
||||
root.dmsBindsFixed();
|
||||
const bindsFile = root.currentProvider === "niri" ? "dms/binds.kdl" : "dms/binds.conf";
|
||||
ToastService.showInfo(I18n.tr("Binds include added"), I18n.tr("%1 is now included in config").arg(bindsFile), "", "keybinds");
|
||||
const bindsRel = root.currentProvider === "niri" ? "dms/binds.kdl" : root.currentProvider === "hyprland" ? "dms/binds.lua" : "dms/binds.conf";
|
||||
ToastService.showInfo(I18n.tr("Binds include added"), I18n.tr("%1 is now included in config").arg(bindsRel), "", "keybinds");
|
||||
Qt.callLater(root.forceReload);
|
||||
}
|
||||
}
|
||||
@@ -262,13 +264,36 @@ Singleton {
|
||||
let script;
|
||||
switch (currentProvider) {
|
||||
case "niri":
|
||||
script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.kdl" && cp "${mainConfigPath}" "${backupPath}" && echo 'include "dms/binds.kdl"' >> "${mainConfigPath}"`;
|
||||
script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: mainConfigPath,
|
||||
backupFile: backupPath,
|
||||
fragmentFile: compositorConfigDir + "/dms/binds.kdl",
|
||||
grepPattern: 'include.*"dms/binds.kdl"',
|
||||
includeLine: 'include "dms/binds.kdl"'
|
||||
});
|
||||
break;
|
||||
case "hyprland":
|
||||
script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.conf" && cp "${mainConfigPath}" "${backupPath}" && echo 'source = ./dms/binds.conf' >> "${mainConfigPath}"`;
|
||||
script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: mainConfigPath,
|
||||
backupFile: backupPath,
|
||||
fragmentFiles: [compositorConfigDir + "/dms/binds.lua", compositorConfigDir + "/dms/binds-user.lua"],
|
||||
includes: [{
|
||||
grepPattern: "dms.binds",
|
||||
includeLine: "require(\"dms.binds\")"
|
||||
}, {
|
||||
grepPattern: "dms.binds-user",
|
||||
includeLine: "require(\"dms.binds-user\")"
|
||||
}]
|
||||
});
|
||||
break;
|
||||
case "mangowc":
|
||||
script = `mkdir -p "${compositorConfigDir}/dms" && touch "${compositorConfigDir}/dms/binds.conf" && cp "${mainConfigPath}" "${backupPath}" && echo 'source = ./dms/binds.conf' >> "${mainConfigPath}"`;
|
||||
script = ConfigIncludeResolve.buildRepairScript({
|
||||
configFile: mainConfigPath,
|
||||
backupFile: backupPath,
|
||||
fragmentFile: compositorConfigDir + "/dms/binds.conf",
|
||||
grepPattern: "source.*dms/binds.conf",
|
||||
includeLine: "source = ./dms/binds.conf"
|
||||
});
|
||||
break;
|
||||
default:
|
||||
fixing = false;
|
||||
@@ -321,6 +346,7 @@ Singleton {
|
||||
"statusMessage": status.statusMessage ?? ""
|
||||
};
|
||||
}
|
||||
_maybeWarnHyprlandLegacyConf();
|
||||
|
||||
if (!_rawData?.binds) {
|
||||
_allBinds = {};
|
||||
@@ -365,10 +391,13 @@ Singleton {
|
||||
for (var i = 0; i < binds.length; i++) {
|
||||
const bind = binds[i];
|
||||
const action = bind.action || "";
|
||||
const sourceStr = bind.source || "config";
|
||||
const keyData = {
|
||||
"key": bind.key || "",
|
||||
"source": bind.source || "config",
|
||||
"isOverride": bind.source === "dms",
|
||||
"source": sourceStr,
|
||||
"isOverride": sourceStr === "dms",
|
||||
"isDMSManaged": sourceStr === "dms" || sourceStr === "dms-default",
|
||||
"hasDefault": bind.hasDefault === true,
|
||||
"cooldownMs": bind.cooldownMs || 0,
|
||||
"flags": bind.flags || "",
|
||||
"allowWhenLocked": bind.allowWhenLocked || false,
|
||||
@@ -456,6 +485,19 @@ Singleton {
|
||||
_pendingSavedKey = bindData.key;
|
||||
}
|
||||
|
||||
property bool _hyprlandLegacyWarnShown: false
|
||||
|
||||
function _maybeWarnHyprlandLegacyConf() {
|
||||
if (_hyprlandLegacyWarnShown)
|
||||
return;
|
||||
if (currentProvider !== "hyprland")
|
||||
return;
|
||||
if (!dmsStatus.exists || dmsStatus.included)
|
||||
return;
|
||||
_hyprlandLegacyWarnShown = true;
|
||||
ToastService.showWarning(I18n.tr("Hyprland config still uses hyprlang"), I18n.tr("DMS Settings now writes Lua. Edits won't apply until you migrate."), "dms setup", "hyprland-migration");
|
||||
}
|
||||
|
||||
function removeBind(key) {
|
||||
if (!key)
|
||||
return;
|
||||
@@ -464,6 +506,14 @@ Singleton {
|
||||
bindRemoved(key);
|
||||
}
|
||||
|
||||
function resetBind(key) {
|
||||
if (!key)
|
||||
return;
|
||||
removeProcess.command = ["dms", "keybinds", "reset", currentProvider, key];
|
||||
removeProcess.running = true;
|
||||
bindRemoved(key);
|
||||
}
|
||||
|
||||
function isDmsAction(action) {
|
||||
return Actions.isDmsAction(action);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user