diff --git a/.gitignore b/.gitignore
index 98a9a2581..115998fdb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,4 +49,10 @@ vendor/
a.out
misc/polkit-action/*.policy
-coverage.csv
\ No newline at end of file
+coverage.csv
+
+# AI
+.cursor/
+.kiro/
+*/AGENTS.md
+AGENTS.md
\ No newline at end of file
diff --git a/display1/auto_brightness.go b/display1/auto_brightness.go
new file mode 100644
index 000000000..7d748e9c9
--- /dev/null
+++ b/display1/auto_brightness.go
@@ -0,0 +1,1337 @@
+// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+package display1
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/godbus/dbus/v5"
+ configManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager"
+ sensorproxy "github.com/linuxdeepin/go-dbus-factory/system/net.hadess.sensorproxy"
+
+ "github.com/linuxdeepin/dde-daemon/display1/brightness"
+)
+
+const (
+ compensationInterval = 150 * time.Millisecond
+ sensorDataTimeout = 1 * time.Second
+ compensationThreshold = 5.0
+ maxSensorLux = 1024.0 // 传感器最大勒克斯值
+ minBrightnessLimit = 0.1 // 最小亮度限制 (10%)
+)
+
+// AutoBrightnessConfig 自动亮度配置结构体
+type AutoBrightnessConfig struct {
+ Enabled bool `json:"enabled"` // 是否启用自动亮度
+ Sensitivity float64 `json:"sensitivity"` // 敏感度 (0.1-3.0)
+ PollingInterval int `json:"polling_interval"` // 轮询间隔(秒) (1-60)
+ ChangeThreshold float64 `json:"change_threshold"` // 变化阈值 (1.0-50.0)
+ BrightnessChangeThreshold float64 `json:"brightness_change_threshold"` // 亮度变化阈值 (0.01-1.0)
+ ManualOverrideDuration int `json:"manual_override_duration"` // 手动调节暂停时间(秒) (60-1800)
+ ManualAdjustDisablesAutoMode bool `json:"manual_adjust_disables_auto_mode"` // 手动调节是否禁用自动模式
+ UseTransition bool `json:"use_transition"` // 自动调节时是否使用渐变效果
+ KalmanProcessNoise float64 `json:"kalman_process_noise"` // 卡尔曼滤波器过程噪声协方差 Q
+ KalmanMeasurementNoise float64 `json:"kalman_measurement_noise"` // 卡尔曼滤波器测量噪声协方差 R
+ KalmanWindowSize int `json:"kalman_window_size"` // 卡尔曼滤波器窗口大小
+}
+
+// DefaultAutoBrightnessConfig 默认自动亮度配置
+var DefaultAutoBrightnessConfig = AutoBrightnessConfig{
+ Enabled: false,
+ Sensitivity: 0.5,
+ PollingInterval: 5,
+ ChangeThreshold: 20.0,
+ BrightnessChangeThreshold: 0.01,
+ ManualOverrideDuration: 300,
+ ManualAdjustDisablesAutoMode: true,
+ UseTransition: true,
+ KalmanProcessNoise: 0.8,
+ KalmanMeasurementNoise: 0.05,
+ KalmanWindowSize: 3,
+}
+
+// DSettings键名已在manager.go中定义
+// AutoBrightnessManager 自动亮度管理器
+type AutoBrightnessManager struct {
+ manager *Manager
+
+ sensorClient *SensorProxyClient
+
+ config AutoBrightnessConfig
+ configManager configManager.Manager
+ sysBus *dbus.Conn
+
+ enabled bool
+ supported bool
+ manualOverride time.Time
+ lastLightLevel int
+ lastAdjustTime time.Time
+ lastBrightness float64
+
+ kalmanFilter *brightness.AdaptiveKalmanFilter
+
+ systemAdjusting bool
+ running bool
+ stopping int32 // atomic: 1 when Stop is in progress
+
+ lastSensorDataTime time.Time
+ compensationTicker *time.Ticker
+ compensationStopCh chan struct{}
+ compensationWg sync.WaitGroup
+
+ // 同步控制
+ mutex sync.RWMutex
+
+ // 重试和降级配置
+ maxRetries int
+ retryInterval time.Duration
+ gracefulDegradation bool
+}
+
+// NewAutoBrightnessManager 创建新的自动亮度管理器
+func NewAutoBrightnessManager() *AutoBrightnessManager {
+ return &AutoBrightnessManager{
+ lastLightLevel: -1,
+ lastBrightness: -1,
+ maxRetries: 3,
+ retryInterval: time.Second * 2,
+ gracefulDegradation: true,
+ }
+}
+
+// Initialize 初始化自动亮度管理器
+func (abm *AutoBrightnessManager) Initialize(manager *Manager) error {
+ if manager == nil {
+ return errors.New("manager cannot be nil")
+ }
+
+ logger.Info("[AutoBrightness] Initializing AutoBrightnessManager")
+
+ abm.mutex.Lock()
+ abm.manager = manager
+ abm.sysBus = manager.sysBus
+ abm.mutex.Unlock()
+
+ err := abm.initConfigManager()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to initialize config manager:", err)
+ }
+
+ builtinMonitor := manager.getBuiltinMonitor()
+ if builtinMonitor == nil {
+ abm.mutex.Lock()
+ abm.supported = false
+ abm.mutex.Unlock()
+ return fmt.Errorf("no builtin monitor found (total monitors: %d)", len(manager.getConnectedMonitors()))
+ }
+
+ canSet, _ := manager.CanSetBrightness(builtinMonitor.Name)
+ if !canSet {
+ abm.mutex.Lock()
+ abm.supported = false
+ abm.mutex.Unlock()
+ return fmt.Errorf("cannot set brightness for builtin monitor: %s", builtinMonitor.Name)
+ }
+
+ sensorProxy := sensorproxy.NewSensorProxy(manager.sysBus)
+ sensorClient := NewSensorProxyClient(sensorProxy, manager.dbusDaemon)
+
+ abm.mutex.Lock()
+ abm.sensorClient = sensorClient
+ abm.mutex.Unlock()
+
+ err = abm.checkSensorAvailability()
+ if err != nil {
+ abm.mutex.Lock()
+ abm.supported = false
+ abm.mutex.Unlock()
+ logger.Warning("[AutoBrightness] Sensor not available:", err)
+ return fmt.Errorf("sensor not available: %w", err)
+ }
+
+ sensorClient.SetServiceChangeCallback(abm.onServiceChange)
+ sensorClient.SetLightLevelChangeCallback(abm.onLightLevelChange)
+
+ abm.mutex.Lock()
+ config, err := abm.getConfig()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to load config, using default:", err)
+ config = DefaultAutoBrightnessConfig
+ }
+ abm.config = config
+ abm.supported = true
+ abm.applyKalmanFilterConfig()
+ abm.mutex.Unlock()
+
+ logger.Info("[AutoBrightness] AutoBrightnessManager initialized successfully")
+ return nil
+}
+
+// Start 启动自动亮度功能
+func (abm *AutoBrightnessManager) Start() error {
+ abm.mutex.Lock()
+ if !abm.supported {
+ abm.mutex.Unlock()
+ return errors.New("auto brightness not supported")
+ }
+
+ abm.resetHistoryState()
+ abm.manualOverride = time.Time{}
+
+ if abm.running {
+ abm.mutex.Unlock()
+ return nil
+ }
+
+ // 如果正在执行 Stop,不启动新实例
+ if atomic.LoadInt32(&abm.stopping) != 0 {
+ abm.mutex.Unlock()
+ return errors.New("auto brightness is stopping")
+ }
+
+ if !abm.config.Enabled {
+ abm.mutex.Unlock()
+ return nil
+ }
+
+ sensorClient := abm.sensorClient
+ manager := abm.manager
+ abm.mutex.Unlock()
+
+ err := sensorClient.Connect(manager.sysSigLoop)
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to connect to sensor proxy:", err)
+ return fmt.Errorf("failed to connect to sensor proxy: %w", err)
+ }
+
+ err = abm.claimLightWithRetry()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to claim light sensor after retries:", err)
+ sensorClient.Disconnect()
+ return fmt.Errorf("failed to claim light sensor: %w", err)
+ }
+
+ abm.mutex.Lock()
+ abm.running = true
+ abm.enabled = true
+ abm.lastSensorDataTime = time.Now()
+ powerSaving := manager.isPowerSaving()
+ sessionActive := manager.sessionActive
+ if !powerSaving && sessionActive {
+ abm.startCompensationTimer()
+ } else {
+ logger.Warningf("[AutoBrightness] Started but powerSaving [%v], session active [%v]", powerSaving, sessionActive)
+ }
+ abm.mutex.Unlock()
+
+ if !powerSaving && sessionActive {
+ go func() {
+ time.Sleep(time.Second)
+ abm.adjustBrightnessOnce()
+ }()
+ }
+
+ logger.Info("[AutoBrightness] AutoBrightnessManager started successfully")
+ return nil
+}
+
+// Stop 停止自动亮度功能
+func (abm *AutoBrightnessManager) Stop() error {
+ abm.mutex.Lock()
+
+ if !abm.running {
+ abm.mutex.Unlock()
+ return nil
+ }
+
+ // 标记正在停止,防止窗口期内 Start() 创建新 ticker
+ atomic.StoreInt32(&abm.stopping, 1)
+ abm.stopCompensationTimer()
+ atomic.StoreInt32(&abm.stopping, 0)
+ abm.restoreSavedBrightness()
+
+ if abm.sensorClient != nil {
+ err := abm.sensorClient.ReleaseLight()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to release light sensor:", err)
+ }
+
+ err = abm.sensorClient.Disconnect()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to disconnect from sensor proxy:", err)
+ }
+ }
+
+ abm.running = false
+ abm.enabled = false
+ abm.mutex.Unlock()
+
+ logger.Info("[AutoBrightness] AutoBrightnessManager stopped successfully")
+ return nil
+}
+
+// Cleanup 清理资源
+func (abm *AutoBrightnessManager) Cleanup() error {
+ // Stop 会处理 running 状态下的完整清理(补偿定时器、亮度恢复、传感器释放与断开)
+ if err := abm.Stop(); err != nil {
+ logger.Warning("[AutoBrightness] Stop during cleanup failed:", err)
+ }
+
+ abm.mutex.Lock()
+ defer abm.mutex.Unlock()
+
+ // Stop 在 running=false 时不会断开连接,需确保 sensorClient 被释放
+ if abm.sensorClient != nil {
+ if err := abm.sensorClient.Disconnect(); err != nil {
+ logger.Warning("[AutoBrightness] Failed to disconnect sensor client:", err)
+ }
+ abm.sensorClient = nil
+ }
+
+ logger.Info("[AutoBrightness] AutoBrightnessManager cleaned up")
+ return nil
+}
+
+// SetEnabled 设置启用状态
+func (abm *AutoBrightnessManager) SetEnabled(enabled bool) error {
+ abm.mutex.Lock()
+ if !abm.supported {
+ abm.mutex.Unlock()
+ return errors.New("[AutoBrightness] Not supported")
+ }
+ // 更新配置
+ abm.config.Enabled = enabled
+ needStart := enabled && !abm.running
+ needStop := !enabled && abm.running
+ abm.mutex.Unlock()
+ // 保存配置
+ err := abm.saveConfig()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to save config:", err)
+ }
+ // 在锁外执行启动/停止操作
+ if needStart {
+ return abm.Start()
+ } else if needStop {
+ return abm.Stop()
+ }
+ return nil
+}
+
+// setSystemAdjusting 设置系统调整标志--用于节能模式或类似功能
+func (abm *AutoBrightnessManager) setSystemAdjusting(adjusting bool) {
+ abm.mutex.Lock()
+ defer abm.mutex.Unlock()
+ abm.systemAdjusting = adjusting
+ logger.Debugf("[AutoBrightness] System adjusting flag set to: %v", adjusting)
+}
+
+// OnManualBrightnessChange 处理手动亮度调节
+func (abm *AutoBrightnessManager) OnManualBrightnessChange() {
+ abm.mutex.Lock()
+ // 如果是系统自动调整(如节能模式),忽略此次调用
+ if abm.systemAdjusting {
+ logger.Debug("[AutoBrightness] Ignoring brightness change from system adjustment")
+ abm.mutex.Unlock()
+ return
+ }
+ if !abm.running {
+ logger.Info("[AutoBrightness] Manual brightness change")
+ abm.mutex.Unlock()
+ return
+ }
+ // 检查配置:手动调节是否禁用自动模式
+ if abm.config.ManualAdjustDisablesAutoMode {
+ abm.config.Enabled = false
+ manager := abm.manager
+ abm.mutex.Unlock()
+ logger.Info("[AutoBrightness] Manual brightness change detected, disabling auto brightness mode")
+ // 异步保存配置并停止功能
+ go func() {
+ err := abm.saveConfig()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to save config:", err)
+ }
+ // 停止自动亮度功能
+ err = abm.Stop()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to stop auto brightness:", err)
+ }
+ // 更新 Manager 属性
+ if manager != nil {
+ manager.setPropAutoBrightnessEnabled(false)
+ }
+ }()
+ return
+ }
+ abm.resetHistoryState()
+ abm.manualOverride = time.Now()
+ overrideDuration := abm.config.ManualOverrideDuration
+ sensorClient := abm.sensorClient
+ abm.mutex.Unlock()
+
+ logger.Infof("[AutoBrightness] Manual brightness change detected, pausing auto adjustment for %d seconds", overrideDuration)
+
+ if sensorClient != nil && sensorClient.IsClaimed() {
+ err := sensorClient.ReleaseLight()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to release light sensor on manual override:", err)
+ }
+ }
+}
+
+// resetHistoryState 重置历史状态,使下次能立即触发亮度调节
+// 注意:此函数假设调用者已经持有锁
+func (abm *AutoBrightnessManager) resetHistoryState() {
+ abm.lastLightLevel = -1
+ abm.lastBrightness = -1
+ abm.lastAdjustTime = time.Time{}
+ abm.lastSensorDataTime = time.Time{}
+ if abm.kalmanFilter != nil {
+ abm.kalmanFilter.Reset()
+ }
+}
+
+// hold 暂停自动亮度调节
+func (abm *AutoBrightnessManager) hold() {
+ abm.mutex.Lock()
+ defer abm.mutex.Unlock()
+ if !abm.running {
+ return
+ }
+
+ abm.stopCompensationTimer()
+}
+
+func (abm *AutoBrightnessManager) resume() {
+ abm.mutex.Lock()
+
+ if !abm.running {
+ abm.mutex.Unlock()
+ return
+ }
+
+ abm.resetHistoryState()
+ abm.startCompensationTimer()
+ abm.mutex.Unlock()
+}
+
+// OnConfigChanged 处理配置变更
+func (abm *AutoBrightnessManager) OnConfigChanged(config AutoBrightnessConfig) {
+ abm.mutex.Lock()
+ oldEnabled := abm.config.Enabled
+ oldSensitivity := abm.config.Sensitivity
+ abm.config = config
+
+ needStart := config.Enabled && !oldEnabled && !abm.running
+ needStop := !config.Enabled && oldEnabled && abm.running
+
+ sensitivityChanged := oldSensitivity != config.Sensitivity
+ needImmediateAdjust := sensitivityChanged && abm.running && config.Enabled
+
+ abm.applyKalmanFilterConfig()
+
+ abm.mutex.Unlock()
+
+ if needStart {
+ abm.Start()
+ } else if needStop {
+ abm.Stop()
+ }
+
+ if needImmediateAdjust {
+ logger.Infof("[AutoBrightness] Sensitivity changed from %.2f to %.2f, triggering immediate adjustment", oldSensitivity, config.Sensitivity)
+ go abm.adjustBrightnessOnce()
+ }
+
+ logger.Infof("[AutoBrightness] Config updated: enabled=%v, sensitivity=%.2f, threshold=%.1f",
+ config.Enabled, config.Sensitivity, config.ChangeThreshold)
+}
+
+// applyKalmanFilterConfig 应用卡尔曼滤波器配置
+func (abm *AutoBrightnessManager) applyKalmanFilterConfig() {
+ if abm.kalmanFilter == nil {
+ abm.kalmanFilter = brightness.NewAdaptiveKalmanFilter(
+ abm.config.KalmanProcessNoise,
+ abm.config.KalmanMeasurementNoise,
+ abm.config.KalmanWindowSize,
+ )
+ logger.Debug("[AutoBrightness] Kalman filter created with config params")
+ return
+ }
+
+ abm.kalmanFilter.SetProcessNoise(abm.config.KalmanProcessNoise)
+ abm.kalmanFilter.SetMeasurementNoise(abm.config.KalmanMeasurementNoise)
+ abm.kalmanFilter.SetWindowSize(abm.config.KalmanWindowSize)
+ logger.Debugf("[AutoBrightness] Kalman filter params updated: Q=%.4f, R=%.4f, window=%d",
+ abm.config.KalmanProcessNoise, abm.config.KalmanMeasurementNoise, abm.config.KalmanWindowSize)
+}
+
+// IsSupported 检查是否支持自动亮度
+func (abm *AutoBrightnessManager) IsSupported() bool {
+ abm.mutex.RLock()
+ defer abm.mutex.RUnlock()
+ return abm.supported
+}
+
+// IsEnabled 检查是否启用
+func (abm *AutoBrightnessManager) IsEnabled() bool {
+ abm.mutex.RLock()
+ defer abm.mutex.RUnlock()
+ return abm.enabled
+}
+
+// GetConfig 获取当前配置
+func (abm *AutoBrightnessManager) GetConfig() AutoBrightnessConfig {
+ abm.mutex.RLock()
+ defer abm.mutex.RUnlock()
+ return abm.config
+}
+
+// GetStatus 获取当前状态信息
+func (abm *AutoBrightnessManager) GetStatus() map[string]interface{} {
+ abm.mutex.RLock()
+ defer abm.mutex.RUnlock()
+ status := map[string]interface{}{
+ "supported": abm.supported,
+ "enabled": abm.enabled,
+ "running": abm.running,
+ "last_light_level": abm.lastLightLevel,
+ "last_brightness": abm.lastBrightness,
+ "manual_override": !abm.manualOverride.IsZero(),
+ "service_available": false,
+ "sensor_claimed": false,
+ }
+ if abm.sensorClient != nil {
+ status["service_available"] = abm.sensorClient.IsServiceAvailable()
+ status["sensor_claimed"] = abm.sensorClient.IsClaimed()
+ }
+ if !abm.manualOverride.IsZero() {
+ duration := time.Duration(abm.config.ManualOverrideDuration) * time.Second
+ remaining := duration - time.Since(abm.manualOverride)
+ if remaining > 0 {
+ status["manual_override_remaining"] = remaining.Seconds()
+ }
+ }
+ return status
+}
+
+// GetManualOverrideRemaining 获取手动调节暂停剩余时间(秒)
+func (abm *AutoBrightnessManager) GetManualOverrideRemaining() float64 {
+ abm.mutex.RLock()
+ defer abm.mutex.RUnlock()
+ if abm.manualOverride.IsZero() {
+ return 0
+ }
+ duration := time.Duration(abm.config.ManualOverrideDuration) * time.Second
+ elapsed := time.Since(abm.manualOverride)
+ remaining := duration - elapsed
+ if remaining <= 0 {
+ return 0
+ }
+ return remaining.Seconds()
+}
+
+// claimLightWithRetry 带重试机制的传感器声明
+func (abm *AutoBrightnessManager) claimLightWithRetry() error {
+ var lastErr error
+ for i := 0; i < abm.maxRetries; i++ {
+ err := abm.sensorClient.ClaimLight()
+ if err == nil {
+ return nil
+ }
+ lastErr = err
+ if i < abm.maxRetries-1 {
+ time.Sleep(abm.retryInterval)
+ }
+ }
+ return lastErr
+}
+
+// onServiceChange 服务状态变化回调
+func (abm *AutoBrightnessManager) onServiceChange(available bool) {
+ var shouldStart bool
+
+ abm.mutex.Lock()
+
+ if available {
+ logger.Info("[AutoBrightness] SensorProxy service became available")
+ if abm.sensorClient != nil {
+ hasLight, err := abm.sensorClient.HasAmbientLight()
+ if err == nil && hasLight {
+ abm.supported = true
+ shouldStart = abm.config.Enabled && !abm.running
+ }
+ }
+ } else {
+ logger.Warning("[AutoBrightness] SensorProxy service became unavailable")
+ if abm.running {
+ abm.stopCompensationTimer()
+ abm.running = false
+ abm.enabled = false
+ }
+ abm.supported = false
+ }
+
+ if abm.manager != nil {
+ abm.manager.setPropAutoBrightnessSupported(abm.supported)
+ }
+
+ abm.mutex.Unlock()
+
+ if shouldStart {
+ abm.Start()
+ }
+}
+
+// onLightLevelChange 光照值变化回调(推送模式)
+func (abm *AutoBrightnessManager) onLightLevelChange(rawLightLevel int) {
+ abm.mutex.Lock()
+ running := abm.running
+ inManualOverride := abm.isInManualOverride()
+ abm.lastSensorDataTime = time.Now()
+ abm.mutex.Unlock()
+
+ if !running || inManualOverride {
+ return
+ }
+
+ if abm.manager != nil && abm.manager.isPowerSaving() {
+ return
+ }
+
+ abm.processLightChange(rawLightLevel)
+}
+
+// adjustBrightnessOnce 立即执行一次亮度调整(用于配置变化时)
+func (abm *AutoBrightnessManager) adjustBrightnessOnce() {
+ abm.mutex.Lock()
+ if !abm.running || !abm.enabled {
+ abm.mutex.Unlock()
+ return
+ }
+
+ sensorClient := abm.sensorClient
+ abm.mutex.Unlock()
+
+ if sensorClient == nil {
+ return
+ }
+
+ rawLightLevel, err := sensorClient.GetCachedLightLevel()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to get light level for immediate adjust:", err)
+ return
+ }
+
+ abm.mutex.Lock()
+ savedLightLevel := abm.lastLightLevel
+ savedAdjustTime := abm.lastAdjustTime
+ abm.lastLightLevel = -1
+ abm.lastAdjustTime = time.Time{}
+ abm.mutex.Unlock()
+
+ abm.processLightChange(rawLightLevel)
+
+ abm.mutex.Lock()
+ if abm.lastLightLevel < 0 {
+ abm.lastLightLevel = savedLightLevel
+ }
+ if abm.lastAdjustTime.IsZero() {
+ abm.lastAdjustTime = savedAdjustTime
+ }
+ abm.mutex.Unlock()
+}
+
+// processLightChange 处理环境光变化(包括卡尔曼滤波和亮度调整)
+func (abm *AutoBrightnessManager) processLightChange(rawLightLevel int) {
+ abm.mutex.Lock()
+
+ if !abm.running {
+ abm.mutex.Unlock()
+ return
+ }
+
+ if abm.manager != nil && abm.manager.isPowerSaving() {
+ abm.mutex.Unlock()
+ return
+ }
+
+ // 应用卡尔曼滤波器处理原始光值
+ if abm.kalmanFilter == nil {
+ logger.Warning("[AutoBrightness] Kalman filter is nil, skipping light change processing")
+ abm.mutex.Unlock()
+ return
+ }
+
+ estimate := abm.kalmanFilter.Update(float64(rawLightLevel))
+ filteredLightLevel := int(estimate)
+ logger.Infof("[AutoBrightness] Kalman filter: raw=%d lux -> filtered=%d lux", rawLightLevel, filteredLightLevel)
+
+ // 计算目标亮度(使用滤波后的值)
+ targetBrightness := abm.calculateTargetBrightness(filteredLightLevel)
+
+ // 检查是否应该调节亮度(包含所有阈值和频率控制逻辑)
+ if !abm.shouldAdjustBrightness(filteredLightLevel, targetBrightness) {
+ abm.mutex.Unlock()
+ return
+ }
+
+ // 记录时间戳(在释放锁之前),用于频率控制
+ now := time.Now()
+
+ // 释放锁后再设置亮度(避免在渐变时持有锁)
+ abm.mutex.Unlock()
+
+ // 设置亮度(不重试,下次轮询会自动重试)
+ err := abm.setBrightness(targetBrightness)
+ if err != nil {
+ logger.Warningf("[AutoBrightness] Failed to set brightness (raw=%d, filtered=%d, target=%.1f%%): %v",
+ rawLightLevel, filteredLightLevel, targetBrightness*100, err)
+ return
+ }
+
+ // 设置成功后更新状态
+ abm.mutex.Lock()
+ abm.lastLightLevel = filteredLightLevel
+ abm.lastBrightness = targetBrightness
+ abm.lastAdjustTime = now
+ abm.mutex.Unlock()
+
+ logger.Infof("[AutoBrightness] Brightness adjusted: raw=%d lux -> filtered=%d lux -> brightness=%.1f%%",
+ rawLightLevel, filteredLightLevel, targetBrightness*100)
+}
+
+func (abm *AutoBrightnessManager) needCompensationWithClient(sensorClient *SensorProxyClient) (bool, int) {
+ if sensorClient == nil {
+ return false, 0
+ }
+
+ abm.mutex.RLock()
+ defer abm.mutex.RUnlock()
+
+ if abm.lastSensorDataTime.IsZero() {
+ return false, 0
+ }
+
+ if time.Since(abm.lastSensorDataTime) < sensorDataTimeout {
+ return false, 0
+ }
+
+ if abm.kalmanFilter == nil {
+ return false, 0
+ }
+
+ sensorValue, err := sensorClient.GetLightLevel()
+ if err != nil {
+ return false, 0
+ }
+
+ filterOutput := abm.kalmanFilter.GetEstimate()
+ diff := math.Abs(filterOutput - float64(sensorValue))
+
+ return diff > compensationThreshold, sensorValue
+}
+
+func (abm *AutoBrightnessManager) ensureSensorClaimed(sensorClient *SensorProxyClient) bool {
+ if sensorClient == nil {
+ return false
+ }
+ if !sensorClient.IsClaimed() {
+ err := sensorClient.ClaimLight()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to re-claim light sensor:", err)
+ return false
+ }
+ logger.Info("[AutoBrightness] Re-claimed light sensor after manual override period")
+ abm.mutex.Lock()
+ abm.resetHistoryState()
+ abm.mutex.Unlock()
+ return true
+ }
+ return false
+}
+
+func (abm *AutoBrightnessManager) compensationTick() {
+ abm.mutex.Lock()
+ if !abm.running {
+ abm.mutex.Unlock()
+ return
+ }
+ inManualOverride := abm.isInManualOverride()
+ sensorClient := abm.sensorClient
+ abm.mutex.Unlock()
+
+ if inManualOverride {
+ return
+ }
+
+ if abm.manager != nil && abm.manager.isPowerSaving() {
+ return
+ }
+
+ if sensorClient == nil {
+ return
+ }
+
+ justReclaimed := abm.ensureSensorClaimed(sensorClient)
+ if justReclaimed {
+ rawLightLevel, err := sensorClient.GetCachedLightLevel()
+ if err == nil {
+ logger.Infof("[AutoBrightness] Triggering adjustment after sensor reclaimed: %d lux", rawLightLevel)
+ abm.processLightChange(rawLightLevel)
+ return
+ }
+ }
+
+ needComp, sensorValue := abm.needCompensationWithClient(sensorClient)
+ if needComp {
+ logger.Infof("[AutoBrightness] Compensation: feeding sensor value %d lux", sensorValue)
+ abm.processLightChange(sensorValue)
+ }
+}
+
+func (abm *AutoBrightnessManager) startCompensationTimer() {
+ if abm.compensationTicker != nil {
+ return
+ }
+ abm.compensationTicker = time.NewTicker(compensationInterval)
+ abm.compensationStopCh = make(chan struct{})
+ abm.compensationWg.Add(1)
+
+ go func() {
+ defer abm.compensationWg.Done()
+ for {
+ select {
+ case <-abm.compensationTicker.C:
+ // 检查停止信号,避免在持有锁的情况下调用 compensationTick()
+ select {
+ case <-abm.compensationStopCh:
+ return
+ default:
+ abm.compensationTick()
+ }
+ case <-abm.compensationStopCh:
+ return
+ }
+ }
+ }()
+}
+
+// stopCompensationTimer 停止补偿定时器并等待补偿 goroutine 退出
+// 注意:调用者必须持有 abm.mutex 锁
+func (abm *AutoBrightnessManager) stopCompensationTimer() {
+ if abm.compensationTicker == nil {
+ return
+ }
+
+ // 先停止 ticker,防止新的 tick 事件
+ abm.compensationTicker.Stop()
+ abm.compensationTicker = nil
+
+ // 关闭停止通道,通知 goroutine 退出
+ if abm.compensationStopCh != nil {
+ close(abm.compensationStopCh)
+ abm.compensationStopCh = nil
+ }
+
+ // 等待 goroutine 退出
+ abm.compensationWg.Wait()
+}
+
+// isInManualOverride 检查是否在手动调节暂停期间
+// 注意:此函数需要读取 abm.manualOverride 和 abm.config,调用者应该持有至少读锁
+func (abm *AutoBrightnessManager) isInManualOverride() bool {
+ if abm.manualOverride.IsZero() {
+ return false
+ }
+ duration := time.Duration(abm.config.ManualOverrideDuration) * time.Second
+ return time.Since(abm.manualOverride) < duration
+}
+
+// calculateTargetBrightness 计算目标亮度
+func (abm *AutoBrightnessManager) calculateTargetBrightness(lightLevel int) float64 {
+ // 优先使用曲线配置
+ if brightness.HasAutoBrightnessCurve() {
+ br := brightness.GetAutoBrightnessValue(lightLevel)
+ if br >= 0 {
+ // 约束到有效范围
+ if br > 1.0 {
+ br = 1.0
+ }
+ // 设置最小亮度,避免屏幕过暗
+ if br < minBrightnessLimit {
+ br = minBrightnessLimit
+ }
+ return br
+ }
+ }
+
+ // 应用敏感度调整
+ adjustedLevel := float64(lightLevel) * abm.config.Sensitivity
+
+ // 简单线性映射
+ brightness := adjustedLevel / maxSensorLux
+
+ // 约束到有效范围
+ if brightness < 0.0 {
+ brightness = 0.0
+ } else if brightness > 1.0 {
+ brightness = 1.0
+ }
+
+ // 设置最小亮度,避免屏幕过暗
+ if brightness < minBrightnessLimit {
+ brightness = minBrightnessLimit
+ }
+ return brightness
+}
+
+// shouldAdjustBrightness 检查是否应该调节亮度(变化阈值和频率控制)
+func (abm *AutoBrightnessManager) shouldAdjustBrightness(lightLevel int, targetBrightness float64) bool {
+ now := time.Now()
+
+ // 检查环境光变化阈值
+ if abm.lastLightLevel >= 0 {
+ lightChange := math.Abs(float64(lightLevel - abm.lastLightLevel))
+ if lightChange < abm.config.ChangeThreshold {
+ return false
+ }
+ }
+
+ // 检查调节频率限制
+ if !abm.lastAdjustTime.IsZero() {
+ timeSinceLastAdjust := now.Sub(abm.lastAdjustTime)
+ minInterval := time.Duration(abm.config.PollingInterval) * time.Second
+ if timeSinceLastAdjust < minInterval {
+ return false
+ }
+ }
+
+ // 检查亮度变化是否足够大
+ if abm.lastBrightness >= 0 {
+ brightnessChange := math.Abs(targetBrightness - abm.lastBrightness)
+ if brightnessChange < abm.config.BrightnessChangeThreshold {
+ return false
+ }
+ }
+ return true
+}
+
+// setBrightness 设置亮度(自动调节专用,不触发手动调节检测)
+func (abm *AutoBrightnessManager) setBrightness(value float64) error {
+ if abm.manager == nil {
+ return errors.New("manager is nil")
+ }
+ // 使用内置显示器
+ builtinMonitor := abm.manager.getBuiltinMonitor()
+ if builtinMonitor == nil {
+ return errors.New("no builtin monitor")
+ }
+ // 根据配置决定是否使用渐变
+ abm.mutex.RLock()
+ useTransition := abm.config.UseTransition
+ abm.mutex.RUnlock()
+ var err error
+ if useTransition {
+ // 使用渐变效果(即使全局渐变功能关闭)
+ if abm.manager.transitionManager != nil {
+ // 使用 setBrightnessWithTransition 强制启用渐变,忽略全局 enabled 标志
+ err = abm.manager.setBrightnessWithTransition(builtinMonitor.Name, value)
+
+ abm.manager.syncPropBrightness()
+
+ if err == nil {
+ return nil
+ }
+ // 渐变失败,返回错误
+ logger.Warning("[AutoBrightness] Transition failed:", err)
+ return err
+ }
+ }
+ // 不强制使用渐变
+ err = abm.manager.setBrightness(builtinMonitor.Name, value)
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to set brightness:", err)
+ }
+ // 不论是否设置失败,均应当向外同步亮度
+ abm.manager.syncPropBrightness()
+ return err
+}
+
+// restoreSavedBrightness 恢复配置中保存的亮度
+func (abm *AutoBrightnessManager) restoreSavedBrightness() {
+ builtinMonitor := abm.manager.getBuiltinMonitor()
+ if builtinMonitor == nil {
+ return
+ }
+ // 从配置中获取保存的亮度
+ savedBrightness := abm.manager.getDefaultMonitorBrightness(builtinMonitor.Name)
+
+ err := abm.manager.setBrightnessAndSync(builtinMonitor.Name, savedBrightness)
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to restore brightness:", err)
+ return
+ }
+ logger.Infof("[AutoBrightness] Restored saved brightness: %.1f%%", savedBrightness*100)
+}
+
+// checkSensorAvailability 检查传感器是否可用(不连接)
+func (abm *AutoBrightnessManager) checkSensorAvailability() error {
+ // 临时连接以检查传感器
+ err := abm.sensorClient.Connect(abm.manager.sysSigLoop)
+ if err != nil {
+ return fmt.Errorf("failed to connect to sensor proxy: %w", err)
+ }
+ // 检查完成后立即断开连接
+ // 实际使用时会在Start()中重新连接
+ defer abm.sensorClient.Disconnect()
+ // 检查是否有环境光传感器
+ hasLight, err := abm.sensorClient.HasAmbientLight()
+ if err != nil {
+ return fmt.Errorf("failed to check ambient light sensor: %w", err)
+ }
+ if !hasLight {
+ return errors.New("no ambient light sensor available")
+ }
+ return nil
+}
+
+// 配置管理方法
+// Validate 验证配置参数的有效性
+func (config *AutoBrightnessConfig) Validate() error {
+ if config.Sensitivity < 0.1 {
+ return errors.New("sensitivity too small")
+ }
+ if config.PollingInterval < 1 {
+ return errors.New("polling interval too small")
+ }
+ if config.ChangeThreshold < 1.0 {
+ return errors.New("change threshold too small")
+ }
+ if config.ManualOverrideDuration < 1 {
+ return errors.New("manual override duration too small")
+ }
+ if config.KalmanProcessNoise <= 0 || config.KalmanProcessNoise > 100 {
+ return errors.New("kalman process noise must be positive and less than 100")
+ }
+ if config.KalmanMeasurementNoise <= 0 || config.KalmanMeasurementNoise > 100 {
+ return errors.New("kalman measurement noise must be positive and less than 100")
+ }
+ if config.KalmanWindowSize < 2 || config.KalmanWindowSize > 100 {
+ return errors.New("kalman window size must be between 2 and 100")
+ }
+ return nil
+}
+
+// initConfigManager 初始化配置管理器
+func (abm *AutoBrightnessManager) initConfigManager() error {
+ ds := configManager.NewConfigManager(abm.sysBus)
+ configPath, err := ds.AcquireManager(0, DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "")
+ if err != nil || configPath == "" {
+ return fmt.Errorf("failed to acquire config manager: %w", err)
+ }
+ abm.configManager, err = configManager.NewManager(abm.sysBus, configPath)
+ if err != nil {
+ return fmt.Errorf("failed to create config manager: %w", err)
+ }
+ // 监听配置变更
+ abm.configManager.InitSignalExt(abm.manager.sysSigLoop, true)
+ _, err = abm.configManager.ConnectValueChanged(func(key string) {
+ abm.onConfigFileChanged()
+ })
+ if err != nil {
+ return fmt.Errorf("failed to connect value changed: %w", err)
+ }
+ logger.Info("[AutoBrightness] Config manager initialized successfully")
+ return nil
+}
+
+// onConfigFileChanged 配置文件变更回调
+func (abm *AutoBrightnessManager) onConfigFileChanged() {
+ config, err := abm.getConfig()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Failed to reload config:", err)
+ return
+ }
+ logger.Info("[AutoBrightness] Config file changed, reloading configuration")
+ // 更新 Manager 的属性
+ abm.manager.setPropAutoBrightnessEnabled(config.Enabled)
+ // 通知配置变更
+ abm.OnConfigChanged(config)
+}
+
+// getConfig 获取自动亮度配置
+func (abm *AutoBrightnessManager) getConfig() (AutoBrightnessConfig, error) {
+ if abm.configManager == nil {
+ logger.Warning("[AutoBrightness] Config manager is nil, using default config")
+ return DefaultAutoBrightnessConfig, nil
+ }
+ var config AutoBrightnessConfig
+ // Enabled
+ if val, err := abm.configManager.Value(0, DSettingsKeyABEnabled); err == nil {
+ if b, ok := val.Value().(bool); ok {
+ config.Enabled = b
+ } else {
+ config.Enabled = DefaultAutoBrightnessConfig.Enabled
+ }
+ } else {
+ config.Enabled = DefaultAutoBrightnessConfig.Enabled
+ }
+ // Sensitivity
+ if val, err := abm.configManager.Value(0, DSettingsKeyABSensitivity); err == nil {
+ switch v := val.Value().(type) {
+ case float64:
+ config.Sensitivity = v
+ case int64:
+ config.Sensitivity = float64(v)
+ default:
+ logger.Warningf("[AutoBrightness] Invalid type for sensitivity: %T", v)
+ config.Sensitivity = DefaultAutoBrightnessConfig.Sensitivity
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default sensitivity")
+ config.Sensitivity = DefaultAutoBrightnessConfig.Sensitivity
+ }
+ // ChangeThreshold
+ if val, err := abm.configManager.Value(0, DSettingsKeyABChangeThreshold); err == nil {
+ switch v := val.Value().(type) {
+ case float64:
+ config.ChangeThreshold = v
+ case int64:
+ config.ChangeThreshold = float64(v)
+ default:
+ logger.Warningf("[AutoBrightness] Invalid type for changeThreshold: %T", v)
+ config.ChangeThreshold = DefaultAutoBrightnessConfig.ChangeThreshold
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default changeThreshold")
+ config.ChangeThreshold = DefaultAutoBrightnessConfig.ChangeThreshold
+ }
+
+ // BrightnessChangeThreshold
+ if val, err := abm.configManager.Value(0, DSettingsKeyABBrightnessChangeThreshold); err == nil {
+ switch v := val.Value().(type) {
+ case float64:
+ config.BrightnessChangeThreshold = v
+ case int64:
+ config.BrightnessChangeThreshold = float64(v)
+ default:
+ logger.Warningf("[AutoBrightness] Invalid type for brightnessChangeThreshold: %T", v)
+ config.BrightnessChangeThreshold = DefaultAutoBrightnessConfig.BrightnessChangeThreshold
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default brightnessChangeThreshold")
+ config.BrightnessChangeThreshold = DefaultAutoBrightnessConfig.BrightnessChangeThreshold
+ }
+
+ // PollingInterval
+ if val, err := abm.configManager.Value(0, DSettingsKeyABPollingInterval); err == nil {
+ switch v := val.Value().(type) {
+ case int64:
+ config.PollingInterval = int(v)
+ case float64:
+ config.PollingInterval = int(v)
+ default:
+ config.PollingInterval = DefaultAutoBrightnessConfig.PollingInterval
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default pollingInterval")
+ config.PollingInterval = DefaultAutoBrightnessConfig.PollingInterval
+ }
+ // ManualOverrideDuration
+ if val, err := abm.configManager.Value(0, DSettingsKeyABManualOverride); err == nil {
+ switch v := val.Value().(type) {
+ case int64:
+ config.ManualOverrideDuration = int(v)
+ case float64:
+ config.ManualOverrideDuration = int(v)
+ default:
+ config.ManualOverrideDuration = DefaultAutoBrightnessConfig.ManualOverrideDuration
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default manualOverrideDuration")
+ config.ManualOverrideDuration = DefaultAutoBrightnessConfig.ManualOverrideDuration
+ }
+ // ManualAdjustDisablesAutoMode
+ if val, err := abm.configManager.Value(0, DSettingsKeyABManualAdjustDisablesAutoMode); err == nil {
+ if b, ok := val.Value().(bool); ok {
+ config.ManualAdjustDisablesAutoMode = b
+ } else {
+ config.ManualAdjustDisablesAutoMode = DefaultAutoBrightnessConfig.ManualAdjustDisablesAutoMode
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default manualAdjustDisablesAutoMode")
+ config.ManualAdjustDisablesAutoMode = DefaultAutoBrightnessConfig.ManualAdjustDisablesAutoMode
+ }
+ // UseTransition
+ if val, err := abm.configManager.Value(0, DSettingsKeyABUseTransition); err == nil {
+ if b, ok := val.Value().(bool); ok {
+ config.UseTransition = b
+ } else {
+ config.UseTransition = DefaultAutoBrightnessConfig.UseTransition
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default useTransition")
+ config.UseTransition = DefaultAutoBrightnessConfig.UseTransition
+ }
+
+ // Curve
+ if val, err := abm.configManager.Value(0, DSettingsKeyABCurve); err == nil {
+ itemList, ok := val.Value().([]dbus.Variant)
+ if ok && len(itemList) > 0 {
+ var points []brightness.AutoBrightnessCurvePoint
+ for _, item := range itemList {
+ pointMap, ok := item.Value().(map[string]dbus.Variant)
+ if !ok {
+ continue
+ }
+ var point brightness.AutoBrightnessCurvePoint
+ if luxVal, ok := pointMap["lux"]; ok {
+ switch v := luxVal.Value().(type) {
+ case int64:
+ point.Lux = int(v)
+ case float64:
+ point.Lux = int(v)
+ }
+ }
+ if brVal, ok := pointMap["br"]; ok {
+ switch v := brVal.Value().(type) {
+ case int64:
+ point.Br = float64(v)
+ case float64:
+ point.Br = v
+ }
+ }
+ points = append(points, point)
+ }
+ brightness.SetAutoBrightnessCurveFromPoints(points)
+ } else {
+ logger.Debug("[AutoBrightness] Curve config is empty, using default linear mapping")
+ }
+ } else {
+ logger.Debug("[AutoBrightness] No curve config found, using default linear mapping")
+ }
+
+ // KalmanProcessNoise
+ if val, err := abm.configManager.Value(0, DSettingsKeyABKalmanProcessNoise); err == nil {
+ switch v := val.Value().(type) {
+ case float64:
+ config.KalmanProcessNoise = v
+ case int64:
+ config.KalmanProcessNoise = float64(v)
+ default:
+ logger.Warningf("[AutoBrightness] Invalid type for kalmanProcessNoise: %T", v)
+ config.KalmanProcessNoise = DefaultAutoBrightnessConfig.KalmanProcessNoise
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default kalmanProcessNoise")
+ config.KalmanProcessNoise = DefaultAutoBrightnessConfig.KalmanProcessNoise
+ }
+
+ // KalmanMeasurementNoise
+ if val, err := abm.configManager.Value(0, DSettingsKeyABKalmanMeasurementNoise); err == nil {
+ switch v := val.Value().(type) {
+ case float64:
+ config.KalmanMeasurementNoise = v
+ case int64:
+ config.KalmanMeasurementNoise = float64(v)
+ default:
+ logger.Warningf("[AutoBrightness] Invalid type for kalmanMeasurementNoise: %T", v)
+ config.KalmanMeasurementNoise = DefaultAutoBrightnessConfig.KalmanMeasurementNoise
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default kalmanMeasurementNoise")
+ config.KalmanMeasurementNoise = DefaultAutoBrightnessConfig.KalmanMeasurementNoise
+ }
+
+ // KalmanWindowSize
+ if val, err := abm.configManager.Value(0, DSettingsKeyABKalmanWindowSize); err == nil {
+ switch v := val.Value().(type) {
+ case int64:
+ config.KalmanWindowSize = int(v)
+ case float64:
+ config.KalmanWindowSize = int(v)
+ default:
+ config.KalmanWindowSize = DefaultAutoBrightnessConfig.KalmanWindowSize
+ }
+ } else {
+ logger.Warning("[AutoBrightness] Config convert failed, using default kalmanWindowSize")
+ config.KalmanWindowSize = DefaultAutoBrightnessConfig.KalmanWindowSize
+ }
+
+ // 验证配置有效性
+ logger.Debugf("[AutoBrightness] Apply config: %v", config)
+ err := config.Validate()
+ if err != nil {
+ logger.Warning("[AutoBrightness] Invalid config, using default:", err)
+ return DefaultAutoBrightnessConfig, nil
+ }
+ return config, nil
+}
+
+// saveConfig 保存自动亮度配置
+func (abm *AutoBrightnessManager) saveConfig() error {
+ if abm.configManager == nil {
+ return errors.New("config manager is nil")
+ }
+ // 在锁内复制配置,避免数据竞争
+ abm.mutex.RLock()
+ config := abm.config
+ abm.mutex.RUnlock()
+ // 验证配置有效性
+ err := config.Validate()
+ if err != nil {
+ return err
+ }
+ // 保存各个配置项
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABEnabled, dbus.MakeVariant(config.Enabled))
+ if err != nil {
+ return err
+ }
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABSensitivity, dbus.MakeVariant(config.Sensitivity))
+ if err != nil {
+ return err
+ }
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABChangeThreshold, dbus.MakeVariant(config.ChangeThreshold))
+ if err != nil {
+ return err
+ }
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABPollingInterval, dbus.MakeVariant(config.PollingInterval))
+ if err != nil {
+ return err
+ }
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABManualOverride, dbus.MakeVariant(config.ManualOverrideDuration))
+ if err != nil {
+ return err
+ }
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABManualAdjustDisablesAutoMode, dbus.MakeVariant(config.ManualAdjustDisablesAutoMode))
+ if err != nil {
+ return err
+ }
+ err = setGlobalDconfValue(DSettingsAutoBrightnessAppID, DSettingsAutoBrightnessName, "",
+ DSettingsKeyABUseTransition, dbus.MakeVariant(config.UseTransition))
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// https://gerrit.uniontech.com/plugins/gitiles/startdde/+/2295aa3ceaae739b0afb12c38b8c879f449631e7
+// https://gerrit.uniontech.com/plugins/gitiles/startdde/+/5ee59c1117d2d8dffc3e4d3e70009693e73ed576
+// https://gerrit.uniontech.com/plugins/gitiles/startdde/+/c99dee92d7bbec19a9140b77d1ffed8c9fa06106
+// https://gerrit.uniontech.com/plugins/gitiles/startdde/+/b2772f4cb6c7418a2f4e8307fcf4ae5ee6f81e2f
+// https://gerrit.uniontech.com/plugins/gitiles/startdde/+/020e8d3486d195b6f40a543141c216e164176b01
+// https://gerrit.uniontech.com/plugins/gitiles/startdde/+/7a546becfbf065a22534d44264760d2e18e1e248
diff --git a/display1/brightness.go b/display1/brightness.go
index 78c1dd8f4..14229ee56 100644
--- a/display1/brightness.go
+++ b/display1/brightness.go
@@ -1,11 +1,10 @@
-// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
package display1
import (
- "encoding/json"
"fmt"
"math"
"os"
@@ -45,6 +44,11 @@ func (m *Manager) saveBrightnessInCfg(valueMap map[string]float64) error {
}
if config.UUID == monitor.uuid {
+ other := monitors.GetByUuidAndName(config.UUID, config.Name)
+ if other != nil && other != monitor {
+ // 存在其他的名字和UUID都对应配置的显示器,不要改该配置
+ continue
+ }
config.Name = name
config.Brightness = v
}
@@ -110,28 +114,19 @@ func (m *Manager) changeBrightness(raised bool) error {
return nil
}
-func (m *Manager) getSavedBrightnessTable() (map[string]float64, error) {
- value := m.getBrightness()
- if value == "" {
- return nil, nil
- }
- var result map[string]float64
- err := json.Unmarshal([]byte(value), &result)
- if err != nil {
- return nil, err
- }
- return result, nil
-}
-
func (m *Manager) initBrightness() {
- brightnessTable, err := m.getSavedBrightnessTable()
- if err != nil {
- logger.Warning(err)
+ m.Brightness = make(map[string]float64)
+ monitors := m.getConnectedMonitors()
+ monitorsId := monitors.getMonitorsId()
+ configs := m.getSuitableSysMonitorConfigs(m.DisplayMode, monitorsId, monitors)
+ for _, config := range configs {
+ if config.Enabled {
+ m.Brightness[config.Name] = config.Brightness
+ }
}
- m.Brightness = brightnessTable
}
-func (m *Manager) getBrightnessSetter() int {
+func (m *Manager) getSetterConfig() int {
// NOTE: 特殊处理龙芯笔记本亮度设置问题
blDir := "/sys/class/backlight/loongson"
_, err := os.Stat(blDir)
@@ -181,21 +176,85 @@ func (m *Manager) isBuiltinMonitor(name string) bool {
return false
}
-func (m *Manager) setMonitorBrightness(monitor *Monitor, brightnessValue float64, temperature int) error {
- if !isValidColorTempValue(int32(temperature)) {
- temperature = defaultTemperatureManual
+func (m *Manager) setMonitorBrightness(monitor *Monitor, brightnessValue float64, forceTransition bool) error {
+ logger.Debug("setMonitorBrightness reality value:", brightnessValue)
+
+ // 使用统一过渡管理器
+ if m.transitionManager != nil {
+ return m.transitionManager.SetBrightness(monitor.Name, brightnessValue, forceTransition)
}
+ // 降级:直接设置亮度
+ setter := m.createBrightnessSetter(monitor)
+ if setter == nil {
+ return fmt.Errorf("failed to create brightness setter for monitor %s", monitor.Name)
+ }
+ return setter(brightnessValue)
+}
+
+func (m *Manager) createBrightnessSetter(monitor *Monitor) func(float64) error {
isBuiltin := m.isBuiltinMonitor(monitor.Name)
- err := brightness.Set(brightnessValue, temperature, m.getBrightnessSetter(), isBuiltin,
- monitor.ID, m.xConn)
- return err
+ _uuid := monitor.uuid
+ if _useWayland {
+ _uuid = monitor.uuidV0
+ }
+
+ // 获取当前色温值,用于 gamma 设置路径
+ temperature := m.getColorTemperatureValue()
+
+ setter := m.getSetterConfig()
+
+ var setterFunc func(float64) error
+
+ switch setter {
+ case brightness.BrightnessSetterBacklight:
+ setterFunc = func(brightnessValue float64) error {
+ return brightness.SetBacklight(brightnessValue)
+ }
+ case brightness.BrightnessSetterAuto:
+ if isBuiltin && brightness.SupportBacklight() {
+ setterFunc = func(brightnessValue float64) error {
+ return brightness.SetBacklight(brightnessValue)
+ }
+ } else {
+ setterFunc = func(brightnessValue float64) error {
+ return brightness.SetOutputGama(brightnessValue, temperature, monitor.ID, m.xConn, _uuid)
+ }
+ }
+ default: // BrightnessSetterGamma
+ setterFunc = func(brightnessValue float64) error {
+ return brightness.SetOutputGama(brightnessValue, temperature, monitor.ID, m.xConn, _uuid)
+ }
+ }
+
+ return setterFunc
}
-func (m *Manager) setBrightnessAux(fake bool, name string, value float64) error {
+// setColorTemperature 设置色温(通过 gamma)
+func (m *Manager) setColorTemperature(monitor *Monitor, brightnessVal float64) error {
+ temperature := m.getColorTemperatureValue()
+ logger.Debug("setColorTemperature", monitor.Name, temperature)
+
+ isBuiltin := m.isBuiltinMonitor(monitor.Name)
+ _uuid := monitor.uuid
+ if _useWayland {
+ _uuid = monitor.uuidV0
+ }
+
+ // 内建显示器使用背光时,色温通过 gamma 设置(亮度为1)
+ if isBuiltin && brightness.SupportBacklight() {
+ brightnessVal = 1
+ }
+
+ return brightness.SetOutputGama(brightnessVal, temperature, monitor.ID, m.xConn, _uuid)
+}
+
+func (m *Manager) setBrightness(name string, value float64) error {
+ logger.Debug("Starting brightness setting", name, value)
monitors := m.getConnectedMonitors()
monitor := monitors.GetByName(name)
if monitor == nil {
+ logger.Debug("Monitor not found:", name)
return InvalidOutputNameError{Name: name}
}
@@ -204,13 +263,15 @@ func (m *Manager) setBrightnessAux(fake bool, name string, value float64) error
monitor.PropsMu.RUnlock()
value = math.Round(value*1000) / 1000 // 通过该方法,用来对亮度值(亮度值范围为0-1)四舍五入保留小数点后三位有效数字
- if !fake && enabled {
- temperature := m.getColorTemperatureValue()
+ if enabled {
// 保持最小亮度,不能全黑
if value <= 0.1 {
value = 0.1
+ } else if value > 1 {
+ value = 1
}
- err := m.setMonitorBrightness(monitor, value, temperature)
+
+ err := m.setMonitorBrightness(monitor, value, false)
if err != nil {
logger.Warningf("failed to set brightness for %s: %v", name, err)
return err
@@ -219,11 +280,9 @@ func (m *Manager) setBrightnessAux(fake bool, name string, value float64) error
monitor.setPropBrightnessWithLock(value)
- return nil
-}
+ logger.Debug("end set brightness", name, value)
-func (m *Manager) setBrightness(name string, value float64) error {
- return m.setBrightnessAux(false, name, value)
+ return nil
}
func (m *Manager) setBrightnessAndSync(name string, value float64) error {
@@ -233,3 +292,50 @@ func (m *Manager) setBrightnessAndSync(name string, value float64) error {
}
return err
}
+
+// setBrightnessWithTransition 使用渐变效果设置亮度(强制启用渐变)
+func (m *Manager) setBrightnessWithTransition(name string, value float64) error {
+ logger.Debug("Starting brightness setting with transition", name, value)
+ monitors := m.getConnectedMonitors()
+ monitor := monitors.GetByName(name)
+ if monitor == nil {
+ logger.Debug("Monitor not found:", name)
+ return InvalidOutputNameError{Name: name}
+ }
+
+ monitor.PropsMu.RLock()
+ enabled := monitor.Enabled
+ monitor.PropsMu.RUnlock()
+
+ value = math.Round(value*1000) / 1000
+ if enabled {
+ if value <= 0.1 {
+ value = 0.1
+ } else if value > 1 {
+ value = 1
+ }
+
+ err := m.setMonitorBrightness(monitor, value, true)
+ if err != nil {
+ logger.Warningf("failed to set brightness for %s: %v", name, err)
+ return err
+ }
+ }
+
+ monitor.setPropBrightnessWithLock(value)
+
+ logger.Debug("end set brightness with transition", name, value)
+
+ return nil
+}
+
+// getDefaultMonitorBrightness 获取默认显示器亮度(带 fallback 逻辑)
+func (m *Manager) getDefaultMonitorBrightness(name string) float64 {
+ if v, ok := m.Brightness[name]; ok {
+ return v
+ }
+ if v, ok := m.Brightness["default"]; ok {
+ return v
+ }
+ return 1
+}
diff --git a/display1/brightness/brightness.go b/display1/brightness/brightness.go
index a2368f385..2618a9bc5 100644
--- a/display1/brightness/brightness.go
+++ b/display1/brightness/brightness.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -7,12 +7,12 @@ package brightness
import (
"fmt"
"math"
+ "sync"
"github.com/godbus/dbus/v5"
backlight "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.backlighthelper1"
displayBl "github.com/linuxdeepin/go-lib/backlight/display"
"github.com/linuxdeepin/go-lib/log"
- "github.com/linuxdeepin/go-lib/multierr"
x "github.com/linuxdeepin/go-x11-client"
"github.com/linuxdeepin/go-x11-client/ext/randr"
)
@@ -42,52 +42,18 @@ func InitBacklightHelper() {
helper = backlight.NewBacklight(sysBus)
}
-func Set(brightness float64, temperature int, setter int, isBuiltin bool, outputId uint32, conn *x.Conn) error {
- if brightness < 0 {
- brightness = 0
- } else if brightness > 1 {
- brightness = 1
- }
-
+// SetOutputGama 设置 Gamma 亮度和色温
+func SetOutputGama(brightness float64, temperature int, outputId uint32, conn *x.Conn, uuid string) error {
output := randr.Output(outputId)
+ return setOutputCrtcGamma(gammaSetting{
+ brightness: brightness,
+ temperature: temperature,
+ }, output, conn)
+}
- // 亮度和色温分开设置,亮度用背光,色温用 gamma
- setBlGamma := func() error {
- var errs error
- err := setBacklight(brightness, output, conn)
- if err != nil {
- errs = multierr.Append(errs, err)
- }
-
- err = setOutputCrtcGamma(gammaSetting{
- brightness: 1,
- temperature: temperature,
- }, output, conn)
- if err != nil {
- errs = multierr.Append(errs, err)
- }
- return errs
- }
-
- // 亮度和色温都用 gamma 值设置
- setGamma := func() error {
- return setOutputCrtcGamma(gammaSetting{
- brightness: brightness,
- temperature: temperature,
- }, output, conn)
- }
-
- setFn := setGamma
- switch setter {
- case BrightnessSetterBacklight:
- setFn = setBlGamma
- case BrightnessSetterAuto:
- if isBuiltin && supportBacklight() {
- setFn = setBlGamma
- }
- //case BrightnessSetterGamma
- }
- return setFn()
+// SupportBacklight 检查是否支持背光调节
+func SupportBacklight() bool {
+ return supportBacklight()
}
// unused function
@@ -206,20 +172,86 @@ func init() {
}
}
-func setBacklight(value float64, output randr.Output, conn *x.Conn) error {
+func _setBacklight(value float64, controller *displayBl.Controller) error {
+ br := int32(float64(controller.MaxBrightness) * value)
+
+ v, ok := GetBacklightCurveValue(value, controller)
+ if ok {
+ logger.Debugf("Brightness curve value: %v", v)
+ br = v
+ }
+
+ const backlightTypeDisplay = 1
+ fmt.Printf("help set brightness %q max %v value %v br %v\n",
+ controller.Name, controller.MaxBrightness, value, br)
+ return helper.SetBrightness(0, backlightTypeDisplay, controller.Name, br)
+}
+
+// backlightControllerCache 背光控制器缓存
+var (
+ cachedBacklightControllers displayBl.Controllers
+ backlightControllersMu sync.RWMutex
+)
+
+// getBacklightControllers 获取缓存的背光控制器列表
+func getBacklightControllers() displayBl.Controllers {
+ backlightControllersMu.RLock()
+ if len(cachedBacklightControllers) > 0 {
+ defer backlightControllersMu.RUnlock()
+ return cachedBacklightControllers
+ }
+ backlightControllersMu.RUnlock()
+
+ backlightControllersMu.Lock()
+ defer backlightControllersMu.Unlock()
+
+ // 双重检查
+ if len(cachedBacklightControllers) > 0 {
+ return cachedBacklightControllers
+ }
+
+ var err error
+ cachedBacklightControllers, err = displayBl.List()
+ if err != nil {
+ logger.Warningf("Failed to list backlight controllers: %v", err)
+ return nil
+ }
+ return cachedBacklightControllers
+}
+
+// SetBacklight 设置背光亮度(供 TransitionManager 回调使用)
+func SetBacklight(brightness float64) error {
+ controllers := getBacklightControllers()
+ if len(controllers) == 0 {
+ return fmt.Errorf("no backlight controllers available")
+ }
+
for _, controller := range controllers {
- err := _setBacklight(value, controller)
+ err := _setBacklight(brightness, controller)
if err != nil {
- fmt.Printf("WARN: failed to set backlight %s: %v", controller.Name, err)
+ logger.Warningf("Failed to set backlight %s: %v", controller.Name, err)
}
}
return nil
}
-func _setBacklight(value float64, controller *displayBl.Controller) error {
- br := int32(float64(controller.MaxBrightness) * value)
- const backlightTypeDisplay = 1
- fmt.Printf("help set brightness %q max %v value %v br %v\n",
- controller.Name, controller.MaxBrightness, value, br)
- return helper.SetBrightness(0, backlightTypeDisplay, controller.Name, br)
+// GetBacklightCurrentValue 获取当前背光亮度百分比 (0.0 - 1.0)
+func GetBacklightCurrentValue() (float64, error) {
+ controllers := getBacklightControllers()
+ if len(controllers) == 0 {
+ return 0.5, fmt.Errorf("no backlight controllers available")
+ }
+
+ // 使用第一个控制器的当前亮度
+ controller := controllers[0]
+ currentBrightness, err := controller.GetActualBrightness()
+ if err != nil {
+ return 0.5, fmt.Errorf("failed to get current brightness: %v", err)
+ }
+
+ if controller.MaxBrightness <= 0 {
+ return 0.5, fmt.Errorf("invalid max brightness: %d", controller.MaxBrightness)
+ }
+
+ return float64(currentBrightness) / float64(controller.MaxBrightness), nil
}
diff --git a/display1/brightness/brightness_transition.go b/display1/brightness/brightness_transition.go
new file mode 100644
index 000000000..452ccacab
--- /dev/null
+++ b/display1/brightness/brightness_transition.go
@@ -0,0 +1,597 @@
+// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package brightness
+
+import (
+ "context"
+ "fmt"
+ "sync"
+ "time"
+)
+
+// BrightnessType 亮度类型
+type BrightnessType int
+
+const (
+ // TypeUnknown 未知类型
+ TypeUnknown BrightnessType = iota
+ // TypeBacklight 背光调节
+ TypeBacklight
+ // TypeGamma Gamma 调节
+ TypeGamma
+)
+
+const (
+ // DefaultMinStepInterval 默认最小步进间隔
+ DefaultMinStepInterval = 100 * time.Millisecond
+
+ // epsilon 用于浮点数比较的极小值
+ epsilon = 1e-6
+)
+
+// TransitionConfig 统一过渡配置
+type TransitionConfig struct {
+ // 是否启用过渡
+ Enabled bool
+
+ // 总调节时长(毫秒)
+ Duration time.Duration
+
+ // 步进百分比(如 0.1 表示 0.1%)
+ StepPercent float64
+
+ // 最小步进间隔(默认100ms)
+ MinStepInterval time.Duration
+}
+
+// DefaultTransitionConfig 默认过渡配置
+func DefaultTransitionConfig() TransitionConfig {
+ return TransitionConfig{
+ Enabled: true,
+ Duration: 4000 * time.Millisecond,
+ StepPercent: 1, // 1% 步进
+ MinStepInterval: DefaultMinStepInterval, // 默认 100ms
+ }
+}
+
+// Validate 验证配置有效性
+func (c *TransitionConfig) Validate() error {
+ if c.Duration <= 0 {
+ c.Duration = 4000 * time.Millisecond
+ }
+
+ if c.StepPercent <= 0 {
+ c.StepPercent = 1
+ }
+
+ // 确保最小步进间隔不小于默认值
+ if c.MinStepInterval < 0 {
+ c.MinStepInterval = DefaultMinStepInterval
+ }
+
+ return nil
+}
+
+// transitionTask 过渡任务
+type transitionTask struct {
+ ctx context.Context
+ cancel context.CancelFunc
+ target float64
+ startValue float64
+ config TransitionConfig
+}
+
+// TransitionExecutor 统一过渡执行器
+type TransitionExecutor struct {
+ mu sync.Mutex
+
+ // 显示器名称
+ monitorName string
+
+ // 亮度类型
+ brightnessType BrightnessType
+
+ // 当前正在进行的调节任务
+ currentTask *transitionTask
+
+ // 当前实时亮度百分比
+ currentPercent float64
+ hasCurrentPercent bool
+
+ // 亮度设置函数(百分比)
+ setterFunc func(percent float64) error
+
+ // 获取当前亮度百分比函数
+ getterFunc func() (float64, error)
+
+ // 配置参数
+ config TransitionConfig
+
+ // goroutine 追踪
+ wg sync.WaitGroup
+}
+
+// NewTransitionExecutor 创建新的过渡执行器
+func NewTransitionExecutor(monitorName string, brightnessType BrightnessType, setter func(float64) error, getter func() (float64, error), config TransitionConfig) *TransitionExecutor {
+ config.Validate()
+
+ return &TransitionExecutor{
+ monitorName: monitorName,
+ brightnessType: brightnessType,
+ setterFunc: setter,
+ getterFunc: getter,
+ config: config,
+ }
+}
+
+// SetBrightness 设置亮度(百分比,0.0 - 1.0)
+func (e *TransitionExecutor) SetBrightness(targetPercent float64) error {
+ return e.SetBrightnessWithForce(targetPercent, false)
+}
+
+// SetBrightnessWithForce 设置亮度,支持强制过渡
+func (e *TransitionExecutor) SetBrightnessWithForce(targetPercent float64, forceTransition bool) error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ // 如果过渡未启用且不是强制过渡,直接设置
+ if !e.config.Enabled && !forceTransition {
+ return e.setterFunc(targetPercent)
+ }
+
+ // 获取当前亮度百分比
+ var currentPercent float64
+ var err error
+
+ // 如果正在过渡,使用实时值
+ if e.hasCurrentPercent && e.currentTask != nil {
+ currentPercent = e.currentPercent
+ } else {
+ currentPercent, err = e.getterFunc()
+ if err != nil {
+ logger.Warningf("[%s] Failed to get current brightness: %v", e.monitorName, err)
+ return e.setterFunc(targetPercent)
+ }
+ }
+
+ // 如果目标值等于当前值,不需要调节
+ if abs(currentPercent-targetPercent) < epsilon {
+ return nil
+ }
+
+ // 取消当前正在进行的任务
+ if e.currentTask != nil {
+ e.cancelTask(e.currentTask)
+ e.currentTask = nil
+ }
+
+ // 创建新的调节任务
+ ctx, cancel := e.createContext()
+ task := &transitionTask{
+ ctx: ctx,
+ cancel: cancel,
+ target: targetPercent,
+ startValue: currentPercent,
+ config: e.config,
+ }
+
+ e.currentTask = task
+ e.currentPercent = currentPercent
+ e.hasCurrentPercent = true
+
+ // 启动过渡协程
+ e.wg.Add(1)
+ go func() {
+ defer e.wg.Done()
+ e.runTransition(task)
+ }()
+
+ return nil
+}
+
+// createContext 创建上下文
+func (e *TransitionExecutor) createContext() (context.Context, context.CancelFunc) {
+ return context.WithCancel(context.Background())
+}
+
+// cancelTask 取消任务
+func (e *TransitionExecutor) cancelTask(task *transitionTask) {
+ if task != nil && task.cancel != nil {
+ task.cancel()
+ }
+}
+
+// isTaskCancelled 检查任务是否被取消
+func (e *TransitionExecutor) isTaskCancelled(ctx context.Context) bool {
+ select {
+ case <-ctx.Done():
+ return true
+ default:
+ return false
+ }
+}
+
+// abs 计算绝对值
+func abs(x float64) float64 {
+ if x < 0 {
+ return -x
+ }
+ return x
+}
+
+// runTransition 执行过渡
+func (e *TransitionExecutor) runTransition(task *transitionTask) {
+ defer func() {
+ e.mu.Lock()
+ if e.currentTask == task {
+ e.currentTask = nil
+ }
+ e.mu.Unlock()
+ }()
+
+ // 如果差值很小,直接设置目标值
+ if abs(task.target-task.startValue) < epsilon {
+ err := e.setterFunc(task.target)
+ if err != nil {
+ logger.Warningf("[%s] Failed to set brightness %.2f%%: %v", e.monitorName, task.target*100, err)
+ }
+ e.mu.Lock()
+ e.currentPercent = task.target
+ e.hasCurrentPercent = false
+ e.mu.Unlock()
+ return
+ }
+
+ // 计算过渡参数
+ totalSteps, stepSize, stepInterval := e.calculateTransitionParams(task)
+
+ if totalSteps == 0 {
+ return
+ }
+
+ logger.Debugf("[%s] Start %s transition: %.2f%% -> %.2f%%, steps: %d, step size: %.4f%%, interval: %v",
+ e.monitorName, e.typeName(), task.startValue*100, task.target*100, totalSteps, stepSize*100, stepInterval)
+
+ // 执行等间隔过渡
+ currentPercent := task.startValue
+ ticker := time.NewTicker(stepInterval)
+ defer ticker.Stop()
+
+ for step := 1; step <= totalSteps; step++ {
+ // 检查是否被取消
+ if e.isTaskCancelled(task.ctx) {
+ logger.Debugf("[%s] Transition cancelled at %.2f%%", e.monitorName, currentPercent*100)
+ return
+ }
+
+ // 计算当前步骤的目标百分比
+ currentPercent += stepSize
+
+ // 确保不超过最终目标值
+ if (stepSize > 0 && currentPercent > task.target) || (stepSize < 0 && currentPercent < task.target) {
+ currentPercent = task.target
+ }
+
+ // 设置亮度
+ err := e.setterFunc(currentPercent)
+ if err != nil {
+ logger.Warningf("[%s] Failed to set brightness %.2f%%: %v", e.monitorName, currentPercent*100, err)
+ return
+ }
+
+ // 更新实时值
+ e.mu.Lock()
+ e.currentPercent = currentPercent
+ e.mu.Unlock()
+
+ // 如果达到目标值,结束过渡
+ if abs(currentPercent-task.target) < epsilon {
+ logger.Debugf("[%s] Transition completed: %.2f%% -> %.2f%%", e.monitorName, task.startValue*100, task.target*100)
+ e.mu.Lock()
+ e.hasCurrentPercent = false
+ e.mu.Unlock()
+ return
+ }
+
+ // 等待下一个步进时间
+ if step < totalSteps {
+ select {
+ case <-task.ctx.Done():
+ logger.Debugf("[%s] Transition cancelled at %.2f%%", e.monitorName, currentPercent*100)
+ return
+ case <-ticker.C:
+ // 继续下一步
+ }
+ }
+ }
+
+ logger.Debugf("[%s] Transition completed: %.2f%% -> %.2f%%", e.monitorName, task.startValue*100, task.target*100)
+ e.mu.Lock()
+ e.hasCurrentPercent = false
+ e.mu.Unlock()
+}
+
+// calculateTransitionParams 计算过渡参数
+func (e *TransitionExecutor) calculateTransitionParams(task *transitionTask) (totalSteps int, stepSize float64, stepInterval time.Duration) {
+ deltaPercent := task.target - task.startValue
+ if abs(deltaPercent) < epsilon {
+ return 0, 0, 0
+ }
+
+ // 计算总步数:总百分比变化 / 步进百分比
+ // 例如:变化 50%,步进 0.1%,则步数 = 50 / 0.1 = 500 步
+ totalSteps = int(abs(deltaPercent*100) / task.config.StepPercent)
+ if totalSteps <= 0 {
+ totalSteps = 1
+ }
+
+ // 计算每步的百分比变化
+ stepSize = deltaPercent / float64(totalSteps)
+
+ // 计算每步的时间间隔
+ stepInterval = task.config.Duration / time.Duration(totalSteps)
+
+ // 确保间隔不小于最小间隔
+ // 如果间隔太小,则根据最小间隔重新计算步数
+ if stepInterval < task.config.MinStepInterval {
+ totalSteps = int(task.config.Duration / task.config.MinStepInterval)
+ if totalSteps <= 0 {
+ totalSteps = 1
+ }
+ stepSize = deltaPercent / float64(totalSteps)
+ stepInterval = task.config.MinStepInterval
+ }
+ return totalSteps, stepSize, stepInterval
+}
+
+// UpdateConfig 更新配置
+func (e *TransitionExecutor) UpdateConfig(config TransitionConfig) {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ if e.currentTask != nil && (e.config.Duration != config.Duration) {
+ e.cancelTask(e.currentTask)
+ e.currentTask = nil
+ logger.Debugf("[%s] Config updated, cancelled current transition", e.monitorName)
+ }
+
+ config.Validate()
+ e.config = config
+}
+
+// Stop 停止当前过渡并等待 goroutine 退出
+func (e *TransitionExecutor) Stop() {
+ e.mu.Lock()
+ if e.currentTask != nil {
+ e.cancelTask(e.currentTask)
+ e.currentTask = nil
+ }
+ e.hasCurrentPercent = false
+ e.mu.Unlock()
+
+ // 等待 goroutine 退出(需要在锁外等待,否则死锁)
+ e.wg.Wait()
+}
+
+// IsRunning 检查是否正在执行过渡
+func (e *TransitionExecutor) IsRunning() bool {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ return e.currentTask != nil
+}
+
+// typeName 获取类型名称
+func (e *TransitionExecutor) typeName() string {
+ switch e.brightnessType {
+ case TypeBacklight:
+ return "Backlight"
+ case TypeGamma:
+ return "Gamma"
+ default:
+ return "Unknown"
+ }
+}
+
+// TransitionManager 统一过渡管理器
+// 按显示器名称管理所有显示器的亮度过渡
+type TransitionManager struct {
+ mu sync.Mutex
+
+ // 执行器映射(按显示器名称)
+ executors map[string]*TransitionExecutor
+
+ // 配置
+ config TransitionConfig
+
+ // 获取显示器亮度类型的回调
+ getBrightnessTypeFunc func(monitorName string) BrightnessType
+
+ // 设置 Backlight 亮度的回调(百分比)
+ setBacklightFunc func(percent float64) error
+
+ // 获取 Backlight 当前亮度的回调(百分比)
+ getBacklightFunc func() (float64, error)
+
+ // 设置 Gamma 亮度的回调
+ setGammaFunc func(monitorName string, percent float64) error
+
+ // 获取 Gamma 当前亮度的回调
+ getGammaFunc func(monitorName string) (float64, error)
+}
+
+// NewTransitionManager 创建统一过渡管理器
+func NewTransitionManager() *TransitionManager {
+ return &TransitionManager{
+ executors: make(map[string]*TransitionExecutor),
+ config: DefaultTransitionConfig(),
+ }
+}
+
+// SetEnabled 设置是否启用过渡
+func (m *TransitionManager) SetEnabled(enabled bool) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.config.Enabled = enabled
+ for _, executor := range m.executors {
+ executor.UpdateConfig(m.config)
+ }
+}
+
+// SetDuration 设置过渡时长(毫秒)
+func (m *TransitionManager) SetDuration(durationMs int) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.config.Duration = time.Duration(durationMs) * time.Millisecond
+ for _, executor := range m.executors {
+ executor.UpdateConfig(m.config)
+ }
+}
+
+// SetStepPercent 设置步进百分比
+func (m *TransitionManager) SetStepPercent(stepPercent float64) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.config.StepPercent = stepPercent
+ for _, executor := range m.executors {
+ executor.UpdateConfig(m.config)
+ }
+}
+
+// SetMinStepInterval 设置最小步进间隔(毫秒)
+func (m *TransitionManager) SetMinStepInterval(intervalMs int) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.config.MinStepInterval = time.Duration(intervalMs) * time.Millisecond
+ if m.config.MinStepInterval < 0 {
+ m.config.MinStepInterval = DefaultMinStepInterval
+ }
+ for _, executor := range m.executors {
+ executor.UpdateConfig(m.config)
+ }
+}
+
+// SetGetBrightnessTypeFunc 设置获取亮度类型的回调
+func (m *TransitionManager) SetGetBrightnessTypeFunc(fn func(monitorName string) BrightnessType) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.getBrightnessTypeFunc = fn
+}
+
+// SetBacklightFuncs 设置 Backlight 回调
+func (m *TransitionManager) SetBacklightFuncs(
+ setFunc func(percent float64) error,
+ getFunc func() (float64, error),
+) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.setBacklightFunc = setFunc
+ m.getBacklightFunc = getFunc
+}
+
+// SetGammaFuncs 设置 Gamma 回调
+func (m *TransitionManager) SetGammaFuncs(
+ setFunc func(monitorName string, percent float64) error,
+ getFunc func(monitorName string) (float64, error),
+) {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ m.setGammaFunc = setFunc
+ m.getGammaFunc = getFunc
+}
+
+// SetBrightness 设置指定显示器的亮度(百分比,0.0 - 1.0)
+func (m *TransitionManager) SetBrightness(monitorName string, targetPercent float64, forceTransition bool) error {
+ m.mu.Lock()
+ executor, exists := m.executors[monitorName]
+ config := m.config
+ getBrightnessType := m.getBrightnessTypeFunc
+ m.mu.Unlock()
+
+ // 如果执行器不存在,创建新的
+ if !exists {
+ var brightnessType BrightnessType = TypeGamma // 默认 Gamma
+ if getBrightnessType != nil {
+ brightnessType = getBrightnessType(monitorName)
+ }
+
+ executor = m.createExecutor(monitorName, brightnessType, config)
+ if executor == nil {
+ return fmt.Errorf("failed to create executor for monitor %s", monitorName)
+ }
+
+ m.mu.Lock()
+ m.executors[monitorName] = executor
+ m.mu.Unlock()
+ }
+
+ return executor.SetBrightnessWithForce(targetPercent, forceTransition)
+}
+
+// createExecutor 创建执行器
+func (m *TransitionManager) createExecutor(monitorName string, brightnessType BrightnessType, config TransitionConfig) *TransitionExecutor {
+ m.mu.Lock()
+ setBacklight := m.setBacklightFunc
+ getBacklight := m.getBacklightFunc
+ setGamma := m.setGammaFunc
+ getGamma := m.getGammaFunc
+ m.mu.Unlock()
+
+ switch brightnessType {
+ case TypeBacklight:
+ setter := func(percent float64) error {
+ if setBacklight == nil {
+ return fmt.Errorf("backlight setter not configured")
+ }
+ return setBacklight(percent)
+ }
+
+ getter := func() (float64, error) {
+ if getBacklight == nil {
+ return 0.5, fmt.Errorf("backlight getter not configured")
+ }
+ return getBacklight()
+ }
+
+ return NewTransitionExecutor(monitorName, TypeBacklight, setter, getter, config)
+
+ default: // TypeGamma
+ setter := func(percent float64) error {
+ if setGamma != nil {
+ return setGamma(monitorName, percent)
+ }
+ return fmt.Errorf("gamma setter not configured")
+ }
+
+ getter := func() (float64, error) {
+ if getGamma == nil {
+ return 0.5, fmt.Errorf("gamma getter not configured")
+ }
+ return getGamma(monitorName)
+ }
+
+ return NewTransitionExecutor(monitorName, TypeGamma, setter, getter, config)
+ }
+}
+
+// Stop 停止所有过渡
+func (m *TransitionManager) Stop() {
+ m.mu.Lock()
+ executors := make([]*TransitionExecutor, 0, len(m.executors))
+ for _, executor := range m.executors {
+ executors = append(executors, executor)
+ }
+ m.mu.Unlock()
+
+ for _, executor := range executors {
+ executor.Stop()
+ }
+}
+
+// GetConfig 获取当前配置
+func (m *TransitionManager) GetConfig() TransitionConfig {
+ m.mu.Lock()
+ defer m.mu.Unlock()
+ return m.config
+}
diff --git a/display1/brightness/colortemp.go b/display1/brightness/colortemp.go
index 00909b5a5..6a0be3cc2 100644
--- a/display1/brightness/colortemp.go
+++ b/display1/brightness/colortemp.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -271,8 +271,17 @@ type gammaSetting struct {
}
func fillColorRamp(gammaR, gammaG, gammaB []uint16, setting gammaSetting) {
- alpha := float64(setting.temperature%100) / 100.0
- tempIndex := ((setting.temperature - 1000) / 100) * 3
+ temperature := setting.temperature
+ if temperature < 1000 {
+ temperature = 1000
+ }
+ maxTemp := 1000 + (len(blackBodyColor)/3-1)*100
+ if temperature > maxTemp {
+ temperature = maxTemp
+ }
+
+ alpha := float64(temperature%100) / 100.0
+ tempIndex := ((temperature - 1000) / 100) * 3
// Approximate white point
whitePoint := interpolateColor(alpha, blackBodyColor[tempIndex:tempIndex+3],
diff --git a/display1/brightness/curve.go b/display1/brightness/curve.go
new file mode 100644
index 000000000..892409d48
--- /dev/null
+++ b/display1/brightness/curve.go
@@ -0,0 +1,753 @@
+// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package brightness
+
+import (
+ "encoding/json"
+ "fmt"
+ "math"
+ "strings"
+ "sync"
+
+ displayBl "github.com/linuxdeepin/go-lib/backlight/display"
+)
+
+const (
+ DefaultScale = 100
+)
+
+// 亮度曲线点
+type BrightnessPoint struct {
+ Percentage int32 `json:"percentage"` // 亮度百分比 (0-100)
+ Value int32 `json:"value"` // 对应的亮度百分比 (0-100),表示最大亮度的百分比
+}
+
+// AutoBrightnessCurvePoint 自动亮度曲线控制点
+type AutoBrightnessCurvePoint struct {
+ Lux int `json:"lux"` // 光感值
+ Br float64 `json:"br"` // 亮度百分比 (0-100)
+}
+
+// 自定义亮度曲线配置
+type BrightnessCurveConfig struct {
+ EDID string `json:"edid,omitempty"` // 屏幕EDID标识(可选,默认曲线不需要)
+ CurvePoints []BrightnessPoint `json:"curve_points"` // 亮度曲线点
+ MaxLimit int32 `json:"max_limit"` // 最大亮度限制百分比 (0-100),表示最大亮度的百分比
+ MaxScale int32 `json:"max_scale"` // 告知外部曲线最大比例
+}
+
+// CurveManager 管理亮度曲线的配置和计算
+type CurveManager struct {
+ mu sync.RWMutex // 统一的锁保护所有状态
+
+ // 亮度限制功能使用的硬件信息
+ configBoardName string
+ deviceBoardName string
+
+ // 亮度限制功能开关与缩放值
+ maxBrightnessUnlimited bool
+ maxScale int32
+
+ // flm机型定制曲线
+ flmCurveFunc func(percentage float64, maxBrightness int) int32
+
+ // 默认曲线函数
+ defaultCurveFunc func(percentage float64, maxBrightness int) int32
+
+ // 受限曲线
+ // 配置上可能会有多项,但只应用第一个匹配上的,因为多控制器可能导致亮度设置错乱
+ limitedCurveFunc func(percentage float64) int32
+
+ // 曲线类型
+ curveType string
+
+ // FLM曲线参数
+ backLightMinValue int32
+ backlightMidValue int32
+
+ // 自动亮度曲线
+ autoBrightnessCurve []AutoBrightnessCurvePoint
+ autoBrightnessCurveFunc func(lux int) float64
+}
+
+// 全局 CurveManager 实例
+var _curveManager = &CurveManager{
+ maxBrightnessUnlimited: true,
+ maxScale: DefaultScale,
+ flmCurveFunc: nil,
+ defaultCurveFunc: nil,
+ limitedCurveFunc: nil,
+}
+
+// InitFlmCurves 初始化FLM曲线
+func InitFlmCurves(backLightMinValue int32, backlightMidValue int32) {
+ _curveManager.initFlmCurves(backLightMinValue, backlightMidValue)
+}
+
+func SetCustomBrightnessCurves(jsonStr string) {
+ _curveManager.setCustomBrightnessCurves(jsonStr)
+}
+
+func SetMaxBrightnessUnlimited(enabled bool) {
+ _curveManager.setMaxBrightnessUnlimited(enabled)
+}
+
+func GetBacklightCurveValue(v float64, c *displayBl.Controller) (int32, bool) {
+ return _curveManager.getCurveValue(v, c)
+}
+
+func SetDeviceBoardName(boardName string) {
+ _curveManager.setDeviceBoardName(boardName)
+}
+
+// SetDefaultBrightnessCurve 设置默认亮度曲线(不依赖硬件信息)
+func SetDefaultBrightnessCurve(jsonStr string) {
+ _curveManager.setDefaultBrightnessCurve(jsonStr)
+}
+
+func IsDeviceSupported() bool {
+ return _curveManager.isDeviceSupported()
+}
+
+func IsMaxLimitCurveSupported() bool {
+ return _curveManager.isMaxLimitCurveSupported()
+}
+
+func GetCurrentMaxScale() int32 {
+ return _curveManager.getCurrentMaxScale()
+}
+
+func SetCurveType(curveType string) {
+ _curveManager.setCurveType(curveType)
+}
+
+// interpolateCurveValue 对曲线点进行线性插值计算
+// percentage: 输入百分比 (0-100)
+// curvePoints: 曲线点数组
+// 返回插值后的曲线值(百分比形式)
+func interpolateCurveValue(percentage float64, curvePoints []BrightnessPoint) float64 {
+ if percentage <= 0 {
+ return float64(curvePoints[0].Value)
+ }
+
+ if percentage >= 100 {
+ return float64(curvePoints[len(curvePoints)-1].Value)
+ }
+
+ // 线性插值
+ curveValue := float64(curvePoints[len(curvePoints)-1].Value)
+ for i := 0; i < len(curvePoints)-1; i++ {
+ p1, p2 := curvePoints[i], curvePoints[i+1]
+ if percentage >= float64(p1.Percentage) && percentage <= float64(p2.Percentage) {
+ x1, x2 := float64(p1.Percentage), float64(p2.Percentage)
+ y1, y2 := float64(p1.Value), float64(p2.Value)
+ if x2 == x1 {
+ curveValue = y1
+ } else {
+ curveValue = y1 + (y2-y1)*(percentage-x1)/(x2-x1)
+ }
+ break
+ }
+ }
+
+ return curveValue
+}
+
+func matchControllerByEDID(controller *displayBl.Controller, configEDID string) bool {
+ if configEDID == "" || controller.DeviceEDID == nil {
+ return false
+ }
+
+ // 厂商把产品型号作为字符串存于edid中,可以直接读到
+ s := string(controller.DeviceEDID)
+ // 检查配置中的EDID标识是否包含在完整的EDID中
+ return strings.Contains(strings.ToLower(s), strings.ToLower(configEDID))
+}
+
+// 配置验证
+func validateBrightnessCurve(curve BrightnessCurveConfig) error {
+ if curve.MaxLimit < 0 {
+ return fmt.Errorf("max_limit must be over 0, got: %d", curve.MaxLimit)
+ }
+
+ if len(curve.CurvePoints) == 0 {
+ return fmt.Errorf("curve_points cannot be empty")
+ }
+
+ // 验证曲线点非递减
+ for i := 1; i < len(curve.CurvePoints); i++ {
+ if curve.CurvePoints[i].Percentage < curve.CurvePoints[i-1].Percentage {
+ return fmt.Errorf("curve points must be in non-descending order")
+ }
+ }
+
+ return nil
+}
+
+func (cm *CurveManager) initFlmCurves(backLightMinValue int32, backlightMidValue int32) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ // 保存FLM曲线参数
+ cm.backLightMinValue = backLightMinValue
+ cm.backlightMidValue = backlightMidValue
+
+ // 生成FLM曲线
+ cm.generateFlmCurveLocked(backLightMinValue, backlightMidValue)
+}
+
+// generateFlmCurveLocked 生成FLM曲线函数(全局唯一)
+// 注意:调用此方法前必须持有写锁
+func (cm *CurveManager) generateFlmCurveLocked(backLightMinValue int32, backlightMidValue int32) {
+ logger.Debugf("Generating FLM curve function with minValue=%d, midValue=%d", backLightMinValue, backlightMidValue)
+
+ const (
+ x1 = 10.0
+ power = 2.2
+ )
+
+ y1 := float64(backLightMinValue)
+ x2 := 100.0
+
+ // FLM曲线函数:根据百分比和最大亮度计算实际亮度值
+ cm.flmCurveFunc = func(percentage float64, maxBrightness int) int32 {
+ y2 := float64(maxBrightness)
+ base := x2 - x1
+ yAdjusted := y2 - y1
+ a := yAdjusted / math.Pow(base, power)
+
+ // 2.2次函数
+ func2p2 := func(x float64) int32 {
+ return int32(a*math.Pow(x-x1, power) + y1)
+ }
+
+ // 线性函数
+ k := (float64(func2p2(float64(backlightMidValue))) - y1) / float64(backlightMidValue-x1)
+ b := y1 - k*x1
+ linearFunc := func(x float64) int32 {
+ return int32(k*x + b)
+ }
+
+ // 分段计算
+ if percentage <= x1 {
+ return backLightMinValue
+ } else if percentage > x1 && percentage <= float64(backlightMidValue) {
+ return linearFunc(percentage)
+ } else if percentage > float64(backlightMidValue) && percentage <= x2 {
+ return func2p2(percentage)
+ }
+ return int32(maxBrightness)
+ }
+
+ logger.Debug("FLM curve function generated")
+}
+
+// generateDefaultCurveFuncLocked 生成默认曲线函数(全局唯一,类似FLM曲线)
+// 注意:调用此方法前必须持有写锁
+func (cm *CurveManager) generateDefaultCurveFuncLocked(curve BrightnessCurveConfig) {
+ logger.Debugf("Generating default curve function with %d points", len(curve.CurvePoints))
+
+ // 默认曲线函数:根据百分比和最大亮度计算实际亮度值
+ cm.defaultCurveFunc = func(percentage float64, maxBrightness int) int32 {
+ // 使用统一的插值函数
+ curveValue := interpolateCurveValue(percentage, curve.CurvePoints)
+
+ // 默认曲线不使用 max_limit,曲线点已经定义了完整的映射关系
+
+ // 转换为实际亮度值
+ return int32(math.Round(curveValue * float64(maxBrightness) / 100.0))
+ }
+
+ logger.Debug("Default curve function generated")
+}
+
+// setCustomBrightnessCurves 设置自定义亮度曲线配置(内部方法)
+func (cm *CurveManager) setCustomBrightnessCurves(jsonStr string) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ cm.limitedCurveFunc = nil
+
+ // 解析配置
+ err := cm.parseCustomBrightnessCurves(jsonStr)
+ if err != nil {
+ logger.Debugf("Failed to parse custom brightness curves: %v", err)
+ return
+ }
+
+ logger.Info("Custom brightness curves set successfully")
+}
+
+// setMaxBrightnessUnlimited 设置最大亮度不受限制(内部方法)
+func (cm *CurveManager) setMaxBrightnessUnlimited(enabled bool) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ cm.maxBrightnessUnlimited = enabled
+ logger.Infof("Set max brightness unlimited: %v", enabled)
+}
+
+// isMaxBrightnessUnlimited 检查是否启用最大亮度不受限制(内部方法)
+func (cm *CurveManager) isMaxBrightnessUnlimited() bool {
+ return cm.maxBrightnessUnlimited
+}
+
+// ParseCustomBrightnessCurves 解析自定义亮度曲线配置
+func (cm *CurveManager) parseCustomBrightnessCurves(jsonStr string) error {
+ var config struct {
+ BoardName string `json:"boardName"`
+ Curves []BrightnessCurveConfig `json:"curves"`
+ }
+
+ // Remove any backslash characters from the input JSON string
+ jsonStr = strings.ReplaceAll(jsonStr, "\\", "")
+
+ logger.Debugf("Parsing custom brightness curves: %s", jsonStr)
+
+ if err := json.Unmarshal([]byte(jsonStr), &config); err != nil {
+ return fmt.Errorf("Custom curve failed to parse JSON: %v", err)
+ }
+
+ // 存储 boardName
+ cm.configBoardName = config.BoardName
+
+ if config.Curves == nil {
+ return fmt.Errorf("Custom curve json contains nothing")
+ }
+
+ ctlrs, err := displayBl.List()
+ if err != nil {
+ return fmt.Errorf("Custom curve failed to list controllers: %v", err)
+ }
+
+ for _, curve := range config.Curves {
+ // 验证 EDID 字段不能为空
+ if curve.EDID == "" {
+ logger.Debug("Skipping curve config with empty EDID")
+ continue
+ }
+
+ for _, controller := range ctlrs {
+ if matchControllerByEDID(controller, curve.EDID) {
+ logger.Infof("Found matching controller for EDID %s: %s", curve.EDID, controller.Name)
+
+ if err := validateBrightnessCurve(curve); err == nil {
+ cm.limitedCurveFunc = cm.generateCustomCurveFunc(curve, int32(controller.MaxBrightness))
+ cm.maxScale = curve.MaxScale
+ return nil
+ }
+ }
+ }
+ }
+
+ logger.Debug("Custom curve config missed all controller")
+ return nil
+}
+
+func (cm *CurveManager) generateCustomCurveFunc(curve BrightnessCurveConfig, controllerMaxBrightness int32) func(float64) int32 {
+ return func(percentage float64) int32 {
+ // 使用统一的插值函数
+ curveValue := interpolateCurveValue(percentage, curve.CurvePoints)
+
+ // 应用max_limit限制
+ // 注意:这里直接读取全局 _curveManager 的状态
+ cm.mu.RLock()
+ unlimited := cm.maxBrightnessUnlimited
+ cm.mu.RUnlock()
+
+ if !unlimited && curve.MaxLimit > 0 && curve.MaxLimit < 100 {
+ curveValue = curveValue * float64(curve.MaxLimit) / 100.0
+ }
+
+ // 转换为实际亮度值
+ return int32(math.Round(curveValue * float64(controllerMaxBrightness) / 100.0))
+ }
+}
+
+// 根据当前设置亮度百分比与控制器,返回由曲线控制的实际亮度
+func (cm *CurveManager) getCurveValue(v float64, c *displayBl.Controller) (int32, bool) {
+ // 将v转为合适的值,注意精度,不要过早转为整数
+ vv := 100 * v
+
+ if cm.curveType == "flm" {
+ if cm.flmCurveFunc == nil {
+ logger.Warning("FLM curve function is not initialized")
+ return int32(v * float64(c.MaxBrightness)), false
+ }
+
+ return cm.flmCurveFunc(vv, c.MaxBrightness), true
+ }
+
+ // 如果存在自定义曲线,检查适用性
+ if !cm.isDeviceSupportedLocked() || cm.maxBrightnessUnlimited || cm.limitedCurveFunc == nil {
+ logger.Debugf("Limited curve not working now, maxBrightnessUnlimited=%v, func exist=%v", cm.maxBrightnessUnlimited, cm.limitedCurveFunc != nil)
+ } else {
+ return cm.limitedCurveFunc(vv), true
+ }
+
+ if cm.defaultCurveFunc != nil {
+ return cm.defaultCurveFunc(vv, c.MaxBrightness), true
+ }
+
+ // 默认无处理的值
+ return int32(v * float64(c.MaxBrightness)), false
+}
+
+// setDeviceBoardName 设置设备板名(内部方法)
+func (cm *CurveManager) setDeviceBoardName(boardName string) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+ cm.deviceBoardName = boardName
+}
+
+// setDefaultBrightnessCurve 设置默认亮度曲线(内部方法)
+func (cm *CurveManager) setDefaultBrightnessCurve(jsonStr string) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ cm.defaultCurveFunc = nil
+
+ err := cm.parseDefaultBrightnessCurve(jsonStr)
+ if err != nil {
+ logger.Warningf("Failed to parse default brightness curve: %v", err)
+ return
+ }
+
+ logger.Info("Default brightness curve set successfully")
+}
+
+// ParseDefaultBrightnessCurve 解析默认亮度曲线配置
+func (cm *CurveManager) parseDefaultBrightnessCurve(jsonStr string) error {
+ // Remove any backslash characters from the input JSON string
+ jsonStr = strings.ReplaceAll(jsonStr, "\\", "")
+
+ logger.Debugf("Parsing default brightness curve: %s", jsonStr)
+
+ var curve BrightnessCurveConfig
+ if err := json.Unmarshal([]byte(jsonStr), &curve); err != nil {
+ return fmt.Errorf("failed to parse JSON: %v", err)
+ }
+
+ // 验证曲线配置
+ if err := validateBrightnessCurve(curve); err != nil {
+ return fmt.Errorf("invalid curve config: %v", err)
+ }
+
+ cm.generateDefaultCurveFuncLocked(curve)
+
+ logger.Infof("Default brightness curve parsed successfully: %d points", len(curve.CurvePoints))
+
+ return nil
+}
+
+// isDeviceSupported 检查设备是否支持(内部方法)
+func (cm *CurveManager) isDeviceSupported() bool {
+ cm.mu.RLock()
+ defer cm.mu.RUnlock()
+ return cm.isDeviceSupportedLocked()
+}
+
+// isDeviceSupportedLocked 检查设备是否支持(需要持有锁)
+func (cm *CurveManager) isDeviceSupportedLocked() bool {
+ if cm.configBoardName != "" && !strings.Contains(strings.ToLower(cm.configBoardName), strings.ToLower(cm.deviceBoardName)) {
+ return false
+ }
+
+ return true
+}
+
+// isMaxLimitCurveSupported 检查是否支持最大亮度限制曲线(内部方法)
+func (cm *CurveManager) isMaxLimitCurveSupported() bool {
+ cm.mu.RLock()
+ defer cm.mu.RUnlock()
+
+ if !cm.isDeviceSupportedLocked() {
+ return false
+ }
+
+ // 检查是否有任何控制器在自定义曲线映射中存在
+ return cm.limitedCurveFunc != nil
+}
+
+// getCurrentMaxScale 获取当前最大缩放值(内部方法)
+func (cm *CurveManager) getCurrentMaxScale() int32 {
+ cm.mu.RLock()
+ defer cm.mu.RUnlock()
+
+ // 如果没有自定义曲线配置,返回默认值 100(表示100%)
+ if cm.limitedCurveFunc == nil || !cm.isDeviceSupportedLocked() {
+ return DefaultScale
+ }
+
+ if cm.maxScale > DefaultScale {
+ return cm.maxScale
+ }
+
+ // 如果没有设置 MaxScale,返回默认值 100(表示100%)
+ return DefaultScale
+}
+
+// setCurveType 设置曲线类型(内部方法)
+func (cm *CurveManager) setCurveType(curveType string) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+ cm.curveType = curveType
+ logger.Infof("Set curve type: %s", curveType)
+}
+
+// SetAutoBrightnessCurve 设置自动亮度曲线配置(JSON字符串)
+func SetAutoBrightnessCurve(jsonStr string) {
+ _curveManager.setAutoBrightnessCurve(jsonStr)
+}
+
+// SetAutoBrightnessCurveFromPoints 设置自动亮度曲线配置(数组)
+func SetAutoBrightnessCurveFromPoints(points []AutoBrightnessCurvePoint) {
+ _curveManager.setAutoBrightnessCurveFromPoints(points)
+}
+
+// GetAutoBrightnessValue 根据光照值获取目标亮度百分比 (0-1)
+func GetAutoBrightnessValue(lux int) float64 {
+ return _curveManager.getAutoBrightnessValue(lux)
+}
+
+// HasAutoBrightnessCurve 检查是否配置了自动亮度曲线
+func HasAutoBrightnessCurve() bool {
+ return _curveManager.hasAutoBrightnessCurve()
+}
+
+// setAutoBrightnessCurve 设置自动亮度曲线(内部方法,从JSON字符串解析)
+func (cm *CurveManager) setAutoBrightnessCurve(jsonStr string) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+
+ cm.autoBrightnessCurveFunc = nil
+ cm.autoBrightnessCurve = nil
+
+ if jsonStr == "" {
+ logger.Info("Auto brightness curve cleared")
+ return
+ }
+
+ jsonStr = strings.ReplaceAll(jsonStr, "\\", "")
+ logger.Debugf("Parsing auto brightness curve: %s", jsonStr)
+
+ var points []AutoBrightnessCurvePoint
+ if err := json.Unmarshal([]byte(jsonStr), &points); err != nil {
+ logger.Warningf("Failed to parse auto brightness curve: %v", err)
+ return
+ }
+
+ cm.setAutoBrightnessCurveFromPointsLocked(points)
+}
+
+// setAutoBrightnessCurveFromPoints 设置自动亮度曲线(内部方法,从数组)
+func (cm *CurveManager) setAutoBrightnessCurveFromPoints(points []AutoBrightnessCurvePoint) {
+ cm.mu.Lock()
+ defer cm.mu.Unlock()
+ cm.setAutoBrightnessCurveFromPointsLocked(points)
+}
+
+// setAutoBrightnessCurveFromPointsLocked 设置自动亮度曲线(内部方法,需持有锁)
+func (cm *CurveManager) setAutoBrightnessCurveFromPointsLocked(points []AutoBrightnessCurvePoint) {
+ cm.autoBrightnessCurveFunc = nil
+ cm.autoBrightnessCurve = nil
+
+ if len(points) == 0 {
+ logger.Info("Auto brightness curve cleared")
+ return
+ }
+
+ if err := validateAutoBrightnessCurve(points); err != nil {
+ logger.Warningf("Invalid auto brightness curve: %v", err)
+ return
+ }
+
+ cm.autoBrightnessCurve = points
+ cm.autoBrightnessCurveFunc = cm.generateAutoBrightnessCurveFunc(points)
+ logger.Infof("Auto brightness curve set successfully with %d points", len(points))
+}
+
+// validateAutoBrightnessCurve 验证自动亮度曲线配置
+func validateAutoBrightnessCurve(points []AutoBrightnessCurvePoint) error {
+ if len(points) == 0 {
+ return fmt.Errorf("curve points cannot be empty")
+ }
+
+ for i, p := range points {
+ if p.Br < 0 || p.Br > 100 {
+ return fmt.Errorf("brightness at index %d must be between 0 and 100, got: %f", i, p.Br)
+ }
+ if i > 0 && p.Lux <= points[i-1].Lux {
+ return fmt.Errorf("lux values must be in ascending order at index %d", i)
+ }
+ }
+
+ return nil
+}
+
+// generateAutoBrightnessCurveFunc 生成自动亮度曲线函数
+func (cm *CurveManager) generateAutoBrightnessCurveFunc(points []AutoBrightnessCurvePoint) func(lux int) float64 {
+ return func(lux int) float64 {
+ if len(points) == 0 {
+ return -1
+ }
+
+ if len(points) == 1 {
+ return points[0].Br / 100.0
+ }
+
+ luxFloat := float64(lux)
+
+ if luxFloat <= float64(points[0].Lux) {
+ return points[0].Br / 100.0
+ }
+
+ if luxFloat >= float64(points[len(points)-1].Lux) {
+ return points[len(points)-1].Br / 100.0
+ }
+
+ for i := 0; i < len(points)-1; i++ {
+ p1, p2 := points[i], points[i+1]
+ if luxFloat >= float64(p1.Lux) && luxFloat <= float64(p2.Lux) {
+ x1, x2 := float64(p1.Lux), float64(p2.Lux)
+ y1, y2 := p1.Br, p2.Br
+ if math.Abs(x1-x2) < 1e-6 {
+ return y1 / 100.0
+ }
+ br := y1 + (y2-y1)*(luxFloat-x1)/(x2-x1)
+ return br / 100.0
+ }
+ }
+
+ return points[len(points)-1].Br / 100.0
+ }
+}
+
+// getAutoBrightnessValue 根据光照值获取目标亮度百分比 (0-1)
+func (cm *CurveManager) getAutoBrightnessValue(lux int) float64 {
+ cm.mu.RLock()
+ defer cm.mu.RUnlock()
+
+ if cm.autoBrightnessCurveFunc == nil {
+ return -1
+ }
+
+ return cm.autoBrightnessCurveFunc(lux)
+}
+
+// hasAutoBrightnessCurve 检查是否配置了自动亮度曲线
+func (cm *CurveManager) hasAutoBrightnessCurve() bool {
+ cm.mu.RLock()
+ defer cm.mu.RUnlock()
+ return cm.autoBrightnessCurveFunc != nil
+}
+
+// GetBacklightCurvePercent 返回当前控制器的实际亮度值对应的百分比
+func GetBacklightCurvePercent(c *displayBl.Controller) float64 {
+ return _curveManager.getBacklightCurvePercent(c)
+}
+
+// 返回当前控制器的实际亮度值对应的百分比
+func (cm *CurveManager) getBacklightCurvePercent(c *displayBl.Controller) float64 {
+ currentBrightness, err := c.GetBrightness()
+ if err != nil {
+ return 1.0
+ }
+ // 其他曲线需根据曲线类型进行逆函数查找
+ if cm.curveType == "flm" {
+ if cm.flmCurveFunc == nil {
+ logger.Warning("FLM curve function is not initialized")
+ return float64(currentBrightness) / float64(c.MaxBrightness)
+ }
+
+ return findBrightnessPercentByCurve(int32(currentBrightness), cm.flmCurveFunc, cm.backLightMinValue, cm.backlightMidValue, int32(c.MaxBrightness))
+ }
+ return float64(currentBrightness) / float64(c.MaxBrightness)
+}
+
+// findBrightnessPercentByCurve 通过背光曲线逆函数查找对应的百分比值
+func findBrightnessPercentByCurve(targetBrightness int32, curveFunc func(float64, int) int32, backLightMinValue int32, backlightMidValue int32, maxBrightness int32) float64 {
+ const (
+ x1 = 10.0
+ x2 = 100.0
+ maxIterations = 50
+ tolerance = 0.01
+ )
+
+ // 边界条件检查
+ if targetBrightness <= backLightMinValue {
+ return x1
+ }
+ if targetBrightness >= maxBrightness {
+ return x2
+ }
+
+ // 计算中间点的亮度值,用于确定搜索范围
+ midBrightness := curveFunc(float64(backlightMidValue), int(maxBrightness))
+
+ // 根据目标亮度值确定搜索范围
+ var left, right float64
+ if targetBrightness <= midBrightness {
+ left = x1
+ right = float64(backlightMidValue)
+ } else {
+ left = float64(backlightMidValue)
+ right = x2
+ }
+
+ // 预计算边界值,避免重复计算
+ leftBrightness := curveFunc(left, int(maxBrightness))
+ rightBrightness := curveFunc(right, int(maxBrightness))
+
+ if leftBrightness == targetBrightness {
+ return left
+ }
+ if rightBrightness == targetBrightness {
+ return right
+ }
+
+ // 使用二分查找法查找对应的百分比值
+ iterations := 0
+ var bestResult float64
+ var bestDiff int32 = maxBrightness
+
+ for right-left > tolerance && iterations < maxIterations {
+ mid := (left + right) / 2
+ calculatedBrightness := curveFunc(mid, int(maxBrightness))
+
+ diff := calculatedBrightness - targetBrightness
+ if diff < 0 {
+ diff = -diff
+ }
+
+ if diff < bestDiff {
+ bestDiff = diff
+ bestResult = mid
+ }
+
+ if calculatedBrightness < targetBrightness {
+ left = mid
+ } else if calculatedBrightness > targetBrightness {
+ right = mid
+ } else {
+ return mid
+ }
+ iterations++
+ }
+
+ result := bestResult
+ if result < x1 {
+ result = x1
+ } else if result > x2 {
+ result = x2
+ }
+
+ if iterations >= maxIterations {
+ logger.Debugf("findBrightnessPercentByCurve: reached max iterations (%d), target: %d, result: %f",
+ maxIterations, targetBrightness, result)
+ }
+
+ return result
+}
diff --git a/display1/brightness/kalman_filter.go b/display1/brightness/kalman_filter.go
new file mode 100644
index 000000000..3935d2d03
--- /dev/null
+++ b/display1/brightness/kalman_filter.go
@@ -0,0 +1,260 @@
+// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package brightness
+
+import (
+ "sync"
+)
+
+// 卡尔曼滤波器默认参数
+const (
+ DefaultProcessNoiseQ = 0.5 // 过程噪声协方差(系统模型的不确定性)
+ DefaultMeasurementNoiseR = 0.02 // 测量噪声协方差(传感器噪声)
+ DefaultWindowSize = 5 // 自适应滤波器窗口大小
+ DefaultVarianceScale = 0.1 // 方差缩放系数(用于自适应调整R)
+)
+
+// KalmanFilter1D 一维卡尔曼滤波器
+// 用于传感器数据的平滑和噪声抑制
+type KalmanFilter1D struct {
+ mu sync.Mutex
+
+ // 系统状态
+ xEst float64 // 估计值
+ PEst float64 // 估计协方差
+
+ // 系统模型参数
+ F float64 // 状态转移矩阵(通常为1,假设状态不变)
+ H float64 // 观测矩阵(通常为1)
+ Q float64 // 过程噪声协方差
+ R float64 // 测量噪声协方差
+
+ // 初始化标志
+ initialized bool
+}
+
+// NewKalmanFilter1D 创建一维卡尔曼滤波器
+// q: 过程噪声协方差(系统模型的不确定性)
+// r: 测量噪声协方差(传感器噪声)
+// initialValue: 初始估计值
+func NewKalmanFilter1D(q, r, initialValue float64) *KalmanFilter1D {
+ return &KalmanFilter1D{
+ F: 1.0, // 假设状态不变
+ H: 1.0, // 直接观测
+ Q: q,
+ R: r,
+ xEst: initialValue,
+ PEst: 1.0,
+ initialized: false,
+ }
+}
+
+// Update 更新滤波器
+// measurement: 测量值
+// 返回: 滤波后的估计值
+func (kf *KalmanFilter1D) Update(measurement float64) float64 {
+ kf.mu.Lock()
+ defer kf.mu.Unlock()
+ return kf.updateUnlocked(measurement)
+}
+
+// updateUnlocked 内部无锁版本,供 AdaptiveKalmanFilter 调用(调用者已持有锁)
+func (kf *KalmanFilter1D) updateUnlocked(measurement float64) float64 {
+ if !kf.initialized {
+ kf.xEst = measurement
+ kf.PEst = 1.0
+ kf.initialized = true
+ logger.Debugf("[AutoBrightness::KalmanFilter] Initialized with measurement: %d", int(measurement))
+ return kf.xEst
+ }
+
+ // 记录输入值
+ logger.Debugf("[AutoBrightness::KalmanFilter] Input measurement: %d, previous estimate: %d", int(measurement), int(kf.xEst))
+
+ // 预测步骤
+ xPred := kf.F * kf.xEst // 状态预测
+ pPred := kf.F*kf.PEst*kf.F + kf.Q // 协方差预测
+
+ // 更新步骤
+ y := measurement - kf.H*xPred // 测量残差
+ s := kf.H*pPred*kf.H + kf.R // 残差协方差
+ k := pPred * kf.H / s // 卡尔曼增益
+
+ // 状态更新
+ oldEstimate := kf.xEst
+ kf.xEst = xPred + k*y
+ kf.PEst = (1.0 - k*kf.H) * pPred
+
+ // 记录输出值
+ logger.Debugf("[AutoBrightness::KalmanFilter] Output estimate: %d (change: %.2f, gain: %.4f)",
+ int(kf.xEst), kf.xEst-oldEstimate, k)
+
+ return kf.xEst
+}
+
+// Reset 重置滤波器
+func (kf *KalmanFilter1D) Reset() {
+ kf.mu.Lock()
+ defer kf.mu.Unlock()
+ kf.initialized = false
+ kf.PEst = 1.0
+}
+
+// GetEstimate 获取当前估计值
+func (kf *KalmanFilter1D) GetEstimate() float64 {
+ kf.mu.Lock()
+ defer kf.mu.Unlock()
+ return kf.xEst
+}
+
+// GetCovariance 获取当前协方差
+func (kf *KalmanFilter1D) GetCovariance() float64 {
+ kf.mu.Lock()
+ defer kf.mu.Unlock()
+ return kf.PEst
+}
+
+// SetProcessNoise 设置过程噪声协方差
+func (kf *KalmanFilter1D) SetProcessNoise(q float64) {
+ kf.mu.Lock()
+ defer kf.mu.Unlock()
+ kf.Q = q
+}
+
+// SetMeasurementNoise 设置测量噪声协方差
+func (kf *KalmanFilter1D) SetMeasurementNoise(r float64) {
+ kf.mu.Lock()
+ defer kf.mu.Unlock()
+ kf.R = r
+}
+
+// setMeasurementNoiseUnlocked 无锁版本,供已持有 KalmanFilter1D.mu 的调用者使用
+func (kf *KalmanFilter1D) setMeasurementNoiseUnlocked(r float64) {
+ kf.R = r
+}
+
+// resetUnlocked 无锁重置版本,供已持有 KalmanFilter1D.mu 的调用者使用
+func (kf *KalmanFilter1D) resetUnlocked() {
+ kf.initialized = false
+ kf.PEst = 1.0
+}
+
+// AdaptiveKalmanFilter 自适应卡尔曼滤波器
+// 根据测量值的方差自动调整噪声参数
+type AdaptiveKalmanFilter struct {
+ mu sync.Mutex
+ *KalmanFilter1D
+ window []float64 // 测量值窗口
+ windowSize int // 窗口大小
+ measurementVariance float64 // 测量方差
+}
+
+// NewAdaptiveKalmanFilter 创建自适应卡尔曼滤波器
+// q: 过程噪声协方差
+// r: 测量噪声协方差
+// window: 滑动窗口大小(用于计算方差)
+func NewAdaptiveKalmanFilter(q, r float64, window int) *AdaptiveKalmanFilter {
+ return &AdaptiveKalmanFilter{
+ KalmanFilter1D: NewKalmanFilter1D(q, r, 0.0),
+ window: make([]float64, 0, window),
+ windowSize: window,
+ }
+}
+
+// NewDefaultAdaptiveKalmanFilter 使用默认参数创建自适应卡尔曼滤波器
+func NewDefaultAdaptiveKalmanFilter() *AdaptiveKalmanFilter {
+ return NewAdaptiveKalmanFilter(DefaultProcessNoiseQ, DefaultMeasurementNoiseR, DefaultWindowSize)
+}
+
+// Update 更新滤波器(自适应版本)
+func (akf *AdaptiveKalmanFilter) Update(measurement float64) float64 {
+ akf.mu.Lock()
+ defer akf.mu.Unlock()
+
+ // 添加到窗口
+ akf.window = append(akf.window, measurement)
+ if len(akf.window) > akf.windowSize {
+ akf.window = akf.window[1:]
+ }
+
+ logger.Debugf("[AutoBrightness::AdaptiveKalmanFilter] Window size: %d/%d, measurement: %d",
+ len(akf.window), akf.windowSize, int(measurement))
+
+ // 计算窗口内测量值的方差
+ if len(akf.window) >= 2 {
+ var sum float64
+ for _, val := range akf.window {
+ sum += val
+ }
+ mean := sum / float64(len(akf.window))
+
+ var variance float64
+ for _, val := range akf.window {
+ diff := val - mean
+ variance += diff * diff
+ }
+ akf.measurementVariance = variance / float64(len(akf.window))
+
+ logger.Debugf("[AutoBrightness::AdaptiveKalmanFilter] Window stats: mean=%.2f, variance=%.4f",
+ mean, akf.measurementVariance)
+
+ // 根据测量方差自适应调整测量噪声
+ // 方差越大,测量噪声越大,越信任估计值
+ if akf.measurementVariance > 0 {
+ oldR := akf.R
+ newR := akf.measurementVariance * 0.1
+ akf.KalmanFilter1D.setMeasurementNoiseUnlocked(newR)
+ logger.Debugf("[AutoBrightness::AdaptiveKalmanFilter] Adjusted measurement noise: R=%.4f -> %.4f",
+ oldR, newR)
+ }
+ }
+
+ // 调用基类的无锁更新方法(已持有 akf.mu,避免死锁)
+ return akf.KalmanFilter1D.updateUnlocked(measurement)
+}
+
+// Reset 重置滤波器
+func (akf *AdaptiveKalmanFilter) Reset() {
+ akf.mu.Lock()
+ defer akf.mu.Unlock()
+ akf.KalmanFilter1D.resetUnlocked()
+ akf.window = akf.window[:0]
+ akf.measurementVariance = 0
+}
+
+// GetMeasurementVariance 获取当前测量方差
+func (akf *AdaptiveKalmanFilter) GetMeasurementVariance() float64 {
+ akf.mu.Lock()
+ defer akf.mu.Unlock()
+ return akf.measurementVariance
+}
+
+// GetWindowSize 获取窗口大小
+func (akf *AdaptiveKalmanFilter) GetWindowSize() int {
+ akf.mu.Lock()
+ defer akf.mu.Unlock()
+ return len(akf.window)
+}
+
+// IsInitialized 检查滤波器是否已初始化(是否收到过数据)
+func (akf *AdaptiveKalmanFilter) IsInitialized() bool {
+ akf.mu.Lock()
+ defer akf.mu.Unlock()
+ return akf.initialized
+}
+
+// SetWindowSize 设置窗口大小
+func (akf *AdaptiveKalmanFilter) SetWindowSize(size int) {
+ akf.mu.Lock()
+ defer akf.mu.Unlock()
+ if size < 2 {
+ size = 2
+ }
+ akf.windowSize = size
+ // 如果当前窗口超过新大小,截断
+ if len(akf.window) > akf.windowSize {
+ akf.window = akf.window[len(akf.window)-akf.windowSize:]
+ }
+}
diff --git a/display1/color_temp.go b/display1/color_temp.go
index f69d991ba..f996277d6 100644
--- a/display1/color_temp.go
+++ b/display1/color_temp.go
@@ -435,15 +435,31 @@ func (m *Manager) setColorTempOneShot() {
defer _setColorTempMu.Unlock()
monitors := m.getConnectedMonitors()
+ // bug204347 部分场景下monitor.Brightness为默认值 从系统配置中获取亮度
+ monitorsId := monitors.getMonitorsId()
+ configs := m.getSuitableSysMonitorConfigs(m.DisplayMode, monitorsId, monitors)
for _, monitor := range monitors {
monitor.PropsMu.RLock()
br := monitor.Brightness
name := monitor.Name
monitor.PropsMu.RUnlock()
- err := m.setBrightness(name, br)
- if err != nil {
- logger.Warning(err)
+ var config *SysMonitorConfig
+ for _, c := range configs {
+ if c.Name == name {
+ config = c
+ break
+ }
+ }
+ if config != nil {
+ br = config.Brightness
+ }
+
+ if canSet, _ := m.CanSetBrightness(name); canSet && br > 0 && monitor.Enabled {
+ err := m.setColorTemperature(monitor, br)
+ if err != nil {
+ logger.Warning(err)
+ }
}
}
}
diff --git a/display1/display.go b/display1/display.go
index 798265bba..daa2eee87 100644
--- a/display1/display.go
+++ b/display1/display.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -93,3 +93,11 @@ func StartPart2() error {
func SetLogLevel(level log.Priority) {
logger.SetLogLevel(level)
}
+
+// Cleanup 清理display模块资源
+func Cleanup() {
+ if _dpy != nil {
+ _dpy.cleanupAutoBrightness()
+ logger.Info("Display module cleaned up")
+ }
+}
diff --git a/display1/display_dbusutil.go b/display1/display_dbusutil.go
index 318891cf6..7b3363f82 100644
--- a/display1/display_dbusutil.go
+++ b/display1/display_dbusutil.go
@@ -170,6 +170,19 @@ func (v *Manager) emitPropChangedMaxBacklightBrightness(value uint32) error {
return v.service.EmitPropertyChanged(v, "MaxBacklightBrightness", value)
}
+func (v *Manager) setPropCurveMaxScale(value int32) (changed bool) {
+ if v.CurveMaxScale != value {
+ v.CurveMaxScale = value
+ v.emitPropChangedCurveMaxScale(value)
+ return true
+ }
+ return false
+}
+
+func (v *Manager) emitPropChangedCurveMaxScale(value int32) error {
+ return v.service.EmitPropertyChanged(v, "CurveMaxScale", value)
+}
+
func (v *Manager) setPropColorTemperatureEnabled(value bool) (changed bool) {
if v.ColorTemperatureEnabled != value {
v.ColorTemperatureEnabled = value
diff --git a/display1/exported_methods_auto.go b/display1/exported_methods_auto.go
index e1eabc163..366b8e0eb 100644
--- a/display1/exported_methods_auto.go
+++ b/display1/exported_methods_auto.go
@@ -94,6 +94,11 @@ func (v *Manager) GetExportedMethods() dbusutil.ExportedMethods {
Fn: v.SetAndSaveBrightness,
InArgs: []string{"outputName", "value"},
},
+ {
+ Name: "SetAutoBrightnessEnabled",
+ Fn: v.SetAutoBrightnessEnabled,
+ InArgs: []string{"enabled"},
+ },
{
Name: "SetBrightness",
Fn: v.SetBrightness,
diff --git a/display1/main.go b/display1/main.go
index 6c6f5466e..695a44294 100644
--- a/display1/main.go
+++ b/display1/main.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -108,6 +108,7 @@ func isInVM() (bool, error) {
}
func (*daemon) Stop() error {
+ Cleanup()
distory()
return nil
}
diff --git a/display1/manager.go b/display1/manager.go
index a88af1891..2f50bc79c 100644
--- a/display1/manager.go
+++ b/display1/manager.go
@@ -89,6 +89,40 @@ const (
DSettingsKeyRotateScreenTimeDelay = "rotateScreenTimeDelay"
DSettingsKeyCustomDisplayMode = "customDisplayMode"
+ // 亮度曲线配置
+ DSettingsKeyBacklightCurveType = "backlight-curve-type"
+ DSettingsKeyBacklightMinValue = "backlight-curve-min-value"
+ DSettingsKeyBacklightMidValue = "backlight-curve-mid-value"
+ DSettingsKeyBrightnessPercentage = "brightness-percentage"
+ DSettingsKeyCanSetBrightnessDelay = "can-set-brightness-delay-interval"
+ DSettingsKeyCustomBrightnessCurves = "custom-brightness-curves"
+ DSettingsKeyDefaultBrightnessCurve = "default-brightness-curve"
+ DSettingsKeyMaxBrightnessUnlimited = "max-brightness-unlimited"
+
+ // 统一亮度过渡配置
+ DSettingsKeyTransitionEnabled = "transition-enabled"
+ DSettingsKeyTransitionDuration = "transition-duration"
+ DSettingsKeyTransitionStepPercent = "transition-step-percent"
+ DSettingsKeyTransitionMinStepInterval = "transition-min-step-interval"
+
+ // 自动亮度配置(独立配置文件)
+ DSettingsAutoBrightnessAppID = "org.deepin.dde.daemon"
+ DSettingsAutoBrightnessName = "org.deepin.Display.AutoBrightness"
+ DSettingsKeyABEnabled = "enabled"
+ DSettingsKeyABSensitivity = "sensitivity"
+ DSettingsKeyABChangeThreshold = "change-threshold"
+ DSettingsKeyABPollingInterval = "polling-interval"
+ DSettingsKeyABManualOverride = "manual-override-duration"
+ DSettingsKeyABManualAdjustDisablesAutoMode = "manual-adjust-disables-auto-mode"
+ DSettingsKeyABUseTransition = "use-transition"
+ DSettingsKeyABBrightnessChangeThreshold = "brightness-change-threshold"
+ DSettingsKeyABCurve = "lux-brightness-curve"
+
+ // 卡尔曼滤波器配置
+ DSettingsKeyABKalmanProcessNoise = "kalman-process-noise"
+ DSettingsKeyABKalmanMeasurementNoise = "kalman-measurement-noise"
+ DSettingsKeyABKalmanWindowSize = "kalman-window-size"
+
customModeDelim = "+"
monitorsIdDelimiter = ","
defaultTemperatureMode = ColorTemperatureModeNone
@@ -163,8 +197,9 @@ type Manager struct {
debugOpts debugOptions
redshiftRunner *redshiftRunner
- sessionActive bool
- newSysCfg *SysRootConfig
+ sessionActive bool
+ sessionActiveMu sync.RWMutex
+ newSysCfg *SysRootConfig
cursorShowed bool
// dconfig com.deepin.Display
@@ -237,6 +272,33 @@ type Manager struct {
xsManager xs.XSettings
isVM bool
+
+ // 自动亮度相关属性
+ AutoBrightnessEnabled bool `prop:"access:rw"`
+ AutoBrightnessSupported bool `prop:"access:r"`
+ CurveMaxScale int32 `prop:"access:r"`
+
+ // 自动亮度管理器
+ autoBrightnessManager *AutoBrightnessManager
+
+ // 统一亮度过渡管理器
+ transitionManager *brightness.TransitionManager
+
+ // 统一亮度过渡配置
+ transitionMu sync.RWMutex
+ transitionEnabled bool
+ transitionDuration int // 毫秒
+ transitionStepPercent float64 // 步进百分比
+ transitionMinStepInterval int // 最小步进间隔(毫秒)
+
+ // 背光曲线配置
+ backlightCurveType string
+ backlightMinValue int32
+ backlightMidValue int32
+
+ powerSaving bool
+ systemAdjustingTimer *time.Timer
+ systemAdjustingTimerMu sync.Mutex
}
type monitorSizeInfo struct {
@@ -331,7 +393,9 @@ func newManager(service *dbusutil.Service) *Manager {
loginManager.InitSignalExt(sysSigLoop, true)
/* 当系统从待机或者休眠状态唤醒时,需要重新获取当前屏幕的状态 */
_, err = loginManager.ConnectPrepareForSleep(func(isSleep bool) {
- if !isSleep {
+ if isSleep {
+ m.holdAutoBrightness()
+ } else {
logger.Info("system Wakeup, need reacquire screen status", isSleep)
m.initScreenRotation()
@@ -341,6 +405,7 @@ func newManager(service *dbusutil.Service) *Manager {
if error != nil {
logger.Warning("Cancel wm blackscreen failed", error)
}
+ m.resumeAutoBrightness()
}
})
@@ -380,10 +445,14 @@ func newManager(service *dbusutil.Service) *Manager {
}
logger.Debug("session active changed", active)
+ m.sessionActiveMu.Lock()
m.sessionActive = active
+ m.sessionActiveMu.Unlock()
if !active {
+ m.holdAutoBrightness()
return
}
+ m.resumeAutoBrightness()
if m.newSysCfg != nil {
m.handleSysConfigUpdated(m.newSysCfg)
m.newSysCfg = nil
@@ -402,7 +471,9 @@ func newManager(service *dbusutil.Service) *Manager {
logger.Warningf("prop active ConnectChanged failed! %v", err)
}
+ m.sessionActiveMu.Lock()
m.sessionActive, err = selfSession.Active().Get(0)
+ m.sessionActiveMu.Unlock()
if err != nil {
logger.Warning(err)
}
@@ -464,6 +535,54 @@ func (m *Manager) initDConfig(sysBus *dbus.Conn) {
m.getCurrentCustomId()
case DSettingsKeyRotateScreenTimeDelay:
m.getRotateScreenTimeDelay()
+ case DSettingsKeyCustomBrightnessCurves:
+ m.getCustomBrightnessCurves()
+ case DSettingsKeyDefaultBrightnessCurve:
+ m.getDefaultBrightnessCurve()
+ case DSettingsKeyBacklightMinValue, DSettingsKeyBacklightMidValue:
+ m.getBacklightMinValue()
+ m.getBacklightMidValue()
+ brightness.InitFlmCurves(m.backlightMinValue, m.backlightMidValue)
+ case DSettingsKeyBacklightCurveType:
+ m.getBacklightCurveType()
+ case DSettingsKeyMaxBrightnessUnlimited:
+ m.getMaxBrightnessUnlimited()
+ case DSettingsKeyTransitionEnabled,
+ DSettingsKeyTransitionDuration,
+ DSettingsKeyTransitionStepPercent,
+ DSettingsKeyTransitionMinStepInterval:
+ m.transitionMu.Lock()
+ // 重新读取所有过渡配置
+ if v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionEnabled); err == nil {
+ m.transitionEnabled = v.Value().(bool)
+ }
+ if v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionDuration); err == nil {
+ switch val := v.Value().(type) {
+ case int64:
+ m.transitionDuration = int(val)
+ case float64:
+ m.transitionDuration = int(val)
+ }
+ }
+ if v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionStepPercent); err == nil {
+ m.transitionStepPercent = v.Value().(float64)
+ }
+ if v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionMinStepInterval); err == nil {
+ switch val := v.Value().(type) {
+ case int64:
+ m.transitionMinStepInterval = int(val)
+ case float64:
+ m.transitionMinStepInterval = int(val)
+ }
+ }
+ m.transitionMu.Unlock()
+ // 更新统一 TransitionManager 配置
+ if m.transitionManager != nil {
+ m.transitionManager.SetEnabled(m.transitionEnabled)
+ m.transitionManager.SetDuration(m.transitionDuration)
+ m.transitionManager.SetStepPercent(m.transitionStepPercent)
+ m.transitionManager.SetMinStepInterval(m.transitionMinStepInterval)
+ }
default:
break
}
@@ -480,6 +599,14 @@ func (m *Manager) loadInitialConfigValues() {
m.getCurrentCustomId()
m.getRotateScreenTimeDelay()
// ColorTemperatureManual will be loaded from user config via applyColorTempConfig()
+ m.getBacklightCurveType()
+ m.getBacklightMinValue()
+ m.getBacklightMidValue()
+ m.getCustomBrightnessCurves()
+ m.getDefaultBrightnessCurve()
+ m.getMaxBrightnessUnlimited()
+ // 初始化FLM曲线
+ brightness.InitFlmCurves(m.backlightMinValue, m.backlightMidValue)
}
func (m *Manager) getDefaultTemperatureManual() {
@@ -599,6 +726,107 @@ func (m *Manager) getBrightness() string {
return v.Value().(string)
}
+func (m *Manager) getBacklightCurveType() {
+ v, err := m.displayConfigMgr.Value(0, DSettingsKeyBacklightCurveType)
+ if err != nil {
+ logger.Warning(err)
+ m.backlightCurveType = "default"
+ brightness.SetCurveType("default")
+ return
+ }
+ m.backlightCurveType = v.Value().(string)
+ logger.Info("Backlight Curve Type:", m.backlightCurveType)
+ brightness.SetCurveType(m.backlightCurveType)
+}
+
+func (m *Manager) getBacklightMinValue() {
+ v, err := m.displayConfigMgr.Value(0, DSettingsKeyBacklightMinValue)
+ if err != nil {
+ logger.Warning(err)
+ m.backlightMinValue = 4
+ return
+ }
+ switch vType := v.Value().(type) {
+ case int64:
+ m.backlightMinValue = int32(vType)
+ case float64:
+ m.backlightMinValue = int32(vType)
+ default:
+ m.backlightMinValue = 4
+ }
+ logger.Info("Backlight min value:", m.backlightMinValue)
+}
+
+func (m *Manager) getBacklightMidValue() {
+ v, err := m.displayConfigMgr.Value(0, DSettingsKeyBacklightMidValue)
+ if err != nil {
+ logger.Warning(err)
+ m.backlightMidValue = 50
+ return
+ }
+ switch vType := v.Value().(type) {
+ case int64:
+ m.backlightMidValue = int32(vType)
+ case float64:
+ m.backlightMidValue = int32(vType)
+ default:
+ m.backlightMidValue = 50
+ }
+ logger.Info("Backlight mid value:", m.backlightMidValue)
+}
+
+func (m *Manager) getCustomBrightnessCurves() {
+ v, err := m.displayConfigMgr.Value(0, DSettingsKeyCustomBrightnessCurves)
+ if err != nil {
+ logger.Warning(err)
+ return
+ }
+
+ jsonStr, ok := v.Value().(string)
+ if !ok {
+ logger.Warning("Custom brightness curves configuration is not a string")
+ return
+ }
+
+ brightness.SetCustomBrightnessCurves(jsonStr)
+ // 更新当前最大缩放值
+ curveMaxScale := brightness.GetCurrentMaxScale()
+
+ m.PropsMu.Lock()
+ m.setPropCurveMaxScale(curveMaxScale)
+ m.PropsMu.Unlock()
+}
+
+func (m *Manager) getDefaultBrightnessCurve() {
+ v, err := m.displayConfigMgr.Value(0, DSettingsKeyDefaultBrightnessCurve)
+ if err != nil {
+ logger.Warning(err)
+ return
+ }
+
+ jsonStr, ok := v.Value().(string)
+ if !ok {
+ logger.Warning("Default brightness curve configuration is not a string")
+ return
+ }
+
+ brightness.SetDefaultBrightnessCurve(jsonStr)
+}
+
+func (m *Manager) getMaxBrightnessUnlimited() {
+ v, err := m.displayConfigMgr.Value(0, DSettingsKeyMaxBrightnessUnlimited)
+ if err != nil {
+ logger.Warning(err)
+ return
+ }
+ enabled, ok := v.Value().(bool)
+ if !ok {
+ logger.Warning("Max brightness unlimited configuration is not a bool")
+ return
+ }
+ brightness.SetMaxBrightnessUnlimited(enabled)
+}
+
// 初始化系统级 display 服务的信号处理
func (m *Manager) initSysDisplay() {
m.sysDisplay.InitSignalExt(m.sysSigLoop, true)
@@ -618,7 +846,10 @@ func (m *Manager) initSysDisplay() {
logger.Debug("get new sysConfig:", spew.Sdump(newSysConfig))
}
- if !m.sessionActive {
+ m.sessionActiveMu.RLock()
+ sessionActive := m.sessionActive
+ m.sessionActiveMu.RUnlock()
+ if !sessionActive {
m.newSysCfg = newSysConfig
return
}
@@ -701,6 +932,14 @@ func (m *Manager) handleSysConfigUpdated(newSysConfig *SysRootConfig) {
go func() {
for _, config := range newMonitorCfgs {
if config.Enabled {
+ // 如果开启了自动亮度且当前处理的是内置显示器,则跳过应用旧的手动亮度
+ builtin := m.getBuiltinMonitor()
+ if builtin != nil && builtin.Name == config.Name && m.AutoBrightnessEnabled && m.autoBrightnessManager != nil {
+ logger.Debugf("Auto brightness enabled, skip manual brightness setup for %s and trigger adjust", config.Name)
+ m.autoBrightnessManager.adjustBrightnessOnce()
+ continue
+ }
+
err := m.setBrightness(config.Name, config.Brightness)
if err != nil {
logger.Warning(err)
@@ -1194,6 +1433,12 @@ func (m *Manager) init() {
// 埋点:启动时记录屏幕信息
m.logDisplayScreenEvent()
+
+ go func() {
+ m.initTransitionConfig()
+ m.initTransitionManager()
+ m.initAutoBrightness()
+ }()
}
// 过滤掉部分模式,尽量不过滤掉 saveMode。
@@ -2268,6 +2513,15 @@ func (m *Manager) applySysMonitorConfigs(mode byte, monitorsId monitorsId, monit
// TODO:色温和设置亮度都调用了setBrightness的接口,逻辑重复了,需要优化。
for _, config := range configs {
if config.Enabled {
+ // 如果开启了自动亮度且当前处理的是内置显示器,则跳过应用旧的手动亮度
+ // 并触发一次自动亮度调节
+ builtin := m.getBuiltinMonitor()
+ if builtin != nil && builtin.Name == config.Name && m.AutoBrightnessEnabled && m.autoBrightnessManager != nil {
+ logger.Debugf("Auto brightness enabled, skip manual brightness setup for %s and trigger adjust", config.Name)
+ m.autoBrightnessManager.adjustBrightnessOnce()
+ continue
+ }
+
err := m.setBrightness(config.Name, config.Brightness)
if err != nil {
logger.Warningf("call setBrightness err: %v, config.Name: %s", err, config.Name)
@@ -3304,7 +3558,6 @@ func (m *Manager) tryToChangeScaleFactor(monitorWidth, monitorHeight uint16) {
}
}
-
// logDisplayScreenEvent records display screen information for event logging
func (m *Manager) logDisplayScreenEvent() {
monitors := m.getConnectedMonitors()
@@ -3323,3 +3576,295 @@ func (m *Manager) logDisplayScreenEvent() {
LogOnStartup(screenCount, displayMode, primaryMonitor)
}
+// getMonitorBrightness 获取显示器当前亮度
+func (m *Manager) getMonitorBrightness(name string) float64 {
+ m.PropsMu.RLock()
+ defer m.PropsMu.RUnlock()
+
+ if v, ok := m.Brightness[name]; ok {
+ return v
+ }
+ return -1 // 返回 -1 表示无法获取
+}
+
+// 手动添加自动亮度相关的属性设置方法
+func (m *Manager) setPropAutoBrightnessEnabled(value bool) (changed bool) {
+ if m.AutoBrightnessEnabled != value {
+ m.AutoBrightnessEnabled = value
+ m.emitPropChangedAutoBrightnessEnabled(value)
+ return true
+ }
+ return false
+}
+
+func (m *Manager) emitPropChangedAutoBrightnessEnabled(value bool) error {
+ return m.service.EmitPropertyChanged(m, "AutoBrightnessEnabled", value)
+}
+
+func (m *Manager) setPropAutoBrightnessSupported(value bool) (changed bool) {
+ if m.AutoBrightnessSupported != value {
+ m.AutoBrightnessSupported = value
+ m.emitPropChangedAutoBrightnessSupported(value)
+ return true
+ }
+ return false
+}
+
+func (m *Manager) emitPropChangedAutoBrightnessSupported(value bool) error {
+ return m.service.EmitPropertyChanged(m, "AutoBrightnessSupported", value)
+}
+
+// 自动亮度管理器集成方法
+
+// initAutoBrightness 初始化自动亮度管理器
+func (m *Manager) initAutoBrightness() {
+ logger.Debug("Initializing auto brightness manager")
+
+ m.autoBrightnessManager = NewAutoBrightnessManager()
+ err := m.autoBrightnessManager.Initialize(m)
+ if err != nil {
+ logger.Info("Auto brightness not supported:", err)
+ m.setPropAutoBrightnessSupported(false)
+ m.setPropAutoBrightnessEnabled(false)
+ return
+ }
+
+ // 设置支持状态
+ m.setPropAutoBrightnessSupported(m.autoBrightnessManager.IsSupported())
+
+ // 获取配置(已在 Initialize 中加载)
+ config, err := m.autoBrightnessManager.getConfig()
+ if err != nil {
+ logger.Warning("Failed to get auto brightness config:", err)
+ config = DefaultAutoBrightnessConfig
+ }
+
+ // 设置启用状态属性
+ m.setPropAutoBrightnessEnabled(config.Enabled)
+
+ // 如果配置启用且支持,则启动
+ if config.Enabled && m.autoBrightnessManager.IsSupported() {
+ err = m.autoBrightnessManager.Start()
+ if err != nil {
+ logger.Warning("Failed to start auto brightness manager:", err)
+ }
+ }
+
+ logger.Info("Auto brightness manager initialized successfully")
+}
+
+// initTransitionManager 初始化亮度过渡管理器
+func (m *Manager) initTransitionManager() {
+ logger.Info("Initializing unified transition manager")
+
+ // 创建统一过渡管理器
+ m.transitionManager = brightness.NewTransitionManager()
+ m.transitionManager.SetEnabled(m.transitionEnabled)
+ m.transitionManager.SetDuration(m.transitionDuration)
+ m.transitionManager.SetStepPercent(m.transitionStepPercent)
+ m.transitionManager.SetMinStepInterval(m.transitionMinStepInterval)
+
+ // 设置亮度类型判断回调
+ m.transitionManager.SetGetBrightnessTypeFunc(func(monitorName string) brightness.BrightnessType {
+ setter := m.getSetterConfig()
+ isBuiltin := m.isBuiltinMonitor(monitorName)
+
+ switch setter {
+ case brightness.BrightnessSetterBacklight:
+ return brightness.TypeBacklight
+ case brightness.BrightnessSetterAuto:
+ if isBuiltin {
+ return brightness.TypeBacklight
+ }
+ return brightness.TypeGamma
+ default: // BrightnessSetterGamma
+ return brightness.TypeGamma
+ }
+ })
+
+ // 设置 Backlight 回调
+ m.transitionManager.SetBacklightFuncs(
+ func(percent float64) error {
+ return brightness.SetBacklight(percent)
+ },
+ func() (float64, error) {
+ return brightness.GetBacklightCurrentValue()
+ },
+ )
+
+ // 设置 Gamma 回调
+ m.transitionManager.SetGammaFuncs(
+ func(monitorName string, percent float64) error {
+ temperature := m.getColorTemperatureValue()
+ monitors := m.getConnectedMonitors()
+ monitor := monitors.GetByName(monitorName)
+ if monitor == nil {
+ return fmt.Errorf("monitor not found: %s", monitorName)
+ }
+ _uuid := monitor.uuid
+ if _useWayland {
+ _uuid = monitor.uuidV0
+ }
+ return brightness.SetOutputGama(percent, temperature, monitor.ID, m.xConn, _uuid)
+ },
+ func(monitorName string) (float64, error) {
+ currentBrightness := m.getMonitorBrightness(monitorName)
+ if currentBrightness < 0 {
+ return 0.5, nil
+ }
+ return currentBrightness, nil
+ },
+ )
+
+ logger.Infof("Unified transition manager initialized: enabled=%v, duration=%dms, stepPercent=%.2f%%, minInterval=%dms",
+ m.transitionEnabled, m.transitionDuration, m.transitionStepPercent, m.transitionMinStepInterval)
+}
+
+// initTransitionConfig 初始化亮度过渡配置
+func (m *Manager) initTransitionConfig() {
+ m.transitionMu.Lock()
+ defer m.transitionMu.Unlock()
+
+ // 默认配置
+ m.transitionEnabled = false
+ m.transitionDuration = 4000
+ m.transitionStepPercent = 1.0
+ m.transitionMinStepInterval = 100
+
+ // 从 DSettings 读取配置
+ getTransitionEnabled := func() {
+ v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionEnabled)
+ if err != nil {
+ logger.Warning(err)
+ m.transitionEnabled = false
+ return
+ }
+ m.transitionEnabled = v.Value().(bool)
+ logger.Info("Transition enabled:", m.transitionEnabled)
+ }
+
+ getTransitionDuration := func() {
+ v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionDuration)
+ if err != nil {
+ logger.Warning(err)
+ m.transitionDuration = 4000
+ return
+ }
+ switch val := v.Value().(type) {
+ case int64:
+ m.transitionDuration = int(val)
+ case float64:
+ m.transitionDuration = int(val)
+ default:
+ m.transitionDuration = 4000
+ }
+ logger.Info("Transition duration:", m.transitionDuration, "ms")
+ }
+
+ getTransitionStepPercent := func() {
+ v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionStepPercent)
+ if err != nil {
+ logger.Warning(err)
+ m.transitionStepPercent = 1.0
+ return
+ }
+ switch val := v.Value().(type) {
+ case int64:
+ m.transitionStepPercent = float64(val)
+ case float64:
+ m.transitionStepPercent = val
+ default:
+ m.transitionStepPercent = 1.0
+ }
+ logger.Info("Transition step percent:", m.transitionStepPercent)
+ }
+
+ getTransitionMinStepInterval := func() {
+ v, err := _dsConfigManager.Value(0, DSettingsKeyTransitionMinStepInterval)
+ if err != nil {
+ logger.Warning(err)
+ m.transitionMinStepInterval = 100
+ return
+ }
+ switch val := v.Value().(type) {
+ case int64:
+ m.transitionMinStepInterval = int(val)
+ case float64:
+ m.transitionMinStepInterval = int(val)
+ default:
+ m.transitionMinStepInterval = 100
+ }
+ logger.Info("Transition min step interval:", m.transitionMinStepInterval, "ms")
+ }
+
+ getTransitionEnabled()
+ getTransitionDuration()
+ getTransitionStepPercent()
+ getTransitionMinStepInterval()
+}
+
+// notifyManualBrightnessChange 通知手动亮度调节
+func (m *Manager) notifyManualBrightnessChange() {
+ if m.autoBrightnessManager != nil {
+ m.autoBrightnessManager.OnManualBrightnessChange()
+ }
+}
+
+// setSystemAdjusting 设置系统调整标志(用于区分系统自动调整和用户手动调整)
+func (m *Manager) isPowerSaving() bool {
+ m.PropsMu.RLock()
+ defer m.PropsMu.RUnlock()
+ return m.powerSaving
+}
+
+// holdAutoBrightness 通知系统休眠
+func (m *Manager) holdAutoBrightness() {
+ if m.autoBrightnessManager != nil {
+ m.autoBrightnessManager.hold()
+ }
+}
+
+// resumeAutoBrightness 通知系统唤醒
+func (m *Manager) resumeAutoBrightness() {
+ if m.autoBrightnessManager != nil {
+ m.autoBrightnessManager.resume()
+ }
+}
+
+// setSystemAdjusting 设置系统调整标志(用于区分系统自动调整和用户手动调整)
+func (m *Manager) setSystemAdjusting(adjusting bool) {
+ if m.autoBrightnessManager != nil {
+ m.autoBrightnessManager.setSystemAdjusting(adjusting)
+ }
+}
+
+// scheduleSystemAdjustingClear 延迟清除系统调整标志
+func (m *Manager) scheduleSystemAdjustingClear(delay time.Duration) {
+ m.systemAdjustingTimerMu.Lock()
+ defer m.systemAdjustingTimerMu.Unlock()
+ if m.systemAdjustingTimer != nil {
+ m.systemAdjustingTimer.Stop()
+ }
+ m.systemAdjustingTimer = time.AfterFunc(delay, func() {
+ m.setSystemAdjusting(false)
+ logger.Debug("system adjusting flag cleared after delay")
+ })
+}
+
+// cleanupAutoBrightness 清理自动亮度资源
+func (m *Manager) cleanupAutoBrightness() {
+ m.systemAdjustingTimerMu.Lock()
+ if m.systemAdjustingTimer != nil {
+ m.systemAdjustingTimer.Stop()
+ m.systemAdjustingTimer = nil
+ }
+ m.systemAdjustingTimerMu.Unlock()
+
+ if m.autoBrightnessManager != nil {
+ err := m.autoBrightnessManager.Cleanup()
+ if err != nil {
+ logger.Warning("Failed to cleanup auto brightness manager:", err)
+ }
+ m.autoBrightnessManager = nil
+ }
+}
diff --git a/display1/manager_ifc.go b/display1/manager_ifc.go
index 0afde107e..303c2ada3 100644
--- a/display1/manager_ifc.go
+++ b/display1/manager_ifc.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -111,9 +111,30 @@ func (m *Manager) AssociateTouchByUUID(outputName, touchUUID string) *dbus.Error
func (m *Manager) ChangeBrightness(raised bool) *dbus.Error {
logger.Debug("dbus call ChangeBrightness", raised)
err := m.changeBrightness(raised)
+ if err == nil {
+ // 通知自动亮度管理器手动调节
+ m.notifyManualBrightnessChange()
+ }
return dbusutil.ToError(err)
}
+// SetAutoBrightnessEnabled 设置自动亮度启用状态
+func (m *Manager) SetAutoBrightnessEnabled(enabled bool) *dbus.Error {
+ logger.Debug("dbus call SetAutoBrightnessEnabled", enabled)
+ if m.autoBrightnessManager == nil {
+ return dbusutil.ToError(errors.New("auto brightness not supported"))
+ }
+
+ err := m.autoBrightnessManager.SetEnabled(enabled)
+ if err != nil {
+ return dbusutil.ToError(err)
+ }
+
+ m.setPropAutoBrightnessEnabled(enabled)
+
+ return nil
+}
+
func (m *Manager) GetBrightness() (map[string]float64, *dbus.Error) {
m.PropsMu.RLock()
defer m.PropsMu.RUnlock()
@@ -159,6 +180,10 @@ func (m *Manager) DeleteCustomMode(name string) *dbus.Error {
// RefreshBrightness 重置亮度,主要被 session/power 模块调用。从配置恢复亮度。
func (m *Manager) RefreshBrightness() *dbus.Error {
logger.Debug("dbus call RefreshBrightness")
+ if m.AutoBrightnessEnabled {
+ logger.Debug("auto brightness enabled, skip RefreshBrightness")
+ return nil
+ }
monitors := m.getConnectedMonitors()
monitorsId := monitors.getMonitorsId()
configs := m.getSuitableSysMonitorConfigs(m.DisplayMode, monitorsId, monitors)
@@ -192,6 +217,9 @@ func (m *Manager) SetAndSaveBrightness(outputName string, value float64) *dbus.E
return dbusutil.ToError(err)
}
+ // 通知自动亮度管理器手动调节
+ m.notifyManualBrightnessChange()
+
err = m.saveBrightnessInCfg(map[string]float64{
outputName: value,
})
@@ -219,6 +247,10 @@ func (m *Manager) SetBrightness(outputName string, value float64) *dbus.Error {
logger.Warning(err)
return dbusutil.ToError(err)
}
+
+ // 通知自动亮度管理器手动调节
+ m.notifyManualBrightnessChange()
+
return nil
}
diff --git a/display1/manager_lid.go b/display1/manager_lid.go
index cc6b11fd5..3c4cdbd65 100644
--- a/display1/manager_lid.go
+++ b/display1/manager_lid.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -6,6 +6,7 @@ package display1
import (
"fmt"
+ "time"
configManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager"
syspower "github.com/linuxdeepin/go-dbus-factory/system/org.deepin.dde.power1"
@@ -25,6 +26,46 @@ const (
func (m *Manager) initLidSwitch() {
logger.Debug("init lid switch")
sysPower := syspower.NewPower(m.sysBus)
+ sysPower.InitSignalExt(m.sysSigLoop, true)
+
+ // 初始化电源模式状态
+ powerMode, err := sysPower.Mode().Get(0)
+ if err != nil {
+ m.PropsMu.Lock()
+ m.powerSaving = false
+ m.PropsMu.Unlock()
+ logger.Warning("failed to get system power mode:", err)
+ } else {
+ m.PropsMu.Lock()
+ m.powerSaving = powerMode == "powersave"
+ m.PropsMu.Unlock()
+ logger.Info("Initial power mode:", powerMode, "powerSaving:", m.powerSaving)
+ }
+
+ // 监听电源模式变化
+ err = sysPower.Mode().ConnectChanged(func(hasValue bool, value string) {
+ if !hasValue {
+ return
+ }
+
+ logger.Debug("system power mode changed:", value)
+ m.setSystemAdjusting(true)
+ m.scheduleSystemAdjustingClear(500 * time.Millisecond)
+ m.PropsMu.Lock()
+ m.powerSaving = value == "powersave"
+ isPowerSaving := m.powerSaving
+ m.PropsMu.Unlock()
+
+ if isPowerSaving {
+ m.holdAutoBrightness()
+ } else {
+ m.resumeAutoBrightness()
+ }
+ })
+ if err != nil {
+ logger.Warning("failed to connect system power mode change:", err)
+ }
+
hasLid, err := sysPower.HasLidSwitch().Get(0)
if err != nil {
logger.Warningf("failed to get lid switch info: %v", err)
@@ -39,15 +80,16 @@ func (m *Manager) initLidSwitch() {
m.builtinMonitor.lidClosed = closed
}
})
- sysPower.InitSignalExt(m.sysSigLoop, true)
sysPower.ConnectLidClosed(func() {
logger.Warning("lid closed signal")
+ m.holdAutoBrightness()
m.handleLidSwitch(sysPower, func(closed bool) {
m.setLidClosed(closed)
})
})
sysPower.ConnectLidOpened(func() {
logger.Warning("lid open signal")
+ m.resumeAutoBrightness()
m.handleLidSwitch(sysPower, func(closed bool) {
m.setLidClosed(closed)
})
diff --git a/display1/monitor.go b/display1/monitor.go
index f18ad21c5..3e77bf4e9 100644
--- a/display1/monitor.go
+++ b/display1/monitor.go
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
+// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
@@ -546,6 +546,15 @@ func (monitors Monitors) GetByUuid(uuid string) *Monitor {
return nil
}
+func (monitors Monitors) GetByUuidAndName(uuid, name string) *Monitor {
+ for _, monitor := range monitors {
+ if monitor.Name == name && monitor.getUuids().Contains(uuid) {
+ return monitor
+ }
+ }
+ return nil
+}
+
const fillModeKeyDelimiter = ":"
func (m *Monitor) generateFillModeKey() string {
diff --git a/display1/sensor_proxy.go b/display1/sensor_proxy.go
new file mode 100644
index 000000000..dcb6f6062
--- /dev/null
+++ b/display1/sensor_proxy.go
@@ -0,0 +1,425 @@
+// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+package display1
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/godbus/dbus/v5"
+ sensorproxy "github.com/linuxdeepin/go-dbus-factory/system/net.hadess.sensorproxy"
+ ofdbus "github.com/linuxdeepin/go-dbus-factory/system/org.freedesktop.dbus"
+ "github.com/linuxdeepin/go-lib/dbusutil"
+ "github.com/linuxdeepin/go-lib/dbusutil/proxy"
+)
+
+// SensorProxyClient 环境光传感器D-Bus客户端
+// 职责:负责从光感服务获取数据并缓存,不负责滤波处理
+type SensorProxyClient struct {
+ sensorProxy sensorproxy.SensorProxy
+ dbusDaemon ofdbus.DBus
+ hasAmbientLight bool
+ claimed bool
+
+ // 事件处理
+ onServiceChange func(bool)
+ onLightLevelChange func(int)
+
+ // 同步控制
+ mutex sync.Mutex
+
+ // 服务监控
+ serviceAvailable bool
+ serviceSigLoop *dbusutil.SignalLoop // 服务监控的 SignalLoop
+
+ // 错误处理
+ maxRetries int
+ retryDelay time.Duration
+
+ // 光感数据缓存
+ lastLightLevel int // 最后一次光感值(lux)
+ lastLightLevelTime time.Time // 最后更新时间
+}
+
+// NewSensorProxyClient 创建新的传感器代理客户端
+func NewSensorProxyClient(proxy sensorproxy.SensorProxy, dbusDaemon ofdbus.DBus) *SensorProxyClient {
+ return &SensorProxyClient{
+ sensorProxy: proxy,
+ dbusDaemon: dbusDaemon,
+ maxRetries: 3,
+ retryDelay: time.Millisecond * 500,
+ lastLightLevel: -1,
+ }
+}
+
+// Connect 连接到SensorProxy服务
+func (c *SensorProxyClient) Connect(sigLoop *dbusutil.SignalLoop) error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if c.sensorProxy == nil {
+ return errors.New("SensorProxy is nil")
+ }
+ // 初始化信号处理
+ c.sensorProxy.InitSignalExt(sigLoop, true)
+ // 检查服务是否可用(带重试机制)
+ err := c.checkServiceAvailableWithRetry()
+ if err != nil {
+ c.serviceAvailable = false
+ return fmt.Errorf("SensorProxy service not available after retries: %w", err)
+ }
+ c.serviceAvailable = true
+ // 检查是否有环境光传感器
+ hasLight, err := c.hasAmbientLightInternal()
+ if err != nil {
+ return fmt.Errorf("failed to check ambient light sensor: %w", err)
+ }
+ c.hasAmbientLight = hasLight
+ if !hasLight {
+ return errors.New("no ambient light sensor available")
+ }
+ // 订阅属性变化信号(LightLevel 等)
+ c.startSignalWatching()
+ // 订阅服务所有者变化信号
+ c.startServiceWatching()
+ return nil
+}
+
+// Disconnect 断开连接
+func (c *SensorProxyClient) Disconnect() error {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ // 释放环境光传感器
+ if c.claimed {
+ err := c.releaseLightInternal()
+ if err != nil {
+ logger.Warning("[SensorProxy] Failed to release light sensor:", err)
+ }
+ c.claimed = false
+ }
+ // 停止服务监控的 SignalLoop
+ if c.serviceSigLoop != nil {
+ c.serviceSigLoop.Stop()
+ c.serviceSigLoop = nil
+ }
+ // 移除所有信号处理器
+ c.sensorProxy.RemoveHandler(proxy.RemoveAllHandlers)
+ c.dbusDaemon.RemoveHandler(proxy.RemoveAllHandlers)
+
+ c.serviceAvailable = false
+ c.hasAmbientLight = false
+ c.lastLightLevel = -1
+ return nil
+}
+
+// ClaimLight 声明对环境光传感器的使用
+func (c *SensorProxyClient) ClaimLight() error {
+ logger.Debug("[SensorProxy] Claiming light sensor")
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if !c.serviceAvailable {
+ return errors.New("SensorProxy service not available")
+ }
+ if !c.hasAmbientLight {
+ return errors.New("no ambient light sensor")
+ }
+ if c.claimed {
+ logger.Debug("[SensorProxy] Light sensor already claimed")
+ return nil
+ }
+ err := c.claimLightWithRetry()
+ if err != nil {
+ return fmt.Errorf("failed to claim light sensor after retries: %w", err)
+ }
+ c.claimed = true
+ return nil
+}
+
+// ReleaseLight 释放环境光传感器
+func (c *SensorProxyClient) ReleaseLight() error {
+ logger.Debug("[SensorProxy] Releasing light sensor")
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ if !c.claimed {
+ logger.Debug("[SensorProxy] Light sensor not claimed")
+ return nil
+ }
+ err := c.releaseLightInternal()
+ if err != nil {
+ logger.Warning("[SensorProxy] Failed to release light sensor:", err)
+ return err
+ }
+ c.claimed = false
+ c.lastLightLevel = -1
+ return nil
+}
+
+// GetCachedLightLevel 获取缓存的环境光强度(返回原始值,不滤波)
+func (c *SensorProxyClient) GetCachedLightLevel() (int, error) {
+ c.mutex.Lock()
+ if !c.serviceAvailable {
+ c.mutex.Unlock()
+ return 0, errors.New("SensorProxy service not available")
+ }
+
+ if !c.hasAmbientLight {
+ c.mutex.Unlock()
+ return 0, errors.New("no ambient light sensor")
+ }
+
+ if !c.claimed {
+ c.mutex.Unlock()
+ return 0, errors.New("light sensor not claimed")
+ }
+
+ needInit := c.lastLightLevel < 0
+ c.mutex.Unlock()
+
+ if needInit {
+ c.initializeCacheFromProperty()
+ }
+
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+
+ if c.lastLightLevel < 0 {
+ return 0, errors.New("no light level data available")
+ }
+
+ logger.Debugf("[AutoBrightness::GetCachedLightLevel] Returning cached raw light level: %d lux", c.lastLightLevel)
+ return c.lastLightLevel, nil
+}
+
+// GetLightLevel 获取实时的环境光强度(从 D-Bus 属性读取)
+func (c *SensorProxyClient) GetLightLevel() (int, error) {
+ c.mutex.Lock()
+ if !c.serviceAvailable {
+ c.mutex.Unlock()
+ return 0, errors.New("SensorProxy service not available")
+ }
+
+ if !c.hasAmbientLight {
+ c.mutex.Unlock()
+ return 0, errors.New("no ambient light sensor")
+ }
+
+ if !c.claimed {
+ c.mutex.Unlock()
+ return 0, errors.New("light sensor not claimed")
+ }
+ c.mutex.Unlock()
+
+ lightLevel, err := c.sensorProxy.LightLevel().Get(0)
+ if err != nil {
+ return 0, fmt.Errorf("failed to get LightLevel property: %w", err)
+ }
+
+ return int(lightLevel), nil
+}
+
+// HasAmbientLight 检查是否有环境光传感器
+func (c *SensorProxyClient) HasAmbientLight() (bool, error) {
+ c.mutex.Lock()
+ if !c.serviceAvailable {
+ c.mutex.Unlock()
+ return false, errors.New("SensorProxy service not available")
+ }
+ c.mutex.Unlock()
+
+ return c.hasAmbientLightInternal()
+}
+
+// SetServiceChangeCallback 设置服务状态变化回调
+func (c *SensorProxyClient) SetServiceChangeCallback(callback func(bool)) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ c.onServiceChange = callback
+}
+
+// SetLightLevelChangeCallback 设置光照值变化回调
+func (c *SensorProxyClient) SetLightLevelChangeCallback(callback func(int)) {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ c.onLightLevelChange = callback
+}
+
+// IsServiceAvailable 检查服务是否可用
+func (c *SensorProxyClient) IsServiceAvailable() bool {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ return c.serviceAvailable
+}
+
+// IsClaimed 检查是否已声明传感器
+func (c *SensorProxyClient) IsClaimed() bool {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
+ return c.claimed
+}
+
+// 内部方法 - 不加锁
+// checkServiceAvailableWithRetry 带重试机制检查服务是否可用
+func (c *SensorProxyClient) checkServiceAvailableWithRetry() error {
+ var lastErr error
+ for i := 0; i < c.maxRetries; i++ {
+ _, err := c.hasAmbientLightInternal()
+ if err == nil {
+ return nil
+ }
+ lastErr = err
+ if i < c.maxRetries-1 {
+ time.Sleep(c.retryDelay)
+ }
+ }
+ return lastErr
+}
+
+// claimLightWithRetry 带重试机制声明环境光传感器
+func (c *SensorProxyClient) claimLightWithRetry() error {
+ var lastErr error
+ for i := 0; i < c.maxRetries; i++ {
+ err := c.claimLightInternal()
+ if err == nil {
+ return nil
+ }
+ lastErr = err
+ if i < c.maxRetries-1 {
+ time.Sleep(c.retryDelay)
+ }
+ }
+ return lastErr
+}
+
+// hasAmbientLightInternal 内部检查环境光传感器
+func (c *SensorProxyClient) hasAmbientLightInternal() (bool, error) {
+ hasLight, err := c.sensorProxy.HasAmbientLight().Get(0)
+ if err != nil {
+ return false, err
+ }
+ return hasLight, nil
+}
+
+// claimLightInternal 内部声明环境光传感器
+func (c *SensorProxyClient) claimLightInternal() error {
+ return c.sensorProxy.ClaimLight(0)
+}
+
+// releaseLightInternal 内部释放环境光传感器
+func (c *SensorProxyClient) releaseLightInternal() error {
+ return c.sensorProxy.ReleaseLight(0)
+}
+
+// startSignalWatching 通过 proxy.Object 订阅属性变化信号
+func (c *SensorProxyClient) startSignalWatching() {
+ _, err := c.sensorProxy.ConnectPropertiesChanged(
+ func(interfaceName string, changedProperties map[string]dbus.Variant,
+ invalidatedProperties []string) {
+ lightLevelVariant, exists := changedProperties["LightLevel"]
+ if !exists {
+ return
+ }
+ lightLevel, ok := lightLevelVariant.Value().(float64)
+ if !ok {
+ logger.Warning("[SensorProxy] Failed to convert LightLevel value to float")
+ return
+ }
+ c.mutex.Lock()
+ claimed := c.claimed
+ c.mutex.Unlock()
+ if claimed {
+ c.lightValueFilter(int(lightLevel))
+ }
+ })
+ if err != nil {
+ logger.Warning("[SensorProxy] Failed to connect PropertiesChanged signal:", err)
+ }
+}
+
+// startServiceWatching 通过 ofdbus.DBus 订阅服务所有者变化信号
+func (c *SensorProxyClient) startServiceWatching() {
+ // 如果已有 sigLoop,先停止它
+ if c.serviceSigLoop != nil {
+ c.serviceSigLoop.Stop()
+ c.serviceSigLoop = nil
+ }
+
+ systemBus, err := dbus.SystemBus()
+ if err != nil {
+ logger.Warning("[SensorProxy] Failed to connect to system bus for service watching:", err)
+ return
+ }
+ sigLoop := dbusutil.NewSignalLoop(systemBus, 10)
+ sigLoop.Start()
+ c.serviceSigLoop = sigLoop
+ c.dbusDaemon.InitSignalExt(sigLoop, true)
+ _, err = c.dbusDaemon.ConnectNameOwnerChanged(
+ func(name, oldOwner, newOwner string) {
+ if name != "net.hadess.SensorProxy" {
+ return
+ }
+ serviceAvailable := newOwner != ""
+ c.mutex.Lock()
+ oldAvailable := c.serviceAvailable
+ c.serviceAvailable = serviceAvailable
+ if !serviceAvailable {
+ c.claimed = false
+ c.hasAmbientLight = false
+ } else if !oldAvailable {
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ hasLight, err := c.HasAmbientLight()
+ if err == nil {
+ c.mutex.Lock()
+ c.hasAmbientLight = hasLight
+ c.mutex.Unlock()
+ }
+ }()
+ }
+ callback := c.onServiceChange
+ c.mutex.Unlock()
+ if callback != nil {
+ go callback(serviceAvailable)
+ }
+ })
+ if err != nil {
+ logger.Warning("[SensorProxy] Failed to connect NameOwnerChanged signal:", err)
+ }
+}
+func (c *SensorProxyClient) lightValueFilter(newValue int) {
+ logger.Infof("[AutoBrightness::RawLightSensor] Raw light sensor value: %d lux", newValue)
+
+ c.mutex.Lock()
+ c.lastLightLevel = newValue
+ c.lastLightLevelTime = time.Now()
+ callback := c.onLightLevelChange
+ c.mutex.Unlock()
+
+ logger.Debugf("[AutoBrightness::CachedLightValue] Cached raw light value: %d lux", newValue)
+
+ if callback != nil {
+ go callback(newValue)
+ }
+}
+
+// initializeCacheFromProperty 从 D-Bus 属性读取当前光照值并缓存
+func (c *SensorProxyClient) initializeCacheFromProperty() {
+ lightLevel, err := c.sensorProxy.LightLevel().Get(0)
+ if err != nil {
+ logger.Warning("[AutoBrightness::LightSensor] Failed to get LightLevel property:", err)
+ return
+ }
+
+ if lightLevel <= 0 {
+ logger.Debug("[AutoBrightness::LightSensor] LightLevel property is zero or negative, skipping cache")
+ return
+ }
+
+ c.mutex.Lock()
+ c.lastLightLevel = int(lightLevel)
+ c.lastLightLevelTime = time.Now()
+ c.mutex.Unlock()
+
+ logger.Infof("[AutoBrightness::LightSensor] Cached raw light value from property: %d lux", int(lightLevel))
+}
diff --git a/docs/auto_backlight/auto_brightness_design.md b/docs/auto_backlight/auto_brightness_design.md
new file mode 100644
index 000000000..b0fef9632
--- /dev/null
+++ b/docs/auto_backlight/auto_brightness_design.md
@@ -0,0 +1,1406 @@
+# 自动亮度调节功能概要设计文档
+
+## 目录
+
+1. [功能概述](#1-功能概述)
+ - 1.1 系统架构图
+2. [核心组件](#2-核心组件)
+ - 2.1 AutoBrightnessManager
+ - 2.2 SensorProxyClient
+ - 2.3 BrightnessTransition
+ - 2.4 配置管理
+3. [状态机](#3-状态机)
+ - 3.1 自动亮度状态转换
+4. [数据结构](#4-数据结构)
+ - 4.1 AutoBrightnessConfig
+ - 4.2 AutoBrightnessManager 状态字段
+ - 4.3 BrightnessTransition 数据结构
+5. [核心流程](#5-核心流程)
+ - 5.1 初始化流程
+ - 5.2 启动流程
+ - 5.3 轮询流程
+ - 5.4 停止流程
+6. [关键算法](#6-关键算法)
+ - 6.1 亮度计算算法
+ - 6.2 调节判断逻辑
+ - 6.3 渐变算法
+ - 6.4 完整数据流图
+7. [亮度渐变机制](#7-亮度渐变机制)
+ - 7.1 渐变流程
+ - 7.2 渐变控制
+ - 7.3 渐变优化
+ - 7.4 自动亮度与渐变集成
+8. [手动调节处理](#8-手动调节处理)
+ - 8.1 两种模式
+ - 8.2 手动调节处理时序
+ - 8.3 系统调整标志
+9. [配置管理](#9-配置管理)
+ - 9.1 DSettings 集成
+ - 9.2 配置项
+ - 9.3 动态更新
+10. [异常处理](#10-异常处理)
+ - 10.1 重试机制
+ - 10.2 优雅降级
+ - 10.3 服务恢复
+11. [并发控制](#11-并发控制)
+ - 11.1 锁策略
+ - 11.2 Goroutine 管理
+12. [系统集成](#12-系统集成)
+ - 12.1 与 Display Manager 集成
+ - 12.2 电源管理集成
+ - 12.3 DBus 接口
+13. [依赖关系](#13-依赖关系)
+ - 13.1 外部依赖
+ - 13.2 内部依赖
+14. [测试要点](#14-测试要点)
+ - 14.1 功能测试
+ - 14.2 异常测试
+ - 14.3 性能测试
+15. [未来扩展](#15-未来扩展)
+ - 15.1 算法优化
+ - 15.2 功能增强
+ - 15.3 性能优化
+16. [典型使用场景](#16-典型使用场景)
+ - 16.1 场景一:用户启用自动亮度
+ - 16.2 场景二:环境光变化触发调节
+ - 16.3 场景三:手动调节后的行为
+ - 16.4 场景四:系统休眠与唤醒
+17. [注意事项](#17-注意事项)
+
+---
+
+## 1. 功能概述
+
+自动亮度调节功能通过环境光传感器自动调整显示器亮度,提升用户体验并节省电能。该功能集成在 StartDDE 的 display 模块中,支持灵活的配置和优雅的降级处理。
+
+### 1.1 系统架构图
+
+```mermaid
+graph TB
+ subgraph "用户层"
+ User[用户]
+ ControlCenter[控制中心]
+ end
+
+ subgraph "StartDDE Display 模块"
+ Manager[Display Manager]
+ ABM[AutoBrightnessManager]
+ BT[BrightnessTransition]
+ Backlight[Backlight 控制]
+ end
+
+ subgraph "配置层"
+ DConf[DSettings/DConf]
+ end
+
+ subgraph "系统服务"
+ SensorProxy[iio-sensor-proxy]
+ Hardware[环境光传感器硬件]
+ end
+
+ subgraph "DBus 通信"
+ DBus[DBus 总线]
+ end
+
+ User -->|手动调节| ControlCenter
+ ControlCenter -->|DBus 调用| Manager
+ Manager -->|管理| ABM
+ Manager -->|使用| BT
+ Manager -->|控制| Backlight
+
+ ABM -->|读取配置| DConf
+ ABM -->|保存配置| DConf
+ BT -->|读取配置| DConf
+
+ ABM -->|DBus 调用| SensorProxy
+ SensorProxy -->|读取| Hardware
+
+ ABM -->|调用| BT
+ BT -->|设置亮度| Backlight
+ ABM -->|设置亮度| Backlight
+
+ Manager -.->|属性通知| DBus
+ ControlCenter -.->|监听属性| DBus
+
+ style ABM fill:#e1f5ff
+ style BT fill:#e1f5ff
+ style Manager fill:#fff4e1
+```
+
+## 2. 核心组件
+
+### 2.1 AutoBrightnessManager
+
+自动亮度管理器,负责整个功能的生命周期管理。
+
+**主要职责:**
+- 初始化和资源管理
+- 配置加载和持久化
+- 传感器数据采集和处理
+- 亮度计算和应用
+- 状态监控和异常处理
+
+### 2.2 SensorProxyClient
+
+传感器代理客户端,封装与 iio-sensor-proxy 服务的交互。
+
+**主要功能:**
+- 连接/断开传感器服务
+- 声明/释放环境光传感器
+- 读取光照强度数据
+- 监听服务状态变化
+
+### 2.3 BrightnessTransition
+
+亮度渐变管理器,提供平滑的亮度过渡效果。
+
+**主要功能:**
+- 渐变效果的启用/禁用控制
+- 渐变参数配置(时长、步进间隔)
+- 多显示器独立渐变状态管理
+- 渐变过程的启动、停止和中断处理
+- 实时亮度值跟踪
+
+### 2.4 配置管理
+
+基于 DSettings (DConfig) 的配置系统,支持动态配置更新。
+
+## 3. 状态机
+
+### 3.1 自动亮度状态转换
+
+```mermaid
+stateDiagram-v2
+ [*] --> 未初始化
+
+ 未初始化 --> 初始化中: Initialize()
+
+ 初始化中 --> 不支持: 检查失败
(无显示器/传感器)
+ 初始化中 --> 已初始化未启用: 检查成功
Enabled=false
+ 初始化中 --> 已初始化已启用: 检查成功
Enabled=true
+
+ 不支持 --> [*]: 功能不可用
+
+ 已初始化未启用 --> 启动中: SetEnabled(true)
+ 已初始化已启用 --> 启动中: Start()
+
+ 启动中 --> 运行中: 传感器声明成功
轮询启动
+ 启动中 --> 已初始化未启用: 启动失败
+
+ 运行中 --> 手动暂停: 用户手动调节
(临时暂停模式)
+ 运行中 --> 停止中: SetEnabled(false)
+ 运行中 --> 休眠暂停: hold()
+ 运行中 --> 服务异常: 传感器服务不可用
+
+ 手动暂停 --> 运行中: 超时恢复
+ 手动暂停 --> 停止中: SetEnabled(false)
+
+ 休眠暂停 --> 运行中: resume()
+ 休眠暂停 --> 停止中: SetEnabled(false)
+
+ 服务异常 --> 不支持: 服务持续不可用
+ 服务异常 --> 运行中: 服务恢复
自动重连
+
+ 停止中 --> 已初始化未启用: 停止完成
+
+ 已初始化未启用 --> 启动中: SetEnabled(true)
+ 已初始化已启用 --> 停止中: SetEnabled(false)
+
+ note right of 运行中
+ - 轮询传感器
+ - 计算亮度
+ - 应用调节
+ end note
+
+ note right of 手动暂停
+ - 释放传感器
+ - 停止调节
+ - 计时等待
+ end note
+
+ note right of 休眠暂停
+ - 停止轮询
+ - 保持状态
+ - 等待唤醒
+ end note
+```
+
+## 4. 数据结构
+
+### 4.1 AutoBrightnessConfig
+
+```go
+type AutoBrightnessConfig struct {
+ Enabled bool // 是否启用
+ Sensitivity float64 // 敏感度 (0.1-3.0)
+ PollingInterval int // 轮询间隔(秒) (1-60)
+ ChangeThreshold float64 // 变化阈值 (1.0-50.0)
+ ManualOverrideDuration int // 手动调节暂停时间(秒) (60-1800)
+ ManualAdjustDisablesAutoMode bool // 手动调节是否禁用自动模式
+ UseTransition bool // 是否使用渐变效果
+}
+```
+
+### 4.2 AutoBrightnessManager 状态字段
+
+- **依赖注入:** manager (复用 display.Manager)
+- **独立组件:** sensorClient, configManager
+- **配置状态:** config, enabled, supported
+- **运行状态:** running, polling, systemAdjusting
+- **历史数据:** lastLightLevel, lastBrightness, lastAdjustTime
+- **手动控制:** manualOverride (时间戳)
+- **轮询控制:** ticker, stopChan, pollingWg
+
+### 4.3 BrightnessTransition 数据结构
+
+**配置字段:**
+- `enabled`: 是否启用渐变
+- `duration`: 从 0% 到 100% 的渐变时长(秒)
+- `stepInterval`: 步进间隔(毫秒)
+
+**状态管理:**
+```go
+type transitionState struct {
+ running bool // 是否正在执行渐变
+ currentValue float64 // 当前渐变的实时亮度值
+ stopCh chan struct{} // 停止信号通道
+ wg sync.WaitGroup // 等待渐变完成
+}
+```
+
+**多显示器支持:**
+- `states map[string]*transitionState`: 每个显示器独立的渐变状态
+
+## 5. 核心流程
+
+### 5.1 初始化流程
+
+```mermaid
+flowchart TD
+ A[Initialize] --> B{检查内置显示器}
+ B -->|不存在| C[返回错误: 无内置显示器]
+ B -->|存在| D{检查亮度调节支持}
+ D -->|不支持| E[返回错误: 不支持亮度调节]
+ D -->|支持| F[创建传感器客户端]
+ F --> G[检查传感器可用性]
+ G -->|不可用| H[返回错误: 传感器不可用]
+ G -->|可用| I[初始化配置管理器]
+ I --> J[加载配置]
+ J -->|失败| K[使用默认配置]
+ J -->|成功| L[设置服务状态回调]
+ K --> L
+ L --> M[标记为已支持]
+ M --> N[初始化完成]
+```
+
+### 5.2 启动流程
+
+```mermaid
+sequenceDiagram
+ participant User as 用户/系统
+ participant ABM as AutoBrightnessManager
+ participant Sensor as SensorProxyClient
+ participant Poller as 轮询器
+
+ User->>ABM: Start()
+ ABM->>ABM: 检查支持状态
+ alt 不支持
+ ABM-->>User: 返回错误
+ else 支持
+ ABM->>ABM: 检查配置是否启用
+ alt 未启用
+ ABM-->>User: 返回 nil
+ else 已启用
+ ABM->>Sensor: Connect()
+ Sensor-->>ABM: 连接成功
+ ABM->>Sensor: ClaimLight() (带重试)
+ loop 最多3次
+ Sensor->>Sensor: 尝试声明传感器
+ alt 成功
+ Sensor-->>ABM: 声明成功
+ else 失败且未达重试上限
+ Sensor->>Sensor: 等待2秒
+ end
+ end
+ alt 声明失败
+ ABM->>Sensor: Disconnect()
+ ABM-->>User: 返回错误
+ else 声明成功
+ ABM->>Poller: startPolling()
+ Poller->>Poller: 创建 ticker
+ Poller->>Poller: 启动 goroutine
+ Poller->>Poller: 立即执行一次采集
+ ABM->>ABM: 更新运行状态
+ ABM-->>User: 启动成功
+ end
+ end
+ end
+```
+
+### 5.3 轮询流程
+
+```mermaid
+flowchart TD
+ A[定时器触发] --> B[pollLightLevel]
+ B --> C{检查运行状态}
+ C -->|未运行| D[返回]
+ C -->|运行中| E{检查手动调节暂停期}
+ E -->|暂停中| D
+ E -->|未暂停| F{传感器已声明?}
+ F -->|否| G[重新声明传感器]
+ G -->|失败| D
+ G -->|成功| H[获取光照强度]
+ F -->|是| H
+ H -->|失败| D
+ H -->|成功| I[processLightChange]
+
+ I --> J[计算目标亮度]
+ J --> K{shouldAdjustBrightness}
+
+ K --> L{手动调节暂停?}
+ L -->|是| M[不调节]
+ L -->|否| N{环境光变化 >= 阈值?}
+ N -->|否| M
+ N -->|是| O{距上次调节 >= 间隔?}
+ O -->|否| M
+ O -->|是| P{亮度变化 >= 5%?}
+ P -->|否| M
+ P -->|是| Q[应用亮度]
+
+ Q --> R{使用渐变?}
+ R -->|是| S[BrightnessTransition.SetBrightnessForced]
+ R -->|否| T[setBrightnessRaw]
+ S --> U[更新历史状态]
+ T --> U
+ U --> V[记录光照/亮度/时间]
+ V --> D
+ M --> D
+```
+
+### 5.4 停止流程
+
+```mermaid
+sequenceDiagram
+ participant User as 用户/系统
+ participant ABM as AutoBrightnessManager
+ participant Poller as 轮询器
+ participant Sensor as SensorProxyClient
+ participant Display as 显示器
+
+ User->>ABM: Stop()
+ ABM->>ABM: 检查运行状态
+ alt 未运行
+ ABM-->>User: 返回 nil
+ else 运行中
+ ABM->>Poller: stopPolling()
+ Poller->>Poller: 停止 ticker
+ Poller->>Poller: 发送停止信号
+ Poller->>Poller: 等待 goroutine 退出
+ Poller-->>ABM: 停止完成
+
+ ABM->>Display: restoreSavedBrightness()
+ Display->>Display: 获取保存的亮度
+ Display->>Display: 应用亮度
+ Display-->>ABM: 恢复完成
+
+ ABM->>Sensor: ReleaseLight()
+ Sensor-->>ABM: 释放完成
+
+ ABM->>Sensor: Disconnect()
+ Sensor-->>ABM: 断开完成
+
+ ABM->>ABM: 更新运行状态
+ ABM-->>User: 停止成功
+ end
+```
+
+## 6. 关键算法
+
+### 6.1 亮度计算算法
+
+```
+目标亮度 = min(max((光照强度 × 敏感度) / 255, 0.1), 1.0)
+```
+
+**说明:**
+- 线性映射:光照强度 0-255 lux → 亮度 0.0-1.0
+- 敏感度调整:支持 0.1-3.0 倍率
+- 最小亮度保护:不低于 10%,避免屏幕过暗
+
+### 6.2 调节判断逻辑
+
+满足以下所有条件才执行调节:
+
+1. **不在手动调节暂停期**
+2. **环境光变化超过阈值**:`|当前光照 - 上次光照| >= ChangeThreshold`
+3. **距上次调节时间足够**:`当前时间 - 上次调节时间 >= PollingInterval`
+4. **亮度变化足够大**:`|目标亮度 - 当前亮度| >= 5%`
+
+### 6.3 渐变算法
+
+**基本原理:**
+将亮度变化分解为多个小步进,在一定时间内逐步完成。
+
+**参数计算:**
+```
+实际渐变时长 = 配置时长 × |亮度差值|
+步进次数 = 实际渐变时长 / 步进间隔
+每步变化量 = 亮度差值 / 步进次数
+```
+
+**示例:**
+- 配置时长:4 秒(0-100% 的时间)
+- 步进间隔:100 毫秒
+- 亮度变化:30% → 80%(差值 50%)
+- 实际时长:4 × 0.5 = 2 秒
+- 步进次数:2000ms / 100ms = 20 步
+- 每步变化:0.5 / 20 = 0.025 (2.5%)
+
+**优化策略:**
+- 最小渐变时长:2 × 步进间隔(避免过短渐变)
+- 变化太小时直接设置(< 0.1%)
+- 支持中途停止和新渐变覆盖
+
+### 6.4 完整数据流图
+
+```mermaid
+flowchart TB
+ Start([定时器触发]) --> A[读取环境光传感器]
+ A -->|光照强度 lux| B[应用敏感度系数]
+ B -->|调整后光照| C[线性映射到 0.0-1.0]
+ C -->|原始亮度| D[应用最小亮度保护]
+ D -->|目标亮度| E{检查调节条件}
+
+ E -->|手动暂停中| End1([跳过])
+ E -->|光照变化 < 阈值| End1
+ E -->|时间间隔不足| End1
+ E -->|亮度变化 < 5%| End1
+ E -->|所有条件满足| F{使用渐变?}
+
+ F -->|否| G[直接设置亮度]
+ F -->|是| H[计算渐变参数]
+
+ H --> I[实际时长 = 配置时长 × 差值]
+ I --> J[步进次数 = 时长 / 间隔]
+ J --> K{时长 >= 最小值?}
+
+ K -->|否| G
+ K -->|是| L[启动渐变 goroutine]
+
+ L --> M[循环步进]
+ M --> N[更新实时亮度]
+ N --> O[调用硬件接口]
+ O --> P{到达目标?}
+
+ P -->|否| Q[等待步进间隔]
+ Q --> M
+ P -->|是| R[同步 DBus 属性]
+
+ G --> S[调用硬件接口]
+ S --> R
+
+ R --> T[更新历史状态]
+ T --> End2([完成])
+
+ style A fill:#e1f5ff
+ style B fill:#e1f5ff
+ style C fill:#e1f5ff
+ style D fill:#e1f5ff
+ style H fill:#fff4e1
+ style L fill:#fff4e1
+ style M fill:#fff4e1
+```
+
+## 7. 亮度渐变机制
+
+### 7.1 渐变流程
+
+```mermaid
+flowchart TD
+ A[SetBrightness] --> B{检查启用状态}
+ B -->|未启用且非强制| C[直接设置亮度]
+ B -->|已启用或强制| D[获取当前亮度]
+
+ D --> E{正在渐变?}
+ E -->|是| F[使用实时亮度值]
+ E -->|否| G[从 Manager 获取]
+
+ F --> H[计算亮度差值]
+ G --> H
+
+ H --> I{差值 < 0.001?}
+ I -->|是| J[无需调整,返回]
+ I -->|否| K[停止之前的渐变]
+
+ K --> L[计算渐变参数]
+ L --> M[实际时长 = 配置时长 × |差值|]
+ M --> N[步进次数 = 实际时长 / 步进间隔]
+ N --> O[每步变化 = 差值 / 步进次数]
+
+ O --> P{实际时长 < 最小时长?}
+ P -->|是| C
+ P -->|否| Q[标记渐变开始]
+
+ Q --> R[启动 goroutine]
+ R --> S[返回不等待]
+
+ R --> T[渐变循环]
+ T --> U{收到停止信号?}
+ U -->|是| V[更新实时值]
+ U -->|否| W[计算下一个亮度]
+
+ W --> X[更新实时值]
+ X --> Y[setBrightnessRaw]
+ Y --> Z{到达目标值?}
+ Z -->|是| AA[同步 DBus 属性]
+ Z -->|否| AB{最后一步?}
+
+ AB -->|否| AC[等待步进间隔或停止信号]
+ AC --> U
+ AB -->|是| AA
+
+ V --> AA
+ AA --> AD[标记渐变结束]
+ AD --> AE[goroutine 退出]
+```
+
+### 7.2 渐变控制
+
+```mermaid
+sequenceDiagram
+ participant Caller as 调用者
+ participant BT as BrightnessTransition
+ participant State as transitionState
+ participant Worker as 渐变 Goroutine
+ participant HW as 硬件
+
+ Note over Caller,HW: 场景1: 启动新渐变
+ Caller->>BT: SetBrightness(monitor, 0.8)
+ BT->>BT: 获取当前亮度 0.3
+ BT->>State: 检查是否正在渐变
+ State-->>BT: running = false
+ BT->>BT: 计算参数 (差值0.5, 20步)
+ BT->>State: 标记 running = true
+ BT->>State: wg.Add(1)
+ BT->>Worker: 启动 goroutine
+ BT-->>Caller: 立即返回
+
+ loop 20次步进
+ Worker->>State: 更新 currentValue
+ Worker->>HW: setBrightnessRaw
+ Worker->>Worker: 等待100ms
+ end
+ Worker->>HW: 同步 DBus 属性
+ Worker->>State: 标记 running = false
+ Worker->>State: wg.Done()
+
+ Note over Caller,HW: 场景2: 中断现有渐变
+ Caller->>BT: SetBrightness(monitor, 0.5)
+ BT->>State: 检查是否正在渐变
+ State-->>BT: running = true, currentValue = 0.6
+ BT->>State: 发送停止信号
+ State->>Worker: stopCh <- signal
+ Worker->>Worker: 收到停止信号
+ Worker->>State: 更新最终 currentValue
+ Worker->>HW: 同步 DBus 属性
+ Worker->>State: wg.Done()
+ BT->>State: wg.Wait() 等待退出
+ BT->>BT: 使用 currentValue 0.6 作为起点
+ BT->>Worker: 启动新渐变 (0.6 -> 0.5)
+ BT-->>Caller: 立即返回
+```
+
+**启动渐变:**
+- 标记 `running = true`
+- 增加 WaitGroup 计数
+- 启动独立 goroutine 执行
+
+**停止渐变:**
+- 发送停止信号到 `stopCh`
+- 等待 WaitGroup 完成
+- 清空残留信号
+
+**中断处理:**
+- 新渐变会先停止旧渐变
+- 使用实时亮度值作为起点
+- 确保平滑过渡
+
+### 7.3 渐变优化
+
+**性能优化:**
+- 每个显示器独立渐变状态
+- 非阻塞启动(立即返回)
+- 只在完成时同步一次属性
+
+**用户体验优化:**
+- 变化太小时直接设置(< 0.1%)
+- 渐变时长按比例缩放
+- 最小渐变时长保护(200ms)
+
+**资源管理:**
+- 使用 WaitGroup 确保 goroutine 正确退出
+- 停止信号使用缓冲通道避免阻塞
+- 清理残留信号防止误触发
+
+### 7.4 自动亮度与渐变集成
+
+**两种调用方式:**
+
+1. **SetBrightness(常规)**
+ - 检查全局 `enabled` 标志
+ - 未启用时直接设置亮度
+
+2. **SetBrightnessForced(强制)**
+ - 忽略全局 `enabled` 标志
+ - 自动亮度专用,确保渐变生效
+
+**配置独立性:**
+- 自动亮度有独立的 `UseTransition` 配置
+- 可以在全局渐变关闭时仍使用渐变
+- 通过 `SetBrightnessForced` 实现
+
+**属性同步:**
+- 渐变过程中不同步属性(减少信号)
+- 只在渐变完成或停止时同步一次
+- 失败时立即同步确保一致性
+
+## 8. 手动调节处理
+
+### 8.1 两种模式
+
+```mermaid
+stateDiagram-v2
+ [*] --> 自动调节运行中
+
+ 自动调节运行中 --> 检查配置: 用户手动调节亮度
+
+ 检查配置 --> 临时暂停模式: ManualAdjustDisablesAutoMode = false
+ 检查配置 --> 完全禁用模式: ManualAdjustDisablesAutoMode = true
+
+ 临时暂停模式 --> 记录暂停时间
+ 记录暂停时间 --> 释放传感器
+ 释放传感器 --> 暂停状态
+
+ 暂停状态 --> 检查超时: 每次轮询检查
+ 检查超时 --> 暂停状态: 未超时
+ 检查超时 --> 重新声明传感器: 超时
+ 重新声明传感器 --> 自动调节运行中
+
+ 完全禁用模式 --> 更新配置Enabled=false
+ 更新配置Enabled=false --> 停止功能
+ 停止功能 --> 已禁用状态
+
+ 已禁用状态 --> 自动调节运行中: 用户手动启用
+```
+
+**模式一:临时暂停(默认)**
+- 手动调节后暂停自动调节指定时间
+- 暂停期间释放传感器资源
+- 超时后自动恢复
+
+**模式二:完全禁用**
+- 手动调节后永久禁用自动亮度
+- 更新配置并停止功能
+- 需用户手动重新启用
+
+### 8.2 手动调节处理时序
+
+```mermaid
+sequenceDiagram
+ participant User as 用户
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+ participant Sensor as SensorProxyClient
+ participant Config as 配置系统
+
+ Note over User,Config: 场景1: 临时暂停模式
+ User->>Manager: 手动调节亮度
+ Manager->>Manager: setBrightness()
+ Manager->>ABM: OnManualBrightnessChange()
+ ABM->>ABM: 检查 systemAdjusting
+ alt systemAdjusting = true
+ ABM->>ABM: 忽略(系统调整)
+ else systemAdjusting = false
+ ABM->>ABM: 检查配置模式
+ alt 临时暂停模式
+ ABM->>ABM: 记录 manualOverride 时间
+ ABM->>Sensor: ReleaseLight()
+ Note over ABM: 暂停 300 秒
+ ABM->>ABM: 轮询时检查超时
+ ABM->>Sensor: ClaimLight() (超时后)
+ ABM->>ABM: 恢复自动调节
+ end
+ end
+
+ Note over User,Config: 场景2: 完全禁用模式
+ User->>Manager: 手动调节亮度
+ Manager->>Manager: setBrightness()
+ Manager->>ABM: OnManualBrightnessChange()
+ ABM->>ABM: 检查配置模式
+ alt 完全禁用模式
+ ABM->>Config: 保存 Enabled = false
+ ABM->>ABM: Stop()
+ ABM->>Manager: setPropAutoBrightnessEnabled(false)
+ end
+```
+
+### 8.3 系统调整标志
+
+通过 `systemAdjusting` 标志区分系统自动调整(如节能模式)和用户手动调整,避免误判。
+
+```mermaid
+sequenceDiagram
+ participant Power as 电源管理
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+
+ Note over Power,ABM: 系统调整场景(节能模式)
+ Power->>Manager: 节能模式降低亮度
+ Manager->>ABM: setSystemAdjusting(true)
+ Manager->>Manager: setBrightness(0.3)
+ Manager->>ABM: OnManualBrightnessChange()
+ ABM->>ABM: 检查 systemAdjusting = true
+ ABM->>ABM: 忽略此次调用
+ Manager->>ABM: setSystemAdjusting(false)
+
+ Note over Power,ABM: 用户手动调整场景
+ Power->>Manager: 用户拖动亮度滑块
+ Manager->>Manager: setBrightness(0.8)
+ Manager->>ABM: OnManualBrightnessChange()
+ ABM->>ABM: 检查 systemAdjusting = false
+ ABM->>ABM: 执行暂停或禁用逻辑
+```
+
+## 9. 配置管理
+
+### 9.1 DSettings 集成
+
+- **AppID:** `org.deepin.startdde`
+- **配置名:** `org.deepin.startdde.display`
+- **配置键前缀:** `autobrightness-*`
+
+### 9.2 配置项
+
+**自动亮度配置:**
+
+| 配置键 | 类型 | 默认值 | 说明 |
+|--------|------|--------|------|
+| enabled | bool | false | 是否启用 |
+| sensitivity | float64 | 0.5 | 敏感度 |
+| polling-interval | int | 3 | 轮询间隔(秒) |
+| change-threshold | float64 | 20.0 | 变化阈值 |
+| manual-override-duration | int | 300 | 手动暂停时间(秒) |
+| manual-adjust-disables-auto-mode | bool | true | 手动调节是否禁用 |
+| use-transition | bool | true | 是否使用渐变 |
+
+**渐变效果配置:**
+
+| 配置键 | 类型 | 默认值 | 说明 |
+|--------|------|--------|------|
+| transition-enabled | bool | true | 全局渐变开关 |
+| transition-duration | int | 4 | 0-100% 渐变时长(秒) |
+| transition-step-interval | int | 100 | 步进间隔(毫秒) |
+
+### 9.3 动态更新
+
+监听配置文件变化,自动重新加载并应用新配置。敏感度变化时立即触发一次亮度调整。
+
+```mermaid
+sequenceDiagram
+ participant User as 用户/控制中心
+ participant DConf as DSettings/DConf
+ participant ABM as AutoBrightnessManager
+ participant BT as BrightnessTransition
+ participant Poller as 轮询器
+
+ Note over User,Poller: 自动亮度配置变更
+ User->>DConf: 修改配置 (如 sensitivity)
+ DConf->>ABM: ValueChanged 信号
+ ABM->>DConf: 读取新配置
+ DConf-->>ABM: 返回配置值
+ ABM->>ABM: OnConfigChanged()
+
+ alt 启用状态变化
+ ABM->>ABM: Start() 或 Stop()
+ else 轮询参数变化
+ ABM->>Poller: stopPolling()
+ ABM->>Poller: startPolling()
+ else 敏感度变化
+ ABM->>ABM: adjustBrightnessOnce()
+ Note over ABM: 使用新敏感度立即调整
+ end
+
+ Note over User,Poller: 渐变配置变更
+ User->>DConf: 修改渐变配置
+ DConf->>BT: ValueChanged 信号
+ BT->>DConf: 读取新配置
+ DConf-->>BT: 返回配置值
+
+ alt transition-enabled 变化
+ BT->>BT: SetEnabled(newValue)
+ else transition-duration 变化
+ BT->>BT: SetDuration(newValue)
+ else transition-step-interval 变化
+ BT->>BT: SetStepInterval(newValue)
+ end
+
+ Note over BT: 新配置立即生效
下次调节时使用
+```
+
+## 10. 异常处理
+
+### 10.1 重试机制
+
+- 传感器声明失败:最多重试 3 次,间隔 2 秒
+- 亮度设置失败:下次轮询自动重试
+
+### 10.2 优雅降级
+
+- 传感器服务不可用:停止功能但不崩溃
+- 配置加载失败:使用默认配置
+- 内置显示器不存在:标记为不支持
+
+### 10.3 服务恢复
+
+监听 iio-sensor-proxy 服务状态,服务恢复后自动重新初始化。
+
+```mermaid
+sequenceDiagram
+ participant Sensor as iio-sensor-proxy
+ participant Client as SensorProxyClient
+ participant ABM as AutoBrightnessManager
+ participant Manager as Display Manager
+
+ Note over Sensor,Manager: 服务异常场景
+ Sensor->>Sensor: 服务崩溃/重启
+ Sensor->>Client: NameOwnerChanged 信号
+ Client->>Client: 检测到服务不可用
+ Client->>ABM: onServiceChange(false)
+ ABM->>ABM: stopPolling()
+ ABM->>ABM: 标记 supported = false
+ ABM->>Manager: setPropAutoBrightnessSupported(false)
+
+ Note over Sensor,Manager: 服务恢复场景
+ Sensor->>Sensor: 服务恢复
+ Sensor->>Client: NameOwnerChanged 信号
+ Client->>Client: 检测到服务可用
+ Client->>ABM: onServiceChange(true)
+ ABM->>Client: HasAmbientLight()
+ Client-->>ABM: true
+ ABM->>ABM: 标记 supported = true
+ ABM->>Manager: setPropAutoBrightnessSupported(true)
+
+ alt 配置已启用
+ ABM->>ABM: Start()
+ ABM->>Client: Connect()
+ ABM->>Client: ClaimLight()
+ ABM->>ABM: startPolling()
+ Note over ABM: 自动恢复功能
+ end
+```
+
+## 11. 并发控制
+
+### 11.1 锁策略
+
+- 使用 `sync.RWMutex` 保护共享状态
+- 读多写少场景使用读锁
+- 避免在持有锁时执行耗时操作
+
+```mermaid
+graph TD
+ A[公共方法调用] --> B{需要修改状态?}
+ B -->|是| C[获取写锁 Lock]
+ B -->|否| D[获取读锁 RLock]
+
+ C --> E{需要耗时操作?}
+ E -->|是| F[释放锁]
+ E -->|否| G[执行操作]
+
+ F --> H[执行耗时操作
如 Start/Stop]
+ H --> I[必要时重新获取锁]
+
+ G --> J[释放写锁 Unlock]
+ D --> K[读取状态]
+ K --> L[释放读锁 RUnlock]
+
+ I --> J
+ J --> M[返回]
+ L --> M
+```
+
+### 11.2 Goroutine 管理
+
+- 轮询 goroutine:通过 `stopChan` 和 `WaitGroup` 安全退出
+- 配置更新回调:异步执行,避免阻塞
+- 幂等停止:`stopPolling()` 可安全多次调用
+
+```mermaid
+sequenceDiagram
+ participant Main as 主线程
+ participant Poller as 轮询 Goroutine
+ participant Worker as 渐变 Goroutine
+
+ Note over Main,Worker: Goroutine 生命周期管理
+
+ Main->>Main: startPolling()
+ Main->>Main: wg.Add(1)
+ Main->>Poller: 启动 goroutine
+ Main->>Main: 返回(不等待)
+
+ loop 轮询循环
+ Poller->>Poller: 等待 ticker 或 stopChan
+ alt 收到 ticker
+ Poller->>Poller: pollLightLevel()
+ else 收到 stopChan
+ Poller->>Poller: wg.Done()
+ Poller->>Poller: 退出
+ end
+ end
+
+ Main->>Main: stopPolling()
+ Main->>Poller: stopChan <- signal
+ Main->>Main: 释放锁
+ Main->>Main: wg.Wait()
+ Note over Main: 等待 goroutine 退出
+ Main->>Main: 重新获取锁
+ Main->>Main: 清空 stopChan
+
+ Note over Main,Worker: 渐变 Goroutine 管理
+ Main->>Main: SetBrightness()
+ Main->>Main: state.wg.Add(1)
+ Main->>Worker: 启动 goroutine
+ Main->>Main: 返回(不等待)
+
+ Worker->>Worker: 执行渐变
+ alt 完成
+ Worker->>Worker: state.wg.Done()
+ else 被停止
+ Worker->>Worker: state.wg.Done()
+ end
+
+ Main->>Main: stopState()
+ Main->>Worker: stopCh <- signal
+ Main->>Main: state.wg.Wait()
+ Note over Main: 等待渐变完成
+```
+
+## 12. 系统集成
+
+### 12.1 与 Display Manager 集成
+
+- 复用 Manager 的显示器管理能力
+- 复用 BrightnessTransition 渐变功能
+- 同步更新 DBus 属性
+
+```mermaid
+graph LR
+ ABM[AutoBrightnessManager] -->|依赖注入| Manager[Display Manager]
+ ABM -->|调用| BT[BrightnessTransition]
+ ABM -->|调用| getBuiltinMonitor
+ ABM -->|调用| canSetBrightness
+ ABM -->|调用| setBrightnessRaw
+ ABM -->|调用| syncPropBrightness
+
+ Manager -->|创建| ABM
+ Manager -->|初始化| BT
+ Manager -->|暴露| DBusAPI[DBus API]
+
+ BT -->|调用| setBrightnessRaw
+ BT -->|调用| syncPropBrightness
+```
+
+### 12.2 电源管理集成
+
+- 支持系统休眠/唤醒事件
+- `hold()`: 休眠前暂停轮询
+- `resume()`: 唤醒后恢复轮询
+
+```mermaid
+sequenceDiagram
+ participant PM as 电源管理
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+ participant Poller as 轮询器
+ participant Display as 显示器
+
+ Note over PM,Display: 系统休眠场景
+ PM->>Manager: PrepareForSleep(true)
+ Manager->>ABM: hold()
+ ABM->>ABM: 设置 systemAdjusting = true
+ ABM->>Poller: stopPolling()
+ Poller->>Poller: 停止 ticker
+ Poller->>Poller: 等待 goroutine 退出
+ Note over ABM: 保持运行状态
但停止轮询
+
+ Note over PM,Display: 系统唤醒场景
+ PM->>Manager: PrepareForSleep(false)
+ Manager->>ABM: resume()
+ ABM->>Poller: startPolling()
+ Poller->>Poller: 创建新 ticker
+ Poller->>Poller: 启动 goroutine
+ Poller->>Poller: 立即采集一次
+ ABM->>ABM: 清除 systemAdjusting
+ Note over ABM: 恢复正常运行
+
+ Note over PM,Display: 亮度变化不触发手动调节检测
+ ABM->>Display: 调整亮度
+ Display->>Manager: 亮度变化通知
+ Manager->>ABM: OnManualBrightnessChange()
+ ABM->>ABM: 检查 systemAdjusting
+ Note over ABM: systemAdjusting = true
忽略此次调用
+```
+
+### 12.3 DBus 接口
+
+通过 Manager 暴露以下属性和方法:
+- `AutoBrightnessSupported` (只读)
+- `AutoBrightnessEnabled` (读写)
+- 配置相关的 Get/Set 方法
+
+```mermaid
+sequenceDiagram
+ participant App as 应用程序
+ participant DBus as DBus
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+
+ Note over App,ABM: 查询支持状态
+ App->>DBus: Get AutoBrightnessSupported
+ DBus->>Manager: 读取属性
+ Manager->>ABM: IsSupported()
+ ABM-->>Manager: true/false
+ Manager-->>DBus: 返回值
+ DBus-->>App: true/false
+
+ Note over App,ABM: 启用自动亮度
+ App->>DBus: Set AutoBrightnessEnabled = true
+ DBus->>Manager: 设置属性
+ Manager->>ABM: SetEnabled(true)
+ ABM->>ABM: Start()
+ ABM-->>Manager: 成功
+ Manager->>DBus: PropertiesChanged 信号
+ DBus->>App: 属性变化通知
+
+ Note over App,ABM: 查询状态信息
+ App->>DBus: Call GetStatus()
+ DBus->>Manager: 调用方法
+ Manager->>ABM: GetStatus()
+ ABM-->>Manager: 状态 map
+ Manager-->>DBus: 返回状态
+ DBus-->>App: 状态信息
+```
+
+## 13. 依赖关系
+
+### 13.1 外部依赖
+
+- **iio-sensor-proxy:** 提供环境光传感器数据
+- **DSettings/DConfig:** 配置管理
+- **DBus:** 进程间通信
+
+### 13.2 内部依赖
+
+- **display.Manager:** 显示器管理和亮度控制
+- **BrightnessTransition:** 亮度渐变效果
+- **backlight:** 底层亮度控制
+
+## 14. 测试要点
+
+### 14.1 功能测试
+
+- 基本启停流程
+- 配置加载和保存
+- 亮度计算准确性
+- 手动调节处理
+
+```mermaid
+graph TD
+ subgraph "基本功能测试"
+ T1[初始化测试] --> T1A{检查支持状态}
+ T1A -->|支持| T1B[验证 supported = true]
+ T1A -->|不支持| T1C[验证 supported = false]
+
+ T2[启动测试] --> T2A[调用 Start]
+ T2A --> T2B{检查传感器}
+ T2B -->|成功| T2C[验证 running = true]
+ T2B -->|失败| T2D[验证错误处理]
+
+ T3[轮询测试] --> T3A[模拟光照变化]
+ T3A --> T3B[等待轮询周期]
+ T3B --> T3C[验证亮度调整]
+
+ T4[停止测试] --> T4A[调用 Stop]
+ T4A --> T4B[验证资源释放]
+ T4B --> T4C[验证 running = false]
+ end
+
+ subgraph "手动调节测试"
+ T5[临时暂停模式] --> T5A[手动调节亮度]
+ T5A --> T5B[验证暂停状态]
+ T5B --> T5C[等待超时]
+ T5C --> T5D[验证自动恢复]
+
+ T6[完全禁用模式] --> T6A[修改配置]
+ T6A --> T6B[手动调节亮度]
+ T6B --> T6C[验证功能停止]
+ T6C --> T6D[验证配置更新]
+ end
+
+ subgraph "渐变测试"
+ T7[渐变效果] --> T7A[设置目标亮度]
+ T7A --> T7B[验证渐变启动]
+ T7B --> T7C[监控步进过程]
+ T7C --> T7D[验证到达目标]
+
+ T8[渐变中断] --> T8A[启动渐变]
+ T8A --> T8B[发起新渐变]
+ T8B --> T8C[验证旧渐变停止]
+ T8C --> T8D[验证新渐变启动]
+ end
+```
+
+### 14.2 异常测试
+
+- 传感器服务不可用
+- 配置文件损坏
+- 并发访问
+- 资源泄漏
+
+```mermaid
+graph TD
+ subgraph "异常场景测试"
+ E1[传感器服务异常] --> E1A[停止 iio-sensor-proxy]
+ E1A --> E1B[验证服务不可用检测]
+ E1B --> E1C[验证优雅降级]
+ E1C --> E1D[重启服务]
+ E1D --> E1E[验证自动恢复]
+
+ E2[配置异常] --> E2A[损坏配置文件]
+ E2A --> E2B[尝试加载配置]
+ E2B --> E2C[验证使用默认配置]
+
+ E3[并发测试] --> E3A[多线程调用]
+ E3A --> E3B[验证无死锁]
+ E3B --> E3C[验证数据一致性]
+
+ E4[资源泄漏] --> E4A[反复启停]
+ E4A --> E4B[监控 goroutine 数量]
+ E4B --> E4C[监控内存使用]
+ E4C --> E4D[验证资源正确释放]
+ end
+
+ subgraph "边界条件测试"
+ B1[极端光照值] --> B1A[测试 0 lux]
+ B1A --> B1B[测试 255 lux]
+ B1B --> B1C[测试超出范围值]
+
+ B2[极端配置] --> B2A[最小轮询间隔]
+ B2A --> B2B[最大敏感度]
+ B2B --> B2C[最小阈值]
+
+ B3[快速变化] --> B3A[光照快速波动]
+ B3A --> B3B[验证防抖动]
+ B3B --> B3C[验证调节频率限制]
+ end
+```
+
+### 14.3 性能测试
+
+- CPU 占用率
+- 内存使用
+- 响应延迟
+- 长时间运行稳定性
+
+```mermaid
+graph LR
+ subgraph "性能指标"
+ P1[CPU 占用] --> P1A[空闲时 < 0.5%]
+ P1A --> P1B[调节时 < 2%]
+
+ P2[内存使用] --> P2A[基础内存 < 5MB]
+ P2A --> P2B[无内存泄漏]
+
+ P3[响应延迟] --> P3A[光照变化到调节 < 5s]
+ P3A --> P3B[配置变更生效 < 1s]
+
+ P4[稳定性] --> P4A[24小时运行测试]
+ P4A --> P4B[无崩溃无异常]
+ end
+
+ subgraph "压力测试"
+ S1[高频调节] --> S1A[每秒变化光照]
+ S1A --> S1B[验证系统稳定]
+
+ S2[长时间运行] --> S2A[连续运行7天]
+ S2A --> S2B[监控资源使用]
+ S2B --> S2C[验证无退化]
+
+ S3[并发压力] --> S3A[多线程频繁调用]
+ S3A --> S3B[验证锁性能]
+ S3B --> S3C[验证无竞争条件]
+ end
+```
+
+## 15. 未来扩展
+
+### 15.1 算法优化
+
+- 支持非线性亮度曲线
+- 机器学习自适应调节
+- 时间段相关的亮度策略
+
+### 15.2 功能增强
+
+- 多显示器独立控制
+- 色温自动调节
+- 用户习惯学习
+
+### 15.3 性能优化
+
+- 事件驱动替代轮询
+- 智能采样频率调整
+- 更精细的电源管理
+
+## 16. 典型使用场景
+
+### 16.1 场景一:用户启用自动亮度
+
+```mermaid
+sequenceDiagram
+ participant User as 用户
+ participant CC as 控制中心
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+ participant Sensor as 传感器
+ participant Display as 显示器
+
+ User->>CC: 打开自动亮度开关
+ CC->>Manager: SetAutoBrightnessEnabled(true)
+ Manager->>ABM: SetEnabled(true)
+ ABM->>ABM: 保存配置
+ ABM->>ABM: Start()
+ ABM->>Sensor: 连接并声明传感器
+ Sensor-->>ABM: 成功
+ ABM->>ABM: 启动轮询
+
+ loop 每3秒
+ ABM->>Sensor: 读取光照强度
+ Sensor-->>ABM: 150 lux
+ ABM->>ABM: 计算目标亮度 58%
+ ABM->>Display: 渐变调整到 58%
+ Display->>Display: 平滑过渡
+ end
+
+ Manager->>CC: PropertiesChanged
+ CC->>User: 显示已启用状态
+```
+
+### 16.2 场景二:环境光变化触发调节
+
+```mermaid
+sequenceDiagram
+ participant Env as 环境
+ participant Sensor as 传感器
+ participant ABM as AutoBrightnessManager
+ participant Display as 显示器
+ participant User as 用户
+
+ Note over Env: 室内光线变暗
+ Env->>Sensor: 光照从 200 lux 降至 50 lux
+
+ ABM->>Sensor: 轮询读取
+ Sensor-->>ABM: 50 lux
+ ABM->>ABM: 计算目标亮度
+ Note over ABM: 当前 78% -> 目标 29%
变化 49% > 阈值 20%
+
+ ABM->>ABM: shouldAdjustBrightness()
+ ABM->>ABM: 所有条件满足
+
+ ABM->>Display: SetBrightnessForced(0.29)
+ Note over Display: 启动渐变
2秒内从 78% -> 29%
+
+ loop 20步,每步100ms
+ Display->>Display: 亮度 -= 2.45%
+ end
+
+ Display->>User: 屏幕亮度平滑降低
+ Note over User: 感觉舒适,无闪烁
+```
+
+### 16.3 场景三:手动调节后的行为
+
+```mermaid
+sequenceDiagram
+ participant User as 用户
+ participant CC as 控制中心
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+ participant Sensor as 传感器
+
+ Note over ABM: 自动亮度运行中
+
+ User->>CC: 手动拖动亮度滑块到 90%
+ CC->>Manager: SetBrightness(0.9)
+ Manager->>Manager: setBrightness()
+ Manager->>ABM: OnManualBrightnessChange()
+
+ alt 临时暂停模式
+ ABM->>ABM: 记录暂停时间
+ ABM->>Sensor: ReleaseLight()
+ Note over ABM: 暂停 300 秒
+
+ Note over ABM,Sensor: 5分钟后
+ ABM->>ABM: 检查超时
+ ABM->>Sensor: ClaimLight()
+ ABM->>ABM: 恢复自动调节
+ Note over User: 自动亮度重新生效
+
+ else 完全禁用模式
+ ABM->>ABM: 保存 Enabled = false
+ ABM->>ABM: Stop()
+ ABM->>Sensor: 释放并断开
+ Manager->>CC: PropertiesChanged
+ CC->>User: 显示已禁用状态
+ Note over User: 需手动重新启用
+ end
+```
+
+### 16.4 场景四:系统休眠与唤醒
+
+```mermaid
+sequenceDiagram
+ participant PM as 电源管理
+ participant Manager as Display Manager
+ participant ABM as AutoBrightnessManager
+ participant Sensor as 传感器
+ participant Display as 显示器
+
+ Note over PM: 用户合上笔记本
+ PM->>Manager: PrepareForSleep(true)
+ Manager->>ABM: hold()
+ ABM->>ABM: systemAdjusting = true
+ ABM->>ABM: stopPolling()
+ Note over ABM: 保持连接但停止轮询
+
+ Note over PM: 系统休眠...
+
+ Note over PM: 用户打开笔记本
+ PM->>Manager: PrepareForSleep(false)
+ Manager->>ABM: resume()
+ ABM->>ABM: startPolling()
+ ABM->>Sensor: 立即读取光照
+ Sensor-->>ABM: 当前光照值
+ ABM->>Display: 调整到合适亮度
+ ABM->>ABM: systemAdjusting = false
+ Note over ABM: 恢复正常运行
+```
+
+## 17. 注意事项
+
+1. **线程安全:** 所有公共方法都需要考虑并发访问
+2. **资源管理:** 确保传感器资源正确释放
+3. **用户体验:** 避免频繁调节造成闪烁
+4. **电源效率:** 合理设置轮询间隔
+5. **降级策略:** 功能不可用时不影响系统稳定性
+
diff --git "a/docs/auto_backlight/\350\207\252\345\212\250\344\272\256\345\272\246\345\212\237\350\203\275\344\275\277\347\224\250\346\214\207\345\215\227.md" "b/docs/auto_backlight/\350\207\252\345\212\250\344\272\256\345\272\246\345\212\237\350\203\275\344\275\277\347\224\250\346\214\207\345\215\227.md"
new file mode 100644
index 000000000..0618bc460
--- /dev/null
+++ "b/docs/auto_backlight/\350\207\252\345\212\250\344\272\256\345\272\246\345\212\237\350\203\275\344\275\277\347\224\250\346\214\207\345\215\227.md"
@@ -0,0 +1,393 @@
+# 自动亮度功能使用指南
+
+## 功能介绍
+
+自动亮度功能可以根据周围环境的光线强度,自动调整笔记本电脑或一体机屏幕的亮度,让您的眼睛更舒适,同时节省电量。
+
+## 配置工具说明
+
+本功能使用 `dde-dconfig` 工具进行配置管理。`dde-dconfig` 是 Deepin 桌面环境的统一配置管理工具。
+
+**基本语法**:
+```bash
+dde-dconfig -a <应用ID> -r <资源名称> -k <配置键> --set -v <值> # 设置配置
+dde-dconfig -a <应用ID> -r <资源名称> -k <配置键> --get # 查询配置
+```
+
+**自动亮度配置参数**:
+- 应用ID:`org.deepin.startdde`
+- 资源名称:`org.deepin.Display.AutoBrightness`
+- 配置键:`enabled`, `sensitivity`, `polling-interval`, `change-threshold`, `manual-override-duration`
+
+## 使用前准备
+
+### 检查设备是否支持
+
+您的设备需要满足以下条件:
+
+1. **有环境光传感器**(大部分现代笔记本都有)
+2. **是内置屏幕**(外接显示器不支持)
+3. **系统服务正常**
+
+### 快速检查方法
+
+打开终端,输入以下命令检查:
+
+```bash
+# 检查是否支持自动亮度
+dbus-send --session --print-reply --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display org.freedesktop.DBus.Properties.Get \
+ string:com.deepin.daemon.Display string:AutoBrightnessSupported
+```
+
+如果返回 `boolean true`,说明您的设备支持此功能。
+
+## 基本使用
+
+### 启用自动亮度
+
+```bash
+# 启用自动亮度
+dbus-send --session --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display com.deepin.daemon.Display.SetAutoBrightnessEnabled \
+ boolean:true
+```
+
+### 关闭自动亮度
+
+```bash
+# 关闭自动亮度
+dbus-send --session --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display com.deepin.daemon.Display.SetAutoBrightnessEnabled \
+ boolean:false
+```
+
+### 查看当前状态
+
+```bash
+# 查看是否已启用
+dbus-send --session --print-reply --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display org.freedesktop.DBus.Properties.Get \
+ string:com.deepin.daemon.Display string:AutoBrightnessEnabled
+
+# 查看当前环境光强度
+dbus-send --session --print-reply --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display org.freedesktop.DBus.Properties.Get \
+ string:com.deepin.daemon.Display string:CurrentLightLevel
+```
+
+## 个性化设置
+
+### 设置参数说明
+
+| 设置项 | 配置键 | 说明 | 取值范围 | 推荐值 |
+|-------|--------|------|---------|--------|
+| **启用状态** | enabled | 是否启用自动亮度功能 | true/false | true |
+| **敏感度** | sensitivity | 控制亮度调节的敏感程度,值越大亮度变化越明显 | 0.1-3.0 | 0.5(标准)|
+| **检测间隔** | polling-interval | 多久检测一次环境光(秒) | 1-60秒 | 3秒(标准)|
+| **变化阈值** | change-threshold | 环境光变化多少才调节亮度(绝对值) | 1.0-50.0 | 20.0(标准)|
+| **暂停时间** | manual-override-duration | 手动调节后暂停自动调节的时间(秒) | 60-1800秒 | 300秒(5分钟)|
+
+### 单独设置配置项
+
+每个配置项都可以使用 `dde-dconfig` 命令单独设置:
+
+```bash
+# 设置敏感度
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.5
+
+# 设置检测间隔(秒)
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 3
+
+# 设置变化阈值
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 20.0
+
+# 设置手动调节暂停时间(秒)
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 300
+```
+
+### 查询单个配置项
+
+```bash
+# 查询敏感度
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --get
+
+# 查询检测间隔
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --get
+
+# 查询变化阈值
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --get
+
+# 查询暂停时间
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --get
+
+# 查询所有配置
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness --list
+```
+
+### 常用配置示例
+
+**注意**:配置修改后会立即生效,无需重启服务。
+
+#### 标准配置(推荐)
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.5
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 3
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 20.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 300
+```
+
+#### 快速响应配置(适合移动办公)
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.8
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 2
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 15.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 180
+```
+
+#### 稳定配置(适合固定办公)
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.4
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 5
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 25.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 600
+```
+
+#### 节能配置(最大化省电)
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.3
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 10
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 30.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 900
+```
+
+## 使用技巧
+
+### 1. 手动调节与自动调节的配合
+
+- **手动调节后**:系统会自动暂停5分钟,避免冲突
+- **立即恢复**:关闭后重新开启自动亮度即可立即恢复
+- **调整暂停时间**:可以修改 `manual_override_duration` 参数
+
+### 2. 不同环境的优化
+
+**明亮办公室**:
+- 敏感度:0.4-0.6
+- 检测间隔:5-10秒
+- 变化阈值:20.0-30.0
+
+**昏暗环境**:
+- 敏感度:0.6-0.8
+- 检测间隔:3-5秒
+- 变化阈值:15.0-20.0
+
+**户外使用**:
+- 敏感度:0.8-1.2
+- 检测间隔:2-3秒
+- 变化阈值:10.0-15.0
+
+### 3. 省电优化
+
+如果您更关心电池续航:
+- 增加检测间隔(10-30秒)
+- 提高变化阈值(25.0-40.0)
+- 降低敏感度(0.3-0.5)
+
+## 常见问题解决
+
+### 问题1:功能不可用
+
+**现象**:显示不支持自动亮度
+
+**解决方法**:
+```bash
+# 检查传感器服务
+sudo systemctl status iio-sensor-proxy
+
+# 如果服务未运行,启动它
+sudo systemctl start iio-sensor-proxy
+sudo systemctl enable iio-sensor-proxy
+```
+
+### 问题2:亮度不调节
+
+**现象**:环境光变化但屏幕亮度不变
+
+**解决方法**:
+1. 检查是否在手动调节暂停期间
+2. 降低变化阈值
+3. 重新启用功能
+
+```bash
+# 方法1:降低变化阈值(更敏感)
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 10.0
+
+# 方法2:重新启用(清除暂停状态)
+dbus-send --session --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display com.deepin.daemon.Display.SetAutoBrightnessEnabled \
+ boolean:false
+dbus-send --session --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display com.deepin.daemon.Display.SetAutoBrightnessEnabled \
+ boolean:true
+```
+
+### 问题3:调节太频繁
+
+**现象**:屏幕亮度变化太频繁,影响使用
+
+**解决方法**:
+```bash
+# 方法1:提高变化阈值(降低敏感度)
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 30.0
+
+# 方法2:增加检测间隔
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 10
+
+# 方法3:使用完整稳定配置
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.4
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 5
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 25.0
+```
+
+### 问题4:反应太慢
+
+**现象**:环境光变化后很久才调节亮度
+
+**解决方法**:
+```bash
+# 方法1:降低变化阈值(更敏感)
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 10.0
+
+# 方法2:减少检测间隔
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 2
+
+# 方法3:使用完整快速响应配置
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.8
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 2
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 15.0
+```
+
+## 高级功能
+
+### 查看详细状态
+
+```bash
+# 查看所有配置项
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness --list
+
+# 查看单个配置项
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k enabled --get
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --get
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --get
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --get
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --get
+
+# 查看运行状态(通过 D-Bus)
+dbus-send --session --print-reply --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display org.freedesktop.DBus.Properties.Get \
+ string:com.deepin.daemon.Display string:AutoBrightnessSupported
+
+dbus-send --session --print-reply --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display org.freedesktop.DBus.Properties.Get \
+ string:com.deepin.daemon.Display string:AutoBrightnessEnabled
+```
+
+### 监控环境光变化
+
+```bash
+# 实时监控环境光强度变化
+watch -n 1 'dbus-send --session --print-reply --dest=com.deepin.daemon.Display \
+ /com/deepin/daemon/Display org.freedesktop.DBus.Properties.Get \
+ string:com.deepin.daemon.Display string:CurrentLightLevel'
+```
+
+### 查看日志
+
+如果遇到问题,可以查看系统日志:
+
+```bash
+# 查看自动亮度相关日志
+journalctl --user -u startdde -f | grep AutoBrightness
+
+# 查看传感器服务日志
+journalctl -u iio-sensor-proxy -f
+```
+
+## 注意事项
+
+1. **仅限内置屏幕**:外接显示器不支持此功能
+2. **传感器位置**:不要遮挡设备上的光线传感器
+3. **电量消耗**:频繁检测会增加电量消耗,建议合理设置检测间隔
+4. **手动优先**:手动调节亮度后会自动暂停功能一段时间
+5. **重启保持**:设置会自动保存,重启后依然有效
+6. **敏感度说明**:敏感度参数控制环境光到屏幕亮度的映射关系,值越大屏幕亮度变化越明显。推荐范围 0.3-1.0,默认 0.5
+
+## 配置参数详解
+
+### 敏感度 (sensitivity)
+
+敏感度控制环境光强度到屏幕亮度的转换比例:
+
+- **0.1-0.3**:低敏感度,适合希望屏幕亮度变化较小的用户
+- **0.4-0.6**:标准敏感度,适合大多数使用场景(推荐)
+- **0.7-1.0**:高敏感度,适合需要明显亮度变化的场景
+- **1.1-3.0**:超高敏感度,仅在特殊场景使用
+
+**计算公式**:屏幕亮度 = (环境光强度 × 敏感度) / 255
+
+### 检测间隔 (polling_interval)
+
+控制多久检测一次环境光强度:
+
+- **1-2秒**:快速响应,适合移动办公,但会增加电量消耗
+- **3-5秒**:标准响应,平衡响应速度和电量消耗(推荐)
+- **6-10秒**:慢速响应,适合固定办公环境
+- **11-60秒**:省电模式,适合电池续航优先的场景
+
+### 变化阈值 (change_threshold)
+
+环境光强度变化超过此值才会触发亮度调节:
+
+- **1.0-10.0**:低阈值,对环境光变化敏感,调节频繁
+- **11.0-25.0**:标准阈值,适合大多数场景(推荐)
+- **26.0-50.0**:高阈值,只在环境光明显变化时才调节
+
+**注意**:阈值过低会导致频繁调节,阈值过高会导致响应迟钝
+
+### 暂停时间 (manual_override_duration)
+
+手动调节亮度后,自动调节功能暂停的时间:
+
+- **60-180秒**:短暂停,适合频繁切换环境的场景
+- **181-600秒**:标准暂停,适合大多数场景(推荐 300秒)
+- **601-1800秒**:长暂停,适合希望手动控制优先的场景
+
+## 推荐设置
+
+### 日常办公
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.5
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 3
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 20.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 300
+```
+
+### 移动办公
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.8
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 2
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 15.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 180
+```
+
+### 省电模式
+```bash
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k sensitivity --set -v 0.3
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k polling-interval --set -v 10
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k change-threshold --set -v 30.0
+dde-dconfig -a org.deepin.startdde -r org.deepin.Display.AutoBrightness -k manual-override-duration --set -v 600
+```
+
+---
+
+**提示**:如果您不熟悉命令行操作,建议使用系统设置界面进行配置(如果可用)。如有问题,可以随时关闭此功能,不会影响正常的手动亮度调节。
diff --git a/go.mod b/go.mod
index fa7129b7f..0e242f9c6 100644
--- a/go.mod
+++ b/go.mod
@@ -8,8 +8,8 @@ require (
github.com/fsnotify/fsnotify v1.8.0
github.com/godbus/dbus/v5 v5.1.0
github.com/jouyouyun/hardware v0.1.8
- github.com/linuxdeepin/dde-api v0.0.0-20260310032929-7f0ab8f52e1b
- github.com/linuxdeepin/go-dbus-factory v0.0.0-20260227070938-bcb8d12841ab
+ github.com/linuxdeepin/dde-api v0.0.0-20260511093853-07ca3f2f1232
+ github.com/linuxdeepin/go-dbus-factory v0.0.0-20260513063723-8eea4924a64a
github.com/linuxdeepin/go-gir v0.0.0-20251204113853-1873b5530f50
github.com/linuxdeepin/go-lib v0.0.0-20260205120541-a1f572ce1442
github.com/linuxdeepin/go-x11-client v0.0.0-20240415051504-c8e43d028ff9
diff --git a/go.sum b/go.sum
index 91968dcd0..763114867 100644
--- a/go.sum
+++ b/go.sum
@@ -28,10 +28,10 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/linuxdeepin/dde-api v0.0.0-20260310032929-7f0ab8f52e1b h1:MkrAGimPW17PAShqzLQ8JURvyZ+UvzbnZuM+XCijAtQ=
-github.com/linuxdeepin/dde-api v0.0.0-20260310032929-7f0ab8f52e1b/go.mod h1:etFJ3bz0/U3wklOLPLn7A4DM5VBQJ4yHhHaWpCEzzV8=
-github.com/linuxdeepin/go-dbus-factory v0.0.0-20260227070938-bcb8d12841ab h1:tzTj6afLE47xQ8oRdeH/xBwgc1Jfo6loOCZyAVO21/4=
-github.com/linuxdeepin/go-dbus-factory v0.0.0-20260227070938-bcb8d12841ab/go.mod h1:dfpixHkqiijg3P7w5QArkMyC7+vlHcutN5R6zHIn8is=
+github.com/linuxdeepin/dde-api v0.0.0-20260511093853-07ca3f2f1232 h1:1jSUHQ4FlFGhhhLiCzbXvcH+JIW92gGLWOQExbl/6SI=
+github.com/linuxdeepin/dde-api v0.0.0-20260511093853-07ca3f2f1232/go.mod h1:etFJ3bz0/U3wklOLPLn7A4DM5VBQJ4yHhHaWpCEzzV8=
+github.com/linuxdeepin/go-dbus-factory v0.0.0-20260513063723-8eea4924a64a h1:1N9cq5hB4mlbUf7YJSZ9+3fb9VffUSxOXxndWlpONAE=
+github.com/linuxdeepin/go-dbus-factory v0.0.0-20260513063723-8eea4924a64a/go.mod h1:dfpixHkqiijg3P7w5QArkMyC7+vlHcutN5R6zHIn8is=
github.com/linuxdeepin/go-gir v0.0.0-20250812023606-b28aaee32ac9/go.mod h1:a0tox5vepTQu5iO6rdKc4diGT+fkyXZlRROM8ULEvaI=
github.com/linuxdeepin/go-gir v0.0.0-20251204113853-1873b5530f50 h1:CLVQdE+YgfvHD2EadN/0hPIhCbbu5cdGyDu4brW9pyw=
github.com/linuxdeepin/go-gir v0.0.0-20251204113853-1873b5530f50/go.mod h1:a0tox5vepTQu5iO6rdKc4diGT+fkyXZlRROM8ULEvaI=
diff --git a/misc/dsg-configs/org.deepin.Display.AutoBrightness.json b/misc/dsg-configs/org.deepin.Display.AutoBrightness.json
new file mode 100644
index 000000000..116bf1457
--- /dev/null
+++ b/misc/dsg-configs/org.deepin.Display.AutoBrightness.json
@@ -0,0 +1,114 @@
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "enabled": {
+ "name": "Auto Brightness Enabled",
+ "description": "启用基于环境光传感器的自动亮度调节",
+ "value": false,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "sensitivity": {
+ "name": "Auto Brightness Sensitivity",
+ "description": "自动亮度调节的敏感度,值越大时调整范围越大",
+ "value": 0.5,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "change-threshold": {
+ "name": "Light Change Threshold",
+ "description": "触发亮度调节的环境光变化阈值",
+ "value": 20.0,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "brightness-change-threshold": {
+ "name": "Brightness Change Threshold",
+ "description": "触发亮度调节的亮度变化阈值(0.01表示1%)",
+ "value": 0.05,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "polling-interval": {
+ "name": "Polling Interval",
+ "description": "环境光采样间隔(秒)",
+ "value": 2,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "manual-override-duration": {
+ "name": "Manual Override Duration",
+ "description": "手动调节后暂停自动调节的时长(秒)",
+ "value": 300,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "manual-adjust-disables-auto-mode": {
+ "name": "Manual Adjust Disables Auto Mode",
+ "description": "手动调节是否禁用自动模式",
+ "value": true,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "kalman-process-noise": {
+ "name": "Kalman Filter Process Noise Q",
+ "description": "卡尔曼滤波器过程噪声协方差(Q),值越大对环境光变化响应越快",
+ "value": 0.8,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "kalman-measurement-noise": {
+ "name": "Kalman Filter Measurement Noise R",
+ "description": "卡尔曼滤波器测量噪声协方差(R),值越大滤波效果越强",
+ "value": 0.05,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "use-transition": {
+ "name": "Use Transition for Auto Brightness",
+ "description": "自动亮度调节时是否使用渐变效果(即使全局渐变功能关闭)",
+ "value": true,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "lux-brightness-curve": {
+ "name": "Lux to Brightness Curve",
+ "description": "环境光到亮度的映射曲线配置",
+ "value": [],
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ },
+ "kalman-window-size": {
+ "name": "Kalman Filter Window Size",
+ "description": "自适应卡尔曼滤波器滑动窗口大小,用于计算测量方差",
+ "value": 3,
+ "visibility": "private",
+ "permissions": "readwrite",
+ "serial": 0,
+ "flags": []
+ }
+ }
+}
diff --git a/misc/dsg-configs/org.deepin.Display.json b/misc/dsg-configs/org.deepin.Display.json
index 79d9ead62..df77f781c 100644
--- a/misc/dsg-configs/org.deepin.Display.json
+++ b/misc/dsg-configs/org.deepin.Display.json
@@ -177,6 +177,116 @@
"description[zh_CN]": "自定义显示模式类型(范围:1-2)",
"permissions": "readwrite",
"visibility": "public"
+ },
+ "transition-enabled": {
+ "value": false,
+ "serial": 0,
+ "flags": [],
+ "name": "Brightness Transition Enabled",
+ "description": "是否启用亮度渐变效果",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "transition-duration": {
+ "value": 4000,
+ "serial": 0,
+ "flags": [],
+ "name": "Brightness Transition Duration",
+ "description": "从 0% 到 100% 完整渐变所需的时间(毫秒)",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "transition-step-percent": {
+ "value": 1,
+ "serial": 0,
+ "flags": [],
+ "name": "Transition Step Percent",
+ "name[zh_CN]": "过渡步进百分比",
+ "description": "Step percentage for brightness transition (e.g., 1 means 1%)",
+ "description[zh_CN]": "亮度过渡步进百分比(如 1 表示每次调节 1%)",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "transition-step-interval": {
+ "value": 100,
+ "serial": 0,
+ "flags": [],
+ "name": "Brightness Transition Step Interval",
+ "description": "渐变过程中每次调整亮度的时间间隔(毫秒)",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "backlight-curve-type": {
+ "value": "default",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "backlightCurveType",
+ "description": "背光曲线类型",
+ "permissions": "readonly",
+ "visibility": "private"
+ },
+ "backlight-curve-min-value": {
+ "value": 4,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "backlightMinValue",
+ "description": "背光曲线起始点",
+ "permissions": "readonly",
+ "visibility": "private"
+ },
+ "backlight-curve-mid-value": {
+ "value": 50,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "backlightMidValue",
+ "description": "背光曲线中间点",
+ "permissions": "readonly",
+ "visibility": "private"
+ },
+ "brightness-percentage": {
+ "value": 100,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "brightnessPercentage",
+ "description": "实际亮度百分比, 取值范围:[50, 100]",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "can-set-brightness-delay-interval": {
+ "value": 0,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "canSetBrightnessDelayInterval",
+ "description": "新增显示器延时获取是否支持亮度调节",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "max-brightness-unlimited": {
+ "value": false,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "maxBrightnessUnlimited",
+ "description": "内置屏幕最大亮度不受限制开关",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "custom-brightness-curves": {
+ "value": "",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "customBrightnessCurves",
+ "description": "内置屏亮度曲线与最大限制",
+ "permissions": "readwrite",
+ "visibility": "private"
+ },
+ "default-brightness-curve": {
+ "value": "",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "defaultBrightnessCurve",
+ "description": "默认亮度曲线配置(不依赖硬件信息)",
+ "permissions": "readwrite",
+ "visibility": "private"
}
}
}