From 06f738b4d33f704e22f6d8fb8047651dbb2e0323 Mon Sep 17 00:00:00 2001 From: fuleyi Date: Wed, 20 May 2026 14:07:04 +0800 Subject: [PATCH] feat: add screen magnifier toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add a new dsettings key `viewZoomEnable` to control whether screen magnifier shortcuts are enabled. 2. Initialize and store a dedicated keybinding config manager so the shortcut manager can read the new keybinding setting at runtime. 3. Apply the magnifier switch during startup by calling `SetViewZoomEnabled`, ensuring the zoom feature state matches persisted configuration immediately after initialization. 4. Listen for `viewZoomEnable` changes in dconfig and dynamically enable or disable the related zoom shortcuts without requiring a restart. 5. Introduce dedicated handling for zoom-related KWin shortcut IDs (`viewZoomIn`, `viewZoomOut`, `viewActualSize`), including adding them only when enabled and removing them when disabled. 6. Prevent zoom shortcuts from being added through the normal KWin shortcut loading flow to avoid duplicate registration and shortcut invalidation during initialization. 7. Control the KWin `zoom` effect through `gdbus` by checking whether the effect is loaded and loading or unloading it based on the configured switch. 8. Use safe fallback behavior by treating the feature as enabled when configuration cannot be read, which helps preserve existing behavior and avoid accidentally disabling magnifier functionality. Log: Added a screen magnifier toggle to dynamically enable or disable zoom shortcuts and the zoom effect Influence: 1. Verify that when `viewZoomEnable` is `true`, `viewZoomIn`, `viewZoomOut`, and `viewActualSize` shortcuts are available and trigger the expected magnifier behavior. 2. Verify that when `viewZoomEnable` is `false`, the related zoom shortcuts are removed and no longer take effect. 3. Test startup behavior with the switch enabled and disabled to confirm the runtime state matches the saved configuration after login or daemon restart. 4. Toggle the setting at runtime and confirm the shortcuts are added or removed immediately without restarting the session. 5. On KWin environments, verify that the `zoom` effect is loaded when enabled and unloaded when disabled. 6. Test fallback behavior when dsettings or `gdbus` access fails, ensuring the daemon does not crash and existing shortcut behavior remains as safe as possible. 7. Verify there is no duplicate shortcut registration or shortcut loss for zoom-related actions during initialization on both X11 and Wayland paths. feat: 添加屏幕放大镜开关 1. 新增 dsettings 配置项 `viewZoomEnable`,用于控制是否启用屏幕放大镜相 关快捷键。 2. 初始化并保存独立的 keybinding 配置管理器,使快捷键管理器能够在运行时 读取该按键绑定配置。 3. 在启动初始化阶段调用 `SetViewZoomEnabled` 应用放大镜开关,确保启动后 缩放功能状态与持久化配置保持一致。 4. 在 dconfig 变更监听中增加对 `viewZoomEnable` 的处理,使相关缩放快捷键 可以在运行时动态启用或禁用,无需重启。 5. 为 KWin 的缩放相关快捷键 ID(`viewZoomIn`、`viewZoomOut`、 `viewActualSize`)增加专门处理逻辑,仅在开关开启时添加,关闭时删除。 6. 在常规 KWin 快捷键加载流程中跳过缩放快捷键,避免初始化阶段重复注册导 致快捷键失效。 7. 通过 `gdbus` 控制 KWin 的 `zoom` 特效,先检查特效加载状态,再根据开关 决定加载或卸载该特效。 8. 对配置读取失败场景采用默认启用的兜底策略,以保持现有行为不变,避免误 关闭放大镜功能。 Log: 新增屏幕放大镜开关,可动态启用或禁用缩放快捷键及缩放特效 Influence: 1. 验证当 `viewZoomEnable` 为 `true` 时,`viewZoomIn`、`viewZoomOut`、 `viewActualSize` 快捷键可用,并能触发预期的放大镜行为。 2. 验证当 `viewZoomEnable` 为 `false` 时,相关缩放快捷键会被移除,且不再 生效。 3. 测试在开关开启和关闭两种情况下的启动行为,确认登录或守护进程重启后运 行时状态与保存配置一致。 4. 在运行时切换该设置,确认无需重启会话即可立即添加或移除对应快捷键。 5. 在 KWin 环境下验证开启时会加载 `zoom` 特效,关闭时会卸载该特效。 6. 测试 dsettings 或 `gdbus` 访问失败时的兜底行为,确保守护进程不会崩 溃,并尽可能保持原有快捷键行为稳定。 7. 验证在 X11 和 Wayland 路径下初始化过程中不会出现缩放快捷键重复注册或 丢失的问题。 PMS: BUG-360027 Change-Id: Ic9d9617178a8bec0427205f18b11d89c97d24d14 --- keybinding1/constants/dsettings.go | 3 +- keybinding1/manager.go | 5 + keybinding1/shortcuts/shortcut_manager.go | 110 +++++++++++++++++- .../org.deepin.dde.daemon.keybinding.json | 10 ++ 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/keybinding1/constants/dsettings.go b/keybinding1/constants/dsettings.go index 5e1b53d5f..fc04081f5 100644 --- a/keybinding1/constants/dsettings.go +++ b/keybinding1/constants/dsettings.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -37,4 +37,5 @@ const ( DSettingsKeyOsdAdjustVolumeState = "osdAdjustVolumeEnabled" DSettingsKeyAmbientLightAdjustBrightness = "ambientLightAdjustBrightness" + DSettingsKeyViewZoomEnable = "viewZoomEnable" ) diff --git a/keybinding1/manager.go b/keybinding1/manager.go index 96b883319..65eba5a74 100644 --- a/keybinding1/manager.go +++ b/keybinding1/manager.go @@ -334,6 +334,9 @@ func (m *Manager) init() { } } + // 初始化时根据 viewZoomEnable 配置启用/禁用缩放功能 + m.shortcutManager.SetViewZoomEnabled(m.wm, m.shortcutManager.IsViewZoomEnabled()) + // init custom shortcuts customConfigFilePath := filepath.Join(basedir.GetUserConfigDir(), customConfigFile) m.customShortcutManager = shortcuts.NewCustomShortcutManager(customConfigFilePath) @@ -485,6 +488,8 @@ func (m *Manager) initDConfig(bus *dbus.Conn) { getNeedXrandrQConfig() case constants.DSettingsKeyDeviceManagerControlEnable: getDeviceManagerControlEnableConfig() + case constants.DSettingsKeyViewZoomEnable: + m.shortcutManager.SetViewZoomEnabled(m.wm, m.shortcutManager.IsViewZoomEnabled()) } }) if err != nil { diff --git a/keybinding1/shortcuts/shortcut_manager.go b/keybinding1/shortcuts/shortcut_manager.go index 0a266d981..7ad838674 100644 --- a/keybinding1/shortcuts/shortcut_manager.go +++ b/keybinding1/shortcuts/shortcut_manager.go @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2018 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -141,6 +141,7 @@ type ShortcutManager struct { shortcutWrapGnomeWmConfigMgr configManager.Manager shortcutEnableConfigMgr configManager.Manager shortcutPlatformMgr configManager.Manager + keybindingConfigMgr configManager.Manager } type KeyEvent struct { @@ -296,6 +297,17 @@ func (sm *ShortcutManager) initDconfig() { if err != nil { logger.Warning(err) } + + keybindingConfigPath, err := ds.AcquireManager(0, constants.DSettingsAppID, constants.DSettingsKeyBindingName, "") + if err != nil || keybindingConfigPath == "" { + logger.Warning(err) + return + } + + sm.keybindingConfigMgr, err = configManager.NewManager(bus, keybindingConfigPath) + if err != nil { + logger.Warning(err) + } } func (sm *ShortcutManager) getSystemConfigCallbacks() ( @@ -1385,6 +1397,94 @@ func (sm *ShortcutManager) AddSpecial() { sm.addWithoutLock(s0) } +var viewZoomIds = map[string]bool{ + "viewZoomIn": true, + "viewZoomOut": true, + "viewActualSize": true, +} + +func (sm *ShortcutManager) IsViewZoomEnabled() bool { + if sm.keybindingConfigMgr == nil { + return true + } + v, err := sm.keybindingConfigMgr.Value(0, constants.DSettingsKeyViewZoomEnable) + if err != nil { + return true + } + return v.Value().(bool) +} + +func (sm *ShortcutManager) DelViewZoomShortcuts() { + for id := range viewZoomIds { + shortcut := sm.GetByIdType(id, ShortcutTypeWM) + if shortcut != nil { + sm.Delete(shortcut) + } + } +} + +func (sm *ShortcutManager) SetViewZoomEnabled(wmObj wm.Wm, enabled bool) { + logger.Debugf("SetViewZoomEnabled: %v", enabled) + + gdbusArgs := func(method string) []string { + return []string{"call", "--session", "--dest", "org.kde.KWin", + "--object-path", "/Effects", + "--method", "org.kde.kwin.Effects." + method, "zoom"} + } + + runGdbus := func(method string) { + if _, err := exec.Command("gdbus", gdbusArgs(method)...).Output(); err != nil { + logger.Warningf("Failed to %s zoom effect: %v", method, err) + } else { + logger.Infof("Successfully %s zoom effect", method) + } + } + + output, err := exec.Command("gdbus", gdbusArgs("isEffectLoaded")...).Output() + if err != nil { + sm.DelViewZoomShortcuts() + logger.Warning(err) + return + } + loadzoom := strings.Contains(strings.ToLower(string(output)), "true") + + if enabled { + sm.addViewZoomKWin(wmObj) + if !loadzoom { + runGdbus("loadEffect") + } + } else { + sm.DelViewZoomShortcuts() + if loadzoom { + runGdbus("unloadEffect") + } + } +} + +func (sm *ShortcutManager) addViewZoomKWin(wmObj wm.Wm) { + idNameMap := getWMIdNameMap() + for id := range viewZoomIds { + keystrokes, err := wmObj.GetAccel(0, id) + if err != nil { + logger.Warningf("failed to get accel for '%s': %v", id, err) + continue + } + if len(keystrokes) == 0 { + keystrokes, err = wmObj.GetDefaultAccel(0, id) + if err != nil { + logger.Warningf("failed to get default accel for '%s': %v", id, err) + continue + } + } + name := idNameMap[id] + if name == "" { + name = id + } + ks := newKWinShortcut(id, name, keystrokes, wmObj) + sm.addWithoutLock(ks) + } +} + func (sm *ShortcutManager) AddKWin(wmObj wm.Wm) { logger.Debug("AddKWin") accels, err := util.GetAllKWinAccels(wmObj) @@ -1405,6 +1505,10 @@ func (sm *ShortcutManager) AddKWin(wmObj wm.Wm) { logger.Debugf("'%s' is abandoned!", accel.Id) continue } + // zoom功能需要根据配置决定是否添加, 这里先不添加,避免在初始化配置时重复设置,导致快捷键失效 + if viewZoomIds[accel.Id] { + continue + } name := idNameMap[accel.Id] if name == "" { name = accel.Id @@ -1431,6 +1535,10 @@ func (sm *ShortcutManager) AddKWinForWayland(wmObj wm.Wm) { if getSystemIdNameMap()[accel.Id] != "" || getMediaIdNameMap()[accel.Id] != "" { continue } + // zoom功能需要根据配置决定是否添加, 这里先不添加,避免在初始化配置时重复设置,导致快捷键失效 + if viewZoomIds[accel.Id] { + continue + } name := idNameMap[accel.Id] if name == "" { diff --git a/misc/dsg-configs/org.deepin.dde.daemon.keybinding.json b/misc/dsg-configs/org.deepin.dde.daemon.keybinding.json index eb2d0b401..0ee546ebf 100644 --- a/misc/dsg-configs/org.deepin.dde.daemon.keybinding.json +++ b/misc/dsg-configs/org.deepin.dde.daemon.keybinding.json @@ -34,6 +34,16 @@ "description": "DeviceManager shortcut effected by DDE software", "permissions": "readonly", "visibility": "private" + }, + "viewZoomEnable": { + "value": true, + "serial": 0, + "flags": [], + "name": "viewZoomEnable", + "name[zh_CN]": "启用/禁用缩放快捷键", + "description": "enable/disable view zoom shortcuts", + "permissions": "readwrite", + "visibility": "private" } } } \ No newline at end of file