Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bf30ba844 | |||
| 1d4e419548 | |||
| 03084f1cd5 | |||
| fa2d64cd1f | |||
| f274d5c8b3 | |||
| 5e0840a59d | |||
| 71a71dcc5e | |||
| 6257e63357 | |||
| 41047adac3 | |||
| acd351f559 | |||
| 768d878981 | |||
| afccfbbc05 | |||
| b25cb6b278 | |||
| e92be07078 | |||
| bc15980ba5 | |||
| 5490b665f3 | |||
| 3c184743d0 | |||
| 46bf8aec49 | |||
| b07d7f3b03 |
+3
-1
@@ -1 +1,3 @@
|
||||
profiles/*.json
|
||||
profiles/
|
||||
*.json
|
||||
__pycache__/
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Make sure a config file was provided
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <config file>"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Attempt to parse the config file
|
||||
INPUT_FILE=$1
|
||||
echo "Parsing config info from $INPUT_FILE..."
|
||||
|
||||
TEST=$(jq '.outputs[]' $INPUT_FILE)
|
||||
|
||||
echo $TEST[0]
|
||||
Executable
+412
@@ -0,0 +1,412 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
from pathlib import Path
|
||||
|
||||
# Try to import PySide6 for the GUI
|
||||
try:
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||||
QListWidget, QListWidgetItem, QPushButton, QInputDialog, QMessageBox, QLabel,
|
||||
QFileDialog
|
||||
)
|
||||
from PySide6.QtGui import QIcon
|
||||
from PySide6.QtCore import Qt, QSize
|
||||
PYSIDE_AVAILABLE = True
|
||||
except ImportError:
|
||||
PYSIDE_AVAILABLE = False
|
||||
|
||||
VERBOSE = True
|
||||
DEFAULT_PROFILE_DIR = Path.home() / ".local/share/kde-display-profiles"
|
||||
|
||||
def log(*args):
|
||||
if VERBOSE:
|
||||
print(*args)
|
||||
|
||||
def run_command(cmd):
|
||||
log(f"[CMD] {cmd}")
|
||||
try:
|
||||
subprocess.run(cmd, shell=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log(f"Error executing command: {e}")
|
||||
|
||||
def get_mode_string(output, mode_id):
|
||||
for mode in output.get('modes', []):
|
||||
if mode['id'] == mode_id:
|
||||
width = mode['size']['width']
|
||||
height = mode['size']['height']
|
||||
refresh_rate = round(mode['refreshRate'])
|
||||
return f"{width}x{height}@{refresh_rate}"
|
||||
return None
|
||||
|
||||
def save_profile(profile_path):
|
||||
log(f"Saving current display profile to {profile_path}...")
|
||||
try:
|
||||
with open(profile_path, 'w') as f:
|
||||
subprocess.run(['kscreen-doctor', '--json'], stdout=f, check=True)
|
||||
log("Profile saved successfully.")
|
||||
except Exception as e:
|
||||
log(f"Error saving profile: {e}")
|
||||
raise e
|
||||
|
||||
def apply_attribute(output_name, attribute, value, value_map=None):
|
||||
if value is None:
|
||||
log(f"Output {output_name} has no attribute {attribute}, skipping...")
|
||||
return
|
||||
|
||||
# If it's a list or dict, something might be wrong or it's a complex structure we don't handle this way
|
||||
if isinstance(value, (list, dict)):
|
||||
log(f"Output {output_name} attribute {attribute} has complex value {value}, skipping...")
|
||||
return
|
||||
|
||||
str_value = str(value).lower()
|
||||
if value_map:
|
||||
if str_value in value_map:
|
||||
value = value_map[str_value]
|
||||
else:
|
||||
log(f"Warning: Value {str_value} not found in map for {attribute}, using as is.")
|
||||
|
||||
cmd = f"kscreen-doctor output.{output_name}.{attribute}.{value}"
|
||||
run_command(cmd)
|
||||
|
||||
def load_profile(profile_path):
|
||||
log(f"Loading display profile from {profile_path}...")
|
||||
if not os.path.exists(profile_path):
|
||||
raise FileNotFoundError(f"Profile file not found: {profile_path}")
|
||||
|
||||
try:
|
||||
with open(profile_path, 'r') as f:
|
||||
profile = json.load(f)
|
||||
except Exception as e:
|
||||
log(f"Error reading profile JSON: {e}")
|
||||
raise e
|
||||
|
||||
outputs = profile.get('outputs', [])
|
||||
commands = []
|
||||
|
||||
# Maps from JSON values to kscreen-doctor options
|
||||
bool_enable_map = {"true": "enable", "false": "disable"}
|
||||
bool_allow_map = {"true": "allow", "false": "disallow"}
|
||||
rgb_range_map = {
|
||||
"0": "automatic", "1": "full", "2": "limited",
|
||||
"automatic": "automatic", "full": "full", "limited": "limited"
|
||||
}
|
||||
rotation_map = {"1": "normal", "2": "left", "4": "inverted", "8": "right"}
|
||||
vrr_policy_map = {
|
||||
"0": "never", "1": "always", "2": "automatic",
|
||||
"never": "never", "always": "always", "automatic": "automatic"
|
||||
}
|
||||
|
||||
def add_attr(output_name, attribute, value, value_map=None):
|
||||
if value is None:
|
||||
return
|
||||
if isinstance(value, (list, dict)):
|
||||
return
|
||||
|
||||
str_value = str(value).lower()
|
||||
if value_map:
|
||||
if str_value in value_map:
|
||||
value = value_map[str_value]
|
||||
|
||||
commands.append(f"output.{output_name}.{attribute}.{value}")
|
||||
|
||||
# 1. Handle Enable/Disable
|
||||
for out in outputs:
|
||||
status = "enable" if out.get('enabled') else "disable"
|
||||
commands.append(f"output.{out['name']}.{status}")
|
||||
|
||||
# 2. Collect other attributes
|
||||
for out in outputs:
|
||||
name = out['name']
|
||||
|
||||
# WCG
|
||||
add_attr(name, "wcg", out.get('wcg'), bool_enable_map)
|
||||
# SDR Brightness
|
||||
add_attr(name, "sdr-brightness", out.get('sdr-brightness'))
|
||||
# VRR Policy
|
||||
add_attr(name, "vrrpolicy", out.get('vrrPolicy'), vrr_policy_map)
|
||||
# RGB Range
|
||||
add_attr(name, "rgbrange", out.get('rgbRange'), rgb_range_map)
|
||||
# Overscan
|
||||
add_attr(name, "overscan", out.get('overscan'))
|
||||
# HDR
|
||||
add_attr(name, "hdr", out.get('hdr'), bool_enable_map)
|
||||
|
||||
# Brightness
|
||||
brightness = out.get('brightness')
|
||||
if brightness is not None:
|
||||
add_attr(name, "brightness", int(float(brightness) * 100))
|
||||
|
||||
# Max BPC
|
||||
max_bpc = out.get('maxBpc')
|
||||
if max_bpc == 0:
|
||||
max_bpc = "automatic"
|
||||
add_attr(name, "maxbpc", max_bpc)
|
||||
|
||||
# DDC/CI
|
||||
add_attr(name, "ddcCi", out.get('ddcCiAllowed'), bool_allow_map)
|
||||
|
||||
# Mirroring
|
||||
replication_source_id = out.get('replicationSource', 0)
|
||||
if replication_source_id != 0:
|
||||
source_name = "none"
|
||||
for other_out in outputs:
|
||||
if other_out.get('id') == replication_source_id:
|
||||
source_name = other_out['name']
|
||||
break
|
||||
add_attr(name, "mirror", source_name)
|
||||
else:
|
||||
add_attr(name, "mirror", "none")
|
||||
|
||||
# ICC Profile
|
||||
icc_path = out.get('iccProfilePath')
|
||||
if icc_path:
|
||||
add_attr(name, "iccProfilePath", icc_path)
|
||||
|
||||
# Mode
|
||||
mode_id = out.get('currentModeId')
|
||||
if mode_id:
|
||||
mode_str = get_mode_string(out, mode_id)
|
||||
if mode_str:
|
||||
add_attr(name, "mode", mode_str)
|
||||
|
||||
# Position
|
||||
pos = out.get('pos')
|
||||
if pos:
|
||||
add_attr(name, "position", f"{pos['x']},{pos['y']}")
|
||||
|
||||
# Scale
|
||||
add_attr(name, "scale", out.get('scale'))
|
||||
|
||||
# Rotation
|
||||
add_attr(name, "rotation", out.get('rotation'), rotation_map)
|
||||
|
||||
# Priority and Primary
|
||||
priority = out.get('priority')
|
||||
if priority is not None:
|
||||
if priority == 1:
|
||||
commands.append(f"output.{name}.primary")
|
||||
commands.append(f"output.{name}.priority.{priority}")
|
||||
|
||||
if commands:
|
||||
full_cmd = ["kscreen-doctor"] + commands
|
||||
log(f"Running atomic command: {' '.join(full_cmd)}")
|
||||
try:
|
||||
subprocess.run(full_cmd, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log(f"Error executing kscreen-doctor: {e}")
|
||||
raise e
|
||||
|
||||
log("Display configuration restored.")
|
||||
|
||||
class DisplayProfileManagerGUI(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("KDE Display Profile Manager")
|
||||
self.setMinimumSize(420, 300)
|
||||
|
||||
# Ensure default directory exists
|
||||
DEFAULT_PROFILE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
self.setup_ui()
|
||||
self.refresh_profiles()
|
||||
|
||||
def setup_ui(self):
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
layout = QVBoxLayout(central_widget)
|
||||
|
||||
# Header layout
|
||||
header_layout = QHBoxLayout()
|
||||
header_layout.addWidget(QLabel("Available Profiles:"))
|
||||
header_layout.addStretch()
|
||||
|
||||
self.refresh_btn = QPushButton()
|
||||
self.refresh_btn.setIcon(QIcon.fromTheme("view-refresh"))
|
||||
self.refresh_btn.setIconSize(QSize(20, 20))
|
||||
self.refresh_btn.setToolTip("Refresh Profiles")
|
||||
self.refresh_btn.setFlat(True)
|
||||
self.refresh_btn.clicked.connect(self.refresh_profiles)
|
||||
header_layout.addWidget(self.refresh_btn)
|
||||
|
||||
layout.addLayout(header_layout)
|
||||
|
||||
self.profile_list = QListWidget()
|
||||
self.profile_list.itemDoubleClicked.connect(self.on_load_clicked)
|
||||
layout.addWidget(self.profile_list)
|
||||
|
||||
btn_layout = QHBoxLayout()
|
||||
self.save_btn = QPushButton("Save Current")
|
||||
self.save_btn.clicked.connect(self.on_save_clicked)
|
||||
btn_layout.addWidget(self.save_btn)
|
||||
|
||||
self.load_btn = QPushButton("Load Selected")
|
||||
self.load_btn.clicked.connect(self.on_load_clicked)
|
||||
btn_layout.addWidget(self.load_btn)
|
||||
|
||||
|
||||
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
def refresh_profiles(self):
|
||||
self.profile_list.clear()
|
||||
if DEFAULT_PROFILE_DIR.exists():
|
||||
profiles = sorted(DEFAULT_PROFILE_DIR.glob("*.json"))
|
||||
for profile in profiles:
|
||||
name = profile.stem
|
||||
# Create item without text to prevent double-rendering/blurriness
|
||||
item = QListWidgetItem(self.profile_list)
|
||||
item.setData(Qt.UserRole, name)
|
||||
|
||||
# Custom widget for the row
|
||||
widget = QWidget()
|
||||
row_layout = QHBoxLayout(widget)
|
||||
row_layout.setContentsMargins(5, 2, 5, 2)
|
||||
row_layout.setSpacing(5)
|
||||
|
||||
label = QLabel(name)
|
||||
# Ensure the label doesn't inherit transparency issues
|
||||
label.setStyleSheet("background: transparent;")
|
||||
row_layout.addWidget(label)
|
||||
row_layout.addStretch()
|
||||
|
||||
# Copy button
|
||||
copy_btn = QPushButton()
|
||||
copy_btn.setIcon(QIcon.fromTheme("edit-copy"))
|
||||
copy_btn.setIconSize(QSize(16, 16))
|
||||
copy_btn.setFlat(True)
|
||||
copy_btn.setToolTip("Copy Load Command")
|
||||
copy_btn.clicked.connect(lambda checked=False, n=name: self.copy_profile_cmd(n))
|
||||
row_layout.addWidget(copy_btn)
|
||||
|
||||
# Delete button
|
||||
del_btn = QPushButton()
|
||||
del_btn.setIcon(QIcon.fromTheme("user-trash"))
|
||||
del_btn.setIconSize(QSize(16, 16))
|
||||
del_btn.setFlat(True)
|
||||
del_btn.setToolTip("Delete Profile")
|
||||
del_btn.clicked.connect(lambda checked=False, n=name: self.delete_profile(n))
|
||||
row_layout.addWidget(del_btn)
|
||||
|
||||
item.setSizeHint(widget.sizeHint())
|
||||
self.profile_list.addItem(item)
|
||||
self.profile_list.setItemWidget(item, widget)
|
||||
|
||||
def get_default_profile_name(self):
|
||||
existing_names = []
|
||||
if DEFAULT_PROFILE_DIR.exists():
|
||||
existing_names = [p.stem for p in DEFAULT_PROFILE_DIR.glob("*.json")]
|
||||
|
||||
i = 1
|
||||
while f"Profile {i}" in existing_names:
|
||||
i += 1
|
||||
return f"Profile {i}"
|
||||
|
||||
def on_save_clicked(self):
|
||||
default_name = self.get_default_profile_name()
|
||||
name, ok = QInputDialog.getText(self, "Save Profile", "Profile Name:", text=default_name)
|
||||
|
||||
if ok and name:
|
||||
profile_path = DEFAULT_PROFILE_DIR / f"{name}.json"
|
||||
if profile_path.exists():
|
||||
reply = QMessageBox.question(self, "Overwrite?",
|
||||
f"Profile '{name}' already exists. Overwrite?",
|
||||
QMessageBox.Yes | QMessageBox.No)
|
||||
if reply == QMessageBox.No:
|
||||
return
|
||||
|
||||
try:
|
||||
save_profile(str(profile_path))
|
||||
self.refresh_profiles()
|
||||
QMessageBox.information(self, "Success", f"Profile '{name}' saved.")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to save profile: {e}")
|
||||
|
||||
def on_load_clicked(self, item=None):
|
||||
if not isinstance(item, QListWidgetItem):
|
||||
item = self.profile_list.currentItem()
|
||||
|
||||
if not item:
|
||||
QMessageBox.warning(self, "No Selection", "Please select a profile to load.")
|
||||
return
|
||||
|
||||
# Retrieve name from UserRole data instead of item.text()
|
||||
profile_name = item.data(Qt.UserRole)
|
||||
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
||||
|
||||
try:
|
||||
load_profile(str(profile_path))
|
||||
QMessageBox.information(self, "Success", f"Profile '{profile_name}' loaded.")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to load profile: {e}")
|
||||
|
||||
def copy_profile_cmd(self, profile_name):
|
||||
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
||||
script_path = os.path.abspath(__file__)
|
||||
cmd = f"python3 {shlex.quote(script_path)} load {shlex.quote(str(profile_path))}"
|
||||
|
||||
clipboard = QApplication.clipboard()
|
||||
clipboard.setText(cmd)
|
||||
|
||||
QMessageBox.information(self, "Command Copied", f"The following command has been copied to your clipboard:\n\n{cmd}")
|
||||
|
||||
def delete_profile(self, profile_name):
|
||||
profile_path = DEFAULT_PROFILE_DIR / f"{profile_name}.json"
|
||||
|
||||
reply = QMessageBox.question(self, "Confirm Delete",
|
||||
f"Are you sure you want to delete profile '{profile_name}'?",
|
||||
QMessageBox.Yes | QMessageBox.No)
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
try:
|
||||
os.remove(profile_path)
|
||||
self.refresh_profiles()
|
||||
QMessageBox.information(self, "Success", f"Profile '{profile_name}' deleted.")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "Error", f"Failed to delete profile: {e}")
|
||||
|
||||
def show_gui():
|
||||
if not PYSIDE_AVAILABLE:
|
||||
print("Error: PySide6 is not installed. Please install it to use the GUI.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Use 'Round' instead of 'PassThrough' as it is often more stable for font rendering
|
||||
if hasattr(Qt, "HighDpiScaleFactorRoundingPolicy"):
|
||||
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.Round)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
window = DisplayProfileManagerGUI()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="KDE Display Profile Manager")
|
||||
subparsers = parser.add_subparsers(dest="command", required=False)
|
||||
|
||||
# Save command
|
||||
save_parser = subparsers.add_parser("save", help="Save current display configuration to a profile")
|
||||
save_parser.add_argument("profile", help="Path to the profile file (e.g., profiles/myprofile.json)")
|
||||
|
||||
# Load command
|
||||
load_parser = subparsers.add_parser("load", help="Load a display configuration from a profile")
|
||||
load_parser.add_argument("profile", help="Path to the profile file")
|
||||
|
||||
# GUI command
|
||||
subparsers.add_parser("gui", help="Launch the GUI manager")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == "save":
|
||||
save_profile(args.profile)
|
||||
elif args.command == "load":
|
||||
load_profile(args.profile)
|
||||
elif args.command == "gui" or args.command is None:
|
||||
show_gui()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
# Make sure a config file was provided
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <config file>"
|
||||
exit
|
||||
fi
|
||||
|
||||
PROFILE=$1
|
||||
|
||||
if [[ ! -f "$PROFILE" ]]; then
|
||||
echo "Profile file not found: $PROFILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "jq not installed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v kscreen-doctor &>/dev/null; then
|
||||
echo "kscreen-doctor not found!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract outputs list
|
||||
enabled_outputs=$(jq -c '.outputs[] | select(.enabled == true)' "$PROFILE")
|
||||
echo "enabled:"
|
||||
echo $enabled_outputs
|
||||
disabled_outputs=$(jq -c '.outputs[] | select(.enabled == false)' "$PROFILE")
|
||||
echo "disabled:"
|
||||
echo $disabled_outputs
|
||||
outputs=$(jq -c '.outputs[]' "$PROFILE")
|
||||
|
||||
# Restore enabled/disabled starting with the enabled monitors
|
||||
# Starting with a disabled monitor might not work if it was
|
||||
# previously the only enabled monitor
|
||||
|
||||
function enable_outputs {
|
||||
local outputs=$1
|
||||
while IFS= read -r out; do
|
||||
id=$(echo "$out" | jq -r '.id')
|
||||
name=$(echo "$out" | jq -r '.name')
|
||||
enabled=$(echo "$out" | jq -r '.enabled')
|
||||
|
||||
# Enable/disable
|
||||
if [[ "$enabled" == "true" ]]; then
|
||||
kscreen-doctor "output.$name.enable"
|
||||
else
|
||||
kscreen-doctor "output.$name.disable"
|
||||
fi
|
||||
done <<< "$outputs"
|
||||
}
|
||||
|
||||
enable_outputs $enabled_outputs
|
||||
enable_outputs $disabled_outputs
|
||||
|
||||
function load_profile_to_outputs {
|
||||
local outputs=$1
|
||||
while IFS= read -r out; do
|
||||
id=$(echo "$out" | jq -r '.id')
|
||||
name=$(echo "$out" | jq -r '.name')
|
||||
posx=$(echo "$out" | jq -r '.pos.x')
|
||||
posy=$(echo "$out" | jq -r '.pos.y')
|
||||
rotation=$(echo "$out" | jq -r '.rotation')
|
||||
scale=$(echo "$out" | jq -r '.scale')
|
||||
mode=$(echo "$out" | jq -r '.currentModeId')
|
||||
priority=$(echo "$out" | jq -r '.priority')
|
||||
|
||||
# Mode (Resolution + refresh)
|
||||
kscreen-doctor "output.$id.mode.$mode"
|
||||
|
||||
# Position
|
||||
kscreen-doctor "output.$id.position.$posx,$posy"
|
||||
|
||||
# Scale
|
||||
kscreen-doctor "output.$id.scale.$scale"
|
||||
|
||||
# Rotation (map from JSON names to kscreen-doctor options)
|
||||
case "$rotation" in
|
||||
"1") kscreen-doctor "output.$id.rotation.normal" ;;
|
||||
"2") kscreen-doctor "output.$id.rotation.left" ;;
|
||||
"4") kscreen-doctor "output.$id.rotation.inverted" ;;
|
||||
"8") kscreen-doctor "output.$id.rotation.right" ;;
|
||||
esac
|
||||
|
||||
# Primary / Not Primary
|
||||
echo $priority
|
||||
if [ $priority -eq 1 ]; then
|
||||
kscreen-doctor "output.$id.primary"
|
||||
fi
|
||||
|
||||
done <<< "$outputs"
|
||||
}
|
||||
|
||||
load_profile_to_outputs $outputs
|
||||
|
||||
#########################
|
||||
# 2. Restore clone groups
|
||||
#########################
|
||||
|
||||
clone_groups=$(jq -c '.clones[]?' "$PROFILE")
|
||||
|
||||
function restore_clone_groups {
|
||||
local clone_groups=$1
|
||||
while IFS= read -r clone; do
|
||||
primary=$(echo "$clone" | jq -r '.[0]')
|
||||
others=$(echo "$clone" | jq -r '.[]' | tail -n +2)
|
||||
|
||||
for o in $others; do
|
||||
kscreen-doctor "output.$o.clone.$primary"
|
||||
done
|
||||
done <<< "$clone_groups"
|
||||
}
|
||||
|
||||
restore_clone_groups $clone_groups
|
||||
|
||||
echo "Display configuration restored."
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
-1297
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
# All Kscreen Doctor attributes
|
||||
- [x] primary
|
||||
- [x] priority
|
||||
- [x] enable
|
||||
- [x] disable
|
||||
- [x] mode
|
||||
- [x] position
|
||||
- [x] scale
|
||||
- [x] orientation / rotation
|
||||
- [x] overscan (0-100)
|
||||
- [x] vrrpolicy (never / always / automatic)
|
||||
- [x] rgbrange (automatic / full / limited)
|
||||
- [x] hdr (enable / disable / toggle)
|
||||
- [x] sdr-brightness (50-10000)
|
||||
- [x] wcg (enable / disable / toggle)
|
||||
- [x] iccprofile (path)
|
||||
- [ ] sdrGamut (0-100)
|
||||
- [ ] maxBrightnessOverride (disable / int)
|
||||
- [ ] maxAverageBrightnessOverride (disable / int)
|
||||
- [ ] minBrightnessOverride (disable / int)
|
||||
- [ ] colorProfileSource (sRBG / ICC / EDID)
|
||||
- [x] brightness (0-100)
|
||||
- [ ] colorPowerTradeoff (preferEfficiency / preferAccuracy)
|
||||
- [ ] dimming (0-100)
|
||||
- [x] mirror ( none / output )
|
||||
- [x] ddcCi (allow / disallow)
|
||||
- [x] maxbpc (automatic / 6-16)
|
||||
- [ ] edrPolicy (never / always)
|
||||
- [ ] sharpness (0 - 100)
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Check for correct usage
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <config name>"
|
||||
exit
|
||||
fi
|
||||
|
||||
OUTPUT_CONFIG_NAME=$1
|
||||
kscreen-doctor --json > $OUTPUT_CONFIG_NAME
|
||||
Reference in New Issue
Block a user