mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-16 08:05:19 -04:00
fix(display): unify compositor output profiles
This commit is contained in:
@@ -445,7 +445,7 @@ Singleton {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract niri settings map from neutral config entry for generateNiriOutputsKdl
|
// Extract niri settings map from a neutral config entry.
|
||||||
function getNiriSettingsFromConfig(configEntry) {
|
function getNiriSettingsFromConfig(configEntry) {
|
||||||
const result = {};
|
const result = {};
|
||||||
for (const outputId in (configEntry.outputs || {})) {
|
for (const outputId in (configEntry.outputs || {})) {
|
||||||
@@ -473,6 +473,28 @@ Singleton {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function backendSettingsFromConfig(configEntry) {
|
||||||
|
switch (CompositorService.compositor) {
|
||||||
|
case "niri":
|
||||||
|
return getNiriSettingsFromConfig(configEntry);
|
||||||
|
case "hyprland":
|
||||||
|
return getHyprlandSettingsFromConfig(configEntry);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function backendMergedSettings() {
|
||||||
|
switch (CompositorService.compositor) {
|
||||||
|
case "niri":
|
||||||
|
return buildMergedNiriSettings();
|
||||||
|
case "hyprland":
|
||||||
|
return buildMergedHyprlandSettings();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ensureEnabledOutput(configEntry) {
|
function ensureEnabledOutput(configEntry) {
|
||||||
const outputKeys = Object.keys(configEntry.outputs || {});
|
const outputKeys = Object.keys(configEntry.outputs || {});
|
||||||
if (outputKeys.length === 0)
|
if (outputKeys.length === 0)
|
||||||
@@ -518,51 +540,12 @@ Singleton {
|
|||||||
WlrOutputService.requestState();
|
WlrOutputService.requestState();
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (CompositorService.compositor) {
|
backendWriteOutputsConfig(outputsData, backendSettingsFromConfig(configEntry), success => {
|
||||||
case "niri":
|
if (success)
|
||||||
{
|
onWriteSuccess();
|
||||||
const paths = getConfigPaths();
|
else
|
||||||
if (!paths) {
|
onWriteFailed();
|
||||||
onWriteFailed();
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
const configContent = generateNiriOutputsKdl(outputsData, getNiriSettingsFromConfig(configEntry));
|
|
||||||
Proc.runCommand("apply-config-write", ["sh", "-c", `mkdir -p "$(dirname "${paths.outputsFile}")" && cat > "${paths.outputsFile}" << 'EOF'\n${configContent}EOF`], (output, exitCode) => {
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
onWriteFailed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onWriteSuccess();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "hyprland":
|
|
||||||
HyprlandService.generateOutputsConfig(outputsData, getHyprlandSettingsFromConfig(configEntry), success => {
|
|
||||||
if (success)
|
|
||||||
onWriteSuccess();
|
|
||||||
else
|
|
||||||
onWriteFailed();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mango":
|
|
||||||
MangoService.generateOutputsConfig(outputsData, success => {
|
|
||||||
if (success)
|
|
||||||
onWriteSuccess();
|
|
||||||
else
|
|
||||||
onWriteFailed();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "dwl":
|
|
||||||
DwlService.generateOutputsConfig(outputsData, success => {
|
|
||||||
if (success)
|
|
||||||
onWriteSuccess();
|
|
||||||
else
|
|
||||||
onWriteFailed();
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
onWriteFailed();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Profile management ─────────────────────────────────────────────────
|
// ── Profile management ─────────────────────────────────────────────────
|
||||||
@@ -867,8 +850,7 @@ Singleton {
|
|||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
outputs = buildOutputsMap();
|
outputs = buildOutputsMap();
|
||||||
reloadSavedOutputs();
|
reloadSavedOutputs();
|
||||||
if (CompositorService.isHyprland)
|
checkIncludeStatus();
|
||||||
checkIncludeStatus();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadSavedOutputs() {
|
function reloadSavedOutputs() {
|
||||||
@@ -1466,12 +1448,12 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const liveOutputs = buildOutputsMap();
|
const liveOutputs = buildOutputsMap();
|
||||||
if (CompositorService.isHyprland && Object.keys(liveOutputs).length > 0) {
|
if (Object.keys(liveOutputs).length > 0) {
|
||||||
outputs = liveOutputs;
|
outputs = liveOutputs;
|
||||||
HyprlandService.generateOutputsConfig(liveOutputs, SettingsData.hyprlandOutputSettings, success => {
|
backendWriteOutputsConfig(liveOutputs, backendMergedSettings(), success => {
|
||||||
fixingInclude = false;
|
fixingInclude = false;
|
||||||
if (!success)
|
if (!success)
|
||||||
ToastService.showError(I18n.tr("Display setup failed"), I18n.tr("Failed to write Hyprland outputs config."), "", "display-config");
|
ToastService.showError(I18n.tr("Display setup failed"), I18n.tr("Failed to write outputs config."), "", "display-config");
|
||||||
checkIncludeStatus();
|
checkIncludeStatus();
|
||||||
WlrOutputService.requestState();
|
WlrOutputService.requestState();
|
||||||
});
|
});
|
||||||
@@ -1570,31 +1552,153 @@ Singleton {
|
|||||||
WlrOutputService.requestState();
|
WlrOutputService.requestState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function backendWriteOutputsConfig(outputsData) {
|
function backendWriteOutputsConfig(outputsData, settingsOrCallback, maybeCallback) {
|
||||||
|
const settings = typeof settingsOrCallback === "function" ? null : settingsOrCallback;
|
||||||
|
const callback = typeof settingsOrCallback === "function" ? settingsOrCallback : maybeCallback;
|
||||||
|
const hasExplicitSettings = settings !== null && settings !== undefined;
|
||||||
|
|
||||||
|
function finish(success) {
|
||||||
|
if (callback)
|
||||||
|
callback(success);
|
||||||
|
}
|
||||||
|
|
||||||
switch (CompositorService.compositor) {
|
switch (CompositorService.compositor) {
|
||||||
case "niri":
|
case "niri":
|
||||||
NiriService.generateOutputsConfig(outputsData);
|
{
|
||||||
break;
|
const niriSettings = hasExplicitSettings ? settings : buildMergedNiriSettings();
|
||||||
case "hyprland":
|
NiriService.generateOutputsConfig(outputsData, niriSettings, success => {
|
||||||
if (readOnly) {
|
if (!success) {
|
||||||
showHyprlandReadOnlyWarning();
|
finish(false);
|
||||||
return false;
|
return;
|
||||||
|
}
|
||||||
|
reloadAndApplyNiriLiveOutputsConfig(outputsData, niriSettings, finish);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "hyprland":
|
||||||
|
{
|
||||||
|
if (readOnly) {
|
||||||
|
showHyprlandReadOnlyWarning();
|
||||||
|
finish(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const hyprlandSettings = hasExplicitSettings ? settings : buildMergedHyprlandSettings();
|
||||||
|
HyprlandService.generateOutputsConfig(outputsData, hyprlandSettings, finish);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
HyprlandService.generateOutputsConfig(outputsData, buildMergedHyprlandSettings());
|
|
||||||
break;
|
|
||||||
case "mango":
|
case "mango":
|
||||||
MangoService.generateOutputsConfig(outputsData);
|
MangoService.generateOutputsConfig(outputsData, finish);
|
||||||
break;
|
break;
|
||||||
case "dwl":
|
case "dwl":
|
||||||
DwlService.generateOutputsConfig(outputsData);
|
DwlService.generateOutputsConfig(outputsData, finish);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WlrOutputService.applyOutputsConfig(outputsData, outputs);
|
WlrOutputService.applyOutputsConfig(outputsData, outputs);
|
||||||
|
finish(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function niriTransformArg(transform) {
|
||||||
|
switch (transform) {
|
||||||
|
case "90":
|
||||||
|
return "90";
|
||||||
|
case "180":
|
||||||
|
return "180";
|
||||||
|
case "270":
|
||||||
|
return "270";
|
||||||
|
case "Flipped":
|
||||||
|
return "flipped";
|
||||||
|
case "Flipped90":
|
||||||
|
return "flipped-90";
|
||||||
|
case "Flipped180":
|
||||||
|
return "flipped-180";
|
||||||
|
case "Flipped270":
|
||||||
|
return "flipped-270";
|
||||||
|
default:
|
||||||
|
return "normal";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLiveNiriOutputName(outputName, outputData) {
|
||||||
|
if (outputs[outputName])
|
||||||
|
return outputName;
|
||||||
|
const targetId = getNiriOutputIdentifier(outputData, outputName);
|
||||||
|
for (const liveName in outputs) {
|
||||||
|
if (getNiriOutputIdentifier(outputs[liveName], liveName) === targetId)
|
||||||
|
return liveName;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyNiriLiveOutputsConfig(outputsData, niriSettings, callback) {
|
||||||
|
const names = Object.keys(outputsData || {});
|
||||||
|
let pending = 0;
|
||||||
|
let failed = false;
|
||||||
|
|
||||||
|
function done(success) {
|
||||||
|
if (callback)
|
||||||
|
callback(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const outputName of names) {
|
||||||
|
const output = outputsData[outputName];
|
||||||
|
if (!output)
|
||||||
|
continue;
|
||||||
|
const liveName = getLiveNiriOutputName(outputName, output);
|
||||||
|
if (!liveName)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const identifier = getNiriOutputIdentifier(output, outputName);
|
||||||
|
const settings = niriSettings?.[outputName] || niriSettings?.[identifier] || {};
|
||||||
|
const config = {};
|
||||||
|
|
||||||
|
if (settings.disabled === true)
|
||||||
|
config.disabled = true;
|
||||||
|
else if (settings.disabled === false)
|
||||||
|
config.disabled = false;
|
||||||
|
|
||||||
|
if (!config.disabled) {
|
||||||
|
if (output.current_mode !== undefined && output.modes && output.modes[output.current_mode]) {
|
||||||
|
const mode = output.modes[output.current_mode];
|
||||||
|
config.mode = mode.width + "x" + mode.height + "@" + (mode.refresh_rate / 1000).toFixed(3);
|
||||||
|
}
|
||||||
|
if (output.logical) {
|
||||||
|
config.scale = output.logical.scale ?? 1.0;
|
||||||
|
config.position = {
|
||||||
|
"x": output.logical.x ?? 0,
|
||||||
|
"y": output.logical.y ?? 0
|
||||||
|
};
|
||||||
|
config.transform = niriTransformArg(output.logical.transform);
|
||||||
|
}
|
||||||
|
if (settings.vrrOnDemand !== undefined)
|
||||||
|
config.vrrOnDemand = settings.vrrOnDemand;
|
||||||
|
else if (output.vrr_enabled !== undefined)
|
||||||
|
config.vrr = output.vrr_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending++;
|
||||||
|
NiriService.applyOutputConfig(liveName, config, success => {
|
||||||
|
failed = failed || !success;
|
||||||
|
pending--;
|
||||||
|
if (pending === 0) {
|
||||||
|
WlrOutputService.requestState();
|
||||||
|
done(!failed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending === 0)
|
||||||
|
done(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadAndApplyNiriLiveOutputsConfig(outputsData, niriSettings, callback) {
|
||||||
|
Proc.runCommand("niri-reload-output-config", ["niri", "msg", "action", "load-config-file"], () => {
|
||||||
|
applyNiriLiveOutputsConfig(outputsData, niriSettings, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeOutputPositions(outputsData) {
|
function normalizeOutputPositions(outputsData) {
|
||||||
const names = Object.keys(outputsData);
|
const names = Object.keys(outputsData);
|
||||||
if (names.length === 0)
|
if (names.length === 0)
|
||||||
@@ -1982,7 +2086,7 @@ Singleton {
|
|||||||
|
|
||||||
const mergedOutputs = buildOutputsWithPendingChanges();
|
const mergedOutputs = buildOutputsWithPendingChanges();
|
||||||
const mergedNiriSettings = buildMergedNiriSettings();
|
const mergedNiriSettings = buildMergedNiriSettings();
|
||||||
const configContent = generateNiriOutputsKdl(mergedOutputs, mergedNiriSettings);
|
const configContent = NiriService.buildOutputsConfig(mergedOutputs, mergedNiriSettings);
|
||||||
|
|
||||||
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
||||||
const tempFile = configDir + "/niri/dms/.outputs-validate-tmp.kdl";
|
const tempFile = configDir + "/niri/dms/.outputs-validate-tmp.kdl";
|
||||||
@@ -2006,7 +2110,7 @@ Singleton {
|
|||||||
if (formatChanged)
|
if (formatChanged)
|
||||||
SettingsData.saveSettings();
|
SettingsData.saveSettings();
|
||||||
commitNiriSettingsChanges();
|
commitNiriSettingsChanges();
|
||||||
backendWriteOutputsConfig(mergedOutputs);
|
backendWriteOutputsConfig(mergedOutputs, mergedNiriSettings);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2083,108 +2187,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateNiriOutputsKdl(outputsData, niriSettings) {
|
|
||||||
let kdlContent = `// Auto-generated by DMS - do not edit manually\n\n`;
|
|
||||||
const sortedNames = Object.keys(outputsData).sort((a, b) => {
|
|
||||||
const la = outputsData[a].logical || {};
|
|
||||||
const lb = outputsData[b].logical || {};
|
|
||||||
return (la.x ?? 0) - (lb.x ?? 0) || (la.y ?? 0) - (lb.y ?? 0);
|
|
||||||
});
|
|
||||||
for (const outputName of sortedNames) {
|
|
||||||
const output = outputsData[outputName];
|
|
||||||
const identifier = getNiriOutputIdentifier(output, outputName);
|
|
||||||
const settings = niriSettings[identifier] || {};
|
|
||||||
kdlContent += `output "${identifier}" {\n`;
|
|
||||||
if (settings.disabled) {
|
|
||||||
kdlContent += ` off\n}\n\n`;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
kdlContent += ` scale ${output.logical.scale ?? 1.0}\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 (settings.vrrOnDemand) {
|
|
||||||
kdlContent += ` variable-refresh-rate on-demand=true\n`;
|
|
||||||
} else if (output.vrr_enabled) {
|
|
||||||
kdlContent += ` variable-refresh-rate\n`;
|
|
||||||
}
|
|
||||||
if (settings.focusAtStartup)
|
|
||||||
kdlContent += ` focus-at-startup\n`;
|
|
||||||
if (settings.backdropColor)
|
|
||||||
kdlContent += ` backdrop-color "${settings.backdropColor}"\n`;
|
|
||||||
kdlContent += generateHotCornersBlock(settings);
|
|
||||||
kdlContent += generateLayoutBlock(settings);
|
|
||||||
kdlContent += `}\n\n`;
|
|
||||||
}
|
|
||||||
return kdlContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateHotCornersBlock(settings) {
|
|
||||||
if (!settings.hotCorners)
|
|
||||||
return "";
|
|
||||||
const hc = settings.hotCorners;
|
|
||||||
if (hc.off)
|
|
||||||
return ` hot-corners {\n off\n }\n`;
|
|
||||||
const corners = hc.corners || [];
|
|
||||||
if (corners.length === 0)
|
|
||||||
return "";
|
|
||||||
let block = ` hot-corners {\n`;
|
|
||||||
for (const corner of corners)
|
|
||||||
block += ` ${corner}\n`;
|
|
||||||
block += ` }\n`;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateLayoutBlock(settings) {
|
|
||||||
if (!settings.layout)
|
|
||||||
return "";
|
|
||||||
const layout = settings.layout;
|
|
||||||
const hasSettings = layout.gaps !== undefined || layout.defaultColumnWidth || layout.presetColumnWidths || layout.alwaysCenterSingleColumn !== undefined;
|
|
||||||
if (!hasSettings)
|
|
||||||
return "";
|
|
||||||
let block = ` layout {\n`;
|
|
||||||
if (layout.gaps !== undefined)
|
|
||||||
block += ` gaps ${layout.gaps}\n`;
|
|
||||||
if (layout.defaultColumnWidth?.type === "proportion") {
|
|
||||||
const val = layout.defaultColumnWidth.value;
|
|
||||||
const formatted = Number.isInteger(val) ? val.toFixed(1) : val.toString();
|
|
||||||
block += ` default-column-width { proportion ${formatted}; }\n`;
|
|
||||||
}
|
|
||||||
if (layout.presetColumnWidths && layout.presetColumnWidths.length > 0) {
|
|
||||||
block += ` preset-column-widths {\n`;
|
|
||||||
for (const preset of layout.presetColumnWidths) {
|
|
||||||
if (preset.type === "proportion") {
|
|
||||||
const val = preset.value;
|
|
||||||
const formatted = Number.isInteger(val) ? val.toFixed(1) : val.toString();
|
|
||||||
block += ` proportion ${formatted}\n`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
block += ` }\n`;
|
|
||||||
}
|
|
||||||
if (layout.alwaysCenterSingleColumn !== undefined)
|
|
||||||
block += layout.alwaysCenterSingleColumn ? ` always-center-single-column\n` : ` always-center-single-column false\n`;
|
|
||||||
block += ` }\n`;
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmChanges(profileId) {
|
function confirmChanges(profileId) {
|
||||||
const outputConfigs = buildCurrentOutputConfigs();
|
const outputConfigs = buildCurrentOutputConfigs();
|
||||||
lastAppliedEntry = {
|
lastAppliedEntry = {
|
||||||
|
|||||||
@@ -1248,15 +1248,36 @@ Singleton {
|
|||||||
|
|
||||||
const commands = [];
|
const commands = [];
|
||||||
|
|
||||||
|
if (config.disabled !== undefined) {
|
||||||
|
commands.push(`niri msg output "${outputName}" ${config.disabled ? "off" : "on"}`);
|
||||||
|
if (config.disabled) {
|
||||||
|
const fullDisableCommand = "{ " + commands.join(" && ") + "; } 2>&1";
|
||||||
|
Proc.runCommand("niri-output-config", ["sh", "-c", fullDisableCommand], (output, exitCode) => {
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
log.warn("Failed to apply output config:", outputName, "exit:", exitCode, output);
|
||||||
|
if (callback)
|
||||||
|
callback(false, output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetchOutputs();
|
||||||
|
if (callback)
|
||||||
|
callback(true, "Success");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config.position !== undefined) {
|
if (config.position !== undefined) {
|
||||||
commands.push(`niri msg output "${outputName}" position ${config.position.x} ${config.position.y}`);
|
commands.push(`niri msg output "${outputName}" position set ${config.position.x} ${config.position.y}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.mode !== undefined) {
|
if (config.mode !== undefined) {
|
||||||
commands.push(`niri msg output "${outputName}" mode ${config.mode}`);
|
commands.push(`niri msg output "${outputName}" mode ${config.mode}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.vrr !== undefined) {
|
if (config.vrrOnDemand !== undefined) {
|
||||||
|
commands.push(`niri msg output "${outputName}" vrr --on-demand ${config.vrrOnDemand ? "on" : "off"}`);
|
||||||
|
} else if (config.vrr !== undefined) {
|
||||||
commands.push(`niri msg output "${outputName}" vrr ${config.vrr ? "on" : "off"}`);
|
commands.push(`niri msg output "${outputName}" vrr ${config.vrr ? "on" : "off"}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1274,10 +1295,10 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullCommand = commands.join(" && ");
|
const fullCommand = "{ " + commands.join(" && ") + "; } 2>&1";
|
||||||
Proc.runCommand("niri-output-config", ["sh", "-c", fullCommand], (output, exitCode) => {
|
Proc.runCommand("niri-output-config", ["sh", "-c", fullCommand], (output, exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
log.warn("Failed to apply output config:", output);
|
log.warn("Failed to apply output config:", outputName, "exit:", exitCode, output);
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(false, output);
|
callback(false, output);
|
||||||
return;
|
return;
|
||||||
@@ -1297,10 +1318,32 @@ Singleton {
|
|||||||
return outputName;
|
return outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateOutputsConfig(outputsData) {
|
function outputSettingsFor(output, outputName, niriSettings) {
|
||||||
|
const identifier = getOutputIdentifier(output, outputName);
|
||||||
|
if (niriSettings)
|
||||||
|
return niriSettings[identifier] || niriSettings[outputName] || {};
|
||||||
|
return SettingsData.getNiriOutputSettings(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformToNiri(transform) {
|
||||||
|
const transformMap = {
|
||||||
|
"Normal": "normal",
|
||||||
|
"90": "90",
|
||||||
|
"180": "180",
|
||||||
|
"270": "270",
|
||||||
|
"Flipped": "flipped",
|
||||||
|
"Flipped90": "flipped-90",
|
||||||
|
"Flipped180": "flipped-180",
|
||||||
|
"Flipped270": "flipped-270"
|
||||||
|
};
|
||||||
|
return transformMap[transform] || "normal";
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOutputsConfig(outputsData, niriSettings) {
|
||||||
const data = outputsData || outputs;
|
const data = outputsData || outputs;
|
||||||
if (!data || Object.keys(data).length === 0)
|
if (!data || Object.keys(data).length === 0)
|
||||||
return;
|
return "";
|
||||||
|
|
||||||
let kdlContent = `// Auto-generated by DMS - do not edit manually\n\n`;
|
let kdlContent = `// Auto-generated by DMS - do not edit manually\n\n`;
|
||||||
|
|
||||||
const sortedNames = Object.keys(data).sort((a, b) => {
|
const sortedNames = Object.keys(data).sort((a, b) => {
|
||||||
@@ -1311,11 +1354,11 @@ Singleton {
|
|||||||
for (const outputName of sortedNames) {
|
for (const outputName of sortedNames) {
|
||||||
const output = data[outputName];
|
const output = data[outputName];
|
||||||
const identifier = getOutputIdentifier(output, outputName);
|
const identifier = getOutputIdentifier(output, outputName);
|
||||||
const niriSettings = SettingsData.getNiriOutputSettings(identifier);
|
const outputSettings = outputSettingsFor(output, outputName, niriSettings);
|
||||||
|
|
||||||
kdlContent += `output "${identifier}" {\n`;
|
kdlContent += `output "${identifier}" {\n`;
|
||||||
|
|
||||||
if (niriSettings.disabled) {
|
if (outputSettings.disabled) {
|
||||||
kdlContent += ` off\n`;
|
kdlContent += ` off\n`;
|
||||||
kdlContent += `}\n\n`;
|
kdlContent += `}\n\n`;
|
||||||
continue;
|
continue;
|
||||||
@@ -1330,17 +1373,7 @@ Singleton {
|
|||||||
kdlContent += ` scale ${output.logical.scale || 1.0}\n`;
|
kdlContent += ` scale ${output.logical.scale || 1.0}\n`;
|
||||||
|
|
||||||
if (output.logical.transform && output.logical.transform !== "Normal") {
|
if (output.logical.transform && output.logical.transform !== "Normal") {
|
||||||
const transformMap = {
|
kdlContent += ` transform "${transformToNiri(output.logical.transform)}"\n`;
|
||||||
"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) {
|
if (output.logical.x !== undefined && output.logical.y !== undefined) {
|
||||||
@@ -1348,25 +1381,38 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.vrr_enabled || niriSettings.vrrOnDemand) {
|
if (output.vrr_enabled || outputSettings.vrrOnDemand) {
|
||||||
const vrrOnDemand = niriSettings.vrrOnDemand ?? false;
|
const vrrOnDemand = outputSettings.vrrOnDemand ?? false;
|
||||||
kdlContent += vrrOnDemand ? ` variable-refresh-rate on-demand=true\n` : ` variable-refresh-rate\n`;
|
kdlContent += vrrOnDemand ? ` variable-refresh-rate on-demand=true\n` : ` variable-refresh-rate\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (niriSettings.focusAtStartup) {
|
if (outputSettings.focusAtStartup) {
|
||||||
kdlContent += ` focus-at-startup\n`;
|
kdlContent += ` focus-at-startup\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (niriSettings.backdropColor) {
|
if (outputSettings.backdropColor) {
|
||||||
kdlContent += ` backdrop-color "${niriSettings.backdropColor}"\n`;
|
kdlContent += ` backdrop-color "${outputSettings.backdropColor}"\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
kdlContent += generateHotCornersBlock(niriSettings);
|
kdlContent += generateHotCornersBlock(outputSettings);
|
||||||
kdlContent += generateLayoutBlock(niriSettings);
|
kdlContent += generateLayoutBlock(outputSettings);
|
||||||
|
|
||||||
kdlContent += `}\n\n`;
|
kdlContent += `}\n\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return kdlContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateOutputsConfig(outputsData, settingsOrCallback, maybeCallback) {
|
||||||
|
const niriSettings = typeof settingsOrCallback === "function" ? null : settingsOrCallback;
|
||||||
|
const callback = typeof settingsOrCallback === "function" ? settingsOrCallback : maybeCallback;
|
||||||
|
const kdlContent = buildOutputsConfig(outputsData, niriSettings);
|
||||||
|
if (!kdlContent) {
|
||||||
|
if (callback)
|
||||||
|
callback(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
const configDir = Paths.strip(StandardPaths.writableLocation(StandardPaths.ConfigLocation));
|
||||||
const niriDmsDir = configDir + "/niri/dms";
|
const niriDmsDir = configDir + "/niri/dms";
|
||||||
const outputsPath = niriDmsDir + "/outputs.kdl";
|
const outputsPath = niriDmsDir + "/outputs.kdl";
|
||||||
@@ -1374,9 +1420,13 @@ Singleton {
|
|||||||
Proc.runCommand("niri-write-outputs", ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${kdlContent}EOF`], (output, exitCode) => {
|
Proc.runCommand("niri-write-outputs", ["sh", "-c", `mkdir -p "${niriDmsDir}" && cat > "${outputsPath}" << 'EOF'\n${kdlContent}EOF`], (output, exitCode) => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
log.warn("Failed to write outputs config:", output);
|
log.warn("Failed to write outputs config:", output);
|
||||||
|
if (callback)
|
||||||
|
callback(false, output);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("Generated outputs config at", outputsPath);
|
log.info("Generated outputs config at", outputsPath);
|
||||||
|
if (callback)
|
||||||
|
callback(true, "");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user