diff --git a/.reuse/dep5 b/.reuse/dep5 index 7c0dd3de..de1fc511 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -10,7 +10,7 @@ Source: https://github.com/linuxdeepin/dde-services # License: ... # Config files -Files: .clang-format *.in *.ini *.json *.conf *.xml *.service *.desktop *.patch +Files: .clang-format *.in *.ini *.json *.conf *.xml *.service *.desktop *.patch *.policy Copyright: None License: CC0-1.0 diff --git a/debian/control b/debian/control index fbca99d1..fc7a3f59 100644 --- a/debian/control +++ b/debian/control @@ -18,6 +18,8 @@ Build-Depends: debhelper (>= 11), libdtk6gui-dev, libdtk6core-dev, libx11-dev, + libxext-dev, + libxss-dev, libxcb-randr0-dev, libxcb1-dev, libxcb-xinput-dev, @@ -41,11 +43,12 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, deepin-service-manager (>> 1.0.21), - dbus + dbus, + deepin-power-control (>= 2.0.1) Recommends: iio-sensor-proxy -Breaks: dde-daemon (<< 6.1.94) -Replaces: dde-daemon (<< 6.1.94) +Breaks: dde-daemon (<< 6.1.102) +Replaces: dde-daemon (<< 6.1.102) Description: deepin desktop-environment plugins service This package provides various system service plugins for Deepin Desktop Environment, including: diff --git a/src/plugin-qt/power/CMakeLists.txt b/src/plugin-qt/power/CMakeLists.txt index 7f412383..6a19076f 100644 --- a/src/plugin-qt/power/CMakeLists.txt +++ b/src/plugin-qt/power/CMakeLists.txt @@ -3,6 +3,8 @@ # SPDX-License-Identifier: LGPL-3.0-or-later add_subdirectory(session) -# TODO: system-level power is provided by dde-daemon's dde-system-daemon -# for both X11 and Wayland. Remove this when unified. -# add_subdirectory(system) +add_subdirectory(system) + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.daemon.power.json + DESTINATION ${CMAKE_INSTALL_DATADIR}/dsg/configs/org.deepin.dde.daemon/ +) diff --git a/src/plugin-qt/power/misc/org.deepin.dde.daemon.power.json b/src/plugin-qt/power/misc/org.deepin.dde.daemon.power.json new file mode 100644 index 00000000..f50c06d7 --- /dev/null +++ b/src/plugin-qt/power/misc/org.deepin.dde.daemon.power.json @@ -0,0 +1,845 @@ + +{ + "magic": "dsg.config.meta", + "version": "1.0", + "contents": { + "BalanceCpuGovernor": { + "value": "ondemand", + "serial": 0, + "flags": [ + "global" + ], + "name": "BalanceCpuGovernor", + "name[zh_CN]": "电源设置-性能模式-平衡模式状态", + "description": "balance cpu governor", + "permissions": "readwrite", + "visibility": "private" + }, + "isFirstGetCpuGovernor": { + "value": true, + "serial": 0, + "flags": [ + "global" + ], + "name": "isFirstGetCpuGovernor", + "name[zh_CN]": "是否第一次获取Cpu支持的Governor模式", + "description": "is first get cpu governor", + "permissions": "readwrite", + "visibility": "private" + }, + "supportCpuGovernors": { + "value": [ + "performance", + "powersave", + "userspace", + "ondemand", + "conservative", + "schedutil" + ], + "serial": 0, + "flags": [ + "global" + ], + "name": "supportCpuGovernors", + "name[zh_CN]": "Cpu支持的Governor模式", + "description": "", + "description[zh_CN]": "", + "permissions": "readwrite", + "visibility": "public" + }, + "specialCpuModeJson": { + "value": "{\"PANGU\":{\"Balance\":true,\"Performace\":true,\"PowerSave\":true}}", + "serial": 0, + "flags": [ + "global" + ], + "name": "specialCpuModeJson", + "name[zh_CN]": "特殊处理的cpu配置", + "description": "", + "description[zh_CN]": "需要进行特殊处理的cpu配置", + "permissions": "readwrite", + "visibility": "public" + }, + "idlePowersaveAspmEnabled": { + "value": true, + "serial": 0, + "flags": [ + "global" + ], + "name": "idle powersave aspm enable", + "name[zh_CN]": "idle时节能模式Aspm启动", + "description": "idle时节能模式Aspm状态", + "permissions": "readwrite", + "visibility": "private" + }, + "powerSavingModeAuto": { + "value": false, + "serial": 0, + "flags": [ + "global" + ], + "name": "power saving mode auto", + "name[zh_CN]": "使用电池自动开启节能模式", + "description": "使用电池自动开启节能模式", + "permissions": "readwrite", + "visibility": "public" + }, + "powerSavingModeEnabled": { + "value": false, + "serial": 0, + "flags": [ + "global" + ], + "name": "power saving mode enabled", + "name[zh_CN]": "是否开启节能模式", + "description": "是否开启节能模式", + "permissions": "readwrite", + "visibility": "public" + }, + "powerSavingModeAutoWhenBatteryLow": { + "value": true, + "serial": 0, + "flags": [ + "global" + ], + "name": "power saving mode auto when battery low", + "name[zh_CN]": "低电量时自动开启节能模式", + "description": "低电量时自动开启节能模式", + "permissions": "readwrite", + "visibility": "public" + }, + "powerSavingModeAutoBatteryPercent": { + "value": 20, + "serial": 0, + "flags": [ + "global" + ], + "name": "power saving mode auto threshold", + "name[zh_CN]": "自动开启节能模式阈值", + "description": "自动开启节能模式阈值", + "permissions": "readwrite", + "visibility": "public" + }, + "powerSavingModeBrightnessDropPercent": { + "value": 20, + "serial": 0, + "flags": [ + "global" + ], + "name": "power saving mode brightness drop percent", + "name[zh_CN]": "节能模式亮度降低百分比", + "description": "节能模式亮度降低百分比", + "permissions": "readwrite", + "visibility": "public" + }, + "mode": { + "value": "balance", + "serial": 0, + "flags": [ + "global" + ], + "name": "power mode", + "name[zh_CN]": "性能模式", + "description": "性能模式", + "permissions": "readwrite", + "visibility": "public" + }, + "allowScreenSaver": { + "value": true, + "serial": 0, + "flags": [ + "global" + ], + "name": "allow screen saver", + "name[zh_CN]": "允许屏幕保护", + "description": "allow screen saver", + "permissions": "readwrite", + "visibility": "public" + }, + "scheduledShutdownState": { + "value": false, + "serial": 0, + "flags": [], + "name": "Scheduled shutdown switch", + "name[zh_CN]": "定时关机开关", + "description": "Scheduled shutdown switch", + "permissions": "readwrite", + "visibility": "public" + }, + "shutdownTime": { + "value": "19:00", + "serial": 0, + "flags": [], + "name": "time of timed shutdown", + "name[zh_CN]": "关机时间", + "description": "time of timed shutdown", + "permissions": "readwrite", + "visibility": "public" + }, + "shutdownRepetition": { + "value": 0, + "serial": 0, + "flags": [], + "name": "type of the shutdown repetition", + "name[zh_CN]": "重复类型, 0-3", + "description": "type of the shutdown repetition", + "permissions": "readwrite", + "visibility": "public" + }, + "customShutdownWeekDays": { + "value": [], + "serial": 0, + "flags": [], + "name": "custom shutdown week days", + "name[zh_CN]": "自定义关机重复日期", + "description": "custom shutdown week days", + "permissions": "readwrite", + "visibility": "public" + }, + "shutdownCountdown": { + "value": 60, + "serial": 0, + "flags": [], + "name": "shutdown countdown", + "name[zh_CN]": "关机倒计时", + "description": "shutdown countdown", + "permissions": "readwrite", + "visibility": "public" + }, + "delayWakeupInterval": { + "value": 0, + "serial": 0, + "flags": [ + "global" + ], + "name": "delay Wake up Interval", + "name[zh_CN]": "延时关闭DDE黑屏程序时间间隔", + "description": "延时关闭DDE黑屏程序时间间隔", + "permissions": "readwrite", + "visibility": "public" + }, + "nextShutdownTime": { + "value": 0, + "serial": 0, + "flags": [], + "name": "next shutdown time", + "name[zh_CN]": "下一次关机时间", + "description": "next shutdown time", + "permissions": "readwrite", + "visibility": "public" + }, + "powerMappingConfig":{ + "value": "{\"balance\":{\"DSPCConfig\":\"balance\"},\"lowBattery\":{\"DSPCConfig\":\"lowbat\"},\"performance\":{\"DSPCConfig\":\"performance\"},\"powersave\":{\"DSPCConfig\":\"saving\"}}", + "serial": 0, + "flags": [ + "global" + ], + "name": "power mapping config", + "name[zh_CN]": "电源性能模式映射配置", + "description": "四种功耗模式对应的配置", + "permissions": "readwrite", + "visibility": "public" + }, + "delayHandleIdleOffIntervalWhenScreenBlack": { + "value": 800, + "serial": 0, + "flags": [ + "global" + ], + "name": "delay HandleIdleOff Interval When ScreenBlack", + "name[zh_CN]": "延时调用HandleIdleOff时间间隔,单位ms", + "description": "延时调用HandleIdleOff时间间隔,单位ms", + "permissions": "readwrite", + "visibility": "public" + }, + "linePowerScreensaverDelay": { + "value": 0, + "serial": 0, + "flags": [], + "name": "linePowerScreensaverDelay", + "name[zh_CN]": "插电时显示屏保的超时时间", + "description": "line power screen saver delay", + "permissions": "readwrite", + "visibility": "public" + }, + "batteryScreensaverDelay": { + "value": 0, + "serial": 0, + "flags": [], + "name": "batteryScreensaverDelay", + "name[zh_CN]": "使用电池时显示屏保的超时时间", + "description": "battery screen saver delay", + "permissions": "readwrite", + "visibility": "public" + }, + "batteryScreenBlackDelay": { + "value": 300, + "serial": 0, + "flags": [], + "name": "batteryScreenBlackDelay", + "name[zh_CN]": "使用电池时的屏幕黑屏延时", + "description": "battery screen black delay", + "permissions": "readwrite", + "visibility": "public" + }, + "batterySleepDelay": { + "value": 900, + "serial": 0, + "flags": [], + "name": "batterySleepDelay", + "name[zh_CN]": "使用电池时的休眠延时", + "description": "battery sleep delay", + "permissions": "readwrite", + "visibility": "public" + }, + "linePowerScreenBlackDelay": { + "value": 900, + "serial": 0, + "flags": [], + "name": "linePowerScreenBlackDelay", + "name[zh_CN]": "插电时屏幕黑屏延时", + "description": "line power screen black delay", + "permissions": "readwrite", + "visibility": "public" + }, + "linePowerSleepDelay": { + "value": 1800, + "serial": 0, + "flags": [], + "name": "linePowerSleepDelay", + "name[zh_CN]": "插电时休眠延时", + "description": "line power sleep delay", + "permissions": "readwrite", + "visibility": "public" + }, + "linePowerLockDelay": { + "value": 900, + "serial": 0, + "flags": [], + "name": "linePowerLockDelay", + "name[zh_CN]": "插电时锁屏延时", + "description": "line power lock delay", + "permissions": "readwrite", + "visibility": "public" + }, + "batteryLockDelay": { + "value": 300, + "serial": 0, + "flags": [], + "name": "batteryLockDelay", + "name[zh_CN]": "使用电池时锁屏延时", + "description": "battery lock delay", + "permissions": "readwrite", + "visibility": "public" + }, + "adjustBrightnessEnabled": { + "value": false, + "serial": 0, + "flags": [], + "name": "adjustBrightnessEnabled", + "name[zh_CN]": "启用自动调节亮度", + "description": "adjust display brigtness enabled", + "permissions": "readwrite", + "visibility": "public" + }, + "ambientLightAdjustBrightness": { + "value": true, + "serial": 0, + "flags": [], + "name": "ambientLightAdjustBrightness", + "name[zh_CN]": "通过环境光自动调节亮度", + "description": "auto adjust backlight brightness according to ambient light intensity", + "permissions": "readwrite", + "visibility": "public" + }, + "screenBlackLock": { + "value": true, + "serial": 0, + "flags": [], + "name": "screenBlackLock", + "name[zh_CN]": "关闭屏幕前锁定", + "description": "screen black lock", + "permissions": "readwrite", + "visibility": "public" + }, + "sleepLock": { + "value": true, + "serial": 0, + "flags": [], + "name": "sleep lock", + "name[zh_CN]": "睡眠前锁定", + "description": "sleep lock", + "permissions": "readwrite", + "visibility": "public" + }, + "lidClosedSleep": { + "value": true, + "serial": 0, + "flags": [], + "name": "lidClosedSleep", + "name[zh_CN]": "插电时合盖休眠", + "description": "Sleep when close the lid if use line power", + "permissions": "readwrite", + "visibility": "public" + }, + "batteryLidClosedSleep": { + "value": true, + "serial": 0, + "flags": [], + "name": "batteryLidClosedSleep", + "name[zh_CN]": "使用电池时合盖休眠", + "description": "Sleep when close the lid if use battery", + "permissions": "readwrite", + "visibility": "public" + }, + "powerButtonPressedExec": { + "value": "dde-shutdown", + "serial": 0, + "flags": [ + "global" + ], + "name": "powerButtonPressedExec", + "name[zh_CN]": "电源键按下执行的命令", + "description": "power button pressed exec", + "permissions": "readwrite", + "visibility": "public" + }, + "fullscreenWorkaroundAppList": { + "value": ["libflash", "chrome", "firefox","mplayer", "operaplugin", "soffice", "wpp", "evince", "vlc", "totem"], + "serial": 0, + "flags": [], + "name": "fullscreenWorkaroundAppList", + "name[zh_CN]": "全屏工作模式应用列表", + "description": "app in list fullscreen inhbit screensaver", + "permissions": "readwrite", + "visibility": "public" + }, + "usePercentageForPolicy": { + "value": true, + "serial": 0, + "flags": [], + "name": "usePercentageForPolicy", + "name[zh_CN]": "使用基于电池百分比的策略", + "description": "Whether battery percentage based policy should be used. It should work around broken firmwares. It is also more reliable than the time left.", + "permissions": "readwrite", + "visibility": "public" + }, + "powerModuleInitialized": { + "value": false, + "serial": 0, + "flags": [], + "name": "powerModuleInitialized", + "name[zh_CN]": "电源模块是否初始化", + "description": "power module initialized", + "permissions": "readwrite", + "visibility": "public" + }, + "lowPowerNotifyThreshold": { + "value": 20, + "serial": 0, + "flags": [], + "name": "lowPowerNotifyThreshold", + "name[zh_CN]": "低电量通知阈值", + "description": "low power notify threshold", + "permissions": "readwrite" + }, + "lowPowerAction": { + "value": 1, + "serial": 0, + "flags": [], + "name": "lowPowerAction", + "name[zh_CN]": "低电量时系统动作", + "description": "0: suspend, 1: hibernate", + "permissions": "readwrite" + }, + "percentageAction": { + "value": 5, + "serial": 0, + "flags": [], + "name": "percentageAction", + "name[zh_CN]": "执行动作的百分比", + "description": "system action when battery low", + "permissions": "readwrite" + }, + "timeToEmptyLow": { + "value": 1200, + "serial": 0, + "flags": [], + "name": "timeToEmptyLow", + "name[zh_CN]": "time to empty low", + "description": "time to empty low", + "permissions": "readwrite" + }, + "timeToEmptyDanger": { + "value": 900, + "serial": 0, + "flags": [], + "name": "timeToEmptyDanger", + "name[zh_CN]": "time to empty low", + "description": "time to empty low", + "permissions": "readwrite" + }, + "timeToEmptyCritical": { + "value": 600, + "serial": 0, + "flags": [], + "name": "timeToEmptyCritical", + "name[zh_CN]": "time to empty low", + "description": "time to empty low", + "permissions": "readwrite" + }, + "timeToEmptyAction": { + "value": 300, + "serial": 0, + "flags": [], + "name": "timeToEmptyAction", + "name[zh_CN]": "system action when battery low", + "description": "system action when battery low", + "permissions": "readwrite" + }, + "linePowerLidClosedAction": { + "value": 1, + "serial": 0, + "flags": [], + "name": "linePowerLidClosedAction", + "name[zh_CN]": "插电状态下合盖执行的动作", + "description": "0:shutdown, 1:suspend, 2:hibernate, 3:turnOffScreen, 4:showSessionUI, 5:doNothing", + "permissions": "readwrite" + }, + "linePowerPressPowerButton": { + "value": 4, + "serial": 0, + "flags": [], + "name": "linePowerPressPowerButton", + "name[zh_CN]": "插电状态下按电源键的动作", + "description": "0:shutdown, 1:suspend, 2:hibernate, 3:turnOffScreen, 4:showSessionUI, 5:doNothing", + "permissions": "readwrite" + }, + "batteryLidClosedAction": { + "value": 1, + "serial": 0, + "flags": [], + "name": "batteryLidClosedAction", + "name[zh_CN]": "使用电池时合盖执行的动作", + "description": "0:shutdown, 1:suspend, 2:hibernate, 3:turnOffScreen, 4:showSessionUI, 5:doNothing", + "permissions": "readwrite" + }, + "batteryPressPowerButton": { + "value": 4, + "serial": 0, + "flags": [], + "name": "batteryPressPowerButton", + "name[zh_CN]": "使用电池时按电源键执行的动作", + "description": "0:shutdown, 1:suspend, 2:hibernate, 3:turnOffScreen, 4:showSessionUI, 5:doNothing", + "permissions": "readwrite" + }, + "lowPowerNotifyEnable": { + "value": true, + "serial": 0, + "flags": [], + "name": "lowPowerNotifyEnable", + "name[zh_CN]": "低电量时打开系统通知", + "description": "enable system notify when low power", + "permissions": "readwrite" + }, + "saveBrightnessWhilePsm": { + "value": "", + "serial": 0, + "flags": [], + "name": "saveBrightnessWhilePsm", + "name[zh_CN]": "省电模式时自动降低亮度", + "description": "brightness saved while power saving mode enabled", + "permissions": "readwrite" + }, + "lightSensorEnabled": { + "value": true, + "serial": 0, + "flags": [], + "name": "lightSensorEnabled", + "name[zh_CN]": "光感应器开关", + "description": "light sensor enabled", + "permissions": "readwrite" + }, + "lowPowerPercentInUpdatingNotify": { + "value": 50, + "serial": 0, + "flags":[], + "name": "lowPowerPercentInUpdatingNotify", + "name[zh_CN]": "更新系统时触发警告的电量", + "description": "low power percent in updating notify", + "permissions": "readwrite" + }, + "highPerformanceEnabled": { + "value": true, + "serial": 0, + "flags": [], + "name": "highPerformanceEnabled", + "name[zh_CN]": "高性能模式开关", + "description": "high performance enabled", + "permissions": "readwrite" + }, + "idleStatePath": { + "value": "/sys/devices/system/loongarch/relax_state", + "serial": 0, + "flags": [ + "global" + ], + "name": "idle state path", + "name[zh_CN]": "通知EC进入短idle的内核节点", + "description": "kernel node to notify EC of short idle state, write 1 to enter, 0 to exit", + "permissions": "readonly", + "visibility": "private" + }, + "idleScreenStatePath": { + "value": "/sys/devices/system/loongarch/idle_state", + "serial": 0, + "flags": [ + "global" + ], + "name": "idle screen state path", + "name[zh_CN]": "通知EC显示器开关状态的内核节点", + "description": "kernel node to notify EC of display state, write 1 to turn off, 0 to turn on", + "permissions": "readonly", + "visibility": "private" + }, + "linePowerShortIdleDelay": { + "value": 300, + "serial": 0, + "flags": [], + "name": "line power short idle delay", + "name[zh_CN]": "插电时短idle延时", + "description": "line power short idle delay", + "permissions": "readwrite", + "visibility": "public" + }, + "batteryShortIdleDelay": { + "value": 300, + "serial": 0, + "flags": [], + "name": "battery short idle delay", + "name[zh_CN]": "使用电池时短idle延时", + "description": "battery short idle delay", + "permissions": "readwrite", + "visibility": "public" + }, + "shortIdleEnable": { + "value": false, + "serial": 0, + "flags": [ + "global" + ], + "name": "short idle enable", + "name[zh_CN]": "是否开启短idle方案", + "description": "enable short idle feature, readonly, override via os-config", + "permissions": "readonly", + "visibility": "private" + }, + "shortIdleState": { + "value": false, + "serial": 0, + "flags": [ + "global" + ], + "name": "short idle state", + "name[zh_CN]": "短idle状态", + "description": "current short idle state, true means entered short idle", + "permissions": "readwrite", + "visibility": "private" + }, + "shortIdleBlacklistApplications": { + "value": [ + "org.deepin.browser.desktop", + "deepin-terminal.desktop" + ], + "serial": 0, + "flags": [ + "global" + ], + "name": "short idle blacklist applications", + "name[zh_CN]": "短idle黑名单应用列表", + "description": "applications that prevent entering short idle when running", + "permissions": "readwrite", + "visibility": "private" + }, + "systemApplications": { + "value": [ + "dde-lock.desktop", + "dde-clipboard.desktop", + "dde-clipboard-daemon.desktop", + "dde-file-manager.desktop", + "dde-open.desktop", + "dde-computer.desktop", + "dde-home.desktop", + "dde-trash.desktop", + "org.deepin.dde.control-center.desktop", + "dde-printer.desktop", + "dde-printer-watch.desktop", + "dde-calendar.desktop", + "dde-introduction.desktop", + "dde-cooperation.desktop", + "dde-update-autostart.desktop", + "dde-calendar-service.desktop", + "deepin-app-store.desktop", + "deepin-app-store-b.desktop", + "deepin-music.desktop", + "deepin-movie.desktop", + "deepin-screen-recorder.desktop", + "deepin-image-viewer.desktop", + "deepin-album.desktop", + "deepin-draw.desktop", + "deepin-reader.desktop", + "deepin-editor.desktop", + "deepin-mail.desktop", + "deepin-voice-note.desktop", + "deepin-manual.desktop", + "deepin-system-monitor.desktop", + "deepin-boot-maker.desktop", + "deepin-devicemanager.desktop", + "deepin-log-viewer.desktop", + "deepin-calculator.desktop", + "deepin-font-manager.desktop", + "deepin-compressor.desktop", + "deepin-deb-installer.desktop", + "deepin-diskmanager.desktop", + "deepin-camera.desktop", + "deepin-data-transfer.desktop", + "deepin-activator-notify.desktop", + "deepin-defender.desktop", + "deepin-defender-session-daemon.desktop", + "deepin-defender-nativirus.desktop", + "uos-service-support.desktop", + "uos-remote-assistance.desktop", + "uos-recovery-gui.desktop", + "uos-ai-assistant.autostart.desktop", + "uos-ai-assistant.desktop", + "auth-dialog.desktop", + "udcp-vnc-exec.desktop", + "udcp-session-exec.desktop", + "udcp-wizzard-exec.desktop", + "deepin-scanner.desktop", + "org.remmina.Remmina.desktop", + "org.remmina.Remmina-file.desktop", + "booster-dtkwidget.desktop", + "gnome-keyring-ssh.desktop", + "gnome-keyring-pkcs11.desktop", + "gnome-keyring-secrets.desktop", + "fcitx5-helper.desktop", + "org.fcitx.Fcitx5.desktop", + "xdg-user-dirs.desktop", + "permission_manager_dbus_session_daemon.desktop", + "downloader.desktop", + "geoclue-demo-agent.desktop", + "filearmor_notify.desktop", + "im-launch.desktop", + "oom-score-adjust.desktop", + "pulseaudio.desktop", + "dde-grand-search.desktop", + "deepin-offline-update-tool.desktop", + "com.uos.drive.desktop", + "com.sogou.ime.ng.fcitx5.uos.configurer.desktop", + "xdg-desktop-portal-gtk.desktop", + "uos-activator.desktop" + ], + "serial": 0, + "flags": [ + "global" + ], + "name": "system applications", + "name[zh_CN]": "系统预装应用列表", + "description": "system pre-installed applications whitelist for short idle", + "permissions": "readwrite", + "visibility": "private" + }, + "systemServices": { + "value": [ + "accounts-daemon.service", + "alsa-restore.service", + "blk-availability.service", + "bluetooth.service", + "cache-udcp.service", + "cron.service", + "cups.service", + "dbus.service", + "dde-api-dbus-proxy-v1.service", + "dde-dconfig-daemon.service", + "dde-system-daemon.service", + "deepin-anything-server.service", + "deepin-authenticate.service", + "deepin-debug-config-service.service", + "deepin-defender-monitornetflow.service", + "deepin-devicecontrol.service", + "deepin-elf-verify.service", + "deepin-grub2.service", + "deepin-login-sound.service", + "deepin-service-group@app.service", + "deepin-service-manager.service", + "deepin-shutdown-sound.service", + "deepin.sync.helper.service", + "event-log.service", + "ftg340conf.service", + "kmod-static-nodes.service", + "lastore-daemon.service", + "ldconfig.service", + "license.service", + "lightdm.service", + "ModemManager.service", + "NetworkManager-wait-online.service", + "NetworkManager.service", + "org.deepin.linglong.PackageManager.service", + "plymouth-quit-wait.service", + "plymouth-read-write.service", + "plymouth-start.service", + "polkit.service", + "rsyslog.service", + "selinux-autorelabel-mark.service", + "systemd-binfmt.service", + "systemd-journal-catalog-update.service", + "systemd-journal-flush.service", + "systemd-journald.service", + "systemd-logind.service", + "systemd-modules-load.service", + "systemd-random-seed.service", + "systemd-remount-fs.service", + "systemd-sysctl.service", + "systemd-sysusers.service", + "systemd-timesyncd.service", + "systemd-tmpfiles-setup-dev-early.service", + "systemd-tmpfiles-setup-dev.service", + "systemd-tmpfiles-setup.service", + "systemd-udev-trigger.service", + "systemd-udevd.service", + "systemd-update-done.service", + "systemd-update-utmp.service", + "systemd-user-sessions.service", + "udcp-log.service", + "udisks2.service", + "uos-ste-eventd.service", + "uos-ste-resourced.service", + "uos-upgrade-delivery-daemon.service", + "upower.service", + "usecd.service", + "wpa_supplicant.service", + "wtmpdb-update-boot.service", + "ssh.service", + "udcp-guard.service", + "com.home.appstore.daemon.service", + "serial-getty@ttyAMA0.service", + "winbind.service", + "tpm2-abrmd.service", + "fprintd.service", + "iio-sensor-proxy.service", + "avahi-daemon.service" + ], + "serial": 0, + "flags": [ + "global" + ], + "name": "system services", + "name[zh_CN]": "系统服务", + "description": "System services", + "description[zh_CN]": "系统服务列表,包含系统级别的后台服务和守护进程,如网络管理、蓝牙服务、电源管理服务等。这些服务在系统启动时自动运行,提供核心功能支持。此配置用于判断是否开启了第三方service文件,如果开启了则不能进入短idle状态。", + "permissions": "readwrite", + "visibility": "private" + } + } +} diff --git a/src/plugin-qt/power/powerconstants.h b/src/plugin-qt/power/powerconstants.h index 8d20684d..509a0424 100644 --- a/src/plugin-qt/power/powerconstants.h +++ b/src/plugin-qt/power/powerconstants.h @@ -14,6 +14,7 @@ namespace PowerDConfig { inline constexpr auto kLinePowerLockDelay = "linePowerLockDelay"; inline constexpr auto kLinePowerLidClosedAction = "linePowerLidClosedAction"; inline constexpr auto kLinePowerPressPowerButton = "linePowerPressPowerButton"; + inline constexpr auto kLinePowerShortIdleDelay = "linePowerShortIdleDelay"; inline constexpr auto kBatteryScreensaverDelay = "batteryScreensaverDelay"; inline constexpr auto kBatteryScreenBlackDelay = "batteryScreenBlackDelay"; @@ -21,11 +22,21 @@ namespace PowerDConfig { inline constexpr auto kBatteryLockDelay = "batteryLockDelay"; inline constexpr auto kBatteryLidClosedAction = "batteryLidClosedAction"; inline constexpr auto kBatteryPressPowerButton = "batteryPressPowerButton"; + inline constexpr auto kBatteryShortIdleDelay = "batteryShortIdleDelay"; inline constexpr auto kScreenBlackLock = "screenBlackLock"; inline constexpr auto kSleepLock = "sleepLock"; inline constexpr auto kPowerButtonPressedExec = "powerButtonPressedExec"; - + inline constexpr auto kShortIdleEnable = "shortIdleEnable"; + inline constexpr auto kShortIdleState = "shortIdleState"; + inline constexpr auto kShortIdleBlacklistApplications = "shortIdleBlacklistApplications"; + inline constexpr auto kSystemApplications = "systemApplications"; + inline constexpr auto kSystemServices = "systemServices"; + inline constexpr auto kFullscreenWorkaroundAppList = "fullscreenWorkaroundAppList"; + inline constexpr auto kAllowScreenSaver = "allowScreenSaver"; + inline constexpr auto kDelayHandleIdleOffIntervalWhenScreenBlack = "delayHandleIdleOffIntervalWhenScreenBlack"; + +inline constexpr auto kDelayWakeupInterval = "delayWakeupInterval"; inline constexpr auto kLowPowerNotifyEnable = "lowPowerNotifyEnable"; inline constexpr auto kLowPowerNotifyThreshold = "lowPowerNotifyThreshold"; inline constexpr auto kUsePercentageForPolicy = "usePercentageForPolicy"; @@ -37,14 +48,16 @@ namespace PowerDConfig { inline constexpr auto kLowPowerAction = "lowPowerAction"; inline constexpr auto kPowerSavingModeBrightnessDropPercent = "powerSavingModeBrightnessDropPercent"; inline constexpr auto kPowerSavingModeEnabled = "powerSavingModeEnabled"; + inline constexpr auto kSaveBrightnessWhilePsm = "saveBrightnessWhilePsm"; inline constexpr auto kPowerSavingModeAuto = "powerSavingModeAuto"; inline constexpr auto kPowerSavingModeAutoWhenBatteryLow = "powerSavingModeAutoWhenBatteryLow"; inline constexpr auto kPowerSavingModeAutoBatteryPercent = "powerSavingModeAutoBatteryPercent"; - inline constexpr auto kLastMode = "lastMode"; + inline constexpr auto kPowerMappingConfig = "powerMappingConfig"; inline constexpr auto kMode = "mode"; inline constexpr auto kAdjustBrightnessEnabled = "adjustBrightnessEnabled"; inline constexpr auto kHighPerformanceEnabled = "highPerformanceEnabled"; + inline constexpr auto kScheduledShutdownState = "scheduledShutdownState"; inline constexpr auto kShutdownTime = "shutdownTime"; inline constexpr auto kShutdownRepetition = "shutdownRepetition"; @@ -80,6 +93,11 @@ namespace PowerDBus { inline constexpr auto kScreensaver = "com.deepin.ScreenSaver"; inline constexpr auto kScreensaverPath = "/com/deepin/ScreenSaver"; + inline constexpr auto kIdleService = "org.deepin.dde.ScreenSaver1"; + inline constexpr auto kX11IdleService = "org.freedesktop.ScreenSaver"; + inline constexpr auto kX11IdlePath = "/org/freedesktop/ScreenSaver"; + inline constexpr auto kX11IdleInterface = "org.freedesktop.ScreenSaver"; + inline constexpr auto kIdlePath = "/org/deepin/dde/ScreenSaver1"; inline constexpr auto kDisplay = "org.deepin.dde.Display1"; inline constexpr auto kDisplayPath = "/org/deepin/dde/Display1"; @@ -99,6 +117,10 @@ namespace PowerDBus { inline constexpr auto kCalendarService = "com.deepin.dataserver.Calendar"; inline constexpr auto kCalendarPath = "/com/deepin/dataserver/Calendar/HuangLi"; inline constexpr auto kCalendarIface = "com.deepin.dataserver.Calendar.HuangLi"; + + inline constexpr auto kDisplayManagerService = "org.freedesktop.DisplayManager"; + inline constexpr auto kDisplayManagerPath = "/org/freedesktop/DisplayManager"; + inline constexpr auto kDisplayManagerIface = "org.freedesktop.DisplayManager"; } namespace PowerFS { @@ -106,7 +128,6 @@ namespace PowerFS { inline constexpr auto kCpuGovernorFmt = "/sys/devices/system/cpu/%1/cpufreq/scaling_governor"; inline constexpr auto kCpuBoostPath = "/sys/devices/system/cpu/cpufreq/boost"; inline constexpr auto kLidStatePath = "/proc/acpi/button/lid/LID/state"; - inline constexpr auto kDpmsStateFile = "/tmp/dpms-state"; inline constexpr auto kNoSuspendFile = "/etc/deepin/no_suspend"; inline constexpr auto kLowPowerCmd = "/usr/lib/deepin-daemon/dde-lowpower"; } diff --git a/src/plugin-qt/power/session/CMakeLists.txt b/src/plugin-qt/power/session/CMakeLists.txt index 9009a3da..bb38a9a9 100644 --- a/src/plugin-qt/power/session/CMakeLists.txt +++ b/src/plugin-qt/power/session/CMakeLists.txt @@ -15,6 +15,7 @@ include(GNUInstallDirs) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core DBus Gui WaylandClient LinguistTools) find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Core) find_package(PkgConfig REQUIRED) +pkg_check_modules(X11 REQUIRED x11 xext xscrnsaver) pkg_check_modules(WAYLAND_PROTOCOLS REQUIRED wayland-protocols) pkg_check_modules(WLR_PROTOCOLS REQUIRED wlr-protocols) pkg_check_modules(TREELAND_PROTOCOLS REQUIRED treeland-protocols) @@ -76,6 +77,7 @@ qt_add_translations(${BIN_NAME} target_include_directories(${BIN_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} + ${X11_INCLUDE_DIRS} ) target_link_libraries(${BIN_NAME} PRIVATE @@ -84,6 +86,7 @@ target_link_libraries(${BIN_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::WaylandClient Dtk${DTK_VERSION_MAJOR}::Core + ${X11_LIBRARIES} ) install(TARGETS ${BIN_NAME} @@ -91,11 +94,11 @@ DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/ ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/plugin-power-session.json -DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/deepin-service-manager/user/ +DESTINATION ${CMAKE_INSTALL_DATADIR}/deepin-service-manager/user/ ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/dbus/org.deepin.dde.Power1.service -DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/services/ +DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/services/ ) install(FILES ${QM_FILES} diff --git a/src/plugin-qt/power/session/idle/idlewatcher.h b/src/plugin-qt/power/session/idle/idlewatcher.h index 7e763413..39388261 100644 --- a/src/plugin-qt/power/session/idle/idlewatcher.h +++ b/src/plugin-qt/power/session/idle/idlewatcher.h @@ -44,3 +44,5 @@ class IdleWatcher : public QObject /// Emitted when the user resumes activity. void resumed(); }; + +IdleWatcher *createX11IdleWatcher(QObject *parent = nullptr); diff --git a/src/plugin-qt/power/session/idle/idlewatcher_wl.cpp b/src/plugin-qt/power/session/idle/idlewatcher_wl.cpp index 371fc6cf..397bf4bc 100644 --- a/src/plugin-qt/power/session/idle/idlewatcher_wl.cpp +++ b/src/plugin-qt/power/session/idle/idlewatcher_wl.cpp @@ -7,6 +7,8 @@ #include #include +#include + Q_DECLARE_LOGGING_CATEGORY(logPowerSession) IdleNotification::IdleNotification(::ext_idle_notification_v1 *id) @@ -185,5 +187,7 @@ uint32_t WaylandIdleWatcher::idleTimeMs() const if (!m_idleTimer.isValid()) return 0; - return static_cast(m_idleTimer.elapsed()); + const qint64 total = static_cast(m_timeoutSec) * 1000 + m_idleTimer.elapsed(); + return static_cast(qMin( + static_cast(std::numeric_limits::max()), total)); } diff --git a/src/plugin-qt/power/session/idle/idlewatcher_x11.cpp b/src/plugin-qt/power/session/idle/idlewatcher_x11.cpp index 4cea4dce..81afb9f2 100644 --- a/src/plugin-qt/power/session/idle/idlewatcher_x11.cpp +++ b/src/plugin-qt/power/session/idle/idlewatcher_x11.cpp @@ -3,17 +3,100 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "idlewatcher.h" +#include "../../powerconstants.h" + +#include +#include +#include +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(logPowerSession) + +using namespace PowerDBus; class X11IdleWatcher : public IdleWatcher { Q_OBJECT public: - using IdleWatcher::IdleWatcher; - bool isValid() const override { return false; } - void setTimeout(uint32_t) override {} - void simulateActivity() override {} - uint32_t idleTimeMs() const override { return 0; } - bool isIdle() const override { return false; } + explicit X11IdleWatcher(QObject *parent = nullptr) + : IdleWatcher(parent) + , m_screenSaver(kX11IdleService, kX11IdlePath, kX11IdleInterface, + QDBusConnection::sessionBus()) + , m_display(XOpenDisplay(nullptr)) + { + auto bus = QDBusConnection::sessionBus(); + const bool idleOn = bus.connect(kX11IdleService, kX11IdlePath, kX11IdleInterface, "IdleOn", + this, SLOT(onIdleOn())); + const bool idleOff = bus.connect(kX11IdleService, kX11IdlePath, kX11IdleInterface, "IdleOff", + this, SLOT(onIdleOff())); + m_valid = idleOn && idleOff && m_display != nullptr; + if (!m_valid) + qWarning(logPowerSession) << "Failed to connect X11 idle signals"; + } + + ~X11IdleWatcher() override + { + if (m_display) + XCloseDisplay(m_display); + } + + bool isValid() const override { return m_valid; } + + void setTimeout(uint32_t timeoutSec) override + { + m_timeoutSec = timeoutSec; + m_screenSaver.asyncCall("SetTimeout", timeoutSec, 0u, false); + } + + void simulateActivity() override + { + m_screenSaver.asyncCall("SimulateUserActivity"); + } + + uint32_t idleTimeMs() const override + { + if (!m_display) + return 0; + XScreenSaverInfo *info = XScreenSaverAllocInfo(); + const bool ok = info && XScreenSaverQueryInfo( + m_display, DefaultRootWindow(m_display), info); + const uint32_t idle = ok ? info->idle : 0; + if (info) + XFree(info); + return idle; + } + + bool isIdle() const override { return m_isIdle; } + +private Q_SLOTS: + void onIdleOn() + { + if (m_isIdle) + return; + m_isIdle = true; + Q_EMIT idled(); + } + + void onIdleOff() + { + if (!m_isIdle) + return; + m_isIdle = false; + Q_EMIT resumed(); + } + +private: + QDBusInterface m_screenSaver; + Display *m_display = nullptr; + uint32_t m_timeoutSec = 300; + bool m_valid = false; + bool m_isIdle = false; }; +IdleWatcher *createX11IdleWatcher(QObject *parent) +{ + return new X11IdleWatcher(parent); +} + #include "idlewatcher_x11.moc" diff --git a/src/plugin-qt/power/session/lidswitchhandler.cpp b/src/plugin-qt/power/session/lidswitchhandler.cpp index 8331915d..60af5bad 100644 --- a/src/plugin-qt/power/session/lidswitchhandler.cpp +++ b/src/plugin-qt/power/session/lidswitchhandler.cpp @@ -36,6 +36,8 @@ LidSwitchHandler::LidSwitchHandler(PowerManager *manager, QObject *parent) void LidSwitchHandler::onLidClosed() { + if (!m_manager || !m_manager->isSessionActive()) + return; qDebug(logPowerSession) << "Lid closed"; m_pendingOpen = false; m_debounce->start(); @@ -43,6 +45,8 @@ void LidSwitchHandler::onLidClosed() void LidSwitchHandler::onLidOpened() { + if (!m_manager || !m_manager->isSessionActive()) + return; qDebug(logPowerSession) << "Lid opened"; m_pendingOpen = true; m_debounce->start(); @@ -50,7 +54,8 @@ void LidSwitchHandler::onLidOpened() void LidSwitchHandler::doLidStateChanged(bool opened) { - if (!m_manager) return; + if (!m_manager) + return; if (!opened) { // 合盖 @@ -65,7 +70,10 @@ void LidSwitchHandler::doLidStateChanged(bool opened) m_manager->doShutdown(); break; case PA_Suspend: - m_manager->doSuspend(); + if (m_manager->useWayland()) + m_manager->doSuspend(); + else + m_manager->doSuspendByFront(); break; case PA_Hibernate: m_manager->doHibernate(); @@ -73,9 +81,6 @@ void LidSwitchHandler::doLidStateChanged(bool opened) case PA_TurnOffScreen: m_manager->doTurnOffScreen(); break; - case PA_Lock: - m_manager->doLock(); - break; case PA_DoNothing: return; default: @@ -86,7 +91,7 @@ void LidSwitchHandler::doLidStateChanged(bool opened) m_manager->setBlackScreenActive(true); } else { if (m_manager->useWayland()) - m_manager->SetPrepareSuspend(PS_Resume); + m_manager->SetPrepareSuspend(PS_Finish); if (auto *iw = m_manager->idleWatcher()) iw->simulateActivity(); diff --git a/src/plugin-qt/power/session/lowpowermanager.cpp b/src/plugin-qt/power/session/lowpowermanager.cpp index e12e4bcf..a62046ce 100644 --- a/src/plugin-qt/power/session/lowpowermanager.cpp +++ b/src/plugin-qt/power/session/lowpowermanager.cpp @@ -10,18 +10,28 @@ #include #include #include +#include Q_DECLARE_LOGGING_CATEGORY(logPowerSession) using namespace PowerFS; using namespace PowerDConfig; -LowPowerManager::LowPowerManager(PowerManager *powerManager, QObject *parent) - : QObject(parent), m_powerManager(powerManager) +LowPowerManager::LowPowerManager(PowerManager *powerManager) + : QDBusAbstractAdaptor(powerManager), m_powerManager(powerManager) { m_countTicker = new QTimer(this); m_countTicker->setInterval(1000); - + m_validationTimer = new QTimer(this); + m_validationTimer->setSingleShot(true); + m_validationTimer->setInterval(20000); + + connect(m_validationTimer, &QTimer::timeout, this, [this] { + if (!configValid()) { + qWarning(logPowerSession) << "Warn-level configuration is invalid; resetting"; + Reset(); + } + }); connect(m_countTicker, &QTimer::timeout, this, [this]() { m_count++; if (!m_powerManager) { @@ -45,17 +55,49 @@ LowPowerManager::LowPowerManager(PowerManager *powerManager, QObject *parent) connect(m_powerManager, &PowerManager::batteryPercentageChanged, this, &LowPowerManager::updateWarnLevel); + connect(m_powerManager, &PowerManager::batteryTimeToEmptyChanged, + this, &LowPowerManager::updateWarnLevel); connect(m_powerManager, &PowerManager::onBatteryChanged, this, &LowPowerManager::updateWarnLevel); + connect(m_powerManager, &PowerManager::lowPowerNotifyThresholdChanged, this, [this] { + setLowPowerNotifyThreshold(m_powerManager->lowPowerNotifyThreshold()); + scheduleValidation(); + }); + connect(m_powerManager, &PowerManager::lowPowerAutoSleepThresholdChanged, this, [this] { + setActionPercentage(m_powerManager->lowPowerAutoSleepThreshold()); + scheduleValidation(); + }); } +#define SET_CONFIG_VALUE(Type, Name, member, signal, dbusName) \ + void LowPowerManager::set##Name(Type value) \ + { \ + if (member == static_cast(value)) \ + return; \ + member = static_cast(value); \ + Q_EMIT signal(); \ + notifyPropertyChanged(dbusName, QVariant::fromValue(value)); \ + updateWarnLevel(); \ + } + +SET_CONFIG_VALUE(bool, UsePercentageForPolicy, m_usePercentageForPolicy, + usePercentageForPolicyChanged, "UsePercentageForPolicy") +SET_CONFIG_VALUE(qint64, LowTime, m_timeToEmptyLow, lowTimeChanged, "LowTime") +SET_CONFIG_VALUE(qint64, DangerTime, m_timeToEmptyDanger, dangerTimeChanged, "DangerTime") +SET_CONFIG_VALUE(qint64, CriticalTime, m_timeToEmptyCritical, criticalTimeChanged, "CriticalTime") +SET_CONFIG_VALUE(qint64, ActionTime, m_timeToEmptyAction, actionTimeChanged, "ActionTime") +SET_CONFIG_VALUE(qint64, LowPowerNotifyThreshold, m_lowPowerNotifyThreshold, + lowPowerNotifyThresholdChanged, "LowPowerNotifyThreshold") +SET_CONFIG_VALUE(qint64, ActionPercentage, m_percentageAction, + actionPercentageChanged, "ActionPercentage") + +#undef SET_CONFIG_VALUE + void LowPowerManager::initConfig(Dtk::Core::DConfig *config) { m_config = config; - if (!m_config) return; - - connect(m_config, &Dtk::Core::DConfig::valueChanged, - this, &LowPowerManager::onConfigChanged); + if (!m_config) + return; m_usePercentageForPolicy = m_config->value(kUsePercentageForPolicy, true).toBool(); m_lowPowerNotifyThreshold = m_config->value(kLowPowerNotifyThreshold, 0).toInt(); @@ -66,28 +108,22 @@ void LowPowerManager::initConfig(Dtk::Core::DConfig *config) m_timeToEmptyAction = static_cast(m_config->value(kTimeToEmptyAction, 0).toLongLong()); } -void LowPowerManager::onConfigChanged(const QString &key) +void LowPowerManager::applyConfigValue(const QString &key, const QVariant &value) { - if (!m_config) return; - if (key == QLatin1String(kUsePercentageForPolicy)) - m_usePercentageForPolicy = m_config->value(kUsePercentageForPolicy, true).toBool(); - else if (key == QLatin1String(kLowPowerNotifyThreshold)) - m_lowPowerNotifyThreshold = m_config->value(kLowPowerNotifyThreshold, 0).toInt(); - else if (key == QLatin1String(kPercentageAction)) - m_percentageAction = m_config->value(kPercentageAction, 0).toInt(); + setUsePercentageForPolicy(value.toBool()); else if (key == QLatin1String(kTimeToEmptyLow)) - m_timeToEmptyLow = static_cast(m_config->value(kTimeToEmptyLow, 0).toLongLong()); + setLowTime(value.toLongLong()); else if (key == QLatin1String(kTimeToEmptyDanger)) - m_timeToEmptyDanger = static_cast(m_config->value(kTimeToEmptyDanger, 0).toLongLong()); + setDangerTime(value.toLongLong()); else if (key == QLatin1String(kTimeToEmptyCritical)) - m_timeToEmptyCritical = static_cast(m_config->value(kTimeToEmptyCritical, 0).toLongLong()); + setCriticalTime(value.toLongLong()); else if (key == QLatin1String(kTimeToEmptyAction)) - m_timeToEmptyAction = static_cast(m_config->value(kTimeToEmptyAction, 0).toLongLong()); + setActionTime(value.toLongLong()); else return; - updateWarnLevel(); + scheduleValidation(); } uint LowPowerManager::getWarnLevel(double percentage, quint64 timeToEmpty) @@ -130,23 +166,25 @@ uint LowPowerManager::getWarnLevel(double percentage, quint64 timeToEmpty) void LowPowerManager::updateWarnLevel() { if (!m_powerManager || !m_powerManager->onBattery()) { + disableTicker(); if (m_currentLevel != None) { m_currentLevel = None; handleLevelChanged(None); } - disableTicker(); - closeLowPower(); return; } double pct = 100.0; - auto bat = m_powerManager->batteryPercentage(); - if (!bat.isEmpty()) - pct = bat.first(); + const auto batteries = m_powerManager->batteryPercentage(); + if (!batteries.isEmpty()) + pct = batteries.first(); - quint64 tte = m_powerManager->batteryTimeToEmpty(); + // Some firmware transiently reports 0%. Keep an already-active warning + // until a credible percentage or AC state arrives. + if (m_usePercentageForPolicy && pct == 0.0 && m_currentLevel != None) + return; - uint newLevel = getWarnLevel(pct, tte); + const uint newLevel = getWarnLevel(pct, m_powerManager->batteryTimeToEmpty()); if (newLevel == m_currentLevel) return; @@ -158,6 +196,10 @@ void LowPowerManager::handleLevelChanged(uint level) { qDebug(logPowerSession) << "Battery level changed: " << level; disableTicker(); + if (m_powerManager && m_powerManager->m_warnLevel != level) { + m_powerManager->m_warnLevel = level; + Q_EMIT m_powerManager->warnLevelChanged(); + } switch (level) { case Action: { @@ -175,16 +217,12 @@ void LowPowerManager::handleLevelChanged(uint level) playBatterySound(); sendNotify(tr("Battery low, please plug in")); break; - case None: { + case None: closeLowPower(); - if (m_powerManager) { - m_powerManager->closeNotify(); - if (m_powerManager->scheduledShutdownState()) - m_powerManager->scheduledShutdown(SchedInit); - } + if (m_powerManager && m_powerManager->scheduledShutdownState()) + m_powerManager->scheduledShutdown(SchedInit); break; } - } } void LowPowerManager::startCountTicker() @@ -251,3 +289,48 @@ void LowPowerManager::closeLowPower() if (!QProcess::startDetached(kLowPowerCmd, {"--quit"})) qWarning(logPowerSession) << "Failed to start dde-lowpower --quit"; } + +void LowPowerManager::scheduleValidation() +{ + m_validationTimer->start(); +} + +bool LowPowerManager::configValid() const +{ + // Legacy dde-daemon only accepted 1%-9% action thresholds; 10% is the + // separate critical threshold, so equality would collapse two warning levels. + return m_timeToEmptyLow > m_timeToEmptyDanger + && m_timeToEmptyDanger > m_timeToEmptyCritical + && m_timeToEmptyCritical > m_timeToEmptyAction + && m_percentageAction < 10; +} + +void LowPowerManager::Reset() +{ + if (!m_config) + return; + static const char *keys[] = { + kUsePercentageForPolicy, + kLowPowerNotifyThreshold, + kPercentageAction, + kTimeToEmptyLow, + kTimeToEmptyDanger, + kTimeToEmptyCritical, + kTimeToEmptyAction, + }; + for (const char *key : keys) + m_config->reset(QLatin1String(key)); +} + +void LowPowerManager::notifyPropertyChanged(const char *name, const QVariant &value) +{ + if (!m_powerManager || !m_powerManager->m_conn) + return; + QDBusMessage message = QDBusMessage::createSignal( + PowerDBus::kPath, QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("PropertiesChanged")); + message << QStringLiteral("org.deepin.dde.Power1.WarnLevelConfig") + << QVariantMap{{QString::fromLatin1(name), value}} + << QStringList(); + m_powerManager->m_conn->send(message); +} diff --git a/src/plugin-qt/power/session/lowpowermanager.h b/src/plugin-qt/power/session/lowpowermanager.h index 0b65755e..3e17c70c 100644 --- a/src/plugin-qt/power/session/lowpowermanager.h +++ b/src/plugin-qt/power/session/lowpowermanager.h @@ -4,25 +4,62 @@ #pragma once -#include +#include #include #include class PowerManager; -class LowPowerManager : public QObject { +class LowPowerManager : public QDBusAbstractAdaptor { + // PowerManager owns adaptor initialization; keep those hooks out of the D-Bus API. + friend class PowerManager; + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.Power1.WarnLevelConfig") + Q_PROPERTY(bool UsePercentageForPolicy READ usePercentageForPolicy WRITE setUsePercentageForPolicy NOTIFY usePercentageForPolicyChanged) + Q_PROPERTY(qint64 LowTime READ lowTime WRITE setLowTime NOTIFY lowTimeChanged) + Q_PROPERTY(qint64 DangerTime READ dangerTime WRITE setDangerTime NOTIFY dangerTimeChanged) + Q_PROPERTY(qint64 CriticalTime READ criticalTime WRITE setCriticalTime NOTIFY criticalTimeChanged) + Q_PROPERTY(qint64 ActionTime READ actionTime WRITE setActionTime NOTIFY actionTimeChanged) + Q_PROPERTY(qint64 LowPowerNotifyThreshold READ lowPowerNotifyThreshold WRITE setLowPowerNotifyThreshold NOTIFY lowPowerNotifyThresholdChanged) + Q_PROPERTY(qint64 ActionPercentage READ actionPercentage WRITE setActionPercentage NOTIFY actionPercentageChanged) + public: - LowPowerManager(PowerManager *powerManager, QObject *parent = nullptr); + explicit LowPowerManager(PowerManager *powerManager); enum Level { None = 0, Remind, Low, Danger, Critical, Action }; void initConfig(Dtk::Core::DConfig *config); + void applyConfigValue(const QString &key, const QVariant &value); -private Q_SLOTS: - void updateWarnLevel(); - void onConfigChanged(const QString &key); + bool usePercentageForPolicy() const { return m_usePercentageForPolicy; } + void setUsePercentageForPolicy(bool value); + qint64 lowTime() const { return static_cast(m_timeToEmptyLow); } + void setLowTime(qint64 value); + qint64 dangerTime() const { return static_cast(m_timeToEmptyDanger); } + void setDangerTime(qint64 value); + qint64 criticalTime() const { return static_cast(m_timeToEmptyCritical); } + void setCriticalTime(qint64 value); + qint64 actionTime() const { return static_cast(m_timeToEmptyAction); } + void setActionTime(qint64 value); + qint64 lowPowerNotifyThreshold() const { return m_lowPowerNotifyThreshold; } + void setLowPowerNotifyThreshold(qint64 value); + qint64 actionPercentage() const { return m_percentageAction; } + void setActionPercentage(qint64 value); + +public Q_SLOTS: + void Reset(); + +Q_SIGNALS: + void usePercentageForPolicyChanged(); + void lowTimeChanged(); + void dangerTimeChanged(); + void criticalTimeChanged(); + void actionTimeChanged(); + void lowPowerNotifyThresholdChanged(); + void actionPercentageChanged(); private: + void updateWarnLevel(); void handleLevelChanged(uint level); void disableTicker(); uint getWarnLevel(double percentage, quint64 timeToEmpty); @@ -32,8 +69,12 @@ private Q_SLOTS: void closeLowPower(); void lockWaitShow(int timeoutMs, bool autoStartAuth); void playBatterySound(); + void scheduleValidation(); + bool configValid() const; + void notifyPropertyChanged(const char *name, const QVariant &value); QTimer *m_countTicker = nullptr; + QTimer *m_validationTimer = nullptr; int m_count = 0; uint m_currentLevel = 0; diff --git a/src/plugin-qt/power/session/plugin-power-session.cpp b/src/plugin-qt/power/session/plugin-power-session.cpp index 007ae74b..19f477c5 100644 --- a/src/plugin-qt/power/session/plugin-power-session.cpp +++ b/src/plugin-qt/power/session/plugin-power-session.cpp @@ -15,8 +15,6 @@ static QTranslator *g_trans = nullptr; extern "C" int DSMRegister(const char *name, void *data) { - if (qEnvironmentVariable("XDG_SESSION_TYPE") != QLatin1String("wayland")) - return 0; g_trans = new QTranslator; const QString &path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, diff --git a/src/plugin-qt/power/session/powermanager.cpp b/src/plugin-qt/power/session/powermanager.cpp index 4c922dfa..732b2c9f 100644 --- a/src/plugin-qt/power/session/powermanager.cpp +++ b/src/plugin-qt/power/session/powermanager.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -26,34 +27,56 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include #include #include +#include #include +#include #include using namespace PowerDBus; using namespace PowerDConfig; using namespace PowerFS; using namespace Dtk::Core; +using ObjectInterfaceMap = QMap; +using ObjectMap = QMap; + +static QStringList desktopFileNames(QStringList applications) +{ + for (QString &application : applications) + application = QFileInfo(application).fileName(); + return applications; +} + +Q_DECLARE_METATYPE(ObjectInterfaceMap) +Q_DECLARE_METATYPE(ObjectMap) Q_LOGGING_CATEGORY(logPowerSession, "dde.power.session") #define DEF_SETTER_PERSIST(T, Suffix, member, signal, dkey) \ void PowerManager::set##Suffix(T v) { \ if (m_##member != v) { m_##member = v; Q_EMIT signal(); \ - if (m_config) m_config->setValue(dkey, QVariant::fromValue(v)); } } + persist(dkey, QVariant::fromValue(v)); } } DEF_SETTER_PERSIST(int, LinePowerScreensaverDelay, linePowerScreensaverDelay, linePowerScreensaverDelayChanged, kLinePowerScreensaverDelay) DEF_SETTER_PERSIST(int, LinePowerScreenBlackDelay, linePowerScreenBlackDelay, linePowerScreenBlackDelayChanged, kLinePowerScreenBlackDelay) DEF_SETTER_PERSIST(int, LinePowerSleepDelay, linePowerSleepDelay, linePowerSleepDelayChanged, kLinePowerSleepDelay) DEF_SETTER_PERSIST(int, LinePowerLockDelay, linePowerLockDelay, linePowerLockDelayChanged, kLinePowerLockDelay) DEF_SETTER_PERSIST(int, BatteryScreensaverDelay, batteryScreensaverDelay, batteryScreensaverDelayChanged, kBatteryScreensaverDelay) +DEF_SETTER_PERSIST(int, LinePowerShortIdleDelay, linePowerShortIdleDelay, linePowerShortIdleDelayChanged, kLinePowerShortIdleDelay) DEF_SETTER_PERSIST(int, BatteryScreenBlackDelay, batteryScreenBlackDelay, batteryScreenBlackDelayChanged, kBatteryScreenBlackDelay) DEF_SETTER_PERSIST(int, BatterySleepDelay, batterySleepDelay, batterySleepDelayChanged, kBatterySleepDelay) DEF_SETTER_PERSIST(int, BatteryLockDelay, batteryLockDelay, batteryLockDelayChanged, kBatteryLockDelay) DEF_SETTER_PERSIST(bool, ScreenBlackLock, screenBlackLock, screenBlackLockChanged, kScreenBlackLock) +DEF_SETTER_PERSIST(int, BatteryShortIdleDelay, batteryShortIdleDelay, batteryShortIdleDelayChanged, kBatteryShortIdleDelay) DEF_SETTER_PERSIST(bool, SleepLock, sleepLock, sleepLockChanged, kSleepLock) DEF_SETTER_PERSIST(int, LinePowerLidClosedAction, linePowerLidClosedAction, linePowerLidClosedActionChanged, kLinePowerLidClosedAction) DEF_SETTER_PERSIST(int, BatteryLidClosedAction, batteryLidClosedAction, batteryLidClosedActionChanged, kBatteryLidClosedAction) @@ -63,8 +86,6 @@ DEF_SETTER_PERSIST(bool, LowPowerNotifyEnable, lowPowerNotifyEnable, lowPowerNot DEF_SETTER_PERSIST(int, LowPowerNotifyThreshold, lowPowerNotifyThreshold, lowPowerNotifyThresholdChanged, kLowPowerNotifyThreshold) DEF_SETTER_PERSIST(int, LowPowerAutoSleepThreshold, lowPowerAutoSleepThreshold, lowPowerAutoSleepThresholdChanged, kPercentageAction) DEF_SETTER_PERSIST(int, LowPowerAction, lowPowerAction, lowPowerActionChanged, kLowPowerAction) -DEF_SETTER_PERSIST(bool, ScheduledShutdownState, scheduledShutdownState, scheduledShutdownStateChanged, kScheduledShutdownState) -DEF_SETTER_PERSIST(int, ShutdownRepetition, shutdownRepetition, shutdownRepetitionChanged, kShutdownRepetition) PowerManager::PowerManager(QDBusConnection *conn, const QString &svc, QObject *parent) : QObject(parent), m_conn(conn) @@ -72,6 +93,10 @@ PowerManager::PowerManager(QDBusConnection *conn, const QString &svc, QObject *p Q_UNUSED(svc); m_useWayland = (qEnvironmentVariable("XDG_SESSION_TYPE") == QLatin1String("wayland")); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); qRegisterMetaType("BatteryIsPresentMap"); qRegisterMetaType("BatteryPercentageMap"); qRegisterMetaType("BatteryStateMap"); @@ -87,7 +112,41 @@ PowerManager::PowerManager(QDBusConnection *conn, const QString &svc, QObject *p PowerManager::~PowerManager() { - if (m_sleepInhibitor) m_sleepInhibitor->unblock(); + if (m_inhibitFd >= 0) + ::close(m_inhibitFd); + if (m_sleepInhibitor) + m_sleepInhibitor->unblock(); + if (m_configReader) { + QMetaObject::invokeMethod(m_configReader, &QObject::deleteLater, + Qt::BlockingQueuedConnection); + m_configReader = nullptr; + } + if (m_conn) + m_conn->unregisterObject(kPath); +} + +// Session and system PowerManager intentionally keep separate persistence paths: +// they use different DConfig lifecycles and must not share state or guards. +void PowerManager::persist(const char *key, const QVariant &value) +{ + if (!m_config || m_applyingConfig) + return; + + const QString configKey = QLatin1String(key); + m_writingConfigKeys.insert(configKey); + m_config->setValue(configKey, value); + m_writingConfigKeys.remove(configKey); +} + +void PowerManager::resetConfig(const char *key) +{ + if (!m_config) + return; + + const QString configKey = QLatin1String(key); + m_writingConfigKeys.insert(configKey); + m_config->reset(configKey); + m_writingConfigKeys.remove(configKey); } bool PowerManager::initialize() @@ -101,23 +160,27 @@ bool PowerManager::initialize() m_powerSavePlan = new PowerSavePlan(this); m_lidSwitch = new LidSwitchHandler(this); m_sleepInhibitor = new SleepInhibitor(this); - m_lowPowerMgr = new LowPowerManager(this, this); + m_lowPowerMgr = new LowPowerManager(this); m_lowPowerMgr->initConfig(m_config); + initAmbientBrightness(); if (m_idleWatcher) { connect(m_idleWatcher, &IdleWatcher::idled, m_powerSavePlan, &PowerSavePlan::HandleIdleOn); connect(m_idleWatcher, &IdleWatcher::resumed, m_powerSavePlan, &PowerSavePlan::HandleIdleOff); } - connect(this, &PowerManager::onBatteryChanged, this, [this]() { m_powerSavePlan->Reset(); }); + connect(this, &PowerManager::onBatteryChanged, this, + [this] { m_powerSavePlan->ResetFromNow(); }); connect(this, &PowerManager::linePowerScreensaverDelayChanged, this, &PowerManager::onLinePowerDelayChanged); connect(this, &PowerManager::linePowerScreenBlackDelayChanged, this, &PowerManager::onLinePowerDelayChanged); connect(this, &PowerManager::linePowerLockDelayChanged, this, &PowerManager::onLinePowerDelayChanged); connect(this, &PowerManager::linePowerSleepDelayChanged, this, &PowerManager::onLinePowerDelayChanged); + connect(this, &PowerManager::linePowerShortIdleDelayChanged, this, &PowerManager::onLinePowerDelayChanged); connect(this, &PowerManager::batteryScreensaverDelayChanged, this, &PowerManager::onBatteryDelayChanged); connect(this, &PowerManager::batteryScreenBlackDelayChanged, this, &PowerManager::onBatteryDelayChanged); connect(this, &PowerManager::batteryLockDelayChanged, this, &PowerManager::onBatteryDelayChanged); connect(this, &PowerManager::batterySleepDelayChanged, this, &PowerManager::onBatteryDelayChanged); + connect(this, &PowerManager::batteryShortIdleDelayChanged, this, &PowerManager::onBatteryDelayChanged); initBatteryWatcher(); initSleepWatcher(); @@ -129,7 +192,8 @@ bool PowerManager::initialize() if (!m_conn->registerObject(kPath, this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals | - QDBusConnection::ExportAllProperties)) { + QDBusConnection::ExportAllProperties | + QDBusConnection::ExportAdaptors)) { qWarning(logPowerSession) << "Failed to register D-Bus object:" << m_conn->lastError().message(); return false; } @@ -138,14 +202,13 @@ bool PowerManager::initialize() // 转成 org.freedesktop.DBus.Properties.PropertiesChanged。 // 用 notifyPropertyChanged 槽函数统一桥接所有 Q_PROPERTY(NOTIFY) 到标准 D-Bus 属性变更通知。 const QMetaObject *mo = metaObject(); - int slotIdx = mo->indexOfSlot("notifyPropertyChanged()"); - if (slotIdx >= 0) { - QMetaMethod slot = mo->method(slotIdx); + const int slotIndex = mo->indexOfSlot("notifyPropertyChanged()"); + if (slotIndex >= 0) { + const QMetaMethod slot = mo->method(slotIndex); for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { - QMetaProperty prop = mo->property(i); - if (!prop.hasNotifySignal()) - continue; - connect(this, prop.notifySignal(), this, slot); + const QMetaProperty property = mo->property(i); + if (property.hasNotifySignal()) + connect(this, property.notifySignal(), this, slot); } } @@ -178,15 +241,15 @@ void PowerManager::initBatteryWatcher() connect(m_proxy, &SessionDBusProxy::PowerSavingModeEnabledChanged, this, &PowerManager::handlePowerSavingModeEnabledChanged); connect(m_proxy, &SessionDBusProxy::PowerSavingModeBrightnessDropPercentChanged, this, &PowerManager::handlePowerSavingModeBrightnessDropPercentChanged); - QTimer::singleShot(1000, this, [this]() { + const auto syncState = [this] { refreshBatteryInfo(); - // 拉取 PSM 初始值 (因为 system 端 registerObject 在 DConfig init 之后, - // 初始 NOTIFY 信号丢失, DDBusInterface 的 GetAll 也不可靠) - if (m_powerSavePlan) { - m_powerSavePlan->onPowerSavingModeEnabledChanged(m_proxy->powerSavingModeEnabled()); - m_powerSavePlan->onBrightnessDropPercentChanged(m_proxy->powerSavingModeBrightnessDropPercent()); - } - }); + if (m_powerSavePlan) + m_powerSavePlan->syncPowerSavingMode( + m_proxy->powerSavingModeEnabled(), + m_proxy->powerSavingModeBrightnessDropPercent()); + }; + syncState(); + QTimer::singleShot(1000, this, syncState); } void PowerManager::initSleepWatcher() @@ -201,34 +264,41 @@ void PowerManager::initSleepWatcher() void PowerManager::refreshBatteryInfo() { - bool hasBattery = m_proxy->hasBattery(); + const bool hasBattery = m_proxy->hasBattery(); if (hasBattery) { - m_batteryIsPresent["Display"] = true; - m_batteryPercentage["Display"] = m_proxy->batteryPercentage(); - m_batteryState["Display"] = m_proxy->batteryStatus(); - m_batteryTimeToEmpty = m_proxy->batteryTimeToEmpty(); - Q_EMIT batteryIsPresentChanged(); - Q_EMIT batteryPercentageChanged(); - Q_EMIT batteryStateChanged(); + m_batteryIsPresent[QStringLiteral("Display")] = true; + m_batteryPercentage[QStringLiteral("Display")] = m_proxy->batteryPercentage(); + m_batteryState[QStringLiteral("Display")] = m_proxy->batteryStatus(); + } else { + m_batteryIsPresent.remove(QStringLiteral("Display")); + m_batteryPercentage.remove(QStringLiteral("Display")); + m_batteryState.remove(QStringLiteral("Display")); + } + Q_EMIT batteryIsPresentChanged(); + Q_EMIT batteryPercentageChanged(); + Q_EMIT batteryStateChanged(); + + const quint64 timeToEmpty = hasBattery ? m_proxy->batteryTimeToEmpty() : 0; + if (m_batteryTimeToEmpty != timeToEmpty) { + m_batteryTimeToEmpty = timeToEmpty; + Q_EMIT batteryTimeToEmptyChanged(); } - bool onBattery = m_proxy->onBattery(); + const bool onBattery = m_proxy->onBattery(); if (onBattery != m_onBattery) { m_onBattery = onBattery; Q_EMIT onBatteryChanged(); } - bool hasLid = m_proxy->hasLidSwitch(); + const bool hasLid = m_proxy->hasLidSwitch(); if (hasLid != m_lidIsPresent) { m_lidIsPresent = hasLid; Q_EMIT lidIsPresentChanged(); } bool hps = m_proxy->isHighPerformanceSupported(); - if (m_config) { - bool e = m_config->value(PowerDConfig::kHighPerformanceEnabled).toBool(); - hps = hps && e; - } + if (m_config) + hps = hps && m_config->value(PowerDConfig::kHighPerformanceEnabled).toBool(); if (hps != m_isHighPerformanceSupported) { m_isHighPerformanceSupported = hps; Q_EMIT isHighPerformanceSupportedChanged(); @@ -253,25 +323,45 @@ void PowerManager::handleHasLidSwitchChanged(bool value) void PowerManager::handleHasBatteryChanged(bool value) { - m_batteryIsPresent["Display"] = value; + if (value) { + m_batteryIsPresent[QStringLiteral("Display")] = true; + } else { + m_batteryIsPresent.remove(QStringLiteral("Display")); + m_batteryPercentage.remove(QStringLiteral("Display")); + m_batteryState.remove(QStringLiteral("Display")); + if (m_batteryTimeToEmpty != 0) { + m_batteryTimeToEmpty = 0; + Q_EMIT batteryTimeToEmptyChanged(); + } + Q_EMIT batteryPercentageChanged(); + Q_EMIT batteryStateChanged(); + } Q_EMIT batteryIsPresentChanged(); } void PowerManager::handleBatteryPercentageChanged(double value) { - m_batteryPercentage["Display"] = value; + if (!m_batteryIsPresent.contains(QStringLiteral("Display"))) + return; + m_batteryPercentage[QStringLiteral("Display")] = value; Q_EMIT batteryPercentageChanged(); } void PowerManager::handleBatteryStatusChanged(uint value) { - m_batteryState["Display"] = value; + if (!m_batteryIsPresent.contains(QStringLiteral("Display"))) + return; + m_batteryState[QStringLiteral("Display")] = value; Q_EMIT batteryStateChanged(); } void PowerManager::handleBatteryTimeToEmptyChanged(quint64 value) { + if (!m_batteryIsPresent.contains(QStringLiteral("Display")) + || m_batteryTimeToEmpty == value) + return; m_batteryTimeToEmpty = value; + Q_EMIT batteryTimeToEmptyChanged(); } void PowerManager::handleIsHighPerformanceSupportedChanged(bool value) @@ -298,6 +388,33 @@ void PowerManager::handlePowerSavingModeBrightnessDropPercentChanged(uint value) m_powerSavePlan->onBrightnessDropPercentChanged(value); } +void PowerManager::initAmbientBrightness() +{ + m_hasAmbientLightSensor = m_proxy->ambientBrightnessSupported(); + connect(m_proxy, &SessionDBusProxy::ambientBrightnessPropertiesChanged, this, + [this](const QString &interface, const QVariantMap &changed, + const QStringList &invalidated) { + if (interface != QLatin1String("org.deepin.dde.AmbientBrightness1") + || (!changed.contains(QStringLiteral("Supported")) + && !invalidated.contains(QStringLiteral("Supported")))) { + return; + } + + const bool supported = changed.contains(QStringLiteral("Supported")) + ? changed.value(QStringLiteral("Supported")).toBool() + : m_proxy->ambientBrightnessSupported(); + if (m_hasAmbientLightSensor == supported) + return; + m_hasAmbientLightSensor = supported; + Q_EMIT hasAmbientLightSensorChanged(); + }); +} + +bool PowerManager::ambientLightAdjustBrightness() const +{ + return m_proxy && m_hasAmbientLightSensor && m_proxy->ambientBrightnessEnabled(); +} + void PowerManager::initLogindInhibit() { auto fd = m_proxy->inhibit( @@ -306,7 +423,7 @@ void PowerManager::initLogindInhibit() m_inhibitFd = fd.isValid() ? dup(fd.fileDescriptor()) : -1; connect(m_proxy, &SessionDBusProxy::login1OwnerChanged, - this, &PowerManager::onLogin1OwnerChanged); + this, &PowerManager::onLogin1OwnerChanged, Qt::UniqueConnection); } void PowerManager::onLogin1OwnerChanged(const QString &name, const QString &, @@ -352,55 +469,141 @@ static QString currentSessionId() return sid; } +static bool runAudioCommand(const QString &program, const QStringList &arguments) +{ + QProcess process; + process.start(program, arguments); + if (process.waitForFinished(5000)) + return process.exitCode() == 0; + + process.kill(); + process.waitForFinished(); + qWarning(logPowerSession) << "Timed out while updating audio suspend state:" << program; + return false; +} + +static void setAudioSuspended(bool suspended) +{ + // Suspend handling must wait for audio to quiesce before releasing the inhibitor; + // keep the legacy synchronous ordering, but cap every external command. + const QString script = QStringLiteral("/usr/libexec/deepin/os-config/pipewire-suspend.sh"); + const QString state = suspended ? QStringLiteral("1") : QStringLiteral("0"); + if (QFile::exists(script) && runAudioCommand(script, {state})) + return; + + const QStringList types = {QStringLiteral("sinks"), QStringLiteral("sources")}; + for (const QString &type : types) { + QProcess list; + list.start(QStringLiteral("pactl"), {QStringLiteral("list"), QStringLiteral("short"), type}); + if (!list.waitForFinished(5000) || list.exitCode() != 0) + continue; + const QString command = type == QLatin1String("sinks") + ? QStringLiteral("suspend-sink") : QStringLiteral("suspend-source"); + for (const QByteArray &line : list.readAllStandardOutput().split('\n')) { + const QString id = QString::fromUtf8(line).section(QLatin1Char('\t'), 0, 0); + if (!id.isEmpty()) + runAudioCommand(QStringLiteral("pactl"), {command, id, state}); + } + } +} + void PowerManager::handleBeforeSleep(bool) { - qDebug(logPowerSession) << "System is going to sleep, prepare suspend state:" << m_prepareSuspendState; - m_prepareSuspendState = PS_Sleeping; - setBlackScreenActive(true); - if (m_sleepLock) { - doLock(true); + if (!isSessionActive()) { + m_sleepCycleHandled = false; + return; + } + m_sleepCycleHandled = true; + m_prepareSuspendState = PS_Prepare; + if (!m_screensaverStateCaptured) { + m_screensaverLockAtAwake = screensaverProperty("lockScreenAtAwake"); + m_screensaverStateCaptured = true; } + setBlackScreenActive(true); + if (m_useWayland && m_sleepLock) + doLock(false); + setAudioSuspended(true); } void PowerManager::handleWakeup() { - qDebug(logPowerSession) << "System woke up"; + if (!m_sleepCycleHandled) + return; + setAudioSuspended(false); + m_sleepCycleHandled = false; m_prepareSuspendState = PS_Resume; + m_screensaverStateCaptured = false; + if (m_useWayland && m_idleWatcher) + m_idleWatcher->simulateActivity(); + if (m_scheduledShutdownState) + scheduledShutdown(SchedInit); + m_delayInActive = true; - QTimer::singleShot(m_delayWakeupInterval * 1000, this, [this]() { + QTimer::singleShot(m_delayWakeupInterval * 1000, this, [this] { m_delayInActive = false; - if (m_sleepLock) { - qCDebug(logPowerSession) << "Locking session after wakeup delay"; + m_prepareSuspendState = PS_Finish; + if (m_screensaverWasRunning && m_screensaverLockAtAwake) doLock(true); - } + m_screensaverWasRunning = false; setBlackScreenActive(false); }); - setDPMSModeOn(); if (m_powerSavePlan) m_powerSavePlan->HandleIdleOff(); - - if (m_scheduledShutdownState) - scheduledShutdown(SchedInit); + setDPMSModeOn(); + m_proxy->refreshBrightness(); + m_proxy->refreshMains(); + m_proxy->refreshBatteries(); +} +bool PowerManager::screensaverProperty(const char *name) const +{ + QDBusInterface screensaver(QStringLiteral("com.deepin.ScreenSaver"), + QStringLiteral("/com/deepin/ScreenSaver"), + QStringLiteral("com.deepin.ScreenSaver"), + QDBusConnection::sessionBus()); + return screensaver.property(name).toBool(); } + void PowerManager::Reset() { - if (!m_config) return; - static const char *keys[] = { - kLinePowerScreenBlackDelay, kLinePowerSleepDelay, kLinePowerLockDelay, - kLinePowerLidClosedAction, kLinePowerPressPowerButton, - kBatteryScreenBlackDelay, kBatterySleepDelay, kBatteryLockDelay, - kBatteryLidClosedAction, kBatteryPressPowerButton, - kScreenBlackLock, kSleepLock, kPowerButtonPressedExec, - kLowPowerNotifyEnable, kLowPowerNotifyThreshold, - kPercentageAction, kPowerSavingModeBrightnessDropPercent, + if (!m_config) + return; + + struct ResetItem { + const char *key; + const char *property; }; - for (auto k : keys) { - if (m_config->value(k).isValid()) { - m_config->reset(k); - } + static const ResetItem items[] = { + { kLinePowerScreenBlackDelay, "LinePowerScreenBlackDelay" }, + { kLinePowerSleepDelay, "LinePowerSleepDelay" }, + { kLinePowerLockDelay, "LinePowerLockDelay" }, + { kLinePowerLidClosedAction, "LinePowerLidClosedAction" }, + { kLinePowerPressPowerButton, "LinePowerPressPowerBtnAction" }, + { kBatteryScreenBlackDelay, "BatteryScreenBlackDelay" }, + { kBatterySleepDelay, "BatterySleepDelay" }, + { kBatteryLockDelay, "BatteryLockDelay" }, + { kBatteryLidClosedAction, "BatteryLidClosedAction" }, + { kBatteryPressPowerButton, "BatteryPressPowerBtnAction" }, + { kScreenBlackLock, "ScreenBlackLock" }, + { kSleepLock, "SleepLock" }, + { kPowerButtonPressedExec, nullptr }, + { kLowPowerNotifyEnable, "LowPowerNotifyEnable" }, + { kLowPowerNotifyThreshold, "LowPowerNotifyThreshold" }, + { kPercentageAction, "LowPowerAutoSleepThreshold" }, + { kPowerSavingModeBrightnessDropPercent, nullptr }, + { kLinePowerShortIdleDelay, "LinePowerShortIdleDelay" }, + { kBatteryShortIdleDelay, "BatteryShortIdleDelay" }, + }; + + m_applyingConfig = true; + for (const auto &item : items) { + resetConfig(item.key); + if (item.property) + setProperty(item.property, m_config->value(QLatin1String(item.key))); } - if (m_powerSavePlan) m_powerSavePlan->Reset(); + m_applyingConfig = false; + if (m_powerSavePlan) + m_powerSavePlan->Reset(); } void PowerManager::SetPrepareSuspend(int state) @@ -423,46 +626,332 @@ bool PowerManager::shouldIgnoreIdleOn() const return m_prepareSuspendState > PS_Finish; } +bool PowerManager::shouldIgnoreIdleOff() const +{ + return m_prepareSuspendState >= PS_Prepare; +} + +bool PowerManager::isSessionActive() const +{ + return m_proxy && m_proxy->sessionActive(); +} + +bool PowerManager::shouldPreventIdle() const +{ + // This runs only for an IdleOn event. A short-lived connection avoids retaining a + // stale X11 handle across server restarts. + if (m_useWayland || m_fullscreenWorkaroundApplications.isEmpty()) + return false; + QStringList fullscreenApps; + fullscreenApps.reserve(m_fullscreenWorkaroundApplications.size()); + for (const QString &app : m_fullscreenWorkaroundApplications) { + // Empty DConfig entries are not app names; QString::contains("") would match + // every fullscreen command line and block idle forever. + if (!app.trimmed().isEmpty()) + fullscreenApps.append(app); + } + if (fullscreenApps.isEmpty()) + return false; + + Display *display = XOpenDisplay(nullptr); + if (!display) + return false; + const Window root = DefaultRootWindow(display); + const Atom activeAtom = XInternAtom(display, "_NET_ACTIVE_WINDOW", True); + const Atom stateAtom = XInternAtom(display, "_NET_WM_STATE", True); + const Atom fullscreenAtom = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", True); + const Atom pidAtom = XInternAtom(display, "_NET_WM_PID", True); + auto property = [display](Window window, Atom atom, Atom type, unsigned long length, + unsigned char **data, unsigned long *count) { + Atom actualType = None; + int format = 0; + unsigned long remaining = 0; + return atom != None && XGetWindowProperty(display, window, atom, 0, length, False, type, + &actualType, &format, count, &remaining, data) == Success; + }; + + unsigned char *data = nullptr; + unsigned long count = 0; + Window active = None; + if (property(root, activeAtom, XA_WINDOW, 1, &data, &count) && data && count) + active = *reinterpret_cast(data); + if (data) XFree(data); + data = nullptr; + if (active == None || !property(active, stateAtom, XA_ATOM, 64, &data, &count)) { + XCloseDisplay(display); + return false; + } + bool fullscreen = false; + const Atom *states = reinterpret_cast(data); + for (unsigned long i = 0; i < count; ++i) + fullscreen |= states[i] == fullscreenAtom; + if (data) XFree(data); + data = nullptr; + unsigned long pid = 0; + if (fullscreen && property(active, pidAtom, XA_CARDINAL, 1, &data, &count) && data && count) + pid = *reinterpret_cast(data); + if (data) XFree(data); + XCloseDisplay(display); + if (!pid) + return false; + const QString procPath = QStringLiteral("/proc/%1").arg(pid); + if (QFileInfo(procPath).ownerId() != ::geteuid()) + return false; + QFile commandLine(procPath + QLatin1String("/cmdline")); + if (!commandLine.open(QIODevice::ReadOnly)) + return false; + const QString command = QString::fromLocal8Bit(commandLine.readAll()); + // Recheck after reading cmdline so a quickly reused pid from another user does not + // inherit the window owner's fullscreen workaround. + if (QFileInfo(procPath).ownerId() != ::geteuid()) + return false; + // Legacy workaround entries are substrings of command-line arguments, not desktop + // ids; keep substring matching so entries such as "chrome" still cover wrappers. + return std::any_of(fullscreenApps.cbegin(), + fullscreenApps.cend(), + [&command](const QString &app) { return command.contains(app); }); +} + +bool PowerManager::canEnterShortIdle() const +{ + // This runs once when entering short idle, not as a polling loop. Fresh application + // and service snapshots are intentional; both synchronous calls are capped at 1 s. + if (m_useWayland) + return true; + + QDBusInterface applications(QStringLiteral("org.desktopspec.ApplicationManager1"), + QStringLiteral("/org/desktopspec/ApplicationManager1"), + QStringLiteral("org.desktopspec.DBus.ObjectManager"), + QDBusConnection::sessionBus()); + applications.setTimeout(1000); + + const QDBusReply managed = applications.call(QStringLiteral("GetManagedObjects")); + if (!managed.isValid()) + qWarning(logPowerSession) << "Failed to list launched applications:" + << managed.error().message(); + const ObjectMap managedApplications = managed.isValid() ? managed.value() : ObjectMap(); + for (const ObjectInterfaceMap &interfaces : managedApplications) { + const QVariantMap properties = interfaces.value( + QStringLiteral("org.desktopspec.ApplicationManager1.Application")); + if (properties.isEmpty() + || qdbus_cast>( + properties.value(QStringLiteral("Instances"))).isEmpty()) + continue; + const QString desktop = QFileInfo( + properties.value(QStringLiteral("DesktopSourcePath")).toString()).fileName(); + if (m_shortIdleBlacklistApplications.contains(desktop)) { + qInfo(logPowerSession) << "Short idle blocked by blacklisted application:" << desktop; + return false; + } + const QString lower = desktop.toLower(); + if (!m_systemApplications.contains(desktop) + && !lower.contains(QStringLiteral("deepin")) + && !lower.contains(QStringLiteral("dde")) + && !lower.contains(QStringLiteral("uos"))) { + qInfo(logPowerSession) << "Short idle blocked by third-party application:" << desktop; + return false; + } + } + + QDBusInterface systemd(QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QDBusConnection::systemBus()); + systemd.setTimeout(1000); + const QDBusMessage reply = systemd.call( + QStringLiteral("ListUnitsByPatterns"), + QStringList{QStringLiteral("running")}, + QStringList{QStringLiteral("*.service")}); + if (reply.type() == QDBusMessage::ErrorMessage || reply.arguments().isEmpty() + || !reply.arguments().constFirst().canConvert()) { + qWarning(logPowerSession) << "Failed to list running services; preventing short idle:" + << reply.errorMessage(); + return false; + } + + const QDBusArgument units = reply.arguments().constFirst().value(); + bool allowed = true; + units.beginArray(); + while (!units.atEnd()) { + QString service, description, loadState, activeState, subState, following, jobType; + QDBusObjectPath unitPath, jobPath; + quint32 jobId = 0; + units.beginStructure(); + units >> service >> description >> loadState >> activeState >> subState >> following + >> unitPath >> jobId >> jobType >> jobPath; + units.endStructure(); + + const QString lower = service.toLower(); + if (allowed && !m_systemServices.contains(service) + && !lower.startsWith(QStringLiteral("dde-")) + && !lower.startsWith(QStringLiteral("deepin-")) + && !lower.startsWith(QStringLiteral("uos-")) + && !lower.startsWith(QStringLiteral("org.deepin.")) + && !lower.startsWith(QStringLiteral("com.deepin.")) + && !lower.startsWith(QStringLiteral("user@"))) { + qInfo(logPowerSession) << "Short idle blocked by third-party service:" << service; + allowed = false; + } + } + units.endArray(); + return allowed; +} + +void PowerManager::setShortIdleState(bool state) +{ + if (m_shortIdleEnabled && m_proxy) + m_proxy->setShortIdleState(state); +} + +void PowerManager::setKernelIdleState(bool state) +{ + if (m_proxy) + m_proxy->setIdleState(state); +} + +void PowerManager::setScreenIdleState(bool state) +{ + if (!m_useWayland && m_proxy) + m_proxy->setScreenState(state); +} + +bool PowerManager::isInConfigOrPowerButtonInhibitors(const QString &whatOnly, + QString &who, + bool blockOnly) const +{ + if (!m_proxy) + return false; + const QDBusMessage reply = m_proxy->inhibitors(); + if (reply.type() == QDBusMessage::ErrorMessage || reply.arguments().isEmpty()) + return false; + const QDBusArgument array = reply.arguments().constFirst().value(); + bool found = false; + array.beginArray(); + while (!array.atEnd()) { + QString what; + QString inhibitingWho; + QString why; + QString mode; + uint uid = 0; + uint pid = 0; + array.beginStructure(); + array >> what >> inhibitingWho >> why >> mode >> uid >> pid; + array.endStructure(); + Q_UNUSED(why) + Q_UNUSED(uid) + Q_UNUSED(pid) + if (!found + && (!blockOnly || mode == QLatin1String("block")) + && what.split(QLatin1Char(':')).contains(whatOnly)) { + who = inhibitingWho; + found = true; + } + } + array.endArray(); + return found; +} + +bool PowerManager::hasMultipleDisplaySessions() const +{ + if (!m_proxy) + return false; + const QDBusMessage reply = m_proxy->listSessions(); + if (reply.type() == QDBusMessage::ErrorMessage || reply.arguments().isEmpty()) + return false; + const QDBusArgument array = reply.arguments().constFirst().value(); + int displaySessions = 0; + array.beginArray(); + while (!array.atEnd()) { + QString id; + uint uid = 0; + QString user; + QString seat; + QDBusObjectPath path; + array.beginStructure(); + array >> id >> uid >> user >> seat >> path; + array.endStructure(); + Q_UNUSED(id) + Q_UNUSED(uid) + Q_UNUSED(user) + Q_UNUSED(seat) + QDBusInterface session(QLatin1String(PowerDBus::kLogin1Service), path.path(), + QLatin1String(PowerDBus::kLogin1Session), + QDBusConnection::systemBus()); + const QVariant type = session.property("Type"); + const QVariant state = session.property("State"); + if (!type.isValid() || !state.isValid()) + continue; + if (type.toString() != QLatin1String("tty") + && state.toString() != QLatin1String("closing")) + ++displaySessions; + } + array.endArray(); + return displaySessions > 1; +} + void PowerManager::doSuspend() { qInfo(logPowerSession) << "Requesting suspend, canSuspend=" << canSuspend(); if (!canSuspend()) return; - - if (!m_useWayland) { - m_proxy->requestSuspendByFront(); + QString who; + if (isInConfigOrPowerButtonInhibitors(QStringLiteral("suspend"), who)) { + qInfo(logPowerSession) << "Suspend blocked by" << who; + doLock(false); return; + } + const auto suspend = [this] { + if (m_useWayland) + m_proxy->requestSuspend(); + else + m_proxy->requestSuspendByFront(); + }; + if (hasMultipleDisplaySessions()) { + doLock(false); + QTimer::singleShot(3000, this, suspend); } else { - m_proxy->requestSuspend(); + suspend(); } } -void PowerManager::doSuspendByFront() { - qInfo(logPowerSession) << "Requesting suspend by front, canSuspend=" << canSuspend(); - if (canSuspend()) { - m_proxy->requestSuspendByFront(); - } +void PowerManager::doSuspendByFront() +{ + if (!canSuspend()) + return; + m_proxy->requestSuspendByFront(); } - -void PowerManager::doShutdown() { - qInfo(logPowerSession) << "Requesting shutdown"; - m_proxy->requestShutdown(); +void PowerManager::doShutdown() +{ + QString who; + if (isInConfigOrPowerButtonInhibitors(QStringLiteral("shutdown"), who, false) + || hasMultipleDisplaySessions()) + m_proxy->requestShutdownByFront(); + else + m_proxy->requestShutdown(); } -void PowerManager::doHibernate() { - qInfo(logPowerSession) << "Requesting hibernate, canHibernate=" << canHibernate(); - if (canHibernate()) { - m_proxy->requestHibernate(); +void PowerManager::doHibernate() +{ + if (!canHibernate()) + return; + QString who; + if (isInConfigOrPowerButtonInhibitors(QStringLiteral("hibernate"), who)) { + qInfo(logPowerSession) << "Hibernate blocked by" << who; + doLock(false); + return; } + if (m_useWayland) + m_proxy->requestHibernate(); + else + m_proxy->requestHibernateByFront(); } void PowerManager::doTurnOffScreen() { qInfo(logPowerSession) << "Turning off screen"; - if (m_screenBlackLock) { - doLock(true); - } - QTimer::singleShot(500, this, [this]() { setDPMSModeOff(); }); + setDPMSModeOff(); + if (m_screenBlackLock) + doLock(true); } void PowerManager::doLock(bool autoStartAuth) @@ -472,29 +961,27 @@ void PowerManager::doLock(bool autoStartAuth) m_proxy->lockSession(currentSessionId()); return; } + if (m_proxy->sessionLocked()) { + qInfo(logPowerSession) << "Session is already locked"; + return; + } m_proxy->showLockAuth(autoStartAuth); } - -void PowerManager::setDPMSModeOn() { +void PowerManager::setDPMSModeOn() +{ qInfo(logPowerSession) << "Setting DPMS mode to on"; - if (m_screenCtrl) { + if (m_screenCtrl && m_screenCtrl->isValid()) { m_screenCtrl->setAllModes(ScreenController::On); + setScreenIdleState(false); } } void PowerManager::setDPMSModeOff() { qInfo(logPowerSession) << "Setting DPMS mode to off"; - if (m_screenCtrl) { + if (m_screenCtrl && m_screenCtrl->isValid()) { m_screenCtrl->setAllModes(ScreenController::Off); - } - - const auto dpmsPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) - + QStringLiteral("/dpms-state"); - QFile f(dpmsPath); - if (f.open(QIODevice::WriteOnly)) { - f.write("1"); - f.close(); + setScreenIdleState(true); } } @@ -522,6 +1009,11 @@ void PowerManager::sendNotify(const QString &s, const QString &b) s, b, QStringList(), QVariantMap(), -1); } +QMap PowerManager::displayBrightness() const +{ + return m_proxy ? m_proxy->brightness() : QMap(); +} + void PowerManager::setDisplayBrightness(const QMap &t) { for (auto it = t.begin(); it != t.end(); ++it) { @@ -536,16 +1028,16 @@ void PowerManager::setAndSaveDisplayBrightness(const QMap &t) } } -// TODO: x11 IdleWatcher *PowerManager::createIdleWatcher() { - return m_useWayland ? new WaylandIdleWatcher(this) : nullptr; + return m_useWayland ? static_cast(new WaylandIdleWatcher(this)) + : createX11IdleWatcher(this); } -// TODO x11 ScreenController *PowerManager::createScreenController() { - return m_useWayland ? new WaylandScreenController(this) : nullptr; + return m_useWayland ? static_cast(new WaylandScreenController(this)) + : createX11ScreenController(this); } void PowerManager::recalculateScheduledShutdown() @@ -553,8 +1045,7 @@ void PowerManager::recalculateScheduledShutdown() qDebug(logPowerSession) << "Recalculating scheduled shutdown, current nextShutdownTime=" << m_nextShutdownTime; m_nextShutdownTime = getNextShutdownTime(0); - if (m_config) - m_config->setValue(PowerDConfig::kNextShutdownTime, m_nextShutdownTime); + persist(PowerDConfig::kNextShutdownTime, m_nextShutdownTime); scheduledShutdown(SchedInit); } @@ -564,9 +1055,12 @@ void PowerManager::initDConfig() if (!m_config) return; + m_configReader = DConfig::create(PowerDConfig::kAppId, PowerDConfig::kPowerName); + if (m_configReader) + m_configReader->moveToThread(DConfig::globalThread()); + using Setter = std::function; - using Hook = std::function; - struct Binding { const char *key; Setter apply; Hook onChange = {}; }; + struct Binding { const char *key; Setter apply; }; std::vector bindings = { // ── Line power delays ── @@ -578,6 +1072,8 @@ void PowerManager::initDConfig() [this](const QVariant &v) { setLinePowerSleepDelay(v.toInt()); } }, { PowerDConfig::kLinePowerLockDelay, [this](const QVariant &v) { setLinePowerLockDelay(v.toInt()); } }, + { PowerDConfig::kLinePowerShortIdleDelay, + [this](const QVariant &v) { setLinePowerShortIdleDelay(v.toInt()); } }, // ── Battery delays ── { PowerDConfig::kBatteryScreensaverDelay, @@ -588,6 +1084,18 @@ void PowerManager::initDConfig() [this](const QVariant &v) { setBatterySleepDelay(v.toInt()); } }, { PowerDConfig::kBatteryLockDelay, [this](const QVariant &v) { setBatteryLockDelay(v.toInt()); } }, + { PowerDConfig::kShortIdleEnable, + [this](const QVariant &v) { m_shortIdleEnabled = v.toBool(); } }, + { PowerDConfig::kShortIdleBlacklistApplications, + [this](const QVariant &v) { m_shortIdleBlacklistApplications = desktopFileNames(v.toStringList()); } }, + { PowerDConfig::kSystemApplications, + [this](const QVariant &v) { m_systemApplications = desktopFileNames(v.toStringList()); } }, + { PowerDConfig::kSystemServices, + [this](const QVariant &v) { m_systemServices = v.toStringList(); } }, + { PowerDConfig::kFullscreenWorkaroundAppList, + [this](const QVariant &v) { m_fullscreenWorkaroundApplications = v.toStringList(); } }, + { PowerDConfig::kBatteryShortIdleDelay, + [this](const QVariant &v) { setBatteryShortIdleDelay(v.toInt()); } }, // ── Locks ── { PowerDConfig::kScreenBlackLock, @@ -608,69 +1116,107 @@ void PowerManager::initDConfig() // ── Low power ── { PowerDConfig::kLowPowerNotifyEnable, [this](const QVariant &v) { setLowPowerNotifyEnable(v.toBool()); } }, + { PowerDConfig::kAdjustBrightnessEnabled, + [this](const QVariant &v) { m_adjustBrightnessEnabled = v.toBool(); } }, { PowerDConfig::kLowPowerNotifyThreshold, [this](const QVariant &v) { setLowPowerNotifyThreshold(v.toInt()); } }, + { PowerDConfig::kAllowScreenSaver, + [this](const QVariant &v) { + m_allowScreenSaver = v.toBool(); + if (m_powerSavePlan) + m_powerSavePlan->setAllowScreenSaver(m_allowScreenSaver); + } }, + { PowerDConfig::kDelayWakeupInterval, + [this](const QVariant &v) { m_delayWakeupInterval = qMax(0, v.toInt()); } }, + { PowerDConfig::kDelayHandleIdleOffIntervalWhenScreenBlack, + [this](const QVariant &v) { + m_delayHandleIdleOffIntervalWhenScreenBlack = qMax(0, v.toInt()); + } }, { PowerDConfig::kPercentageAction, [this](const QVariant &v) { setLowPowerAutoSleepThreshold(v.toInt()); } }, + { PowerDConfig::kUsePercentageForPolicy, + [this](const QVariant &v) { + if (m_lowPowerMgr) + m_lowPowerMgr->applyConfigValue(QLatin1String(PowerDConfig::kUsePercentageForPolicy), v); + } }, + { PowerDConfig::kTimeToEmptyLow, + [this](const QVariant &v) { + if (m_lowPowerMgr) + m_lowPowerMgr->applyConfigValue(QLatin1String(PowerDConfig::kTimeToEmptyLow), v); + } }, + { PowerDConfig::kTimeToEmptyDanger, + [this](const QVariant &v) { + if (m_lowPowerMgr) + m_lowPowerMgr->applyConfigValue(QLatin1String(PowerDConfig::kTimeToEmptyDanger), v); + } }, + { PowerDConfig::kTimeToEmptyCritical, + [this](const QVariant &v) { + if (m_lowPowerMgr) + m_lowPowerMgr->applyConfigValue(QLatin1String(PowerDConfig::kTimeToEmptyCritical), v); + } }, + { PowerDConfig::kTimeToEmptyAction, + [this](const QVariant &v) { + if (m_lowPowerMgr) + m_lowPowerMgr->applyConfigValue(QLatin1String(PowerDConfig::kTimeToEmptyAction), v); + } }, { PowerDConfig::kLowPowerAction, [this](const QVariant &v) { setLowPowerAction(v.toInt()); } }, // ── Scheduled shutdown ── { PowerDConfig::kScheduledShutdownState, - [this](const QVariant &v) { setScheduledShutdownState(v.toBool()); }, - [this]() { - if (m_scheduledShutdownState) { - recalculateScheduledShutdown(); - } else { - scheduledShutdown(SchedCancel); - } - } - }, + [this](const QVariant &v) { setScheduledShutdownState(v.toBool()); } }, { PowerDConfig::kShutdownTime, - [this](const QVariant &v) { setShutdownTime(v.toString()); }, - [this]() { - if (m_scheduledShutdownState) { - recalculateScheduledShutdown(); - } - } - }, + [this](const QVariant &v) { setShutdownTime(v.toString()); } }, { PowerDConfig::kShutdownRepetition, - [this](const QVariant &v) { setShutdownRepetition(v.toInt()); }, - [this]() { - if (m_scheduledShutdownState) { - recalculateScheduledShutdown(); - } - } - }, + [this](const QVariant &v) { setShutdownRepetition(v.toInt()); } }, { PowerDConfig::kCustomShutdownWeekDays, - [this](const QVariant &v) { setCustomShutdownWeekDays(v.toByteArray()); }, - [this]() { - if (m_scheduledShutdownState) { - recalculateScheduledShutdown(); + [this](const QVariant &v) { + QByteArray days; + if (v.metaType().id() == QMetaType::QByteArray) { + days = v.toByteArray(); + } else { + for (const QVariant &day : v.toList()) + days.append(static_cast(day.toInt())); } - } - }, + setCustomShutdownWeekDays(days); + } }, { PowerDConfig::kShutdownCountdown, [this](const QVariant &v) { m_shutdownCountdown = v.toInt(); } }, { PowerDConfig::kNextShutdownTime, [this](const QVariant &v) { m_nextShutdownTime = v.toLongLong(); } }, }; + m_loadingConfig = true; + m_applyingConfig = true; for (const auto &b : bindings) b.apply(m_config->value(QLatin1String(b.key))); + m_applyingConfig = false; + m_loadingConfig = false; connect(m_config, &DConfig::valueChanged, this, [this, bindings = std::move(bindings)](const QString &k) { - QVariant v = m_config->value(k); - qDebug(logPowerSession) << "DConfig value changed:" << k << "=" << v; + if (m_writingConfigKeys.contains(k)) + return; for (const auto &b : bindings) { - if (k == QLatin1String(b.key)) { - b.apply(v); - if (b.onChange) - b.onChange(); + if (k != QLatin1String(b.key)) + continue; + + auto *reader = m_configReader; + if (!reader) return; - } + + const auto apply = b.apply; + QMetaObject::invokeMethod(reader, [this, reader, k, apply] { + const QVariant value = reader->value(k); + QMetaObject::invokeMethod(this, [this, k, value, apply] { + qDebug(logPowerSession) << "DConfig value changed:" << k << "=" << value; + m_applyingConfig = true; + apply(value); + m_applyingConfig = false; + }); + }); + return; } }); @@ -680,23 +1226,58 @@ void PowerManager::initDConfig() << " lock=" << m_linePowerLockDelay; } +void PowerManager::setScheduledShutdownState(bool v) +{ + if (m_scheduledShutdownState == v) + return; + + m_scheduledShutdownState = v; + Q_EMIT scheduledShutdownStateChanged(); + persist(kScheduledShutdownState, v); + if (!m_loadingConfig) { + if (v) + recalculateScheduledShutdown(); + else + scheduledShutdown(SchedCancel); + } +} + +void PowerManager::setShutdownRepetition(int v) +{ + if (m_shutdownRepetition == v) + return; + + m_shutdownRepetition = v; + Q_EMIT shutdownRepetitionChanged(); + persist(kShutdownRepetition, v); + if (!m_loadingConfig && m_scheduledShutdownState) + recalculateScheduledShutdown(); +} + void PowerManager::setShutdownTime(const QString &v) { if (m_shutdownTime != v) { m_shutdownTime = v; Q_EMIT shutdownTimeChanged(); - if (m_config) m_config->setValue(kShutdownTime, v); + persist(kShutdownTime, v); + if (!m_loadingConfig && m_scheduledShutdownState) + recalculateScheduledShutdown(); } } void PowerManager::setCustomShutdownWeekDays(const QByteArray &v) { - if (m_customShutdownWeekDays != v) { - m_customShutdownWeekDays = v; Q_EMIT customShutdownWeekDaysChanged(); - if (m_config) m_config->setValue(kCustomShutdownWeekDays, QVariant(v)); - } + if (m_customShutdownWeekDays == v) + return; + m_customShutdownWeekDays = v; + Q_EMIT customShutdownWeekDaysChanged(); + QVariantList days; + for (const char day : v) + days.append(static_cast(day)); + persist(kCustomShutdownWeekDays, days); + if (!m_loadingConfig && m_scheduledShutdownState) + recalculateScheduledShutdown(); } - void PowerManager::closeNotify() { if (m_shutdownNotifyId == 0) return; @@ -739,7 +1320,7 @@ void PowerManager::initScheduledShutdown() if (m_scheduledShutdownState) { if (m_nextShutdownTime == 0) { m_nextShutdownTime = getNextShutdownTime(0); - if (m_config) m_config->setValue(kNextShutdownTime, m_nextShutdownTime); + persist(kNextShutdownTime, m_nextShutdownTime); } scheduledShutdown(SchedInit); } @@ -751,7 +1332,7 @@ void PowerManager::onSystemTimeChanged() return; m_nextShutdownTime = getNextShutdownTime(0); - if (m_config) m_config->setValue(kNextShutdownTime, m_nextShutdownTime); + persist(kNextShutdownTime, m_nextShutdownTime); scheduledShutdown(SchedInit); } @@ -848,18 +1429,17 @@ void PowerManager::scheduledShutdown(int state) case SchedWaitingToNotify: { m_shutdownStatus = SchedWaitingToNotify; QDateTime notifyAt = next.addSecs(-m_shutdownCountdown); - qint64 msToNotify = now.msecsTo(notifyAt); - if (msToNotify < 0) msToNotify = 0; - m_shutdownTimer->start(msToNotify); + const qint64 msToNotify = qMax(0, now.msecsTo(notifyAt)); + m_shutdownTimer->start(static_cast(qMin( + msToNotify, std::numeric_limits::max()))); disconnect(m_shutdownTimer, &QTimer::timeout, this, nullptr); - connect(m_shutdownTimer, &QTimer::timeout, this, [this]() { - scheduledShutdown(SchedCountdowning); - }); + connect(m_shutdownTimer, &QTimer::timeout, this, + [this] { scheduledShutdown(SchedInit); }); break; } case SchedCountdowning: { m_shutdownStatus = SchedCountdowning; - int remaining = m_shutdownCountdown + 1; + int remaining = m_shutdownCountdown; shutdownCountdownNotify(remaining, true); // Use member m_countdownTimer (matching Go: m.shutdownTimer in countdown goroutine) @@ -880,22 +1460,19 @@ void PowerManager::scheduledShutdown(int state) case SchedShutdown: case SchedCancel: case SchedTimeout: { - if (state == SchedTimeout) { + if (state == SchedCancel || state == SchedTimeout) closeNotify(); - } m_shutdownStatus = state; if (m_shutdownRepetition == RepOnce) { m_scheduledShutdownState = false; m_nextShutdownTime = 0; - if (m_config) { - m_config->setValue(kNextShutdownTime, 0); - m_config->setValue(kScheduledShutdownState, false); - } + persist(kNextShutdownTime, 0); + persist(kScheduledShutdownState, false); Q_EMIT scheduledShutdownStateChanged(); } else { m_nextShutdownTime = getNextShutdownTime(m_nextShutdownTime); - if (m_config) m_config->setValue(kNextShutdownTime, m_nextShutdownTime); + persist(kNextShutdownTime, m_nextShutdownTime); qint64 t = now.secsTo(next) / 60; if (t > 0) { @@ -918,13 +1495,15 @@ bool PowerManager::isWorkday(const QDateTime &date) const int year = date.date().year(); int month = date.date().month(); QString reply = m_proxy->getFestivalMonth(year, month); - if (reply.isEmpty()) { - int dow = date.date().dayOfWeek(); - return dow != Qt::Saturday && dow != Qt::Sunday; - } + // Match the legacy daemon: an empty reply means the calendar call failed, while a + // valid empty array below means no holiday overrides and falls back to weekdays. + if (reply.isEmpty()) + return false; QJsonDocument doc = QJsonDocument::fromJson(reply.toUtf8()); - if (!doc.isArray() || doc.array().isEmpty()) { + if (!doc.isArray()) + return false; + if (doc.array().isEmpty()) { int dow = date.date().dayOfWeek(); return dow != Qt::Saturday && dow != Qt::Sunday; } @@ -949,12 +1528,15 @@ bool PowerManager::isWorkday(const QDateTime &date) const bool PowerManager::isCustomDay(const QDateTime &date) const { - int dow = date.date().dayOfWeek(); - for (int i = 0; i < m_customShutdownWeekDays.size(); ++i) { - if (m_customShutdownWeekDays[i] - '0' == dow) - return true; - } - return false; + const int day = date.date().dayOfWeek(); + // DConfig stores these as numeric byte values (the legacy daemon used []byte), not + // as the textual string "135"; the explicit conversion preserves that contract. + return std::any_of(m_customShutdownWeekDays.cbegin(), + m_customShutdownWeekDays.cend(), + [day](char configured) { + const auto value = static_cast(configured); + return value == day || (day == Qt::Sunday && value == 0); + }); } qint64 PowerManager::getNextShutdownTime(qint64 baseTime) const diff --git a/src/plugin-qt/power/session/powermanager.h b/src/plugin-qt/power/session/powermanager.h index dda5eaf0..3a04cf48 100644 --- a/src/plugin-qt/power/session/powermanager.h +++ b/src/plugin-qt/power/session/powermanager.h @@ -8,6 +8,7 @@ #include #include +#include #include using BatteryPercentageMap = QMap; @@ -45,11 +46,13 @@ enum RepetitionMode { }; enum PrepareSuspendState { - PS_Normal = 0, // 正常/空闲 - PS_LidClose = 3, // 合盖进入挂起流程 - PS_Finish = 4, // 阈值:> PS_Finish 表示处于休眠/唤醒过渡中,应忽略 idle 事件 - PS_Resume = 5, // 从挂起中唤醒 / 开盖恢复 - PS_Sleeping = 6, // 即将进入休眠 (handleBeforeSleep) + PS_Unknown = 1, + PS_LidOpen = 2, + PS_LidClose = 3, + PS_Finish = 4, + PS_Resume = 5, + PS_Prepare = 6, + PS_ButtonClick = 7, }; enum PowerAction { @@ -57,7 +60,7 @@ enum PowerAction { PA_Suspend = 1, // 休眠 PA_Hibernate = 2, // 休眠 PA_TurnOffScreen = 3, // 关闭显示器 - PA_Lock = 4, // 锁屏 + PA_ShowShutdownInterface = 4, // 显示关机界面 PA_DoNothing = 5, // 无操作 }; @@ -67,6 +70,7 @@ class PowerManager : public QObject Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.Power1") Q_PROPERTY(bool OnBattery READ onBattery NOTIFY onBatteryChanged) + Q_PROPERTY(bool UseWayland READ useWayland CONSTANT) Q_PROPERTY(bool LidIsPresent READ lidIsPresent NOTIFY lidIsPresentChanged) Q_PROPERTY(BatteryIsPresentMap BatteryIsPresent READ batteryIsPresent NOTIFY batteryIsPresentChanged) Q_PROPERTY(BatteryPercentageMap BatteryPercentage READ batteryPercentage NOTIFY batteryPercentageChanged) @@ -79,11 +83,13 @@ class PowerManager : public QObject Q_PROPERTY(int LinePowerScreenBlackDelay READ linePowerScreenBlackDelay WRITE setLinePowerScreenBlackDelay NOTIFY linePowerScreenBlackDelayChanged) Q_PROPERTY(int LinePowerSleepDelay READ linePowerSleepDelay WRITE setLinePowerSleepDelay NOTIFY linePowerSleepDelayChanged) Q_PROPERTY(int LinePowerLockDelay READ linePowerLockDelay WRITE setLinePowerLockDelay NOTIFY linePowerLockDelayChanged) + Q_PROPERTY(int LinePowerShortIdleDelay READ linePowerShortIdleDelay WRITE setLinePowerShortIdleDelay NOTIFY linePowerShortIdleDelayChanged) Q_PROPERTY(int BatteryScreensaverDelay READ batteryScreensaverDelay WRITE setBatteryScreensaverDelay NOTIFY batteryScreensaverDelayChanged) Q_PROPERTY(int BatteryScreenBlackDelay READ batteryScreenBlackDelay WRITE setBatteryScreenBlackDelay NOTIFY batteryScreenBlackDelayChanged) Q_PROPERTY(int BatterySleepDelay READ batterySleepDelay WRITE setBatterySleepDelay NOTIFY batterySleepDelayChanged) Q_PROPERTY(int BatteryLockDelay READ batteryLockDelay WRITE setBatteryLockDelay NOTIFY batteryLockDelayChanged) + Q_PROPERTY(int BatteryShortIdleDelay READ batteryShortIdleDelay WRITE setBatteryShortIdleDelay NOTIFY batteryShortIdleDelayChanged) Q_PROPERTY(bool ScreenBlackLock READ screenBlackLock WRITE setScreenBlackLock NOTIFY screenBlackLockChanged) Q_PROPERTY(bool SleepLock READ sleepLock WRITE setSleepLock NOTIFY sleepLockChanged) @@ -116,6 +122,7 @@ public Q_SLOTS: void TurnOffScreen(); void TurnOnScreen(); +public: bool onBattery() const { return m_onBattery; } bool lidIsPresent() const { return m_lidIsPresent; } BatteryIsPresentMap batteryIsPresent() const { return m_batteryIsPresent; } @@ -134,6 +141,8 @@ public Q_SLOTS: void setLinePowerSleepDelay(int v); int linePowerLockDelay() const { return m_linePowerLockDelay; } void setLinePowerLockDelay(int v); + int linePowerShortIdleDelay() const { return m_linePowerShortIdleDelay; } + void setLinePowerShortIdleDelay(int v); int batteryScreensaverDelay() const { return m_batteryScreensaverDelay; } void setBatteryScreensaverDelay(int v); @@ -143,6 +152,8 @@ public Q_SLOTS: void setBatterySleepDelay(int v); int batteryLockDelay() const { return m_batteryLockDelay; } void setBatteryLockDelay(int v); + int batteryShortIdleDelay() const { return m_batteryShortIdleDelay; } + void setBatteryShortIdleDelay(int v); bool screenBlackLock() const { return m_screenBlackLock; } void setScreenBlackLock(bool v); @@ -166,6 +177,8 @@ public Q_SLOTS: void setLowPowerAutoSleepThreshold(int v); int lowPowerAction() const { return m_lowPowerAction; } void setLowPowerAction(int v); + bool ambientLightAdjustBrightness() const; + bool adjustBrightnessEnabled() const { return m_adjustBrightnessEnabled; } bool scheduledShutdownState() const { return m_scheduledShutdownState; } void setScheduledShutdownState(bool v); @@ -181,12 +194,24 @@ public Q_SLOTS: IdleWatcher *idleWatcher() const { return m_idleWatcher; } ScreenController *screenController() const { return m_screenCtrl; } bool useWayland() const { return m_useWayland; } + bool allowScreenSaver() const { return m_allowScreenSaver; } + int idleOffDelayWhenScreenBlack() const { return m_delayHandleIdleOffIntervalWhenScreenBlack; } bool shouldIgnoreIdleOn() const; + bool shouldIgnoreIdleOff() const; + bool isSessionActive() const; + void setShortIdleState(bool state); + void setScreenIdleState(bool state); + void setKernelIdleState(bool state); + bool canEnterShortIdle() const; + bool shouldPreventIdle() const; + bool shortIdleEnabled() const { return m_shortIdleEnabled; } int prepareSuspendState() const { return static_cast(m_prepareSuspendState); } void doSuspend(); + void doSuspendByFront(); void doShutdown(); void doHibernate(); + void doHibernateByFront(); void doTurnOffScreen(); void doLock(bool autoStartAuth = true); bool canSuspend() const; @@ -196,6 +221,7 @@ public Q_SLOTS: void handleBeforeSleep(bool beforeSleep); void handleWakeup(); void sendNotify(const QString &summary, const QString &body); + QMap displayBrightness() const; void setDisplayBrightness(const QMap &table); void setAndSaveDisplayBrightness(const QMap &table); void setBlackScreenActive(bool active); @@ -233,16 +259,19 @@ private Q_SLOTS: void batteryIsPresentChanged(); void batteryPercentageChanged(); void batteryStateChanged(); + void batteryTimeToEmptyChanged(); void hasAmbientLightSensorChanged(); void warnLevelChanged(); void isHighPerformanceSupportedChanged(); void linePowerScreensaverDelayChanged(); void linePowerScreenBlackDelayChanged(); void linePowerSleepDelayChanged(); + void linePowerShortIdleDelayChanged(); void linePowerLockDelayChanged(); void batteryScreensaverDelayChanged(); void batteryScreenBlackDelayChanged(); void batterySleepDelayChanged(); + void batteryShortIdleDelayChanged(); void batteryLockDelayChanged(); void screenBlackLockChanged(); void sleepLockChanged(); @@ -260,12 +289,22 @@ private Q_SLOTS: void customShutdownWeekDaysChanged(); private: - void doSuspendByFront(); - -private: + // These state-machine collaborators need coordinated access without exposing + // implementation details through the public PowerManager API. + friend class LowPowerManager; + friend class PowerSavePlan; IdleWatcher *createIdleWatcher(); ScreenController *createScreenController(); void initDConfig(); + void persist(const char *key, const QVariant &value); + void resetConfig(const char *key); + void initAmbientBrightness(); + bool screensaverProperty(const char *name) const; + bool isInConfigOrPowerButtonInhibitors(const QString &whatOnly, + QString &who, + bool blockOnly = true) const; + bool hasMultipleDisplaySessions() const; + void initSubmodules(); void recalculateScheduledShutdown(); qint64 getNextShutdownTime(qint64 baseTime) const; @@ -281,6 +320,10 @@ private Q_SLOTS: IdleWatcher *m_idleWatcher = nullptr; ScreenController *m_screenCtrl = nullptr; Dtk::Core::DConfig *m_config = nullptr; + Dtk::Core::DConfig *m_configReader = nullptr; + QSet m_writingConfigKeys; + bool m_applyingConfig = false; + bool m_loadingConfig = false; PowerSavePlan *m_powerSavePlan = nullptr; LidSwitchHandler *m_lidSwitch = nullptr; SleepInhibitor *m_sleepInhibitor = nullptr; @@ -294,16 +337,19 @@ private Q_SLOTS: BatteryStateMap m_batteryState; quint64 m_batteryTimeToEmpty = 0; bool m_hasAmbientLightSensor = false; + // Shared warning state updated by LowPowerManager and consumed by shutdown logic. uint m_warnLevel = 0; bool m_isHighPerformanceSupported = false; int m_linePowerScreensaverDelay = 0; int m_linePowerScreenBlackDelay = 0; int m_linePowerSleepDelay = 0; int m_linePowerLockDelay = 0; + int m_linePowerShortIdleDelay = 0; int m_batteryScreensaverDelay = 0; int m_batteryScreenBlackDelay = 0; int m_batterySleepDelay = 0; int m_batteryLockDelay = 0; + int m_batteryShortIdleDelay = 0; bool m_screenBlackLock = false; bool m_sleepLock = false; int m_linePowerLidClosedAction = 0; @@ -314,18 +360,27 @@ private Q_SLOTS: int m_lowPowerNotifyThreshold = 0; int m_lowPowerAutoSleepThreshold = 0; int m_lowPowerAction = 0; + QStringList m_shortIdleBlacklistApplications; + QStringList m_systemApplications; + QStringList m_systemServices; + QStringList m_fullscreenWorkaroundApplications; + bool m_allowScreenSaver = true; + int m_delayHandleIdleOffIntervalWhenScreenBlack = 0; + bool m_adjustBrightnessEnabled = true; + bool m_shortIdleEnabled = false; bool m_scheduledShutdownState = false; QString m_shutdownTime; int m_shutdownRepetition = 0; QByteArray m_customShutdownWeekDays; - PrepareSuspendState m_prepareSuspendState = PS_Normal; + PrepareSuspendState m_prepareSuspendState = PS_Unknown; bool m_screensaverWasRunning = false; bool m_screensaverLockAtAwake = false; bool m_screensaverStateCaptured = false; bool m_delayInActive = false; - int m_delayWakeupInterval = 2; + bool m_sleepCycleHandled = false; + int m_delayWakeupInterval = 0; int m_inhibitFd = -1; QTimer *m_shutdownTimer = nullptr; diff --git a/src/plugin-qt/power/session/powersaveplan.cpp b/src/plugin-qt/power/session/powersaveplan.cpp index dc08621c..e61ccaff 100644 --- a/src/plugin-qt/power/session/powersaveplan.cpp +++ b/src/plugin-qt/power/session/powersaveplan.cpp @@ -6,15 +6,27 @@ #include "powermanager.h" #include "idle/idlewatcher.h" #include "screen/screencontroller.h" +#include "sessiondbusproxy.h" #include "../powerconstants.h" #include +#include +#include +#include +#include #include +#include +#include +#include #include +#include +#include +#include Q_DECLARE_LOGGING_CATEGORY(logPowerSession) using namespace PowerDBus; +using namespace PowerDConfig; using namespace PowerFS; static bool canAdd(const QString &type, int delay, @@ -39,16 +51,32 @@ static bool canAdd(const QString &type, int delay, } return false; } +static double dropBrightness(double value, uint percent) +{ + const double ratio = std::clamp(1.0 - static_cast(percent) / 100.0, + 0.1, 1.0); + return std::clamp(std::round(value * ratio * 100.0) / 100.0, 0.1, 1.0); +} -PowerSavePlan::PowerSavePlan(PowerManager *powerManager, QObject *parent) - : QObject(parent), m_powerManager(powerManager) +static double restoreBrightness(double value, uint percent) { + const double ratio = std::clamp(1.0 - static_cast(percent) / 100.0, + 0.1, 1.0); + return std::clamp(std::round(value / ratio * 100.0) / 100.0, 0.1, 1.0); +} + +PowerSavePlan::PowerSavePlan(PowerManager *powerManager, QObject *parent) + : QObject(parent) + , m_allowScreenSaver(powerManager ? powerManager->allowScreenSaver() : true) + , m_powerManager(powerManager) +{ } void PowerSavePlan::Start() { Reset(); + initializePowerSavingBrightness(); } void PowerSavePlan::Reset() @@ -62,13 +90,29 @@ void PowerSavePlan::Reset() OnLinePower(); } +void PowerSavePlan::ResetFromNow() +{ + if (!m_powerManager) + return; + if (m_powerManager->onBattery()) { + Update(m_powerManager->batteryScreensaverDelay(), m_powerManager->batteryLockDelay(), + m_powerManager->batteryScreenBlackDelay(), m_powerManager->batterySleepDelay(), + m_powerManager->batteryShortIdleDelay(), true); + } else { + Update(m_powerManager->linePowerScreensaverDelay(), m_powerManager->linePowerLockDelay(), + m_powerManager->linePowerScreenBlackDelay(), m_powerManager->linePowerSleepDelay(), + m_powerManager->linePowerShortIdleDelay(), true); + } +} + void PowerSavePlan::OnBattery() { if (!m_powerManager) return; Update(m_powerManager->batteryScreensaverDelay(), m_powerManager->batteryLockDelay(), - m_powerManager->batteryScreenBlackDelay(), m_powerManager->batterySleepDelay()); + m_powerManager->batteryScreenBlackDelay(), m_powerManager->batterySleepDelay(), + m_powerManager->batteryShortIdleDelay()); } void PowerSavePlan::OnLinePower() @@ -77,11 +121,13 @@ void PowerSavePlan::OnLinePower() return; Update(m_powerManager->linePowerScreensaverDelay(), m_powerManager->linePowerLockDelay(), - m_powerManager->linePowerScreenBlackDelay(), m_powerManager->linePowerSleepDelay()); + m_powerManager->linePowerScreenBlackDelay(), m_powerManager->linePowerSleepDelay(), + m_powerManager->linePowerShortIdleDelay()); } void PowerSavePlan::Update(int screenSaverStartDelay, int lockDelay, - int screenBlackDelay, int sleepDelay) + int screenBlackDelay, int sleepDelay, int shortIdleDelay, + bool resetFromNow) { interruptTasks(); m_metaTasks.clear(); @@ -93,7 +139,7 @@ void PowerSavePlan::Update(int screenSaverStartDelay, int lockDelay, if (sleepDelay > 0 && canAdd("sleep", sleepDelay, m_metaTasks)) { m_metaTasks.append({sleepDelay, 0, "sleep", [this]{ - m_powerManager->doSuspend(); + sleep(); }}); } @@ -110,13 +156,18 @@ void PowerSavePlan::Update(int screenSaverStartDelay, int lockDelay, } }}); } - - if (screenBlackDelay > 0 && canAdd("screenBlack", screenBlackDelay, m_metaTasks)) { - m_metaTasks.append({screenBlackDelay, 0, "screenBlack", [this]{ + if (screenBlackDelay > 0) { + m_metaTasks.append({screenBlackDelay, 0, "screenBlack", [this] { screenBlack(); }}); } + if (shortIdleDelay > 0) { + m_metaTasks.append({shortIdleDelay, 0, "shortIdle", [this]{ + setShortIdle(true); + }}); + } + int min = 0; for (const auto &t : m_metaTasks) { if (t.delay < min || min == 0) { @@ -124,15 +175,24 @@ void PowerSavePlan::Update(int screenSaverStartDelay, int lockDelay, } } + const qint64 elapsed = m_isIdle && !resetFromNow && m_powerManager->idleWatcher() + ? m_powerManager->idleWatcher()->idleTimeMs() : 0; + setScreenSaverTimeout(min); for (auto &t : m_metaTasks) { int nSecs = t.delay - min; t.realDelay = nSecs > 0 ? nSecs * 1000 : 1; } - if (m_isIdle) { - HandleIdleOn(); + for (auto task : std::as_const(m_metaTasks)) { + const qint64 remaining = resetFromNow + ? task.delay * 1000LL + : qMax(1, task.delay * 1000LL - elapsed); + task.realDelay = static_cast(qMin( + remaining, std::numeric_limits::max())); + scheduleTask(task); + } } } @@ -148,30 +208,84 @@ void PowerSavePlan::HandleIdleOn() qDebug(logPowerSession) << "shouldIgnoreIdleOn is true, ignoring idle on event"; return; } + if (!m_powerManager->useWayland() && !m_powerManager->isSessionActive()) { + qDebug(logPowerSession) << "Ignoring idle event for inactive X11 session"; + return; + } + if (!m_powerManager->useWayland() && m_powerManager->shouldPreventIdle()) { + qDebug(logPowerSession) << "Preventing idle for fullscreen workaround application"; + if (auto *idleWatcher = m_powerManager->idleWatcher()) + idleWatcher->simulateActivity(); + return; + } m_isIdle = true; - for (const auto &t : m_metaTasks) { + auto *idleWatcher = m_powerManager->idleWatcher(); + const qint64 elapsed = idleWatcher ? idleWatcher->idleTimeMs() : 0; + + for (auto t : m_metaTasks) { + if (idleWatcher) { + const qint64 remaining = t.delay * 1000LL - elapsed; + t.realDelay = static_cast(qMin( + qMax(1, remaining), std::numeric_limits::max())); + } scheduleTask(t); } if (QFile::exists(kNoSuspendFile) && m_powerManager->screenBlackLock()) - m_powerManager->screenController()->setAllModes(ScreenController::On); + m_powerManager->doLock(true); } void PowerSavePlan::HandleIdleOff() { - qDebug(logPowerSession) << "HandleIdleOff"; + if (!m_powerManager) + return; + const int action = m_powerManager->onBattery() + ? m_powerManager->batteryPressPowerBtnAction() + : m_powerManager->linePowerPressPowerBtnAction(); + if (action == PA_TurnOffScreen) { + QTimer::singleShot(qMin(m_powerManager->idleOffDelayWhenScreenBlack(), 2500), + this, &PowerSavePlan::handleIdleOff); + return; + } + handleIdleOff(); +} + +void PowerSavePlan::handleIdleOff() +{ m_isIdle = false; - interruptTasks(); - if (!m_powerManager) return; - m_powerManager->SetPrepareSuspend(static_cast(PS_Normal)); - if (m_powerManager->screenController()) { - m_powerManager->screenController()->setAllModes(ScreenController::On); + setShortIdle(false); + m_powerManager->setScreenIdleState(false); + if (m_powerManager->shouldIgnoreIdleOff()) { + m_powerManager->SetPrepareSuspend(PS_Finish); + return; } + m_powerManager->SetPrepareSuspend(PS_Finish); + interruptTasks(); + m_powerManager->setDPMSModeOn(); + m_powerManager->setBlackScreenActive(false); resetBrightness(); } +void PowerSavePlan::setShortIdle(bool state) +{ + if (!m_powerManager) + return; + qInfo(logPowerSession) << "Changing short idle state:" << state; + if (!m_powerManager->shortIdleEnabled()) { + qInfo(logPowerSession) << "Short idle is disabled by DConfig"; + return; + } + if (state && !m_powerManager->canEnterShortIdle()) { + return; + } + m_powerManager->setShortIdleState(state); + QTimer::singleShot(300, m_powerManager, [manager = m_powerManager, state]() { + manager->setKernelIdleState(state); + }); +} + void PowerSavePlan::scheduleTask(const MetaTask &t) { qDebug(logPowerSession) << "Scheduling task" << t.name << "to run in" << t.realDelay << "ms"; @@ -195,37 +309,49 @@ void PowerSavePlan::setScreenSaverTimeout(int seconds) { if (!m_powerManager) return; - auto *iw = m_powerManager->idleWatcher(); - if (iw && m_powerManager->useWayland()) { - iw->setTimeout(static_cast(seconds)); - return; - } - - // FIXME(mhduiy): delete or use in x11? - // QDBusInterface iface(kScreensaver, kScreensaverPath, kScreensaver, - // QDBusConnection::sessionBus()); - // iface.call("SetTimeout", static_cast(seconds), 0u, false); + if (auto *iw = m_powerManager->idleWatcher()) + iw->setTimeout(static_cast(qMax(0, seconds))); } void PowerSavePlan::startScreensaver() { - if (m_powerManager->useWayland()) { - // Wayland no need to call start + if (m_powerManager->useWayland()) return; - } - - if (qEnvironmentVariable("DESKTOP_CAN_SCREENSAVER") == "N") - return; - - if (!m_allowScreenSaver) + if (qEnvironmentVariable("DESKTOP_CAN_SCREENSAVER") == "N" || !m_allowScreenSaver) return; QDBusInterface iface(kScreensaver, kScreensaverPath, kScreensaver, - QDBusConnection::sessionBus()); + QDBusConnection::sessionBus()); iface.call("Start"); m_screensaverRunning = true; } +void PowerSavePlan::sleep() +{ + if (!m_powerManager) + return; + if (m_powerManager->useWayland()) { + stopScreensaver(); + m_powerManager->doSuspend(); + return; + } + + if (!m_powerManager->m_screensaverStateCaptured) { + m_powerManager->m_screensaverLockAtAwake = + m_powerManager->screensaverProperty("lockScreenAtAwake"); + m_powerManager->m_screensaverStateCaptured = true; + } + m_powerManager->m_screensaverWasRunning = + m_powerManager->screensaverProperty("isRunning"); + if (m_powerManager->m_screensaverWasRunning) { + QDBusInterface screensaver(kScreensaver, kScreensaverPath, kScreensaver, + QDBusConnection::sessionBus()); + screensaver.asyncCall(QStringLiteral("Stop")); + m_screensaverRunning = false; + } + m_powerManager->doSuspendByFront(); +} + void PowerSavePlan::stopScreensaver() { if (!m_screensaverRunning) @@ -239,88 +365,270 @@ void PowerSavePlan::stopScreensaver() void PowerSavePlan::screenBlack() { qDebug(logPowerSession) << "Blackening screen"; - if (!m_powerManager) { + if (!m_powerManager) return; + + bool adjustBrightness = !m_powerManager->useWayland() + && m_powerManager->adjustBrightnessEnabled(); + if (adjustBrightness) { + saveCurrentBrightness(); + adjustBrightness = !m_oldBrightness.isEmpty(); + QMap brightness; + for (auto it = m_oldBrightness.cbegin(); it != m_oldBrightness.cend(); ++it) + brightness.insert(it.key(), it.value() * 0.5); + m_powerManager->setDisplayBrightness(brightness); } - saveCurrentBrightness(); - if (auto *sc = m_powerManager->screenController()) - sc->setAllModes(ScreenController::Off); - if (m_powerManager->screenBlackLock()) { - QTimer::singleShot(200, this, [this]() { - if (m_powerManager) { - m_powerManager->doLock(); - } + + auto finish = [this, adjustBrightness]() { + if (!m_powerManager) + return; + m_powerManager->m_screensaverWasRunning = m_screensaverRunning; + QDBusMessage query = QDBusMessage::createMethodCall( + kScreensaver, kScreensaverPath, + QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Get")); + query.setArguments({QLatin1String(kScreensaver), QStringLiteral("isRunning")}); + query.setAutoStartService(false); + auto *watcher = new QDBusPendingCallWatcher( + QDBusConnection::sessionBus().asyncCall(query), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *finishedWatcher) { + const QDBusPendingReply reply = *finishedWatcher; + finishedWatcher->deleteLater(); + if (!m_powerManager || reply.isError()) + return; + m_powerManager->m_screensaverWasRunning = reply.value().variant().toBool(); + if (!m_powerManager->m_screensaverWasRunning) + return; + QDBusInterface screensaver(kScreensaver, kScreensaverPath, kScreensaver, + QDBusConnection::sessionBus()); + screensaver.asyncCall(QStringLiteral("Stop")); + m_screensaverRunning = false; + }); + if (adjustBrightness) { + QMap brightness; + for (auto it = m_oldBrightness.cbegin(); it != m_oldBrightness.cend(); ++it) + brightness.insert(it.key(), 0.02); + m_powerManager->setDisplayBrightness(brightness); + } + if (m_powerManager->useWayland()) { + if (auto *screen = m_powerManager->screenController()) + screen->setAllModes(ScreenController::Off); + } else { + m_powerManager->setDPMSModeOff(); + } + if (m_powerManager->screenBlackLock()) + QTimer::singleShot(200, m_powerManager, [manager = m_powerManager]() { manager->doLock(); }); + }; + // scheduleTask tracks this timer so HandleIdleOff can cancel the staged blackout. + scheduleTask({0, m_powerManager->useWayland() ? 0 : 5000, + QStringLiteral("screenBlackFinish"), finish}); +} + +void PowerSavePlan::syncPowerSavingMode(bool enabled, uint brightnessDropPercent) +{ + m_psmEnabled = enabled; + m_psmDrop = brightnessDropPercent; +} + +void PowerSavePlan::initializePowerSavingBrightness() +{ + if (!m_powerManager || m_powerManager->useWayland() || !m_powerManager->m_proxy) + return; + + auto *proxy = m_powerManager->m_proxy; + connect(proxy, &SessionDBusProxy::BrightnessChanged, + this, &PowerSavePlan::handleBrightnessChanged); + connect(proxy, &SessionDBusProxy::PowerSavingModeBrightnessDataChanged, + this, [this](const QString &data) { + if (!m_powerManager->isSessionActive()) + loadPowerSavingBrightness(data); + }); + connect(proxy, &SessionDBusProxy::SessionActiveChanged, this, [this](bool active) { + if (active) + m_sessionActiveGrace.restart(); + }); + m_sessionActiveGrace.start(); + + const QString shared = proxy->powerSavingModeBrightnessData(); + const QString saved = m_powerManager->m_config + ? m_powerManager->m_config->value(QLatin1String(kSaveBrightnessWhilePsm)).toString() + : QString(); + loadPowerSavingBrightness(shared.isEmpty() ? saved : shared); + m_lastBrightness = m_powerManager->displayBrightness(); + + if (shared.isEmpty()) { + const double ratio = qMax(0.1, 1.0 - static_cast(m_psmDrop) / 100.0); + for (auto it = m_brightnessState.begin(); it != m_brightnessState.end(); ++it) { + if (!it->manuallyModified) + continue; + it->manuallyModified = false; + it->saved = qMin(1.0, it->latest / ratio); + } + savePowerSavingBrightness(); + + const bool savedEnabled = m_powerManager->m_config + && m_powerManager->m_config->value(QLatin1String(kPowerSavingModeEnabled)).toBool(); + if (savedEnabled != m_psmEnabled) + onPowerSavingModeEnabledChanged(m_psmEnabled); + } else { + m_powerManager->persist(kPowerSavingModeEnabled, m_psmEnabled); + } +} +void PowerSavePlan::loadPowerSavingBrightness(const QString &data) +{ + m_brightnessState.clear(); + const QJsonDocument document = QJsonDocument::fromJson(data.toUtf8()); + if (!document.isArray()) + return; + for (const QJsonValue &value : document.array()) { + const QJsonObject object = value.toObject(); + const QString monitor = object.value(QStringLiteral("MonitorName")).toString(); + if (monitor.isEmpty()) + continue; + m_brightnessState.insert(monitor, { + object.value(QStringLiteral("BrightnessSaved")).toDouble(), + object.value(QStringLiteral("BrightnessLatest")).toDouble(), + object.value(QStringLiteral("ManuallyModified")).toBool() }); } } +void PowerSavePlan::savePowerSavingBrightness() +{ + QJsonArray array; + for (auto it = m_brightnessState.cbegin(); it != m_brightnessState.cend(); ++it) { + array.append(QJsonObject { + { QStringLiteral("MonitorName"), it.key() }, + { QStringLiteral("BrightnessSaved"), it->saved }, + { QStringLiteral("BrightnessLatest"), it->latest }, + { QStringLiteral("ManuallyModified"), it->manuallyModified } + }); + } + const QString data = QString::fromUtf8(QJsonDocument(array).toJson(QJsonDocument::Compact)); + m_powerManager->persist(kSaveBrightnessWhilePsm, data); + m_powerManager->m_proxy->setPowerSavingModeBrightnessData(data); +} + +void PowerSavePlan::handleBrightnessChanged(const QMap &brightness) +{ + if (!m_psmEnabled) { + m_lastBrightness = brightness; + return; + } + const bool ownChange = (m_psmEnabledGrace.isValid() && m_psmEnabledGrace.elapsed() < 2000) + || (m_psmPercentGrace.isValid() && m_psmPercentGrace.elapsed() < 2000) + || (m_sessionActiveGrace.isValid() && m_sessionActiveGrace.elapsed() < 3000); + if (!ownChange) { + bool changed = false; + for (auto it = brightness.cbegin(); it != brightness.cend(); ++it) { + if (!m_lastBrightness.contains(it.key()) + || std::abs(m_lastBrightness.value(it.key()) - it.value()) < 1e-6) + continue; + auto state = m_brightnessState.find(it.key()); + if (state == m_brightnessState.end()) + continue; + state->latest = it.value(); + state->manuallyModified = true; + changed = true; + } + if (changed) + savePowerSavingBrightness(); + } + m_lastBrightness = brightness; +} + void PowerSavePlan::onPowerSavingModeEnabledChanged(bool enabled) { m_psmEnabled = enabled; - qDebug(logPowerSession) << "PowerSavingModeEnabled changed to" << enabled << ", drop=" << m_psmDrop; + qDebug(logPowerSession) << "PowerSavingModeEnabled changed to" << enabled + << ", drop=" << m_psmDrop; + if (m_powerManager) + m_powerManager->persist(kPowerSavingModeEnabled, enabled); + + if (enabled && m_powerManager && !m_powerManager->useWayland() + && m_powerManager->isSessionActive() + && !(m_powerManager->hasAmbientLightSensor() + && m_powerManager->ambientLightAdjustBrightness())) { + m_lastBrightness = m_powerManager->displayBrightness(); + m_brightnessState.clear(); + for (auto it = m_lastBrightness.cbegin(); it != m_lastBrightness.cend(); ++it) + m_brightnessState.insert(it.key(), {it.value(), 0, false}); + savePowerSavingBrightness(); + m_psmEnabledGrace.restart(); + } if (enabled) applyBrightnessDrop(); else - resetBrightness(); + restorePowerSavingBrightness(); } void PowerSavePlan::onBrightnessDropPercentChanged(uint value) { - m_psmDrop = value; - qDebug(logPowerSession) << "BrightnessDropPercent changed to" << value << ", enabled=" << m_psmEnabled ; + qDebug(logPowerSession) << "BrightnessDropPercent changed to" << value + << ", enabled=" << m_psmEnabled; + + if (m_psmEnabled && m_powerManager && !m_powerManager->useWayland()) { + if (!m_powerManager->isSessionActive() + || (m_powerManager->hasAmbientLightSensor() + && m_powerManager->ambientLightAdjustBrightness())) { + m_psmDrop = value; + return; + } + m_psmPercentGrace.restart(); + auto brightness = m_powerManager->displayBrightness(); + for (auto it = brightness.begin(); it != brightness.end(); ++it) + it.value() = dropBrightness(restoreBrightness(it.value(), m_psmDrop), value); + m_powerManager->setAndSaveDisplayBrightness(brightness); + m_psmDrop = value; + return; + } + m_psmDrop = value; if (m_psmEnabled) applyBrightnessDrop(); } void PowerSavePlan::saveCurrentBrightness() { - m_oldBrightness.clear(); - if (!m_powerManager) return; - - auto *sc = m_powerManager->screenController(); - if (!sc || !sc->supportsBrightness()) { - m_oldBrightness["default"] = 1.0; // legacy placeholder + if (!m_oldBrightness.isEmpty()) + return; + if (!m_powerManager) + return; + if (!m_powerManager->useWayland()) { + m_oldBrightness = m_powerManager->displayBrightness(); return; } - int n = sc->outputCount(); - for (int i = 0; i < n; ++i) { - double b = sc->brightness(i); - if (b >= 0.0) { - m_oldBrightness[QString::number(i)] = b; - qDebug(logPowerSession) << "saveBrightness: output" << i << "= " << b; - } + auto *screen = m_powerManager->screenController(); + if (!screen || !screen->supportsBrightness()) + return; + for (int i = 0; i < screen->outputCount(); ++i) { + const double brightness = screen->brightness(i); + if (brightness >= 0.0) + m_oldBrightness.insert(QString::number(i), brightness); } - - if (m_oldBrightness.isEmpty()) - m_oldBrightness["default"] = 1.0; } void PowerSavePlan::resetBrightness() { - qDebug(logPowerSession) << "Resetting brightness to original values"; - if (!m_powerManager) { m_oldBrightness.clear(); return; } - - auto *sc = m_powerManager->screenController(); - if (!sc || !sc->supportsBrightness()) { + if (!m_powerManager->useWayland()) { + m_powerManager->setDisplayBrightness(m_oldBrightness); m_oldBrightness.clear(); return; } - for (auto it = m_oldBrightness.cbegin(); it != m_oldBrightness.cend(); ++it) { - if (it.key() == QLatin1String("default")) - continue; - bool ok = false; - int idx = it.key().toInt(&ok); - if (ok) { - sc->setBrightness(idx, it.value()); - qDebug(logPowerSession) << "Restored brightness: output" << idx << "->" << it.value(); + auto *screen = m_powerManager->screenController(); + if (screen && screen->supportsBrightness()) { + for (auto it = m_oldBrightness.cbegin(); it != m_oldBrightness.cend(); ++it) { + bool ok = false; + const int index = it.key().toInt(&ok); + if (ok) + screen->setBrightness(index, it.value()); } } m_oldBrightness.clear(); @@ -328,33 +636,71 @@ void PowerSavePlan::resetBrightness() void PowerSavePlan::applyBrightnessDrop() { - if (!m_powerManager) return; + if (!m_powerManager) + return; - auto *sc = m_powerManager->screenController(); - if (!sc || !sc->supportsBrightness()) { - qWarning(logPowerSession) << "applyBrightnessDrop: brightness not supported"; + if (!m_powerManager->useWayland()) { + if (!m_powerManager->isSessionActive() + || (m_powerManager->hasAmbientLightSensor() + && m_powerManager->ambientLightAdjustBrightness())) { + return; + } + auto brightness = m_powerManager->displayBrightness(); + for (auto it = brightness.begin(); it != brightness.end(); ++it) + it.value() = dropBrightness(it.value(), m_psmDrop); + m_powerManager->setAndSaveDisplayBrightness(brightness); return; } - // Save original brightness if we haven't already - if (m_oldBrightness.isEmpty()) - saveCurrentBrightness(); - - int n = sc->outputCount(); - double ratio = 1.0 - static_cast(m_psmDrop) / 100.0; - ratio = std::clamp(ratio, 0.1, 1.0); // never drop below 10% - - qDebug(logPowerSession) << "applyBrightnessDrop: ratio=" << ratio << ", outputs=" << n << ", oldBrightness=" << m_oldBrightness; - - for (int i = 0; i < n; ++i) { - QString key = QString::number(i); - double original = m_oldBrightness.value(key, -1.0); - if (original < 0.0) continue; + auto *screen = m_powerManager->screenController(); + if (!screen || !screen->supportsBrightness()) { + qWarning(logPowerSession) << "applyBrightnessDrop: brightness not supported"; + return; + } + if (m_powerSavingBrightness.isEmpty()) { + for (int i = 0; i < screen->outputCount(); ++i) { + const double brightness = screen->brightness(i); + if (brightness >= 0.0) + m_powerSavingBrightness.insert(QString::number(i), brightness); + } + } + const double ratio = std::clamp(1.0 - static_cast(m_psmDrop) / 100.0, + 0.1, 1.0); + for (auto it = m_powerSavingBrightness.cbegin(); + it != m_powerSavingBrightness.cend(); ++it) { + bool ok = false; + const int index = it.key().toInt(&ok); + if (ok) + screen->setBrightness(index, std::clamp(it.value() * ratio, 10.0, 100.0)); + } +} +void PowerSavePlan::restorePowerSavingBrightness() +{ + if (!m_powerManager) + return; - double target = original * ratio; - target = std::clamp(target, 10.0, 100.0); + if (!m_powerManager->useWayland()) { + if (!m_powerManager->isSessionActive() + || (m_powerManager->hasAmbientLightSensor() + && m_powerManager->ambientLightAdjustBrightness())) { + return; + } + auto brightness = m_powerManager->displayBrightness(); + for (auto it = brightness.begin(); it != brightness.end(); ++it) + it.value() = restoreBrightness(it.value(), m_psmDrop); + m_powerManager->setAndSaveDisplayBrightness(brightness); + return; + } - sc->setBrightness(i, target); - qDebug(logPowerSession) << "drop brightness: output" << i << ": " << original << " -> " << target; + auto *screen = m_powerManager->screenController(); + if (screen && screen->supportsBrightness()) { + for (auto it = m_powerSavingBrightness.cbegin(); + it != m_powerSavingBrightness.cend(); ++it) { + bool ok = false; + const int index = it.key().toInt(&ok); + if (ok) + screen->setBrightness(index, it.value()); + } } + m_powerSavingBrightness.clear(); } diff --git a/src/plugin-qt/power/session/powersaveplan.h b/src/plugin-qt/power/session/powersaveplan.h index f9a72e66..ab533d38 100644 --- a/src/plugin-qt/power/session/powersaveplan.h +++ b/src/plugin-qt/power/session/powersaveplan.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -27,8 +28,10 @@ class PowerSavePlan : public QObject { void Start(); void Reset(); + void ResetFromNow(); void Update(int screenSaverStartDelay, int lockDelay, - int screenBlackDelay, int sleepDelay); + int screenBlackDelay, int sleepDelay, int shortIdleDelay, + bool resetFromNow = false); void HandleIdleOn(); void HandleIdleOff(); void OnBattery(); @@ -36,26 +39,48 @@ class PowerSavePlan : public QObject { void onPowerSavingModeEnabledChanged(bool enabled); void onBrightnessDropPercentChanged(uint value); + void syncPowerSavingMode(bool enabled, uint brightnessDropPercent); + void setAllowScreenSaver(bool allow) { m_allowScreenSaver = allow; } private: void startScreensaver(); void stopScreensaver(); + void handleIdleOff(); void screenBlack(); + void sleep(); + void setShortIdle(bool state); void interruptTasks(); void setScreenSaverTimeout(int seconds); void saveCurrentBrightness(); void resetBrightness(); void applyBrightnessDrop(); + void restorePowerSavingBrightness(); void scheduleTask(const MetaTask &t); + void initializePowerSavingBrightness(); + void handleBrightnessChanged(const QMap &brightness); + void loadPowerSavingBrightness(const QString &data); + void savePowerSavingBrightness(); + + struct BrightnessState { + double saved = 0; + double latest = 0; + bool manuallyModified = false; + }; QVector m_metaTasks; QVector m_timers; QMap m_oldBrightness; + QMap m_powerSavingBrightness; + QMap m_brightnessState; + QMap m_lastBrightness; bool m_screensaverRunning = false; bool m_isIdle = false; bool m_allowScreenSaver = true; bool m_psmEnabled = false; uint m_psmDrop = 0; bool m_lockFired = false; + QElapsedTimer m_psmEnabledGrace; + QElapsedTimer m_psmPercentGrace; + QElapsedTimer m_sessionActiveGrace; PowerManager *m_powerManager = nullptr; }; diff --git a/src/plugin-qt/power/session/screen/screencontroller.h b/src/plugin-qt/power/session/screen/screencontroller.h index d22d2017..5d02f6b0 100644 --- a/src/plugin-qt/power/session/screen/screencontroller.h +++ b/src/plugin-qt/power/session/screen/screencontroller.h @@ -58,3 +58,5 @@ class ScreenController : public QObject /// Emitted when the brightness of an output changes. void brightnessChanged(int index, double value); }; + +ScreenController *createX11ScreenController(QObject *parent = nullptr); diff --git a/src/plugin-qt/power/session/screen/screencontroller_x11.cpp b/src/plugin-qt/power/session/screen/screencontroller_x11.cpp index 964b2560..e0ceda19 100644 --- a/src/plugin-qt/power/session/screen/screencontroller_x11.cpp +++ b/src/plugin-qt/power/session/screen/screencontroller_x11.cpp @@ -4,15 +4,68 @@ #include "screencontroller.h" +#include + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(logPowerSession) + class X11ScreenController : public ScreenController { Q_OBJECT public: - using ScreenController::ScreenController; - bool isValid() const override { return false; } - int outputCount() const override { return 0; } - Mode mode(int) const override { return On; } - void setMode(int, Mode) override {} + explicit X11ScreenController(QObject *parent = nullptr) + : ScreenController(parent) + , m_display(XOpenDisplay(nullptr)) + { + int eventBase = 0; + int errorBase = 0; + m_valid = m_display && DPMSQueryExtension(m_display, &eventBase, &errorBase) + && DPMSCapable(m_display); + if (!m_valid) + qWarning(logPowerSession) << "X11 DPMS is unavailable"; + } + + ~X11ScreenController() override + { + if (m_display) + XCloseDisplay(m_display); + } + + bool isValid() const override { return m_valid; } + int outputCount() const override { return m_valid ? 1 : 0; } + + Mode mode(int index) const override + { + if (!m_valid || index != 0) + return On; + CARD16 level = DPMSModeOn; + BOOL enabled = False; + return DPMSInfo(m_display, &level, &enabled) && enabled && level != DPMSModeOn + ? Off : On; + } + + void setMode(int index, Mode mode) override + { + if (!m_valid || index != 0) + return; + if (!DPMSForceLevel(m_display, mode == On ? DPMSModeOn : DPMSModeOff)) { + qWarning(logPowerSession) << "Failed to set X11 DPMS mode" << mode; + return; + } + XFlush(m_display); + Q_EMIT modeChanged(index, mode); + } + +private: + Display *m_display = nullptr; + bool m_valid = false; }; +ScreenController *createX11ScreenController(QObject *parent) +{ + return new X11ScreenController(parent); +} + #include "screencontroller_x11.moc" diff --git a/src/plugin-qt/power/session/sessiondbusproxy.cpp b/src/plugin-qt/power/session/sessiondbusproxy.cpp index 52db99b3..2aaa9363 100644 --- a/src/plugin-qt/power/session/sessiondbusproxy.cpp +++ b/src/plugin-qt/power/session/sessiondbusproxy.cpp @@ -6,11 +6,20 @@ #include "../powerconstants.h" #include +#include +#include #include +#include #include using namespace PowerDBus; +namespace { +constexpr auto kAmbientBrightnessService = "org.deepin.dde.AmbientBrightness1"; +constexpr auto kAmbientBrightnessPath = "/org/deepin/dde/AmbientBrightness1"; +constexpr auto kAmbientBrightnessInterface = "org.deepin.dde.AmbientBrightness1"; +} + SessionDBusProxy::SessionDBusProxy(QObject *parent) : QObject(parent) , m_powerInter(new DDBusInterface( @@ -33,7 +42,7 @@ SessionDBusProxy::SessionDBusProxy(QObject *parent) QDBusConnection::sessionBus(), this)) , m_displayInter(new DDBusInterface( kDisplay, kDisplayPath, kDisplay, - QDBusConnection::sessionBus(), this)) + QDBusConnection::sessionBus(), nullptr)) , m_notificationsInter(new DDBusInterface( kNotifications, kNotificationsPath, kNotifications, QDBusConnection::sessionBus(), this)) @@ -49,7 +58,11 @@ SessionDBusProxy::SessionDBusProxy(QObject *parent) , m_freedesktopDBusInter(new DDBusInterface( kFreedesktopDBus, kFreedesktopPath, kFreedesktopDBus, QDBusConnection::systemBus(), this)) + , m_ambientBrightnessInter(new DDBusInterface( + kAmbientBrightnessService, kAmbientBrightnessPath, kAmbientBrightnessInterface, + QDBusConnection::sessionBus(), this)) { + m_displayInter->setParent(this); QDBusConnection::sessionBus().connect( m_notificationsInter->service(), m_notificationsInter->path(), m_notificationsInter->interface(), @@ -64,6 +77,18 @@ SessionDBusProxy::SessionDBusProxy(QObject *parent) m_freedesktopDBusInter->service(), m_freedesktopDBusInter->path(), m_freedesktopDBusInter->interface(), "NameOwnerChanged", this, SIGNAL(login1OwnerChanged(QString,QString,QString))); + + QDBusConnection::sessionBus().connect( + kAmbientBrightnessService, kAmbientBrightnessPath, + QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("PropertiesChanged"), this, + SIGNAL(ambientBrightnessPropertiesChanged(QString,QVariantMap,QStringList))); + + QDBusConnection::sessionBus().connect( + kDisplay, kDisplayPath, QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("PropertiesChanged"), this, + SLOT(handleDisplayPropertiesChanged(QString,QVariantMap,QStringList))); + } bool SessionDBusProxy::onBattery() const @@ -111,11 +136,66 @@ uint SessionDBusProxy::powerSavingModeBrightnessDropPercent() const return m_powerInter->property("PowerSavingModeBrightnessDropPercent").toUInt(); } +QString SessionDBusProxy::powerSavingModeBrightnessData() const +{ + return m_powerInter->property("PowerSavingModeBrightnessData").toString(); +} + +void SessionDBusProxy::setPowerSavingModeBrightnessData(const QString &value) +{ + m_powerInter->setProperty("PowerSavingModeBrightnessData", value); +} +void SessionDBusProxy::handleDisplayPropertiesChanged(const QString &interface, + const QVariantMap &changed, + const QStringList &) +{ + if (interface == QLatin1String(kDisplay) + && changed.contains(QStringLiteral("Brightness"))) + Q_EMIT BrightnessChanged( + qdbus_cast>(changed.value(QStringLiteral("Brightness")))); +} + + + +void SessionDBusProxy::setShortIdleState(bool state) +{ + m_powerInter->asyncCall("SetShortIdleState", state); +} + +void SessionDBusProxy::setIdleState(bool state) +{ + QDBusInterface daemon(kDaemonService, kDaemonPath, kDaemonService, + QDBusConnection::systemBus()); + daemon.asyncCall("SetIdleState", state); +} + +void SessionDBusProxy::setScreenState(bool state) +{ + QDBusInterface daemon(kDaemonService, kDaemonPath, kDaemonService, + QDBusConnection::systemBus()); + daemon.asyncCall("SetScreenState", state); +} + +void SessionDBusProxy::refreshBatteries() +{ + m_powerInter->asyncCall("RefreshBatteries"); +} + +void SessionDBusProxy::refreshMains() +{ + m_powerInter->asyncCall("RefreshMains"); +} + bool SessionDBusProxy::sessionActive() const { return m_sessionWatcherInter->property("IsActive").toBool(); } +bool SessionDBusProxy::sessionLocked() const +{ + return m_sessionManagerInter->property("Locked").toBool(); +} + void SessionDBusProxy::requestSuspend() { m_sessionManagerInter->asyncCall("RequestSuspend"); @@ -148,6 +228,16 @@ void SessionDBusProxy::requestSuspendByFront() m_shutdownFrontInter->asyncCall("Suspend"); } +void SessionDBusProxy::requestHibernateByFront() +{ + m_shutdownFrontInter->asyncCall("Hibernate"); +} + +void SessionDBusProxy::requestShutdownByFront() +{ + m_shutdownFrontInter->asyncCall("Shutdown"); +} + void SessionDBusProxy::showLockAuth(bool autoStart) { m_lockFrontInter->call("ShowAuth", autoStart); @@ -170,9 +260,36 @@ QDBusUnixFileDescriptor SessionDBusProxy::inhibit(const QString &what, const QSt return {}; } +QDBusMessage SessionDBusProxy::inhibitors() const +{ + return m_login1Inter->call(QStringLiteral("ListInhibitors")); +} + +QDBusMessage SessionDBusProxy::listSessions() const +{ + return m_login1Inter->call(QStringLiteral("ListSessions")); +} + void SessionDBusProxy::setBlackScreenActive(bool active) { m_blackScreenInter->asyncCall("setActive", active); + QDBusInterface kwin(QStringLiteral("org.kde.KWin"), QStringLiteral("/BlackScreen"), + QStringLiteral("org.kde.kwin.BlackScreen"), + QDBusConnection::sessionBus()); + kwin.asyncCall(QStringLiteral("setActive"), active); +} + +QMap SessionDBusProxy::brightness() const +{ + QDBusInterface properties(kDisplay, kDisplayPath, + QStringLiteral("org.freedesktop.DBus.Properties"), + QDBusConnection::sessionBus()); + const QDBusReply reply = properties.call( + QStringLiteral("Get"), QString::fromLatin1(kDisplay), + QStringLiteral("Brightness")); + return reply.isValid() + ? qdbus_cast>(reply.value().variant()) + : QMap(); } void SessionDBusProxy::setBrightness(const QString &monitor, double value) @@ -185,6 +302,21 @@ void SessionDBusProxy::setAndSaveBrightness(const QString &monitor, double value m_displayInter->asyncCall("SetAndSaveBrightness", monitor, value); } +void SessionDBusProxy::refreshBrightness() +{ + m_displayInter->asyncCall("RefreshBrightness"); +} + +bool SessionDBusProxy::ambientBrightnessSupported() const +{ + return m_ambientBrightnessInter->property("Supported").toBool(); +} + +bool SessionDBusProxy::ambientBrightnessEnabled() const +{ + return m_ambientBrightnessInter->property("Enabled").toBool(); +} + uint SessionDBusProxy::notify(uint replaceId, const QString &appName, const QString &icon, const QString &title, const QString &body, const QStringList &actions, const QVariantMap &hints, int timeout) diff --git a/src/plugin-qt/power/session/sessiondbusproxy.h b/src/plugin-qt/power/session/sessiondbusproxy.h index 1fd30492..983e5772 100644 --- a/src/plugin-qt/power/session/sessiondbusproxy.h +++ b/src/plugin-qt/power/session/sessiondbusproxy.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include @@ -16,7 +17,7 @@ class SessionDBusProxy : public QObject { public: explicit SessionDBusProxy(QObject *parent = nullptr); - // ── Power (system bus) — DDBusInterface 自动转发 PropertiesChanged ── + // ── Power (system bus) ── Q_PROPERTY(bool OnBattery READ onBattery NOTIFY OnBatteryChanged) bool onBattery() const; @@ -44,9 +45,19 @@ class SessionDBusProxy : public QObject { Q_PROPERTY(uint PowerSavingModeBrightnessDropPercent READ powerSavingModeBrightnessDropPercent NOTIFY PowerSavingModeBrightnessDropPercentChanged) uint powerSavingModeBrightnessDropPercent() const; + Q_PROPERTY(QString PowerSavingModeBrightnessData READ powerSavingModeBrightnessData NOTIFY PowerSavingModeBrightnessDataChanged) + QString powerSavingModeBrightnessData() const; + void setPowerSavingModeBrightnessData(const QString &value); + void setShortIdleState(bool state); + void setIdleState(bool state); + void setScreenState(bool state); + void refreshBatteries(); + + void refreshMains(); // ── SessionWatcher ── Q_PROPERTY(bool SessionActive READ sessionActive NOTIFY SessionActiveChanged) bool sessionActive() const; + bool sessionLocked() const; // ── SessionManager ── void requestSuspend(); @@ -57,6 +68,8 @@ class SessionDBusProxy : public QObject { // ── ShutdownFront ── void requestSuspendByFront(); + void requestHibernateByFront(); + void requestShutdownByFront(); // ── LockFront ── void showLockAuth(bool autoStart); @@ -66,12 +79,21 @@ class SessionDBusProxy : public QObject { QDBusUnixFileDescriptor inhibit(const QString &what, const QString &who, const QString &why, const QString &mode); + QDBusMessage inhibitors() const; + QDBusMessage listSessions() const; // ── BlackScreen ── void setBlackScreenActive(bool active); // ── Display ── + QMap brightness() const; void setBrightness(const QString &monitor, double value); void setAndSaveBrightness(const QString &monitor, double value); + void refreshBrightness(); + Q_SIGNAL void BrightnessChanged(const QMap &value); + + // ── AmbientBrightness ── + bool ambientBrightnessSupported() const; + bool ambientBrightnessEnabled() const; // ── Notifications ── uint notify(uint replaceId, const QString &appName, const QString &icon, @@ -83,7 +105,7 @@ class SessionDBusProxy : public QObject { QString getFestivalMonth(int year, int month); signals: - // DDBusInterface 自动转发:属性名 + Changed + // Forwarded from org.freedesktop.DBus.PropertiesChanged by DDBusInterface. void OnBatteryChanged(bool value); void HasLidSwitchChanged(bool value); void HasBatteryChanged(bool value); @@ -93,11 +115,21 @@ class SessionDBusProxy : public QObject { void IsHighPerformanceSupportedChanged(bool value); void PowerSavingModeEnabledChanged(bool value); void PowerSavingModeBrightnessDropPercentChanged(uint value); + void PowerSavingModeBrightnessDataChanged(const QString &value); void SessionActiveChanged(bool value); void notifyActionInvoked(uint id, const QString &actionKey); void timeUpdate(); void login1OwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void ambientBrightnessPropertiesChanged(const QString &interface, + const QVariantMap &changed, + const QStringList &invalidated); + + +private Q_SLOTS: + void handleDisplayPropertiesChanged(const QString &interface, + const QVariantMap &changed, + const QStringList &invalidated); private: DDBusInterface *m_powerInter; @@ -112,4 +144,5 @@ class SessionDBusProxy : public QObject { DDBusInterface *m_calendarInter; DDBusInterface *m_timedateInter; DDBusInterface *m_freedesktopDBusInter; + DDBusInterface *m_ambientBrightnessInter; }; diff --git a/src/plugin-qt/power/session/sleepinhibitor.cpp b/src/plugin-qt/power/session/sleepinhibitor.cpp index f6d799ca..675fee48 100644 --- a/src/plugin-qt/power/session/sleepinhibitor.cpp +++ b/src/plugin-qt/power/session/sleepinhibitor.cpp @@ -35,20 +35,26 @@ SleepInhibitor::~SleepInhibitor() void SleepInhibitor::handleSleep(bool beforeSleep) { if (beforeSleep) { - unblock(); + m_prepared = true; Q_EMIT aboutToSleep(); - } else { - Q_EMIT wokeUp(); - block(); + unblock(); + return; } + if (!m_prepared) + return; + Q_EMIT wokeUp(); + block(); + m_prepared = false; } void SleepInhibitor::onNameOwnerChanged(const QString &name, const QString &, const QString &newOwner) { - if (name == QLatin1String(kLogin1Service) && !newOwner.isEmpty()) + if (name == QLatin1String(kLogin1Service) && !newOwner.isEmpty() && m_fd >= 0) { + unblock(); inhibit(); + } } void SleepInhibitor::inhibit() diff --git a/src/plugin-qt/power/session/sleepinhibitor.h b/src/plugin-qt/power/session/sleepinhibitor.h index c4b3c092..620b4864 100644 --- a/src/plugin-qt/power/session/sleepinhibitor.h +++ b/src/plugin-qt/power/session/sleepinhibitor.h @@ -27,4 +27,5 @@ private Q_SLOTS: private: void inhibit(); int m_fd = -1; + bool m_prepared = false; }; diff --git a/src/plugin-qt/power/system/CMakeLists.txt b/src/plugin-qt/power/system/CMakeLists.txt index fb2bc424..3dbe0946 100644 --- a/src/plugin-qt/power/system/CMakeLists.txt +++ b/src/plugin-qt/power/system/CMakeLists.txt @@ -37,11 +37,14 @@ install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/ ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/plugin-power-system.json - DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/deepin-service-manager/system/ -) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.Power1.service - DESTINATION lib/systemd/system/ + DESTINATION ${CMAKE_INSTALL_DATADIR}/deepin-service-manager/system/ ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/dbus/org.deepin.dde.Power1.service - DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/dbus-1/system-services/ + DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/system-services/ +) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/dbus/org.deepin.dde.Power1.conf + DESTINATION ${CMAKE_INSTALL_DATADIR}/dbus-1/system.d/ +) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/misc/org.deepin.dde.power.policy + DESTINATION ${CMAKE_INSTALL_DATADIR}/polkit-1/actions/ ) diff --git a/src/plugin-qt/power/system/batterydevice.cpp b/src/plugin-qt/power/system/batterydevice.cpp new file mode 100644 index 00000000..105b36e7 --- /dev/null +++ b/src/plugin-qt/power/system/batterydevice.cpp @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "batterydevice.h" +#include "../powerconstants.h" + +#include +#include +#include +#include +#include +#include + +using namespace PowerDBus; + +BatteryDevice::BatteryDevice(QString sysfsPath, QObject *parent) + : QObject(parent) + , m_sysfsPath(std::move(sysfsPath)) +{ + refresh(); + + const QMetaObject *mo = metaObject(); + const int slotIndex = mo->indexOfSlot("notifyPropertyChanged()"); + if (slotIndex >= 0) { + const QMetaMethod slot = mo->method(slotIndex); + for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { + const QMetaProperty property = mo->property(i); + if (property.hasNotifySignal()) + connect(this, property.notifySignal(), this, slot); + } + } +} +void BatteryDevice::Refresh() +{ + refresh(); + if (m_refreshDone) + m_refreshDone(); +} + +void BatteryDevice::setRefreshDoneCallback(std::function callback) +{ + m_refreshDone = callback; +} + +void BatteryDevice::setStatus(uint status) +{ + if (m_status != status) { + m_status = status; + Q_EMIT statusChanged(); + } +} + + +QString BatteryDevice::readText(const QString &name) const +{ + QFile file(m_sysfsPath + '/' + name); + return file.open(QIODevice::ReadOnly) ? QString::fromUtf8(file.readAll()).trimmed() : QString(); +} + +double BatteryDevice::readScaled(const QString &name) const +{ + bool ok = false; + const double value = readText(name).toDouble(&ok); + return ok ? value / 1000000.0 : 0.0; +} + +bool BatteryDevice::refresh() +{ + const bool isBattery = readText("type").compare( + QLatin1String("Battery"), Qt::CaseInsensitive) == 0; + const QString present = isBattery ? readText(QStringLiteral("present")) : QString(); + if (!isBattery || (!present.isEmpty() && present == QLatin1String("0"))) { + if (m_present) { + m_present = false; + Q_EMIT isPresentChanged(); + } + return false; + } + + double voltageDesign = readScaled("voltage_max_design"); + if (voltageDesign <= 1) + voltageDesign = readScaled("voltage_min_design"); + if (voltageDesign <= 1) + voltageDesign = readScaled("voltage_present"); + if (voltageDesign <= 1) + voltageDesign = readScaled("voltage_now"); + if (voltageDesign <= 1) + voltageDesign = 10; + + double energy = readScaled("energy_now"); + if (energy < 0.01) + energy = readScaled("energy_avg"); + + double energyFull = readScaled("energy_full"); + double energyFullDesign = readScaled("energy_full_design"); + if (energyFull < 0.01) { + energyFull = readScaled("charge_full") * voltageDesign; + energyFullDesign = readScaled("charge_full_design") * voltageDesign; + } + if (energyFull < 0.01 && energyFullDesign > 0.01) + energyFull = energyFullDesign; + + double capacity = 100; + if (energyFull > 0 && energyFullDesign > 0) + capacity = qBound(0.0, energyFull * 100.0 / energyFullDesign, 100.0); + + double energyRate = qAbs(readScaled("power_now")); + if (energyRate < 0.01) { + if (energy < 0.01) { + energy = readScaled("charge_now"); + if (energy < 0.01) + energy = readScaled("charge_avg"); + energy *= voltageDesign; + } + const double chargeFull = qMax(readScaled("charge_full"), + readScaled("charge_full_design")); + energyRate = qAbs(readScaled("current_now")); + if (chargeFull > 0) + energyRate *= voltageDesign; + } + + if (energy > energyFull) + energyFull = energy; + + double voltage = readScaled("voltage_now"); + if (voltage < 0.01) + voltage = readScaled("voltage_avg"); + if (energyRate > 100 || energy < 0.1) + energyRate = 0; + + bool percentageValid = false; + double percentage = readText("capacity").toDouble(&percentageValid); + if (percentageValid) { + percentage = qBound(0.0, percentage, 100.0); + if (energy < 0.1 && energyFull > 0) + energy = energyFull * percentage / 100.0; + } else if (energyFull > 0) { + percentage = qBound(0.0, energy * 100.0 / energyFull, 100.0); + } else { + percentage = 0; + } + + const QString state = readText("status"); + uint status = 0; + if (state.compare(QLatin1String("Charging"), Qt::CaseInsensitive) == 0) + status = 1; + else if (state.compare(QLatin1String("Discharging"), Qt::CaseInsensitive) == 0) + status = 2; + else if (state.compare(QLatin1String("Not charging"), Qt::CaseInsensitive) == 0) + status = 3; + else if (state.compare(QLatin1String("Full"), Qt::CaseInsensitive) == 0) + status = 4; + else if (state.compare(QLatin1String("FullCharging"), Qt::CaseInsensitive) == 0) + status = 5; + + quint64 timeToEmpty = status == 2 && energyRate > 0 + ? static_cast(3600.0 * energy / energyRate) : 0; + quint64 timeToFull = status == 1 && energyRate > 0 + ? static_cast(3600.0 * qMax(0.0, energyFull - energy) / energyRate) : 0; + if (timeToEmpty > 240ULL * 60 * 60) + timeToEmpty = 0; + if (timeToFull > 20ULL * 60 * 60 || (status == 1 && m_status == 2)) + timeToFull = 0; + +#define UPDATE(member, value, signal) \ + do { \ + const auto next = (value); \ + if (member != next) { member = next; Q_EMIT signal(); } \ + } while (false) + + UPDATE(m_present, readText("present") != QLatin1String("0"), isPresentChanged); + UPDATE(m_manufacturer, readText("manufacturer"), manufacturerChanged); + UPDATE(m_modelName, readText("model_name"), modelNameChanged); + UPDATE(m_serialNumber, readText("serial_number"), serialNumberChanged); + UPDATE(m_name, QFileInfo(m_sysfsPath).fileName(), nameChanged); + UPDATE(m_technology, readText("technology"), technologyChanged); + UPDATE(m_energy, energy, energyChanged); + UPDATE(m_energyFull, energyFull, energyFullChanged); + UPDATE(m_energyFullDesign, energyFullDesign, energyFullDesignChanged); + UPDATE(m_energyRate, energyRate, energyRateChanged); + UPDATE(m_voltage, voltage, voltageChanged); + UPDATE(m_percentage, percentage, percentageChanged); + UPDATE(m_capacity, capacity, capacityChanged); + UPDATE(m_status, status, statusChanged); + UPDATE(m_timeToEmpty, timeToEmpty, timeToEmptyChanged); + UPDATE(m_timeToFull, timeToFull, timeToFullChanged); + UPDATE(m_updateTime, QDateTime::currentSecsSinceEpoch(), updateTimeChanged); +#undef UPDATE + return true; +} + +void BatteryDevice::notifyPropertyChanged() +{ + const int signalIndex = senderSignalIndex(); + const QMetaObject *mo = metaObject(); + for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { + const QMetaProperty property = mo->property(i); + if (!property.hasNotifySignal() || property.notifySignal().methodIndex() != signalIndex) + continue; + QDBusMessage message = QDBusMessage::createSignal( + objectPath().path(), QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("PropertiesChanged")); + message << QStringLiteral("org.deepin.dde.Power1.Battery") + << QVariantMap{{QString::fromLatin1(property.name()), property.read(this)}} + << QStringList(); + QDBusConnection::systemBus().send(message); + return; + } +} + +QDBusObjectPath BatteryDevice::objectPath() const +{ + // Preserve existing BAT0-style paths and legacy punctuation mappings. Any other + // character is hex-escaped to keep the object path valid for unusual sysfs names. + QString name; + for (const QChar ch : QFileInfo(m_sysfsPath).fileName()) { + if (ch.isLetterOrNumber() || ch == QLatin1Char('_')) { + name.append(ch); + } else if (ch == QLatin1Char('-')) { + name.append(QStringLiteral("_x0")); + } else if (ch == QLatin1Char('.')) { + name.append(QStringLiteral("_x1")); + } else if (ch == QLatin1Char(':')) { + name.append(QStringLiteral("_x2")); + } else { + name.append(QStringLiteral("_x") + QString::number(ch.unicode(), 16)); + } + } + return QDBusObjectPath(QString::fromLatin1(kPath) + "/battery_" + name); +} diff --git a/src/plugin-qt/power/system/batterydevice.h b/src/plugin-qt/power/system/batterydevice.h new file mode 100644 index 00000000..bf81ab3e --- /dev/null +++ b/src/plugin-qt/power/system/batterydevice.h @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: LGPL-3.0-or-later +#pragma once + +#include +#include +#include +#include + +class BatteryDevice : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.Power1.Battery") + + Q_PROPERTY(QString SysfsPath READ sysfsPath CONSTANT) + Q_PROPERTY(bool IsPresent READ isPresent NOTIFY isPresentChanged) + Q_PROPERTY(QString Manufacturer READ manufacturer NOTIFY manufacturerChanged) + Q_PROPERTY(QString ModelName READ modelName NOTIFY modelNameChanged) + Q_PROPERTY(QString SerialNumber READ serialNumber NOTIFY serialNumberChanged) + Q_PROPERTY(QString Name READ name NOTIFY nameChanged) + Q_PROPERTY(QString Technology READ technology NOTIFY technologyChanged) + Q_PROPERTY(double Energy READ energy NOTIFY energyChanged) + Q_PROPERTY(double EnergyFull READ energyFull NOTIFY energyFullChanged) + Q_PROPERTY(double EnergyFullDesign READ energyFullDesign NOTIFY energyFullDesignChanged) + Q_PROPERTY(double EnergyRate READ energyRate NOTIFY energyRateChanged) + Q_PROPERTY(double Voltage READ voltage NOTIFY voltageChanged) + Q_PROPERTY(double Percentage READ percentage NOTIFY percentageChanged) + Q_PROPERTY(double Capacity READ capacity NOTIFY capacityChanged) + Q_PROPERTY(uint Status READ status NOTIFY statusChanged) + Q_PROPERTY(quint64 TimeToEmpty READ timeToEmpty NOTIFY timeToEmptyChanged) + Q_PROPERTY(quint64 TimeToFull READ timeToFull NOTIFY timeToFullChanged) + Q_PROPERTY(qint64 UpdateTime READ updateTime NOTIFY updateTimeChanged) + +public: + explicit BatteryDevice(QString sysfsPath, QObject *parent = nullptr); + +public Q_SLOTS: + void Refresh(); + +private Q_SLOTS: + void notifyPropertyChanged(); + +public: + bool refresh(); + QDBusObjectPath objectPath() const; + void setRefreshDoneCallback(std::function callback); + void setStatus(uint status); + + QString sysfsPath() const { return m_sysfsPath; } + bool isPresent() const { return m_present; } + QString manufacturer() const { return m_manufacturer; } + QString modelName() const { return m_modelName; } + QString serialNumber() const { return m_serialNumber; } + QString name() const { return m_name; } + QString technology() const { return m_technology; } + double energy() const { return m_energy; } + double energyFull() const { return m_energyFull; } + double energyFullDesign() const { return m_energyFullDesign; } + double energyRate() const { return m_energyRate; } + double voltage() const { return m_voltage; } + double percentage() const { return m_percentage; } + double capacity() const { return m_capacity; } + uint status() const { return m_status; } + quint64 timeToEmpty() const { return m_timeToEmpty; } + quint64 timeToFull() const { return m_timeToFull; } + qint64 updateTime() const { return m_updateTime; } + +Q_SIGNALS: + void isPresentChanged(); + void manufacturerChanged(); + void modelNameChanged(); + void serialNumberChanged(); + void nameChanged(); + void technologyChanged(); + void energyChanged(); + void energyFullChanged(); + void energyFullDesignChanged(); + void energyRateChanged(); + void voltageChanged(); + void percentageChanged(); + void capacityChanged(); + void statusChanged(); + void timeToEmptyChanged(); + void timeToFullChanged(); + void updateTimeChanged(); + +private: + QString readText(const QString &name) const; + double readScaled(const QString &name) const; + + QString m_sysfsPath; + bool m_present = false; + QString m_manufacturer; + QString m_modelName; + QString m_serialNumber; + QString m_name; + QString m_technology; + double m_energy = 0; + double m_energyFull = 0; + double m_energyFullDesign = 0; + double m_energyRate = 0; + double m_voltage = 0; + double m_percentage = 0; + double m_capacity = 0; + uint m_status = 0; + quint64 m_timeToEmpty = 0; + quint64 m_timeToFull = 0; + qint64 m_updateTime = 0; + std::function m_refreshDone; +}; diff --git a/src/plugin-qt/power/system/batterymanager.cpp b/src/plugin-qt/power/system/batterymanager.cpp index c2b10881..e905a01b 100644 --- a/src/plugin-qt/power/system/batterymanager.cpp +++ b/src/plugin-qt/power/system/batterymanager.cpp @@ -4,6 +4,7 @@ #include "batterymanager.h" #include "powermanager.h" +#include "batterydevice.h" #include "../powerconstants.h" #include @@ -14,6 +15,7 @@ #include #include #include +#include #include @@ -28,10 +30,10 @@ BatteryManager::BatteryManager(SystemPowerManager *mgr, QObject *parent) probe(); initUdev(); - m_batteryPollTimer = new QTimer(this); - m_batteryPollTimer->setInterval(60000); - connect(m_batteryPollTimer, &QTimer::timeout, this, &BatteryManager::pollBattery); - m_batteryPollTimer->start(); + auto *batteryPollTimer = new QTimer(this); + batteryPollTimer->setInterval(60000); + connect(batteryPollTimer, &QTimer::timeout, this, &BatteryManager::refreshBatteries); + batteryPollTimer->start(); } BatteryManager::~BatteryManager() @@ -48,57 +50,167 @@ BatteryManager::~BatteryManager() void BatteryManager::probe() { - QDir ps("/sys/class/power_supply"); - for (const auto &entry : ps.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { - QFile tf("/sys/class/power_supply/" + entry + "/type"); - if (tf.open(QIODevice::ReadOnly)) { - if (tf.readAll().trimmed() == "Battery") { m_hasBattery = true; break; } + syncDevices(); + refreshMains(); + pollBattery(); +} + +void BatteryManager::syncDevices() +{ + QDir supplies("/sys/class/power_supply"); + QStringList paths; + for (const auto &entry : supplies.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + const QString path = supplies.filePath(entry); + QFile type(path + "/type"); + if (!type.open(QIODevice::ReadOnly) + || QString::fromUtf8(type.readAll()).trimmed().compare( + QLatin1String("Battery"), Qt::CaseInsensitive) != 0) { + continue; + } + QFile scope(path + "/scope"); + if (scope.open(QIODevice::ReadOnly) + && QString::fromUtf8(scope.readAll()).trimmed().compare( + QLatin1String("device"), Qt::CaseInsensitive) == 0) { + continue; + } + paths.append(path); + } + + for (int i = m_batteries.size() - 1; i >= 0; --i) { + auto *battery = m_batteries.at(i); + if (paths.contains(battery->sysfsPath())) + continue; + m_batteries.removeAt(i); + m_mgr->unregisterBattery(battery); + battery->deleteLater(); + } + for (const auto &path : paths) { + const auto exists = std::any_of(m_batteries.cbegin(), m_batteries.cend(), + [&path](const auto *battery) { return battery->sysfsPath() == path; }); + if (!exists) { + auto *battery = new BatteryDevice(path, this); + if (!battery->isPresent()) { + delete battery; + continue; + } + battery->setRefreshDoneCallback([this] { refreshBatteries(); }); + m_batteries.append(battery); + m_mgr->registerBattery(battery); } } - if (m_hasBattery) { m_mgr->updateHasBattery(true); pollBattery(); } - // AC initial state will be picked up by udev on next event, - // or by the fallback poll if no udev events arrive. } -void BatteryManager::pollBattery() +void BatteryManager::refreshBatteries() { - if (!m_hasBattery) { - QDir ps("/sys/class/power_supply"); - for (const auto &entry : ps.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { - QFile tf("/sys/class/power_supply/" + entry + "/type"); - if (tf.open(QIODevice::ReadOnly) && tf.readAll().trimmed() == "Battery") - { m_hasBattery = true; m_mgr->updateHasBattery(true); break; } - } + syncDevices(); + for (int i = m_batteries.size() - 1; i >= 0; --i) { + auto *battery = m_batteries.at(i); + battery->refresh(); + if (battery->isPresent()) + continue; + m_batteries.removeAt(i); + m_mgr->unregisterBattery(battery); + battery->deleteLater(); } - if (!m_hasBattery) return; - - double pct = 100.0; uint status = 0; quint64 tte = 0; double cap = 100.0; - QDir ps("/sys/class/power_supply"); - for (const auto &entry : ps.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { - QFile tf("/sys/class/power_supply/" + entry + "/type"); - if (!tf.open(QIODevice::ReadOnly) || tf.readAll().trimmed() != "Battery") continue; - auto readInt = [&](const QString &f) -> int { - QFile ff("/sys/class/power_supply/" + entry + "/" + f); - if (ff.open(QIODevice::ReadOnly)) return ff.readAll().trimmed().toInt(); - return 0; - }; - QFile cf("/sys/class/power_supply/" + entry + "/capacity"); - if (cf.open(QIODevice::ReadOnly)) { pct = cf.readAll().trimmed().toInt(); cf.close(); } - QFile sf("/sys/class/power_supply/" + entry + "/status"); - if (sf.open(QIODevice::ReadOnly)) { - QString s = sf.readAll().trimmed(); sf.close(); - if (s == "Charging") status = 1; - else if (s == "Discharging") { status = 2; int pw = readInt("power_now"); - tte = pw > 0 ? static_cast(readInt("energy_now")) * 3600 / pw : 0; } - else if (s == "Full") status = 4; + pollBattery(); +} + +void BatteryManager::refreshMains() +{ + QDir supplies("/sys/class/power_supply"); + bool found = false; + bool online = false; + for (const auto &entry : supplies.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + const QString path = supplies.filePath(entry); + QFile type(path + "/type"); + if (!type.open(QIODevice::ReadOnly) + || QString::fromUtf8(type.readAll()).trimmed().compare( + QLatin1String("Mains"), Qt::CaseInsensitive) != 0) { + continue; } - int ef = readInt("energy_full"), efd = readInt("energy_full_design"); - if (efd > 0) cap = ef * 100.0 / efd; + found = true; + QFile state(path + "/online"); + online = state.open(QIODevice::ReadOnly) && state.readAll().trimmed() == "1"; break; } - bool chg = (pct != m_percentage || status != m_status || tte != m_timeToEmpty || cap != m_capacity); - m_percentage = pct; m_status = status; m_timeToEmpty = tte; m_capacity = cap; - if (chg) { m_mgr->updateBatteryInfo(pct, status, tte, m_timeToFull, cap); Q_EMIT batteryChanged(); } + const bool onBattery = found ? !online : m_hasBattery; + if (m_onBattery != onBattery) { + m_onBattery = onBattery; + Q_EMIT onBatteryChanged(onBattery); + } +} + +void BatteryManager::pollBattery() +{ + m_hasBattery = !m_batteries.isEmpty(); + m_mgr->updateHasBattery(m_hasBattery); + if (m_batteries.isEmpty()) { + m_mgr->updateBatteryInfo(0, 0, 0, 0, 0); + return; + } + + double percentage = 0; + uint status = 0; + quint64 timeToEmpty = 0; + quint64 timeToFull = 0; + double capacity = 100; + + if (m_batteries.size() == 1) { + const auto *battery = m_batteries.first(); + percentage = battery->percentage(); + status = battery->status(); + timeToEmpty = battery->timeToEmpty(); + timeToFull = battery->timeToFull(); + capacity = battery->capacity(); + } else { + double energy = 0; + double energyFull = 0; + double energyFullDesign = 0; + double energyRate = 0; + QList states; + for (auto *battery : std::as_const(m_batteries)) { + energy += battery->energy(); + energyFull += battery->energyFull(); + energyFullDesign += battery->energyFullDesign(); + energyRate += battery->energyRate(); + states.append(battery->status()); + } + + percentage = energyFull > 0 + ? qBound(0.0, energy * 100.0 / energyFull, 100.0) + : m_batteries.first()->percentage(); + const bool allSame = std::all_of(states.cbegin(), states.cend(), + [first = states.first()](uint value) { return value == first; }); + if (allSame) + status = states.first(); + else if (states.contains(2)) + status = 2; + else if (states.contains(1)) + status = 1; + if (status == 2 && energyRate > 0) + timeToEmpty = static_cast(3600.0 * energy / energyRate); + else if (status == 1 && energyRate > 0) + timeToFull = static_cast(3600.0 * qMax(0.0, energyFull - energy) / energyRate); + if (energyFullDesign > 0) + capacity = qBound(0.0, energyFull * 100.0 / energyFullDesign, 100.0); + } + + if (timeToEmpty > 240ULL * 60 * 60) + timeToEmpty = 0; + if (timeToFull > 20ULL * 60 * 60) + timeToFull = 0; + if (status == 0) { + if (m_onBattery) + status = 2; + else if (percentage == 100) + status = 4; + else + status = 3; + } + + m_mgr->updateBatteryInfo(percentage, status, timeToEmpty, timeToFull, capacity); + for (auto *battery : std::as_const(m_batteries)) + battery->setStatus(status); } // ── udev-based AC / battery monitoring ────────────────────────── @@ -139,67 +251,41 @@ void BatteryManager::initUdev() void BatteryManager::onUdevEvent() { - if (!m_udevMon) return; + if (!m_udevMon) + return; auto *dev = udev_monitor_receive_device(m_udevMon); - if (!dev) return; - - const char *action = udev_device_get_action(dev); - const char *type = udev_device_get_sysattr_value(dev, "type"); + if (!dev) + return; - if (!action || !type) { + const char *actionValue = udev_device_get_action(dev); + const char *typeValue = udev_device_get_sysattr_value(dev, "type"); + if (!actionValue || !typeValue) { udev_device_unref(dev); return; } + const char *scopeValue = udev_device_get_sysattr_value(dev, "scope"); + const QString action = QString::fromUtf8(actionValue); + const QString type = QString::fromUtf8(typeValue); + const QString scope = scopeValue ? QString::fromUtf8(scopeValue) : QString(); + const bool systemBattery = type.compare(QLatin1String("Battery"), Qt::CaseInsensitive) == 0 + && scope.compare(QLatin1String("device"), Qt::CaseInsensitive) != 0; - if (strcmp(action, "change") == 0) { - if (strcmp(type, "Mains") == 0 || strcmp(type, "USB") == 0) { - refreshACFromUdev(dev); + if (action == QLatin1String("change")) { + if (type.compare(QLatin1String("Mains"), Qt::CaseInsensitive) == 0) { + refreshMains(); scheduleBatteryRefreshAfterAC(); - } else if (strcmp(type, "Battery") == 0) { - refreshBatteryFromUdev(dev); - } - } else if (strcmp(action, "add") == 0) { - if (strcmp(type, "Battery") == 0) { - m_hasBattery = true; - m_mgr->updateHasBattery(true); - pollBattery(); - } - } else if (strcmp(action, "remove") == 0) { - if (strcmp(type, "Battery") == 0) { - // re-probe to see if any batteries remain - m_hasBattery = false; - QDir ps("/sys/class/power_supply"); - for (const auto &entry : ps.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { - QFile tf("/sys/class/power_supply/" + entry + "/type"); - if (tf.open(QIODevice::ReadOnly) && tf.readAll().trimmed() == "Battery") { - m_hasBattery = true; - break; - } - } - if (!m_hasBattery) - m_mgr->updateHasBattery(false); + } else if (systemBattery) { + refreshBatteries(); } + } else if (systemBattery) { + probe(); } udev_device_unref(dev); } -void BatteryManager::refreshACFromUdev(struct udev_device *dev) -{ - const char *online = udev_device_get_sysattr_value(dev, "online"); - bool onBatt = !(online && strcmp(online, "1") == 0); - if (onBatt != m_onBattery) { - m_onBattery = onBatt; - Q_EMIT onBatteryChanged(onBatt); - } -} -void BatteryManager::refreshBatteryFromUdev(struct udev_device *) -{ - // udev notified us of a battery change; full poll picks up all values - pollBattery(); -} // AC 变更后, 在 1s, 3s, 5s, 10s, 15s, ... 60s 递进刷新电池 void BatteryManager::scheduleBatteryRefreshAfterAC() @@ -208,5 +294,5 @@ void BatteryManager::scheduleBatteryRefreshAfterAC() 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000}; for (int d : delays) - QTimer::singleShot(d, this, &BatteryManager::pollBattery); + QTimer::singleShot(d, this, &BatteryManager::refreshBatteries); } diff --git a/src/plugin-qt/power/system/batterymanager.h b/src/plugin-qt/power/system/batterymanager.h index c9f0d0dc..9f9adc78 100644 --- a/src/plugin-qt/power/system/batterymanager.h +++ b/src/plugin-qt/power/system/batterymanager.h @@ -6,7 +6,6 @@ struct udev; struct udev_monitor; class QSocketNotifier; -class QTimer; class SystemPowerManager; class BatteryManager : public QObject { @@ -15,31 +14,26 @@ class BatteryManager : public QObject { explicit BatteryManager(SystemPowerManager *mgr, QObject *parent = nullptr); ~BatteryManager() override; void probe(); - bool hasBattery() const { return m_hasBattery; } + bool onBattery() const { return m_onBattery; } + void refreshBatteries(); + void refreshMains(); Q_SIGNALS: - void batteryChanged(); void onBatteryChanged(bool onBattery); private: void pollBattery(); void initUdev(); void onUdevEvent(); - void refreshACFromUdev(struct udev_device *dev); - void refreshBatteryFromUdev(struct udev_device *dev); void scheduleBatteryRefreshAfterAC(); + void syncDevices(); SystemPowerManager *m_mgr = nullptr; bool m_hasBattery = false; bool m_onBattery = false; - double m_percentage = 100.0; - uint m_status = 0; - quint64 m_timeToEmpty = 0; - quint64 m_timeToFull = 0; - double m_capacity = 100.0; + QList m_batteries; struct udev *m_udev = nullptr; struct udev_monitor *m_udevMon = nullptr; QSocketNotifier *m_udevNotifier = nullptr; - QTimer *m_batteryPollTimer = nullptr; }; diff --git a/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.conf b/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.conf new file mode 100644 index 00000000..1c8cf3e9 --- /dev/null +++ b/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.conf @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.service b/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.service index 28803446..7fe8877b 100644 --- a/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.service +++ b/src/plugin-qt/power/system/misc/dbus/org.deepin.dde.Power1.service @@ -1,5 +1,4 @@ [D-BUS Service] Name=org.deepin.dde.Power1 Exec=/usr/bin/deepin-service-manager -n org.deepin.dde.Power1 -User=deepin-daemon -SystemdService=org.deepin.dde.Power1.service +SystemdService=deepin-service-plugin@org.deepin.dde.Power1.service diff --git a/src/plugin-qt/power/system/misc/org.deepin.dde.Power1.service b/src/plugin-qt/power/system/misc/org.deepin.dde.Power1.service deleted file mode 100644 index e0fba96f..00000000 --- a/src/plugin-qt/power/system/misc/org.deepin.dde.Power1.service +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=org.deepin.dde.Power1 (System) -RefuseManualStart=no -RefuseManualStop=no - -[Service] -Type=dbus -BusName=org.deepin.dde.Power1 -ExecStart=/usr/bin/deepin-service-manager -n org.deepin.dde.Power1 -Restart=on-failure -Slice=system.slice diff --git a/src/plugin-qt/power/system/misc/org.deepin.dde.power.policy b/src/plugin-qt/power/system/misc/org.deepin.dde.power.policy new file mode 100644 index 00000000..418e0aff --- /dev/null +++ b/src/plugin-qt/power/system/misc/org.deepin.dde.power.policy @@ -0,0 +1,30 @@ + + + + + + + Check Authentication + Authentication is required to perform this action + + no + no + auth_admin + + Check Authentication + Authentication is required to perform this action + Check Authentication + Autenticazione richiesta per eseguire questa operazione + Check Authentication + 当前操作需要密码认证 + Hitelesítés ellenőrzése + Hitelesítés szükséges a művelet végrehajtásához + Check Authentication + 目前操作需要密碼認證 + Check Authentication + 當前操作需要密碼認證 + Check Authentication + Tämän toiminnon suorittaminen edellyttää todennusta + + diff --git a/src/plugin-qt/power/system/plugin-power-system.cpp b/src/plugin-qt/power/system/plugin-power-system.cpp index 25c5a5c1..fcf215b7 100644 --- a/src/plugin-qt/power/system/plugin-power-system.cpp +++ b/src/plugin-qt/power/system/plugin-power-system.cpp @@ -11,7 +11,12 @@ extern "C" int DSMRegister(const char *name, void *data) { auto conn = reinterpret_cast(data); g_mgr = new SystemPowerManager(conn, QString::fromLatin1(name)); - return g_mgr->initialize() ? 0 : -1; + if (!g_mgr->initialize()) { + delete g_mgr; + g_mgr = nullptr; + return -1; + } + return 0; } extern "C" int DSMUnRegister(const char *, void *) diff --git a/src/plugin-qt/power/system/powermanager.cpp b/src/plugin-qt/power/system/powermanager.cpp index 5c10e19f..bcb71ad9 100644 --- a/src/plugin-qt/power/system/powermanager.cpp +++ b/src/plugin-qt/power/system/powermanager.cpp @@ -4,6 +4,7 @@ #include "powermanager.h" #include "batterymanager.h" +#include "batterydevice.h" #include "systemdbusproxy.h" #include "../powerconstants.h" @@ -11,8 +12,14 @@ #include #include #include +#include #include +#include #include +#include +#include +#include +#include #include #include @@ -21,6 +28,32 @@ using namespace PowerFS; using namespace PowerDConfig; Q_LOGGING_CATEGORY(logPowerSystem, "dde.power.system") +static constexpr auto kLegacyDBusError = "org.deepin.dde.DBus.Error.Unnamed"; + +static bool readProcLidClosed(bool &closed) +{ + QFile file{QLatin1String(kLidStatePath)}; + if (!file.open(QIODevice::ReadOnly)) + return false; + const QByteArray state = file.readAll(); + if (state.contains("closed")) { + closed = true; + return true; + } + if (state.contains("open")) { + closed = false; + return true; + } + return false; +} + +static bool isValidPowerMode(const QString &mode) +{ + return mode == QLatin1String("balance") + || mode == QLatin1String("powersave") + || mode == QLatin1String("performance") + || mode == QLatin1String("lowBattery"); +} SystemPowerManager::SystemPowerManager(QDBusConnection *conn, const QString &svc, QObject *p) @@ -30,6 +63,18 @@ SystemPowerManager::SystemPowerManager(QDBusConnection *conn, const QString &svc Q_UNUSED(svc); } +SystemPowerManager::~SystemPowerManager() +{ + if (m_configReader) { + QMetaObject::invokeMethod(m_configReader, &QObject::deleteLater, + Qt::BlockingQueuedConnection); + m_configReader = nullptr; + } + for (auto *battery : std::as_const(m_batteries)) + m_conn->unregisterObject(battery->objectPath().path()); + m_conn->unregisterObject(kPath); +} + bool SystemPowerManager::initialize() { bool ok = m_conn->registerObject(kPath, this, @@ -41,66 +86,155 @@ bool SystemPowerManager::initialize() return false; } - initLidSwitch(); - initPowerSavingDConfig(); - initCpuGovernor(); - - auto *battery = new BatteryManager(this, this); - connect(battery, &BatteryManager::onBatteryChanged, this, [this](bool onBatt) { - qDebug(logPowerSystem) << "onBatteryChanged:" << onBatt << " prev=" << m_onBattery; - if (m_onBattery != onBatt) { - m_onBattery = onBatt; - Q_EMIT onBatteryChanged(); - recalcBatteryLow(); - updatePowerMode(false); + m_powerControlProcess = new QProcess(this); + connect(m_powerControlProcess, + qOverload(&QProcess::finished), + this, [this](int exitCode, QProcess::ExitStatus status) { + if (status != QProcess::NormalExit || exitCode != 0) + qWarning(logPowerSystem) << "deepin-power-control failed:" << exitCode; + runNextPowerControl(); + }); + connect(m_powerControlProcess, &QProcess::errorOccurred, this, + [this](QProcess::ProcessError error) { + if (error == QProcess::FailedToStart) { + qWarning(logPowerSystem) << "Failed to start deepin-power-control"; + QMetaObject::invokeMethod(this, &SystemPowerManager::runNextPowerControl, + Qt::QueuedConnection); } }); - connect(battery, &BatteryManager::batteryChanged, this, [battery]() { - battery->probe(); + initLidSwitch(); + initPowerSavingDConfig(); + + + m_batteryManager = new BatteryManager(this, this); + m_onBattery = m_batteryManager->onBattery(); + connect(m_batteryManager, &BatteryManager::onBatteryChanged, this, [this](bool onBatt) { + if (m_onBattery == onBatt) + return; + m_onBattery = onBatt; + Q_EMIT onBatteryChanged(); + recalcBatteryLow(); + updatePowerMode(false); }); + + const QMetaObject *mo = metaObject(); + const int slotIndex = mo->indexOfSlot("notifyPropertyChanged()"); + if (slotIndex >= 0) { + const QMetaMethod slot = mo->method(slotIndex); + for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { + const QMetaProperty property = mo->property(i); + if (property.hasNotifySignal()) + connect(this, property.notifySignal(), this, slot); + } + } + + m_initDone = true; + recalcBatteryLow(); + initializePowerMode(); return true; } +void SystemPowerManager::initializePowerMode() +{ + if (m_mode == QLatin1String("performance")) { + updatePowerMode(true); + return; + } + + QDBusInterface displayManager(kDisplayManagerService, kDisplayManagerPath, + kDisplayManagerIface, QDBusConnection::systemBus()); + const auto sessions = qdbus_cast>( + displayManager.property("Sessions")); + if (!sessions.isEmpty()) { + updatePowerMode(true); + return; + } + + applyMode(QStringLiteral("performance")); + if (!QDBusConnection::systemBus().connect( + kDisplayManagerService, kDisplayManagerPath, kDisplayManagerIface, + QStringLiteral("SessionAdded"), this, + SLOT(onDisplaySessionAdded(QDBusObjectPath)))) { + qWarning(logPowerSystem) << "Failed to watch display-manager sessions"; + updatePowerMode(true); + } +} + +void SystemPowerManager::onDisplaySessionAdded(const QDBusObjectPath &) +{ + QDBusConnection::systemBus().disconnect( + kDisplayManagerService, kDisplayManagerPath, kDisplayManagerIface, + QStringLiteral("SessionAdded"), this, + SLOT(onDisplaySessionAdded(QDBusObjectPath))); + updatePowerMode(true); +} + +void SystemPowerManager::registerBattery(BatteryDevice *battery) +{ + if (!battery || m_batteries.contains(battery)) + return; + if (!m_conn->registerObject(battery->objectPath().path(), battery, + QDBusConnection::ExportAllSlots | + QDBusConnection::ExportAllProperties)) { + qWarning(logPowerSystem) << "Failed to register battery" << battery->objectPath().path(); + return; + } + m_batteries.append(battery); + Q_EMIT BatteryAdded(battery->objectPath()); +} + +void SystemPowerManager::unregisterBattery(BatteryDevice *battery) +{ + if (!battery || !m_batteries.removeOne(battery)) + return; + m_conn->unregisterObject(battery->objectPath().path()); + Q_EMIT BatteryRemoved(battery->objectPath()); +} void SystemPowerManager::initLidSwitch() { - SystemDBusProxy proxy(this); - QString chassis = proxy.chassis(); - if (chassis != "laptop" && chassis != "convertible") + SystemDBusProxy proxy; + const QString chassis = proxy.chassis(); + if (chassis != QLatin1String("laptop") && chassis != QLatin1String("convertible")) return; - m_hasLidSwitch = proxy.lidIsPresent(); - if (!m_hasLidSwitch) { - // 后备: 尝试 /proc/acpi/button/lid/LID/state - QFile f(kLidStatePath); - if (f.exists()) { - m_hasLidSwitch = true; - } + QDBusInterface upower(kUPowerService, kUPowerPath, kUPowerService, + QDBusConnection::systemBus()); + const QVariant lidIsPresent = upower.property("LidIsPresent"); + const QVariant lidIsClosed = upower.property("LidIsClosed"); + const bool watchUPower = lidIsPresent.isValid() && lidIsClosed.isValid(); + if (watchUPower) { + m_hasLidSwitch = lidIsPresent.toBool(); + m_lidClosed = lidIsClosed.toBool(); + } else { + if (!readProcLidClosed(m_lidClosed)) + return; + m_hasLidSwitch = true; } if (!m_hasLidSwitch) return; - Q_EMIT hasLidSwitchChanged(); - - // 读取初始状态 - QDBusInterface upower(kUPowerService, kUPowerPath, "org.freedesktop.DBus.Properties", - QDBusConnection::systemBus()); - if (upower.isValid()) { - QDBusReply reply = upower.call("Get", kUPowerService, "LidIsClosed"); - if (reply.isValid()) { - bool closed = reply.value().toBool(); - m_lidClosed = closed; - Q_EMIT lidClosedChanged(); - handleLidSwitchEvent(closed); - } + if (watchUPower) { + QDBusConnection::systemBus().connect( + kUPowerService, kUPowerPath, + "org.freedesktop.DBus.Properties", "PropertiesChanged", + this, SLOT(onUPowerPropertiesChanged(QString,QVariantMap,QStringList))); + return; } - // 持续监听 UPower 的 PropertiesChanged 信号,确保每次合盖/开盖都能收到通知 - QDBusConnection::systemBus().connect( - kUPowerService, kUPowerPath, - "org.freedesktop.DBus.Properties", "PropertiesChanged", - this, SLOT(onUPowerPropertiesChanged(QString,QVariantMap,QStringList))); + // ponytail: poll the legacy proc fallback only when UPower is unavailable. + auto *timer = new QTimer(this); + timer->setInterval(1000); + connect(timer, &QTimer::timeout, this, [this] { + bool closed = false; + if (!readProcLidClosed(closed) || m_lidClosed == closed) + return; + m_lidClosed = closed; + Q_EMIT lidClosedChanged(); + handleLidSwitchEvent(closed); + }); + timer->start(); } void SystemPowerManager::onUPowerPropertiesChanged(const QString &interface, @@ -135,95 +269,194 @@ void SystemPowerManager::initPowerSavingDConfig() m_config = Dtk::Core::DConfig::create(kAppId, kPowerName, "", this); if (!m_config) return; - auto load = [this](const QString &k) { - QVariant v = m_config->value(k); - qDebug(logPowerSystem) << "DConfig load: key=" << k << " value=" << v; - if (k == QLatin1String(kPowerSavingModeEnabled)) - setPowerSavingModeEnabled(v.toBool()); - else if (k == QLatin1String(kPowerSavingModeAuto)) - setPowerSavingModeAuto(v.toBool()); - else if (k == QLatin1String(kPowerSavingModeAutoWhenBatteryLow)) - setPowerSavingModeAutoWhenBatteryLow(v.toBool()); - else if (k == QLatin1String(kPowerSavingModeBrightnessDropPercent)) - setPowerSavingModeBrightnessDropPercent(v.toUInt()); - else if (k == QLatin1String(kPowerSavingModeAutoBatteryPercent)) - setPowerSavingModeAutoBatteryPercent(v.toUInt()); - else if (k == QLatin1String(kMode)) { - QString m = v.toString(); - static const QStringList valid = {"balance", "powersave", "performance"}; - if (valid.contains(m) && !m.isEmpty()) - setMode(m); - } - else if (k == QLatin1String(kLastMode)) { - QString lm = v.toString(); - if (!lm.isEmpty()) m_lastMode = lm; + migrateLegacyConfig(); + + m_configReader = Dtk::Core::DConfig::create(kAppId, kPowerName); + if (m_configReader) + m_configReader->moveToThread(Dtk::Core::DConfig::globalThread()); + + m_powerMappingConfig = m_config->value(kPowerMappingConfig).toString(); + + auto apply = [this](const QString &key, const QVariant &value) { + if (key == QLatin1String(kPowerSavingModeEnabled)) + setPowerSavingModeEnabled(value.toBool()); + else if (key == QLatin1String(kPowerSavingModeAuto)) + setPowerSavingModeAuto(value.toBool()); + else if (key == QLatin1String(kPowerSavingModeAutoWhenBatteryLow)) + setPowerSavingModeAutoWhenBatteryLow(value.toBool()); + else if (key == QLatin1String(kPowerSavingModeBrightnessDropPercent)) + setPowerSavingModeBrightnessDropPercent(value.toUInt()); + else if (key == QLatin1String(kPowerSavingModeAutoBatteryPercent)) + setPowerSavingModeAutoBatteryPercent(value.toUInt()); + else if (key == QLatin1String(kShortIdleEnable)) + m_shortIdleEnabled = value.toBool(); + else if (key == QLatin1String(kMode)) { + QString mode = value.toString(); + if (!isValidPowerMode(mode)) { + mode = QStringLiteral("balance"); + m_config->setValue(QLatin1String(kMode), mode); + } + if (m_loadingConfig) { + m_mode = mode; + if (mode != QLatin1String("powersave")) + m_lastMode = mode; + } else { + if (m_mode == mode) + return; + m_suppressModeUpdate = true; + if (m_mode == QLatin1String("powersave") + || mode == QLatin1String("powersave")) { + setPowerSavingModeAuto(false); + setPowerSavingModeAutoWhenBatteryLow(false); + } + m_suppressModeUpdate = false; + setMode(mode); + } + } else if (key == QLatin1String(kPowerMappingConfig)) { + m_powerMappingConfig = value.toString(); } }; + auto load = [this, apply](const char *key) { + const QString configKey = QLatin1String(key); + const QVariant value = m_config->value(configKey); + qDebug(logPowerSystem) << "DConfig load:" << configKey << value; + apply(configKey, value); + }; + + m_loadingConfig = true; + m_applyingConfig = true; load(kPowerSavingModeEnabled); load(kMode); load(kPowerSavingModeAuto); load(kPowerSavingModeAutoWhenBatteryLow); load(kPowerSavingModeBrightnessDropPercent); load(kPowerSavingModeAutoBatteryPercent); - load(kLastMode); - - updatePowerMode(true); + load(kShortIdleEnable); + load(kPowerMappingConfig); + m_applyingConfig = false; + m_loadingConfig = false; connect(m_config, &Dtk::Core::DConfig::valueChanged, this, - [this, load](const QString &key) { - qDebug(logPowerSystem) << "DConfig valueChanged: key=" << key; - load(key); - if (key == QLatin1String(kMode)) { - QString newMode = m_config->value(key).toString(); - setMode(newMode); + [this, apply](const QString &key) { + if (m_writingConfigKeys.contains(key)) return; - } - if (key == QLatin1String(kPowerSavingModeAutoBatteryPercent)) { - recalcBatteryLow(); - } - if (key == QLatin1String(kPowerSavingModeAutoWhenBatteryLow) - || key == QLatin1String(kPowerSavingModeAutoBatteryPercent)) { - recalcBatteryLow(); - updatePowerMode(false); - } + + auto *reader = m_configReader; + if (!reader) + return; + + QMetaObject::invokeMethod(reader, [this, reader, key, apply] { + const QVariant value = reader->value(key); + QMetaObject::invokeMethod(this, [this, key, value, apply] { + qDebug(logPowerSystem) << "DConfig value changed:" << key << value; + m_applyingConfig = true; + apply(key, value); + m_applyingConfig = false; + }); + }); }); } -void SystemPowerManager::initCpuGovernor() +void SystemPowerManager::migrateLegacyConfig() { - QFile gf("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"); - if (gf.open(QIODevice::ReadOnly)) { - QString gov = gf.readAll().trimmed(); - gf.close(); - if (!gov.isEmpty()) - setCpuGovernor(gov); + const QString legacyPath = QStringLiteral("/var/lib/dde-daemon/power/config.json"); + QFileInfo fileInfo(legacyPath); + if (!fileInfo.exists() || fileInfo.isSymLink()) + return; + + QFile file(legacyPath); + if (!file.open(QIODevice::ReadOnly)) + return; + QJsonParseError error; + const QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &error); + file.close(); + if (error.error != QJsonParseError::NoError || !document.isObject()) { + qWarning(logPowerSystem) << "Cannot migrate legacy power config:" << error.errorString(); + return; } - QFile af("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors"); - if (af.open(QIODevice::ReadOnly)) { - QStringList avail = QString(af.readAll()).split(' ', Qt::SkipEmptyParts); - af.close(); - bool hp = avail.contains("performance") || avail.contains("ondemand"); - bool ps = avail.contains("powersave") || avail.contains("ondemand"); - if (m_hpSupported != hp) { - m_hpSupported = hp; - Q_EMIT isHighPerformanceSupportedChanged(); - } + bool migrated = true; + auto setBool = [this, &migrated](const char *key, bool value) { + const QString configKey = QLatin1String(key); + m_config->setValue(configKey, value); + migrated = migrated && (m_config->value(configKey).toBool() == value); + }; + auto setUInt = [this, &migrated](const char *key, uint value) { + const QString configKey = QLatin1String(key); + m_config->setValue(configKey, value); + migrated = migrated && (m_config->value(configKey).toUInt() == value); + }; + auto setString = [this, &migrated](const char *key, const QString &value) { + const QString configKey = QLatin1String(key); + m_config->setValue(configKey, value); + migrated = migrated && (m_config->value(configKey).toString() == value); + }; - if (m_psSupported != ps) { - m_psSupported = ps; - Q_EMIT isPowerSaveSupportedChanged(); - } + const QJsonObject config = document.object(); + if (config.contains(QStringLiteral("PowerSavingModeEnabled"))) + setBool(kPowerSavingModeEnabled, + config.value(QStringLiteral("PowerSavingModeEnabled")).toBool()); + if (config.contains(QStringLiteral("PowerSavingModeAuto"))) + setBool(kPowerSavingModeAuto, + config.value(QStringLiteral("PowerSavingModeAuto")).toBool()); + if (config.contains(QStringLiteral("PowerSavingModeAutoWhenBatteryLow"))) + setBool(kPowerSavingModeAutoWhenBatteryLow, + config.value(QStringLiteral("PowerSavingModeAutoWhenBatteryLow")).toBool()); + if (config.contains(QStringLiteral("PowerSavingModeBrightnessDropPercent"))) { + uint drop = config.value(QStringLiteral("PowerSavingModeBrightnessDropPercent")).toInt(); + if (drop == 0) + drop = 20; + setUInt(kPowerSavingModeBrightnessDropPercent, drop); } + if (config.contains(QStringLiteral("PowerSavingModeAutoBatteryPercent"))) { + uint percent = config.value(QStringLiteral("PowerSavingModeAutoBatteryPercent")).toInt(); + if (percent < 10) + percent = 20; + setUInt(kPowerSavingModeAutoBatteryPercent, percent); + } + if (config.contains(QStringLiteral("Mode"))) { + QString mode = config.value(QStringLiteral("Mode")).toString(); + if (!isValidPowerMode(mode)) + mode = QStringLiteral("balance"); + setString(kMode, mode); + } + + // This is a one-time migration, matching dde-daemon. Keeping the legacy file would + // reapply stale values over DConfig on every service restart. If the DConfig backend + // does not echo the migrated values, preserve the legacy file for the next startup. + if (!migrated) { + qWarning(logPowerSystem) << "DConfig verification failed; legacy power config kept"; + return; + } + + // Move the source out of the legacy path instead of unlinking it. That preserves a + // recovery copy and avoids treating a racy replacement as a file to delete. + if (QFileInfo(legacyPath).isSymLink()) + return; + if (!QFile::rename(legacyPath, legacyPath + QLatin1String(".migrated"))) + qWarning(logPowerSystem) << "Failed to archive migrated legacy power config"; +} - QFile bf("/sys/devices/system/cpu/cpufreq/boost"); - if (bf.open(QIODevice::ReadOnly)) { - setCpuBoost(bf.readAll().trimmed() == "1"); - bf.close(); +void SystemPowerManager::enqueuePowerControl(const QStringList &arguments) +{ + m_powerControlQueue.enqueue(arguments); + if (!m_powerControlBusy) + runNextPowerControl(); +} + +void SystemPowerManager::runNextPowerControl() +{ + if (m_powerControlQueue.isEmpty()) { + m_powerControlBusy = false; + return; } + m_powerControlBusy = true; + m_powerControlProcess->start(QStringLiteral("/usr/sbin/deepin-power-control"), + m_powerControlQueue.dequeue()); } + void SystemPowerManager::updateHasBattery(bool has) { if (m_hasBattery != has) { @@ -239,8 +472,6 @@ void SystemPowerManager::updateBatteryInfo(double pct, uint status, qWarning(logPowerSystem) << "batteryPercentage changed:" << m_batteryPercentage << "→" << pct; m_batteryPercentage = pct; Q_EMIT batteryPercentageChanged(); - recalcBatteryLow(); - updatePowerMode(false); } if (m_batteryStatus != status) { @@ -262,61 +493,209 @@ void SystemPowerManager::updateBatteryInfo(double pct, uint status, m_batteryCapacity = cap; Q_EMIT batteryCapacityChanged(); } + const bool wasBatteryLow = m_batteryLow; + recalcBatteryLow(); + if (wasBatteryLow != m_batteryLow) + updatePowerMode(false); + Q_EMIT BatteryDisplayUpdate(QDateTime::currentSecsSinceEpoch()); } -QList SystemPowerManager::GetBatteries() { - return {}; +void SystemPowerManager::notifyPropertyChanged() +{ + const int signalIndex = senderSignalIndex(); + const QMetaObject *mo = metaObject(); + for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i) { + const QMetaProperty property = mo->property(i); + if (!property.hasNotifySignal() || property.notifySignal().methodIndex() != signalIndex) + continue; + QDBusMessage message = QDBusMessage::createSignal( + kPath, QStringLiteral("org.freedesktop.DBus.Properties"), + QStringLiteral("PropertiesChanged")); + message << QLatin1String(kInterface) + << QVariantMap{{QString::fromLatin1(property.name()), property.read(this)}} + << QStringList(); + m_conn->send(message); + return; + } } -void SystemPowerManager::Refresh() +QList SystemPowerManager::GetBatteries() +{ + QList paths; + paths.reserve(m_batteries.size()); + for (const auto *battery : std::as_const(m_batteries)) + paths.append(battery->objectPath()); + return paths; +} + +void SystemPowerManager::Refresh() { - RefreshBatteries(); RefreshMains(); + RefreshBatteries(); } void SystemPowerManager::RefreshBatteries() { - + if (m_batteryManager) + m_batteryManager->refreshBatteries(); } void SystemPowerManager::RefreshMains() { - + if (m_batteryManager) + m_batteryManager->refreshMains(); } -void SystemPowerManager::setMode(const QString &v) +// This mirrors the session persistence helper while retaining the system plugin's +// own loading/applying guards and DConfig instance. +void SystemPowerManager::persist(const char *key, const QVariant &value) { - static const QStringList valid = {"balance", "powersave", "performance"}; - if (!valid.contains(v)) { - qWarning(logPowerSystem) << "invalid mode: " << v; + if (!m_config || m_loadingConfig) + return; + const QString configKey = QLatin1String(key); + if (m_applyingConfig && m_config->value(configKey) == value) return; + m_writingConfigKeys.insert(configKey); + m_config->setValue(configKey, value); + m_writingConfigKeys.remove(configKey); +} + +QString SystemPowerManager::mappedDspcMode(const QString &mode) const +{ + // Parse on demand so runtime DConfig mapping changes take effect immediately; this + // path is used only when a power mode is applied. + const QJsonObject mapping = QJsonDocument::fromJson(m_powerMappingConfig.toUtf8()).object(); + const QJsonObject entry = mapping.value(mode).toObject(); + return entry.value(QStringLiteral("DSPCConfig")).toString(); +} + +void SystemPowerManager::applyMode(const QString &mode) +{ + const QString logicalMode = m_batteryLow && mode == QLatin1String("powersave") + ? QStringLiteral("lowBattery") : mode; + QString dspc = mappedDspcMode(logicalMode); + if (dspc.isEmpty()) + dspc = logicalMode == QLatin1String("lowBattery") ? QStringLiteral("lowbat") + : logicalMode == QLatin1String("powersave") ? QStringLiteral("saving") + : logicalMode; + if (dspc != QLatin1String("performance") && dspc != QLatin1String("balance") + && dspc != QLatin1String("saving") && dspc != QLatin1String("lowbat")) { + qWarning(logPowerSystem) << "Ignoring invalid DSPC mode mapping:" << dspc; + dspc = logicalMode == QLatin1String("lowBattery") ? QStringLiteral("lowbat") + : logicalMode == QLatin1String("powersave") ? QStringLiteral("saving") + : logicalMode; } + enqueuePowerControl({QStringLiteral("set"), dspc}); +} - if (m_mode == v) { - qWarning(logPowerSystem) << "setMode: same mode " << v << ", skip"; +void SystemPowerManager::setMode(const QString &mode) +{ + if (!isValidPowerMode(mode)) { + qWarning(logPowerSystem) << "Invalid power mode" << mode; return; } - m_mode = v; - Q_EMIT modeChanged(); - QString dspc; - if (v == "performance") { - dspc = "performance"; - } else if (v == "powersave") { - dspc = "saving"; - } else { - dspc = "balance"; + const bool powerSaving = mode == QLatin1String("powersave") + || mode == QLatin1String("lowBattery"); + const QString exposedMode = mode == QLatin1String("lowBattery") + ? QStringLiteral("powersave") : mode; + + applyMode(mode); + const bool wasSettingMode = m_settingMode; + m_settingMode = true; + setPowerSavingModeEnabled(powerSaving); + m_settingMode = wasSettingMode; + if (!powerSaving) + m_lastMode = mode; + if (m_shortIdleState) { + QTimer::singleShot(500, this, [this]() { + if (m_shortIdleState) + applyMode(QStringLiteral("powersave")); + }); } - if (!QProcess::startDetached("/usr/sbin/deepin-power-control", {"set", dspc})) - qWarning(logPowerSystem) << "Failed to start deepin-power-control set" << dspc; + if (m_mode == exposedMode) + return; + m_mode = exposedMode; + Q_EMIT modeChanged(); + persist(kMode, exposedMode); +} - setPowerSavingModeEnabled(v == "powersave"); +void SystemPowerManager::setPowerSavingModeEnabled(bool value) +{ + if (m_psmEnabled == value) + return; + m_psmEnabled = value; + Q_EMIT powerSavingModeEnabledChanged(); + persist(kPowerSavingModeEnabled, value); + if (m_loadingConfig || m_settingMode) + return; - if (m_lastMode != v && v != "powersave") { - m_lastMode = v; - if (m_config) m_config->setValue(kLastMode, v); - } + m_suppressModeUpdate = true; + setPowerSavingModeAuto(false); + setPowerSavingModeAutoWhenBatteryLow(false); + m_suppressModeUpdate = false; + setMode(value ? QStringLiteral("powersave") : QStringLiteral("balance")); +} + +void SystemPowerManager::setPowerSavingModeAuto(bool value) +{ + if (m_psmAuto == value) + return; + m_psmAuto = value; + Q_EMIT powerSavingModeAutoChanged(); + persist(kPowerSavingModeAuto, value); + if (!m_loadingConfig && !m_suppressModeUpdate) + updatePowerMode(false); +} + +void SystemPowerManager::setPowerSavingModeAutoWhenBatteryLow(bool value) +{ + if (m_psmAutoLow == value) + return; + m_psmAutoLow = value; + Q_EMIT powerSavingModeAutoWhenBatteryLowChanged(); + persist(kPowerSavingModeAutoWhenBatteryLow, value); + recalcBatteryLow(); + if (!m_loadingConfig && !m_suppressModeUpdate) + updatePowerMode(false); +} + +void SystemPowerManager::setPowerSavingModeBrightnessDropPercent(uint value) +{ + if (m_psmDrop == value) + return; + m_psmDrop = value; + Q_EMIT powerSavingModeBrightnessDropPercentChanged(); + persist(kPowerSavingModeBrightnessDropPercent, value); +} + +void SystemPowerManager::setPowerSavingModeAutoBatteryPercent(uint value) +{ + if (m_psmAutoPct == value) + return; + m_psmAutoPct = value; + Q_EMIT powerSavingModeAutoBatteryPercentChanged(); + persist(kPowerSavingModeAutoBatteryPercent, value); + recalcBatteryLow(); + if (!m_loadingConfig && !m_suppressModeUpdate) + updatePowerMode(false); +} + +void SystemPowerManager::setPowerSavingModeBrightnessData(const QString &value) +{ + if (m_psmBrightnessData == value) + return; + m_psmBrightnessData = value; + Q_EMIT powerSavingModeBrightnessDataChanged(); +} + +void SystemPowerManager::setSupportSwitchPowerMode(bool value) +{ + if (m_supportSwitchPowerMode == value) + return; + m_supportSwitchPowerMode = value; + Q_EMIT supportSwitchPowerModeChanged(); } void SystemPowerManager::SetCpuGovernor(const QString &gov) @@ -335,19 +714,94 @@ void SystemPowerManager::LockCpuFreq(const QString &gov, int lockTime) Q_UNUSED(lockTime); } +void SystemPowerManager::SetMode(const QString &mode) +{ + if (m_mode == mode) { + const QString error = QStringLiteral("repeat set mode"); + if (calledFromDBus()) + sendErrorReply(QLatin1String(kLegacyDBusError), error); + else + qWarning(logPowerSystem) << error; + return; + } + if (!isValidPowerMode(mode)) { + const QString error = QStringLiteral("PowerMode \"%1\" mode is not supported").arg(mode); + if (calledFromDBus()) + sendErrorReply(QLatin1String(kLegacyDBusError), error); + else + qWarning(logPowerSystem) << error; + return; + } + + m_suppressModeUpdate = true; + if (m_mode == QLatin1String("powersave") || mode == QLatin1String("powersave")) { + setPowerSavingModeAuto(false); + setPowerSavingModeAutoWhenBatteryLow(false); + } + m_suppressModeUpdate = false; + setMode(mode); +} + +void SystemPowerManager::SetTlpMode(const QString &mode) +{ + QString error; + if (m_tlpMode == mode) + error = QStringLiteral("repeat set tlp mode"); + else if (!isValidPowerMode(mode)) + error = QStringLiteral("PowerMode \"%1\" mode is not supported").arg(mode); + if (!error.isEmpty()) { + if (calledFromDBus()) + sendErrorReply(QLatin1String(kLegacyDBusError), error); + else + qWarning(logPowerSystem) << error; + return; + } + + m_tlpMode = mode; + Q_EMIT tlpModeChanged(); + applyMode(mode); +} + +void SystemPowerManager::SetShortIdleState(bool state) +{ + qInfo(logPowerSystem) << "SetShortIdleState:" << state; + if (!m_shortIdleEnabled) { + qInfo(logPowerSystem) << "Short idle is disabled by DConfig"; + return; + } + if (m_shortIdleState == state) { + qInfo(logPowerSystem) << "Short idle state is unchanged:" << state; + return; + } + + m_shortIdleState = state; + Q_EMIT shortIdleStateChanged(); + persist(kShortIdleState, state); + + const QString powerState = state ? QStringLiteral("powersave") : m_mode; + applyMode(powerState); + enqueuePowerControl({QStringLiteral("idle"), QStringLiteral("wifi"), + state ? QStringLiteral("on") : QStringLiteral("off")}); +} + void SystemPowerManager::recalcBatteryLow() { bool old = m_batteryLow; - m_batteryLow = m_onBattery && m_batteryPercentage > 0 + // Legacy dde-daemon tracked low battery from capacity only. AC state gates the + // general battery-power auto switch; the low-battery auto switch is independent. + m_batteryLow = m_hasBattery && m_batteryPercentage <= static_cast(m_psmAutoPct); qDebug(logPowerSystem) << "recalcBatteryLow:" << old << "→" << m_batteryLow - << "(OnBattery=" << m_onBattery + << "(HasBattery=" << m_hasBattery << " pct=" << m_batteryPercentage << " threshold=" << m_psmAutoPct << ")"; } void SystemPowerManager::updatePowerMode(bool init) { + if (!m_initDone) + return; + bool enablePowerSave = m_psmAuto && m_onBattery; bool enableLowPower = m_psmAutoLow && m_batteryLow; @@ -359,15 +813,13 @@ void SystemPowerManager::updatePowerMode(bool init) << " currentMode=" << m_mode << " lastMode=" << m_lastMode; - if (!m_psmAuto && !m_psmAutoLow && !init) { - qDebug(logPowerSystem) << " → both auto off, restoring lastMode:" << m_lastMode; - setMode(m_lastMode); + // When both automatic switches are disabled, preserve the user's current mode. + // dde-daemon returned here instead of forcing a rollback to lastMode. + if (!m_psmAuto && !m_psmAutoLow && !init) return; - } QString target = init ? m_mode : m_lastMode; if (enablePowerSave || enableLowPower) target = "powersave"; - qDebug(logPowerSystem) << " → setMode(" << target << ")"; setMode(target); } diff --git a/src/plugin-qt/power/system/powermanager.h b/src/plugin-qt/power/system/powermanager.h index 6a8793d1..0a28b49c 100644 --- a/src/plugin-qt/power/system/powermanager.h +++ b/src/plugin-qt/power/system/powermanager.h @@ -4,11 +4,16 @@ #pragma once #include #include +#include #include #include +#include +#include #include -class SystemPowerManager : public QObject { +class BatteryDevice; +class QProcess; +class SystemPowerManager : public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.deepin.dde.Power1") @@ -31,26 +36,35 @@ class SystemPowerManager : public QObject { WRITE setPowerSavingModeBrightnessDropPercent NOTIFY powerSavingModeBrightnessDropPercentChanged) Q_PROPERTY(uint PowerSavingModeAutoBatteryPercent READ powerSavingModeAutoBatteryPercent WRITE setPowerSavingModeAutoBatteryPercent NOTIFY powerSavingModeAutoBatteryPercentChanged) - Q_PROPERTY(QString Mode READ mode WRITE setMode NOTIFY modeChanged) + Q_PROPERTY(QString PowerSavingModeBrightnessData READ powerSavingModeBrightnessData WRITE setPowerSavingModeBrightnessData NOTIFY powerSavingModeBrightnessDataChanged) + Q_PROPERTY(bool ShortIdleState READ shortIdleState NOTIFY shortIdleStateChanged) + Q_PROPERTY(QString TlpMode READ tlpMode NOTIFY tlpModeChanged) + Q_PROPERTY(QString Mode READ mode NOTIFY modeChanged) Q_PROPERTY(bool IsHighPerformanceSupported READ isHighPerformanceSupported NOTIFY isHighPerformanceSupportedChanged) + Q_PROPERTY(bool IsBalanceSupported READ isBalanceSupported NOTIFY isBalanceSupportedChanged) Q_PROPERTY(bool IsPowerSaveSupported READ isPowerSaveSupported NOTIFY isPowerSaveSupportedChanged) - Q_PROPERTY(QString CpuGovernor READ cpuGovernor WRITE setCpuGovernor NOTIFY cpuGovernorChanged) - Q_PROPERTY(bool CpuBoost READ cpuBoost WRITE setCpuBoost NOTIFY cpuBoostChanged) + Q_PROPERTY(bool SupportSwitchPowerMode READ supportSwitchPowerMode WRITE setSupportSwitchPowerMode NOTIFY supportSwitchPowerModeChanged) + Q_PROPERTY(QString CpuGovernor READ cpuGovernor NOTIFY cpuGovernorChanged) + Q_PROPERTY(bool CpuBoost READ cpuBoost NOTIFY cpuBoostChanged) public: explicit SystemPowerManager(QDBusConnection *conn, const QString &svc, QObject *p = nullptr); bool initialize(); + ~SystemPowerManager() override; + void registerBattery(BatteryDevice *battery); + void unregisterBattery(BatteryDevice *battery); void updateHasBattery(bool has); void updateBatteryInfo(double pct, uint status, quint64 tte, quint64 ttf, double cap); void initLidSwitch(); void initPowerSavingDConfig(); - void initCpuGovernor(); private Q_SLOTS: void onUPowerPropertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated); + void notifyPropertyChanged(); + void onDisplaySessionAdded(const QDBusObjectPath &session); bool onBattery() const { return m_onBattery; } bool hasBattery() const { return m_hasBattery; } @@ -62,21 +76,27 @@ private Q_SLOTS: quint64 batteryTimeToFull() const { return m_batteryTimeToFull; } double batteryCapacity() const { return m_batteryCapacity; } bool powerSavingModeEnabled() const { return m_psmEnabled; } - void setPowerSavingModeEnabled(bool v) { if (m_psmEnabled != v) { m_psmEnabled = v; Q_EMIT powerSavingModeEnabledChanged(); } } + void setPowerSavingModeEnabled(bool v); bool powerSavingModeAuto() const { return m_psmAuto; } - void setPowerSavingModeAuto(bool v) { if (m_psmAuto != v) { m_psmAuto = v; Q_EMIT powerSavingModeAutoChanged(); } } + void setPowerSavingModeAuto(bool v); bool powerSavingModeAutoWhenBatteryLow() const { return m_psmAutoLow; } - void setPowerSavingModeAutoWhenBatteryLow(bool v) { if (m_psmAutoLow != v) { m_psmAutoLow = v; Q_EMIT powerSavingModeAutoWhenBatteryLowChanged(); } } + void setPowerSavingModeAutoWhenBatteryLow(bool v); uint powerSavingModeBrightnessDropPercent() const { return m_psmDrop; } - void setPowerSavingModeBrightnessDropPercent(uint v) { if (m_psmDrop != v) { m_psmDrop = v; Q_EMIT powerSavingModeBrightnessDropPercentChanged(); } } + void setPowerSavingModeBrightnessDropPercent(uint v); uint powerSavingModeAutoBatteryPercent() const { return m_psmAutoPct; } - void setPowerSavingModeAutoBatteryPercent(uint v) { if (m_psmAutoPct != v) { m_psmAutoPct = v; Q_EMIT powerSavingModeAutoBatteryPercentChanged(); } } + void setPowerSavingModeAutoBatteryPercent(uint v); + QString powerSavingModeBrightnessData() const { return m_psmBrightnessData; } + void setPowerSavingModeBrightnessData(const QString &v); + bool shortIdleState() const { return m_shortIdleState; } + QString tlpMode() const { return m_tlpMode; } QString mode() const { return m_mode; } - void setMode(const QString &v); QString lastMode() const { return m_lastMode; } bool batteryLow() const { return m_batteryLow; } bool isHighPerformanceSupported() const { return m_hpSupported; } bool isPowerSaveSupported() const { return m_psSupported; } + bool isBalanceSupported() const { return m_balanceSupported; } + bool supportSwitchPowerMode() const { return m_supportSwitchPowerMode; } + void setSupportSwitchPowerMode(bool v); QString cpuGovernor() const { return m_governor; } void setCpuGovernor(const QString &v) { if (m_governor != v) { m_governor = v; Q_EMIT cpuGovernorChanged(); } } bool cpuBoost() const { return m_boost; } @@ -90,6 +110,9 @@ public Q_SLOTS: void SetCpuGovernor(const QString &gov); void SetCpuBoost(bool on); void LockCpuFreq(const QString &gov, int lockTime); + void SetMode(const QString &mode); + void SetTlpMode(const QString &mode); + void SetShortIdleState(bool state); Q_SIGNALS: void onBatteryChanged(); @@ -98,6 +121,9 @@ public Q_SLOTS: void lidClosedChanged(); void LidClosed(); void LidOpened(); + void BatteryDisplayUpdate(qint64 timestamp); + void BatteryAdded(const QDBusObjectPath &path); + void BatteryRemoved(const QDBusObjectPath &path); void batteryPercentageChanged(); void batteryStatusChanged(); void batteryTimeToEmptyChanged(); @@ -108,16 +134,29 @@ public Q_SLOTS: void powerSavingModeAutoWhenBatteryLowChanged(); void powerSavingModeBrightnessDropPercentChanged(); void powerSavingModeAutoBatteryPercentChanged(); + void powerSavingModeBrightnessDataChanged(); + void shortIdleStateChanged(); + void tlpModeChanged(); void modeChanged(); void isHighPerformanceSupportedChanged(); void isPowerSaveSupportedChanged(); + void isBalanceSupportedChanged(); + void supportSwitchPowerModeChanged(); void cpuGovernorChanged(); void cpuBoostChanged(); private: void handleLidSwitchEvent(bool closed); void updatePowerMode(bool init = false); + void initializePowerMode(); void recalcBatteryLow(); + void applyMode(const QString &mode); + void setMode(const QString &mode); + QString mappedDspcMode(const QString &mode) const; + void persist(const char *key, const QVariant &value); + void migrateLegacyConfig(); + void enqueuePowerControl(const QStringList &arguments); + void runNextPowerControl(); QDBusConnection *m_conn; @@ -135,12 +174,31 @@ public Q_SLOTS: bool m_psmAutoLow = false; uint m_psmDrop = 0; uint m_psmAutoPct = 20; + QString m_psmBrightnessData; + bool m_shortIdleEnabled = false; + bool m_shortIdleState = false; + QString m_tlpMode; QString m_mode = "balance"; QString m_lastMode = "balance"; bool m_batteryLow = false; - QString m_governor = "powersave"; + QString m_governor; bool m_boost = true; bool m_hpSupported = true; bool m_psSupported = true; + bool m_balanceSupported = true; + bool m_supportSwitchPowerMode = false; + QString m_powerMappingConfig; + bool m_loadingConfig = false; + bool m_initDone = false; + bool m_suppressModeUpdate = false; + bool m_settingMode = false; Dtk::Core::DConfig *m_config = nullptr; + Dtk::Core::DConfig *m_configReader = nullptr; + QSet m_writingConfigKeys; + bool m_applyingConfig = false; + QList m_batteries; + class BatteryManager *m_batteryManager = nullptr; + QProcess *m_powerControlProcess = nullptr; + QQueue m_powerControlQueue; + bool m_powerControlBusy = false; }; diff --git a/src/plugin-qt/power/system/systemdbusproxy.cpp b/src/plugin-qt/power/system/systemdbusproxy.cpp index 794ea700..6c74b273 100644 --- a/src/plugin-qt/power/system/systemdbusproxy.cpp +++ b/src/plugin-qt/power/system/systemdbusproxy.cpp @@ -3,14 +3,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include "systemdbusproxy.h" -#include "../powerconstants.h" #include #include -#include -#include - -using namespace PowerDBus; SystemDBusProxy::SystemDBusProxy(QObject *parent) : QObject(parent) @@ -25,17 +20,3 @@ QString SystemDBusProxy::chassis() const QDBusConnection::systemBus()); return iface.property("Chassis").toString(); } - -bool SystemDBusProxy::lidIsPresent() const -{ - QDBusInterface iface(kUPowerService, kUPowerPath, kUPowerService, - QDBusConnection::systemBus()); - return iface.property("LidIsPresent").toBool(); -} - -bool SystemDBusProxy::lidIsClosed() const -{ - QDBusInterface iface(kUPowerService, kUPowerPath, kUPowerService, - QDBusConnection::systemBus()); - return iface.property("LidIsClosed").toBool(); -} diff --git a/src/plugin-qt/power/system/systemdbusproxy.h b/src/plugin-qt/power/system/systemdbusproxy.h index 59e3ab94..534bac9b 100644 --- a/src/plugin-qt/power/system/systemdbusproxy.h +++ b/src/plugin-qt/power/system/systemdbusproxy.h @@ -12,6 +12,4 @@ class SystemDBusProxy : public QObject { explicit SystemDBusProxy(QObject *parent = nullptr); QString chassis() const; - bool lidIsPresent() const; - bool lidIsClosed() const; }; diff --git a/src/plugin-qt/shortcut/tools/dde-shortcut-tool/powercontroller.cpp b/src/plugin-qt/shortcut/tools/dde-shortcut-tool/powercontroller.cpp index ec1fb787..3ecce7df 100644 --- a/src/plugin-qt/shortcut/tools/dde-shortcut-tool/powercontroller.cpp +++ b/src/plugin-qt/shortcut/tools/dde-shortcut-tool/powercontroller.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -469,9 +468,6 @@ void PowerController::systemTurnOffScreen() undoPrepareSuspend(); - QFile dpmsState("/tmp/dpms-state"); - if (dpmsState.open(QIODevice::WriteOnly | QIODevice::Truncate)) - dpmsState.write("1"); } void PowerController::showShutdownUI()