mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
displays: add hyprland HDR options
This commit is contained in:
@@ -361,6 +361,7 @@ Singleton {
|
||||
property var screenPreferences: ({})
|
||||
property var showOnLastDisplay: ({})
|
||||
property var niriOutputSettings: ({})
|
||||
property var hyprlandOutputSettings: ({})
|
||||
|
||||
property var barConfigs: [
|
||||
{
|
||||
@@ -1382,6 +1383,51 @@ Singleton {
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getHyprlandOutputSetting(outputId, key, defaultValue) {
|
||||
if (!hyprlandOutputSettings[outputId])
|
||||
return defaultValue;
|
||||
return hyprlandOutputSettings[outputId][key] !== undefined ? hyprlandOutputSettings[outputId][key] : defaultValue;
|
||||
}
|
||||
|
||||
function setHyprlandOutputSetting(outputId, key, value) {
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
if (!updated[outputId])
|
||||
updated[outputId] = {};
|
||||
updated[outputId][key] = value;
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeHyprlandOutputSetting(outputId, key) {
|
||||
if (!hyprlandOutputSettings[outputId] || !(key in hyprlandOutputSettings[outputId]))
|
||||
return;
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
delete updated[outputId][key];
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function getHyprlandOutputSettings(outputId) {
|
||||
const settings = hyprlandOutputSettings[outputId];
|
||||
return settings ? JSON.parse(JSON.stringify(settings)) : {};
|
||||
}
|
||||
|
||||
function setHyprlandOutputSettings(outputId, settings) {
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
updated[outputId] = settings;
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
function removeHyprlandOutputSettings(outputId) {
|
||||
if (!hyprlandOutputSettings[outputId])
|
||||
return;
|
||||
const updated = JSON.parse(JSON.stringify(hyprlandOutputSettings));
|
||||
delete updated[outputId];
|
||||
hyprlandOutputSettings = updated;
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: leftWidgetsModel
|
||||
}
|
||||
|
||||
@@ -259,6 +259,7 @@ var SPEC = {
|
||||
screenPreferences: { def: {} },
|
||||
showOnLastDisplay: { def: {} },
|
||||
niriOutputSettings: { def: {} },
|
||||
hyprlandOutputSettings: { def: {} },
|
||||
|
||||
barConfigs: { def: [{
|
||||
id: "default",
|
||||
|
||||
@@ -25,11 +25,13 @@ Singleton {
|
||||
|
||||
property var pendingChanges: ({})
|
||||
property var pendingNiriChanges: ({})
|
||||
property var pendingHyprlandChanges: ({})
|
||||
property var originalNiriSettings: null
|
||||
property var originalHyprlandSettings: null
|
||||
property var originalOutputs: null
|
||||
property string originalDisplayNameMode: ""
|
||||
property bool formatChanged: originalDisplayNameMode !== "" && originalDisplayNameMode !== SettingsData.displayNameMode
|
||||
property bool hasPendingChanges: Object.keys(pendingChanges).length > 0 || Object.keys(pendingNiriChanges).length > 0 || formatChanged
|
||||
property bool hasPendingChanges: Object.keys(pendingChanges).length > 0 || Object.keys(pendingNiriChanges).length > 0 || Object.keys(pendingHyprlandChanges).length > 0 || formatChanged
|
||||
|
||||
property bool validatingConfig: false
|
||||
property string validationError: ""
|
||||
@@ -85,9 +87,48 @@ Singleton {
|
||||
const parsed = parseOutputsConfig(content);
|
||||
const filtered = filterDisconnectedOnly(parsed);
|
||||
savedOutputs = filtered;
|
||||
|
||||
if (CompositorService.isHyprland)
|
||||
initHyprlandSettingsFromConfig(parsed);
|
||||
});
|
||||
}
|
||||
|
||||
function initHyprlandSettingsFromConfig(parsedOutputs) {
|
||||
const current = JSON.parse(JSON.stringify(SettingsData.hyprlandOutputSettings));
|
||||
let changed = false;
|
||||
|
||||
for (const outputName in parsedOutputs) {
|
||||
const output = parsedOutputs[outputName];
|
||||
const settings = output.hyprlandSettings;
|
||||
if (!settings)
|
||||
continue;
|
||||
|
||||
if (current[outputName])
|
||||
continue;
|
||||
|
||||
const hasSettings = settings.colorManagement || settings.bitdepth ||
|
||||
settings.sdrBrightness !== undefined || settings.sdrSaturation !== undefined;
|
||||
if (!hasSettings)
|
||||
continue;
|
||||
|
||||
current[outputName] = {};
|
||||
if (settings.colorManagement)
|
||||
current[outputName].colorManagement = settings.colorManagement;
|
||||
if (settings.bitdepth)
|
||||
current[outputName].bitdepth = settings.bitdepth;
|
||||
if (settings.sdrBrightness !== undefined)
|
||||
current[outputName].sdrBrightness = settings.sdrBrightness;
|
||||
if (settings.sdrSaturation !== undefined)
|
||||
current[outputName].sdrSaturation = settings.sdrSaturation;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
SettingsData.hyprlandOutputSettings = current;
|
||||
SettingsData.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
function filterDisconnectedOnly(parsedOutputs) {
|
||||
const result = {};
|
||||
const liveNames = Object.keys(outputs);
|
||||
@@ -165,17 +206,46 @@ Singleton {
|
||||
const result = {};
|
||||
const lines = content.split("\n");
|
||||
for (const line of lines) {
|
||||
const match = line.match(/^\s*monitor\s*=\s*([^,]+),\s*(\d+)x(\d+)@([\d.]+),\s*(-?\d+)x(-?\d+),\s*([\d.]+)(?:,\s*transform,\s*(\d+))?(?:,\s*vrr,\s*(\d+))?/);
|
||||
const match = line.match(/^\s*monitor\s*=\s*([^,]+),\s*(\d+)x(\d+)@([\d.]+),\s*(-?\d+)x(-?\d+),\s*([\d.]+)/);
|
||||
if (!match)
|
||||
continue;
|
||||
const name = match[1].trim();
|
||||
const rest = line.substring(line.indexOf(match[7]) + match[7].length);
|
||||
|
||||
let transform = 0, vrr = false, bitdepth = undefined, cm = undefined;
|
||||
let sdrBrightness = undefined, sdrSaturation = undefined;
|
||||
|
||||
const transformMatch = rest.match(/,\s*transform,\s*(\d+)/);
|
||||
if (transformMatch)
|
||||
transform = parseInt(transformMatch[1]);
|
||||
|
||||
const vrrMatch = rest.match(/,\s*vrr,\s*(\d+)/);
|
||||
if (vrrMatch)
|
||||
vrr = vrrMatch[1] === "1";
|
||||
|
||||
const bitdepthMatch = rest.match(/,\s*bitdepth,\s*(\d+)/);
|
||||
if (bitdepthMatch)
|
||||
bitdepth = parseInt(bitdepthMatch[1]);
|
||||
|
||||
const cmMatch = rest.match(/,\s*cm,\s*(\w+)/);
|
||||
if (cmMatch)
|
||||
cm = cmMatch[1];
|
||||
|
||||
const sdrBrightnessMatch = rest.match(/,\s*sdrbrightness,\s*([\d.]+)/);
|
||||
if (sdrBrightnessMatch)
|
||||
sdrBrightness = parseFloat(sdrBrightnessMatch[1]);
|
||||
|
||||
const sdrSaturationMatch = rest.match(/,\s*sdrsaturation,\s*([\d.]+)/);
|
||||
if (sdrSaturationMatch)
|
||||
sdrSaturation = parseFloat(sdrSaturationMatch[1]);
|
||||
|
||||
result[name] = {
|
||||
"name": name,
|
||||
"logical": {
|
||||
"x": parseInt(match[5]),
|
||||
"y": parseInt(match[6]),
|
||||
"scale": parseFloat(match[7]),
|
||||
"transform": hyprlandToTransform(parseInt(match[8] || "0"))
|
||||
"transform": hyprlandToTransform(transform)
|
||||
},
|
||||
"modes": [
|
||||
{
|
||||
@@ -185,8 +255,14 @@ Singleton {
|
||||
}
|
||||
],
|
||||
"current_mode": 0,
|
||||
"vrr_enabled": match[9] === "1",
|
||||
"vrr_supported": true
|
||||
"vrr_enabled": vrr,
|
||||
"vrr_supported": true,
|
||||
"hyprlandSettings": {
|
||||
"bitdepth": bitdepth,
|
||||
"colorManagement": cm,
|
||||
"sdrBrightness": sdrBrightness,
|
||||
"sdrSaturation": sdrSaturation
|
||||
}
|
||||
};
|
||||
}
|
||||
return result;
|
||||
@@ -426,7 +502,7 @@ Singleton {
|
||||
NiriService.generateOutputsConfig(outputsData);
|
||||
break;
|
||||
case "hyprland":
|
||||
HyprlandService.generateOutputsConfig(outputsData);
|
||||
HyprlandService.generateOutputsConfig(outputsData, buildMergedHyprlandSettings());
|
||||
break;
|
||||
case "dwl":
|
||||
DwlService.generateOutputsConfig(outputsData);
|
||||
@@ -582,6 +658,42 @@ Singleton {
|
||||
originalNiriSettings = JSON.parse(JSON.stringify(SettingsData.niriOutputSettings));
|
||||
}
|
||||
|
||||
function getHyprlandOutputIdentifier(output, outputName) {
|
||||
if (SettingsData.displayNameMode === "model" && output?.make && output?.model)
|
||||
return "desc:" + output.make + " " + output.model;
|
||||
return outputName;
|
||||
}
|
||||
|
||||
function getHyprlandSetting(output, outputName, key, defaultValue) {
|
||||
if (!CompositorService.isHyprland)
|
||||
return defaultValue;
|
||||
const identifier = getHyprlandOutputIdentifier(output, outputName);
|
||||
const pending = pendingHyprlandChanges[identifier];
|
||||
if (pending && (key in pending)) {
|
||||
const val = pending[key];
|
||||
return (val !== null && val !== undefined) ? val : defaultValue;
|
||||
}
|
||||
return SettingsData.getHyprlandOutputSetting(identifier, key, defaultValue);
|
||||
}
|
||||
|
||||
function setHyprlandSetting(output, outputName, key, value) {
|
||||
if (!CompositorService.isHyprland)
|
||||
return;
|
||||
initOriginalHyprlandSettings();
|
||||
const identifier = getHyprlandOutputIdentifier(output, outputName);
|
||||
const newPending = JSON.parse(JSON.stringify(pendingHyprlandChanges));
|
||||
if (!newPending[identifier])
|
||||
newPending[identifier] = {};
|
||||
newPending[identifier][key] = value;
|
||||
pendingHyprlandChanges = newPending;
|
||||
}
|
||||
|
||||
function initOriginalHyprlandSettings() {
|
||||
if (originalHyprlandSettings)
|
||||
return;
|
||||
originalHyprlandSettings = JSON.parse(JSON.stringify(SettingsData.hyprlandOutputSettings));
|
||||
}
|
||||
|
||||
function initOriginalOutputs() {
|
||||
if (!originalOutputs)
|
||||
originalOutputs = JSON.parse(JSON.stringify(outputs));
|
||||
@@ -667,8 +779,10 @@ Singleton {
|
||||
function clearPendingChanges() {
|
||||
pendingChanges = {};
|
||||
pendingNiriChanges = {};
|
||||
pendingHyprlandChanges = {};
|
||||
originalOutputs = null;
|
||||
originalNiriSettings = null;
|
||||
originalHyprlandSettings = null;
|
||||
originalDisplayNameMode = "";
|
||||
}
|
||||
|
||||
@@ -719,6 +833,22 @@ Singleton {
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("Layout") + " → " + I18n.tr("Modified"));
|
||||
}
|
||||
|
||||
for (const outputId in pendingHyprlandChanges) {
|
||||
const changes = pendingHyprlandChanges[outputId];
|
||||
if (changes.bitdepth !== undefined)
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("Bit Depth") + " → " + changes.bitdepth);
|
||||
if (changes.colorManagement !== undefined)
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("Color Management") + " → " + changes.colorManagement);
|
||||
if (changes.sdrBrightness !== undefined)
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("SDR Brightness") + " → " + changes.sdrBrightness);
|
||||
if (changes.sdrSaturation !== undefined)
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("SDR Saturation") + " → " + changes.sdrSaturation);
|
||||
if (changes.supportsHdr !== undefined)
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("Force HDR") + " → " + (changes.supportsHdr ? I18n.tr("Yes") : I18n.tr("No")));
|
||||
if (changes.supportsWideColor !== undefined)
|
||||
changeDescriptions.push(outputId + ": " + I18n.tr("Force Wide Color") + " → " + (changes.supportsWideColor ? I18n.tr("Yes") : I18n.tr("No")));
|
||||
}
|
||||
|
||||
if (CompositorService.isNiri) {
|
||||
validateAndApplyNiriConfig(changeDescriptions);
|
||||
return;
|
||||
@@ -729,6 +859,9 @@ Singleton {
|
||||
if (formatChanged)
|
||||
SettingsData.saveSettings();
|
||||
|
||||
if (CompositorService.isHyprland)
|
||||
commitHyprlandSettingsChanges();
|
||||
|
||||
const mergedOutputs = buildOutputsWithPendingChanges();
|
||||
backendWriteOutputsConfig(mergedOutputs);
|
||||
}
|
||||
@@ -788,6 +921,34 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function buildMergedHyprlandSettings() {
|
||||
const merged = JSON.parse(JSON.stringify(SettingsData.hyprlandOutputSettings));
|
||||
for (const outputId in pendingHyprlandChanges) {
|
||||
if (!merged[outputId])
|
||||
merged[outputId] = {};
|
||||
for (const key in pendingHyprlandChanges[outputId]) {
|
||||
const val = pendingHyprlandChanges[outputId][key];
|
||||
if (val === null || val === undefined)
|
||||
delete merged[outputId][key];
|
||||
else
|
||||
merged[outputId][key] = val;
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
function commitHyprlandSettingsChanges() {
|
||||
for (const outputId in pendingHyprlandChanges) {
|
||||
for (const key in pendingHyprlandChanges[outputId]) {
|
||||
const val = pendingHyprlandChanges[outputId][key];
|
||||
if (val === null || val === undefined)
|
||||
SettingsData.removeHyprlandOutputSetting(outputId, key);
|
||||
else
|
||||
SettingsData.setHyprlandOutputSetting(outputId, key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateNiriOutputsKdl(outputsData, niriSettings) {
|
||||
let kdlContent = `// Auto-generated by DMS - do not edit manually\n\n`;
|
||||
for (const outputName in outputsData) {
|
||||
@@ -893,6 +1054,7 @@ Singleton {
|
||||
function revertChanges() {
|
||||
const hadFormatChange = originalDisplayNameMode !== "";
|
||||
const hadNiriChanges = originalNiriSettings !== null;
|
||||
const hadHyprlandChanges = originalHyprlandSettings !== null;
|
||||
|
||||
if (hadFormatChange) {
|
||||
SettingsData.displayNameMode = originalDisplayNameMode;
|
||||
@@ -904,7 +1066,15 @@ Singleton {
|
||||
SettingsData.saveSettings();
|
||||
}
|
||||
|
||||
if (!originalOutputs && !hadNiriChanges) {
|
||||
if (hadHyprlandChanges) {
|
||||
SettingsData.hyprlandOutputSettings = JSON.parse(JSON.stringify(originalHyprlandSettings));
|
||||
SettingsData.saveSettings();
|
||||
}
|
||||
|
||||
pendingHyprlandChanges = {};
|
||||
pendingNiriChanges = {};
|
||||
|
||||
if (!originalOutputs && !hadNiriChanges && !hadHyprlandChanges) {
|
||||
if (hadFormatChange)
|
||||
backendWriteOutputsConfig(buildOutputsWithPendingChanges());
|
||||
clearPendingChanges();
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property string outputName: ""
|
||||
property var outputData: null
|
||||
property bool expanded: false
|
||||
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: headerRow.implicitHeight + Theme.spacingS * 2
|
||||
color: headerMouse.containsMouse ? Theme.withAlpha(Theme.primary, 0.1) : "transparent"
|
||||
radius: Theme.cornerRadius / 2
|
||||
|
||||
Row {
|
||||
id: headerRow
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: Theme.spacingS
|
||||
anchors.rightMargin: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: root.expanded ? "expand_more" : "chevron_right"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Compositor Settings")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: headerMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.expanded = !root.expanded
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: settingsColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: root.expanded
|
||||
topPadding: Theme.spacingS
|
||||
|
||||
property int currentBitdepth: {
|
||||
DisplayConfigState.pendingHyprlandChanges;
|
||||
return DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "bitdepth", 8);
|
||||
}
|
||||
property bool is10Bit: currentBitdepth === 10
|
||||
|
||||
property string currentCm: {
|
||||
DisplayConfigState.pendingHyprlandChanges;
|
||||
return DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "colorManagement", "auto");
|
||||
}
|
||||
property bool isHdrMode: currentCm === "hdr" || currentCm === "hdredid"
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: I18n.tr("10-bit Color")
|
||||
description: I18n.tr("Enable 10-bit color depth for wider color gamut and HDR support")
|
||||
checked: settingsColumn.is10Bit
|
||||
onToggled: checked => {
|
||||
if (checked) {
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "bitdepth", 10);
|
||||
} else {
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "bitdepth", null);
|
||||
if (settingsColumn.isHdrMode)
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "colorManagement", "auto");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: settingsColumn.is10Bit
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.withAlpha(Theme.outline, 0.15)
|
||||
}
|
||||
|
||||
DankDropdown {
|
||||
width: parent.width
|
||||
text: I18n.tr("Color Gamut")
|
||||
addHorizontalPadding: true
|
||||
currentValue: {
|
||||
DisplayConfigState.pendingHyprlandChanges;
|
||||
const val = DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "colorManagement", "auto");
|
||||
return cmLabelMap[val] || I18n.tr("Auto (Wide)");
|
||||
}
|
||||
options: [I18n.tr("Auto (Wide)"), I18n.tr("Wide (BT2020)"), "DCI-P3", "Apple P3", "Adobe RGB", "EDID", "HDR", I18n.tr("HDR (EDID)")]
|
||||
|
||||
property var cmValueMap: ({
|
||||
[I18n.tr("Auto (Wide)")]: "auto",
|
||||
[I18n.tr("Wide (BT2020)")]: "wide",
|
||||
"DCI-P3": "dcip3",
|
||||
"Apple P3": "dp3",
|
||||
"Adobe RGB": "adobe",
|
||||
"EDID": "edid",
|
||||
"HDR": "hdr",
|
||||
[I18n.tr("HDR (EDID)")]: "hdredid"
|
||||
})
|
||||
|
||||
property var cmLabelMap: ({
|
||||
"auto": I18n.tr("Auto (Wide)"),
|
||||
"wide": I18n.tr("Wide (BT2020)"),
|
||||
"dcip3": "DCI-P3",
|
||||
"dp3": "Apple P3",
|
||||
"adobe": "Adobe RGB",
|
||||
"edid": "EDID",
|
||||
"hdr": "HDR",
|
||||
"hdredid": I18n.tr("HDR (EDID)")
|
||||
})
|
||||
|
||||
onValueChanged: value => {
|
||||
const cmValue = cmValueMap[value] || "auto";
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "colorManagement", cmValue);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: warningColumn.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius / 2
|
||||
color: Theme.withAlpha(Theme.warning, 0.15)
|
||||
border.color: Theme.withAlpha(Theme.warning, 0.3)
|
||||
border.width: 1
|
||||
visible: settingsColumn.isHdrMode
|
||||
|
||||
Column {
|
||||
id: warningColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
DankIcon {
|
||||
name: "warning"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.warning
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
StyledText {
|
||||
text: I18n.tr("Experimental Feature")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.warning
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
StyledText {
|
||||
text: I18n.tr("HDR mode is experimental. Verify your monitor supports HDR before enabling.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: settingsColumn.isHdrMode
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.withAlpha(Theme.outline, 0.15)
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("HDR Tone Mapping")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceVariantText
|
||||
leftPadding: Theme.spacingM
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Column {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("SDR Brightness")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
width: parent.width
|
||||
height: 40
|
||||
placeholderText: "1.0 - 2.0"
|
||||
text: {
|
||||
DisplayConfigState.pendingHyprlandChanges;
|
||||
const val = DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "sdrBrightness", null);
|
||||
return val !== null ? val.toString() : "";
|
||||
}
|
||||
onEditingFinished: {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed) {
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "sdrBrightness", null);
|
||||
return;
|
||||
}
|
||||
const val = parseFloat(trimmed);
|
||||
if (isNaN(val) || val < 0.1 || val > 5.0)
|
||||
return;
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "sdrBrightness", parseFloat(val.toFixed(2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: (parent.width - Theme.spacingM) / 2
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("SDR Saturation")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
width: parent.width
|
||||
height: 40
|
||||
placeholderText: "0.5 - 1.5"
|
||||
text: {
|
||||
DisplayConfigState.pendingHyprlandChanges;
|
||||
const val = DisplayConfigState.getHyprlandSetting(root.outputData, root.outputName, "sdrSaturation", null);
|
||||
return val !== null ? val.toString() : "";
|
||||
}
|
||||
onEditingFinished: {
|
||||
const trimmed = text.trim();
|
||||
if (!trimmed) {
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "sdrSaturation", null);
|
||||
return;
|
||||
}
|
||||
const val = parseFloat(trimmed);
|
||||
if (isNaN(val) || val < 0.0 || val > 3.0)
|
||||
return;
|
||||
DisplayConfigState.setHyprlandSetting(root.outputData, root.outputName, "sdrSaturation", parseFloat(val.toFixed(2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,7 +263,7 @@ StyledRect {
|
||||
case "niri":
|
||||
return "NiriOutputSettings.qml";
|
||||
case "hyprland":
|
||||
return "";
|
||||
return "HyprlandOutputSettings.qml";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -14,21 +14,27 @@ Singleton {
|
||||
readonly property string outputsPath: hyprDmsDir + "/outputs.conf"
|
||||
|
||||
function getOutputIdentifier(output, outputName) {
|
||||
if (SettingsData.displayNameMode === "model" && output.make && output.model) {
|
||||
if (SettingsData.displayNameMode === "model" && output.make && output.model)
|
||||
return "desc:" + output.make + " " + output.model;
|
||||
}
|
||||
return outputName;
|
||||
}
|
||||
|
||||
function generateOutputsConfig(outputsData) {
|
||||
function generateOutputsConfig(outputsData, hyprlandSettings) {
|
||||
if (!outputsData || Object.keys(outputsData).length === 0)
|
||||
return;
|
||||
|
||||
const settings = hyprlandSettings || SettingsData.hyprlandOutputSettings;
|
||||
let lines = ["# Auto-generated by DMS - do not edit manually", ""];
|
||||
let monitorv2Blocks = [];
|
||||
|
||||
for (const outputName in outputsData) {
|
||||
const output = outputsData[outputName];
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
const identifier = getOutputIdentifier(output, outputName);
|
||||
const outputSettings = settings[identifier] || {};
|
||||
|
||||
let resolution = "preferred";
|
||||
if (output.modes && output.current_mode !== undefined) {
|
||||
const mode = output.modes[output.current_mode];
|
||||
@@ -39,10 +45,8 @@ Singleton {
|
||||
const x = output.logical?.x ?? 0;
|
||||
const y = output.logical?.y ?? 0;
|
||||
const position = x + "x" + y;
|
||||
|
||||
const scale = output.logical?.scale ?? 1.0;
|
||||
|
||||
const identifier = getOutputIdentifier(output, outputName);
|
||||
let monitorLine = "monitor = " + identifier + ", " + resolution + ", " + position + ", " + scale;
|
||||
|
||||
const transform = transformToHyprland(output.logical?.transform ?? "Normal");
|
||||
@@ -52,7 +56,53 @@ Singleton {
|
||||
if (output.vrr_supported && output.vrr_enabled)
|
||||
monitorLine += ", vrr, 1";
|
||||
|
||||
if (outputSettings.bitdepth && outputSettings.bitdepth !== 8)
|
||||
monitorLine += ", bitdepth, " + outputSettings.bitdepth;
|
||||
|
||||
if (outputSettings.colorManagement && outputSettings.colorManagement !== "auto")
|
||||
monitorLine += ", cm, " + outputSettings.colorManagement;
|
||||
|
||||
if (outputSettings.sdrBrightness !== undefined && outputSettings.sdrBrightness !== 1.0)
|
||||
monitorLine += ", sdrbrightness, " + outputSettings.sdrBrightness;
|
||||
|
||||
if (outputSettings.sdrSaturation !== undefined && outputSettings.sdrSaturation !== 1.0)
|
||||
monitorLine += ", sdrsaturation, " + outputSettings.sdrSaturation;
|
||||
|
||||
lines.push(monitorLine);
|
||||
|
||||
const needsMonitorv2 = outputSettings.supportsHdr || outputSettings.supportsWideColor ||
|
||||
outputSettings.sdrMinLuminance !== undefined || outputSettings.sdrMaxLuminance !== undefined ||
|
||||
outputSettings.minLuminance !== undefined || outputSettings.maxLuminance !== undefined ||
|
||||
outputSettings.maxAvgLuminance !== undefined;
|
||||
|
||||
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("");
|
||||
|
||||
@@ -5,7 +5,6 @@ import QtCore
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import qs.Common
|
||||
import "../Common/KeybindActions.js" as Actions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user