feat(icc): add dms icc set-temp and show per-output temps in status

The per-output temperature override was reachable from the Display Config
slider and from `wayland.icc.setTemp` over IPC, but not from the CLI.

- `dms icc set-temp <output> <kelvin>` (alias `setTemp`) sets a per-output
  override, 1000-10000K, and 0 clears it so the output follows the night
  light schedule again. Range checking matches the daemon so an invalid
  value fails in the CLI instead of over IPC.
- `dms icc status` gains a Temp column (the override in K, or `schedule`),
  so an override can be verified without opening the settings UI.

Verified against a running daemon: `dms icc status` reports 7000K for the
three overridden outputs and `schedule` for the remaining one.
This commit is contained in:
KIDult2226
2026-09-13 00:48:05 +08:00
parent 1c5cae105b
commit 648d1dd4fd
+89 -7
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/AvengeMedia/DankMaterialShell/core/internal/icc"
@@ -55,8 +56,20 @@ var iccStatusCmd = &cobra.Command{
Run: runICCStatus,
}
var iccSetTempCmd = &cobra.Command{
Use: "set-temp <output> <kelvin>",
Aliases: []string{"setTemp"},
Short: "Set the color temperature for a specific output",
Long: "Set the color temperature for a specific output, overriding the\n" +
"night light temperature for that output only.\n\n" +
"Accepts 1000-10000K; 0 removes the override so the output follows the\n" +
"night light schedule again.",
Args: cobra.ExactArgs(2),
Run: runICCSetTemp,
}
func init() {
iccCmd.AddCommand(iccListCmd, iccInfoCmd, iccApplyCmd, iccRemoveCmd, iccStatusCmd)
iccCmd.AddCommand(iccListCmd, iccInfoCmd, iccApplyCmd, iccRemoveCmd, iccStatusCmd, iccSetTempCmd)
}
func getICCConfigDir() string {
@@ -308,6 +321,73 @@ func runICCRemove(cmd *cobra.Command, args []string) {
fmt.Printf("Removed ICC profile from output '%s'\n", outputName)
}
func runICCSetTemp(cmd *cobra.Command, args []string) {
outputName := args[0]
temp, err := strconv.Atoi(args[1])
if err != nil {
log.Fatalf("Invalid temperature %q: expected a number in kelvin", args[1])
}
if temp != 0 && (temp < 1000 || temp > 10000) {
log.Fatalf("Temperature %d out of range (1000-10000, or 0 to follow the schedule)", temp)
}
req := models.Request{
Method: "wayland.icc.setTemp",
Params: map[string]any{
"output": outputName,
"temp": temp,
},
}
resp, err := sendServerRequest(req)
if err != nil {
log.Fatalf("Failed to set output temperature: %v", err)
}
if resp.Error != "" {
log.Fatalf("Server error: %s", resp.Error)
}
if temp == 0 {
fmt.Printf("Output '%s' follows the night light temperature again\n", outputName)
return
}
fmt.Printf("Set output '%s' color temperature to %dK\n", outputName, temp)
}
// fetchICCOutputTemps asks the running server for the per-output overrides.
// Outputs without an override report 0.
func fetchICCOutputTemps() map[string]int {
temps := make(map[string]int)
resp, err := sendServerRequest(models.Request{Method: "wayland.icc.getTemps"})
if err != nil || resp.Result == nil {
return temps
}
resultBytes, err := json.Marshal(*resp.Result)
if err != nil {
return temps
}
var raw map[string]any
if err := json.Unmarshal(resultBytes, &raw); err != nil {
return temps
}
for output, value := range raw {
if f, ok := value.(float64); ok {
temps[output] = int(f)
}
}
return temps
}
// formatOutputTemp renders a per-output override, or the schedule marker.
func formatOutputTemp(temps map[string]int, output string) string {
temp, ok := temps[output]
if !ok || temp == 0 {
return "schedule"
}
return strconv.Itoa(temp) + "K"
}
func runICCStatus(cmd *cobra.Command, args []string) {
req := models.Request{
Method: "wayland.icc.getStatus",
@@ -342,10 +422,12 @@ func runICCStatus(cmd *cobra.Command, args []string) {
}
}
temps := fetchICCOutputTemps()
fmt.Println("ICC Profile Status:")
fmt.Println(strings.Repeat("─", 90))
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s\n", "Output", "Profile", "Version", "ColorSpace", "Active")
fmt.Println(" " + strings.Repeat("─", 86))
fmt.Println(strings.Repeat("─", 100))
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s %-9s\n", "Output", "Profile", "Version", "ColorSpace", "Active", "Temp")
fmt.Println(" " + strings.Repeat("─", 96))
shown := make(map[string]bool)
for _, output := range outputs {
@@ -371,9 +453,9 @@ func runICCStatus(cmd *cobra.Command, args []string) {
if active {
activeStr = "yes"
}
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s\n", output, desc, version, colorSpace, activeStr)
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s %-9s\n", output, desc, version, colorSpace, activeStr, formatOutputTemp(temps, output))
} else {
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s\n", output, "(none)", "", "", "no")
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s %-9s\n", output, "(none)", "", "", "no", formatOutputTemp(temps, output))
}
}
@@ -402,6 +484,6 @@ func runICCStatus(cmd *cobra.Command, args []string) {
if active {
activeStr = "yes"
}
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s\n", output, desc, version, colorSpace, activeStr)
fmt.Printf(" %-20s %-20s %-10s %-10s %-7s %-9s\n", output, desc, version, colorSpace, activeStr, formatOutputTemp(temps, output))
}
}