// SPDX-FileCopyrightText: 2018 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

// keybinding 包实现键盘快捷键管理功能
// 包括系统快捷键、媒体键、自定义快捷键的注册、处理和执行
package keybinding

import (
	"encoding/json"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync/atomic"
	"time"

	dbus "github.com/godbus/dbus"
	"github.com/linuxdeepin/dde-daemon/keybinding/shortcuts"
	airplanemode "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.airplanemode"
	backlight "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.helper.backlight"
	inputdevices "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.inputdevices"
	keyevent "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.keyevent"
	kwayland "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.kwayland"
	network "github.com/linuxdeepin/go-dbus-factory/com.deepin.daemon.network"
	lockfront "github.com/linuxdeepin/go-dbus-factory/com.deepin.dde.lockfront"
	shutdownfront "github.com/linuxdeepin/go-dbus-factory/com.deepin.dde.shutdownfront"
	sessionmanager "github.com/linuxdeepin/go-dbus-factory/com.deepin.sessionmanager"
	power "github.com/linuxdeepin/go-dbus-factory/com.deepin.system.power"
	systeminfo "github.com/linuxdeepin/go-dbus-factory/com.deepin.system.systeminfo"
	wm "github.com/linuxdeepin/go-dbus-factory/com.deepin.wm"
	configManager "github.com/linuxdeepin/go-dbus-factory/org.desktopspec.ConfigManager"
	DisplayManager "github.com/linuxdeepin/go-dbus-factory/org.freedesktop.DisplayManager"
	login1 "github.com/linuxdeepin/go-dbus-factory/org.freedesktop.login1"
	networkmanager "github.com/linuxdeepin/go-dbus-factory/org.freedesktop.networkmanager"
	gio "github.com/linuxdeepin/go-gir/gio-2.0"
	"github.com/linuxdeepin/go-lib/dbusutil"
	"github.com/linuxdeepin/go-lib/dbusutil/gsprop"
	"github.com/linuxdeepin/go-lib/dbusutil/proxy"
	"github.com/linuxdeepin/go-lib/gsettings"
	"github.com/linuxdeepin/go-lib/strv"
	"github.com/linuxdeepin/go-lib/xdg/basedir"
	x "github.com/linuxdeepin/go-x11-client"
	"github.com/linuxdeepin/go-x11-client/util/keysyms"
)

//go:generate dbusutil-gen em -type Manager

const (
	// shortcut signals:
	shortcutSignalChanged = "Changed"
	shortcutSignalAdded   = "Added"
	shortcutSignalDeleted = "Deleted"

	gsSchemaKeyboard          = "com.deepin.dde.keyboard"
	gsKeyNumLockState         = "numlock-state"
	gsKeySaveNumLockState     = "save-numlock-state"
	gsKeyShortcutSwitchLayout = "shortcut-switch-layout"
	gsKeyShowCapsLockOSD      = "capslock-toggle"
	gsKeyUpperLayerWLAN       = "upper-layer-wlan"

	gsSchemaSystem         = "com.deepin.dde.keybinding.system"
	gsSchemaSystemPlatform = "com.deepin.dde.keybinding.system.platform"
	gsSchemaSystemEnable   = "com.deepin.dde.keybinding.system.enable"
	gsSchemaMediaKey       = "com.deepin.dde.keybinding.mediakey"
	gsSchemaGnomeWM        = "com.deepin.wrap.gnome.desktop.wm.keybindings"
	gsSchemaSessionPower   = "com.deepin.dde.power"

	customConfigFile = "deepin/dde-daemon/keybinding/custom.ini"
	CapslockKey      = 58
	NumlockKey       = 69
	KeyPress         = 1
)

const (
	DSettingsAppID                          = "org.deepin.dde.daemon"
	DSettingsKeyBindingName                 = "org.deepin.dde.daemon.keybinding"
	DSettingsKeyWirelessControlEnable       = "wirelessControlEnable"
	DSettingsKeyNeedXrandrQDevices          = "need-xrandr-q-devices"
	DSettingsKeyDeviceManagerControlEnable  = "deviceManagerControlEnable"
	DSettingsKeyKbBacklightLedPath          = "kbBacklightLedPath"
	DSettingsKeyDelayTriggerDpmsOffDuration = "delayTriggerDpmsOffDuration"

	DSettingsControlCenterAppID             = "org.deepin.dde.control-center"
	DSettingsControlCenterCommoninfo        = "org.deepin.dde.control-center.commoninfo"
	DSettingsControlCenterMagnifierEnabled  = "magnifierEnabled"
)

const ( // power按键事件的响应
	powerActionShutdown int32 = iota
	powerActionSuspend
	powerActionHibernate
	powerActionTurnOffScreen
	powerActionShowUI
)

var _useWayland bool

func setUseWayland(value bool) {
	_useWayland = value
}

// Manager 是键盘快捷键管理器的核心结构
// 负责管理所有快捷键的注册、事件处理和系统交互
// Manager 键盘快捷键管理器核心结构
type Manager struct {
	service *dbusutil.Service // DBus服务对象
	// properties
	NumLockState         gsprop.Enum // NumLock键状态(0:关闭,1:开启,2:未知)
	ShortcutSwitchLayout gsprop.Uint `prop:"access:rw"` // 切换键盘布局的快捷键ID

	conn       *x.Conn              // X11连接
	keySymbols *keysyms.KeySymbols  // 键盘符号映射

	gsKeyboard       *gio.Settings // 键盘相关设置
	gsSystem         *gio.Settings // 系统快捷键设置
	gsSystemPlatform *gio.Settings // 系统平台快捷键设置
	gsSystemEnable   *gio.Settings // 系统快捷键启用状态
	gsMediaKey       *gio.Settings // 媒体键设置
	gsGnomeWM        *gio.Settings // GNOME窗口管理器快捷键设置
	gsPower          *gio.Settings // 电源管理设置

	enableListenGSettings      bool   // 是否启用GSettings监听
	delayNetworkStateChange    bool   // 延迟网络状态变更标志
	canExcuteSuspendOrHiberate bool   // 是否可以执行待机或休眠
	prepareForSleep            bool   // 是否正在准备睡眠
	clickNum                   uint32 // 媒体键点击次数
	shortcutCmd                string // 当前快捷键命令
	shortcutKey                string // 当前快捷键按键
	shortcutKeyCmd             string // 当前快捷键按键命令
	customShortcutManager      *shortcuts.CustomShortcutManager // 自定义快捷键管理器

	lockFront     lockfront.LockFront     // 锁屏前端接口
	shutdownFront shutdownfront.ShutdownFront // 关机前端接口

	sessionSigLoop            *dbusutil.SignalLoop // 会话DBus信号循环
	systemSigLoop             *dbusutil.SignalLoop  // 系统DBus信号循环
	startManager              sessionmanager.StartManager // 启动管理器
	sessionManager            sessionmanager.SessionManager // 会话管理器
	airplane                  airplanemode.AirplaneMode // 飞行模式接口
	networkmanager            networkmanager.Manager // 网络管理器
	backlightHelper           backlight.Backlight // 背光控制助手
	keyboard                  inputdevices.Keyboard // 键盘设备
	keyboardLayout            string // 当前键盘布局
	wm                        wm.Wm // 窗口管理器接口
	waylandOutputMgr          kwayland.OutputManagement // Wayland输出管理
	login1Manager             login1.Manager // 登录管理器
	keyEvent                  keyevent.KeyEvent // 键盘事件接口
	displayManager            DisplayManager.DisplayManager // 显示管理器
	network                   network.Network // 网络接口
	specialKeycodeBindingList map[SpecialKeycodeMapKey]func() // 特殊按键绑定列表

	// controllers 控制器
	audioController       *AudioController // 音频控制器
	mediaPlayerController *MediaPlayerController // 媒体播放控制器
	displayController     *DisplayController // 显示控制器
	kbdLightController    *KbdLightController // 键盘背光控制器
	touchPadController    *TouchPadController // 触摸板控制器

	shortcutManager *shortcuts.ShortcutManager // 快捷键管理器
	// shortcut action handlers 快捷键处理器
	handlers             []shortcuts.KeyEventFunc // 快捷键处理函数列表
	lastKeyEventTime     time.Time // 上次按键事件时间
	lastMethodCalledTime time.Time // 上次方法调用时间
	delayUpdateRfTimer   *time.Timer // 延迟更新射频状态定时器
	grabScreenKeystroke  *shortcuts.Keystroke // 屏幕抓取快捷键

	// for switch kbd layout 用于切换键盘布局
	switchKbdLayoutState SKLState // 键盘布局切换状态
	sklWaitQuit          chan int // 键盘布局切换退出通道

	// dsg config DSG配置
	wifiControlEnable           bool     // WiFi控制启用状态
	needXrandrQDevice           []string // 需要xrandr查询的设备列表
	deviceManagerControlEnable  bool     // 设备管理器控制启用状态
	DisabledSystemShortcutsList strv.Strv // 禁用的系统快捷键列表
	kbBacklightLedPath          string   // 键盘背光灯路径
	delayTriggerDpmsOffDuration float64  // 延迟触发DPMS关闭时长

	configManagerPath dbus.ObjectPath // 配置管理器路径

	dmiInfo             systeminfo.DMIInfo // DMI系统信息
	rfkillState         bool // 射频状态
	repeatCount         int // 重复计数
	fnLockCount         int // Fn锁定计数
	fnLocking           bool // Fn锁定状态
	lastDPMSOffTime     time.Time // 上次DPMS关闭时间
	capableOfDPMS       bool // 是否支持DPMS
	waitLockVisibleChan chan bool // 等待锁屏可见通道
	waitLockVisibleFlag int64 // 等待锁屏可见标志

	magnifierEnabled     bool // 放大镜启用状态

	// nolint
	signals *struct { // DBus信号
		Added, Deleted, Changed struct {
			id  string
			typ int32
		}

		KeyEvent struct {
			pressed   bool
			keystroke string
		}
	}
}

// SKLState Switch keyboard Layout state
type SKLState uint

const (
	SKLStateNone SKLState = iota
	SKLStateWait
	SKLStateOSDShown
)

// newManager 创建并初始化键盘快捷键管理器
// newManager 创建键盘快捷键管理器实例
// 参数: service - DBus服务对象
// 返回值: 管理器实例和错误信息
func newManager(service *dbusutil.Service) (*Manager, error) {
	setUseWayland(strings.Contains(os.Getenv("XDG_SESSION_TYPE"), "wayland"))
	conn, err := x.NewConn()
	if err != nil {
		logger.Warning(err)
		return nil, err
	}

	sessionBus := service.Conn()
	sysBus, err := dbus.SystemBus()
	if err != nil {
		logger.Warning(err)
		return nil, err
	}

	var m = Manager{
		service:               service,
		enableListenGSettings: true,
		conn:                  conn,
		keySymbols:            keysyms.NewKeySymbols(conn),
		handlers:              make([]shortcuts.KeyEventFunc, shortcuts.ActionTypeCount),
	}

	m.sessionSigLoop = dbusutil.NewSignalLoop(sessionBus, 10)
	m.systemSigLoop = dbusutil.NewSignalLoop(sysBus, 10)
	m.waitLockVisibleChan = make(chan bool, 1)

	if _useWayland {
		m.waylandOutputMgr = kwayland.NewOutputManagement(sessionBus)
	}
	m.login1Manager = login1.NewManager(sysBus)
	m.login1Manager.InitSignalExt(m.systemSigLoop, true)
	_, err = m.login1Manager.ConnectPrepareForSleep(func(isSleep bool) {
		logger.Debugf("PreparingForSleep status changed, isSleep: %v", isSleep)
		m.prepareForSleep = isSleep
		// 待机或休眠时，唤醒后的1秒内不响应待机和休眠操作，避免按电源键唤醒时再次进入待机或休眠
		if !isSleep {
			m.canExcuteSuspendOrHiberate = false
			time.AfterFunc(1*time.Second, func() {
				m.canExcuteSuspendOrHiberate = true
			})
			// 添加休眠唤醒后的隐私状态检查
			logger.Debug("System waking up, checking privacy status")
			go time.AfterFunc(3*time.Second, m.checkPrivacyStatusAfterWakeup)
		} else {
			m.canExcuteSuspendOrHiberate = false
		}
	})
	if err != nil {
		logger.Warning("failed to connect signal PrepareForSleep:", err)
	}
	m.prepareForSleep, _ = m.login1Manager.PreparingForSleep().Get(0)
	m.displayManager = DisplayManager.NewDisplayManager(sysBus)
	m.shutdownFront = shutdownfront.NewShutdownFront(sessionBus)
	m.gsKeyboard = gio.NewSettings(gsSchemaKeyboard)
	m.NumLockState.Bind(m.gsKeyboard, gsKeyNumLockState)
	m.ShortcutSwitchLayout.Bind(m.gsKeyboard, gsKeyShortcutSwitchLayout)
	m.sessionSigLoop.Start()
	m.systemSigLoop.Start()

	m.initNumLockState(sysBus)
	m.initDSettings(sysBus)

	m.init()

	m.syncMagnifierStatus()

	return &m, nil
}

// init 初始化键盘快捷键管理器
// 包括加载设置、创建控制器、注册快捷键等
// init 初始化键盘快捷键管理器
// 包括加载设置、创建控制器、注册快捷键等
func (m *Manager) init() {
	sessionBus := m.service.Conn()
	sysBus, _ := dbus.SystemBus()
	m.delayNetworkStateChange = true
	m.canExcuteSuspendOrHiberate = true

	// init settings
	m.gsSystem = gio.NewSettings(gsSchemaSystem)
	m.gsSystemPlatform = gio.NewSettings(gsSchemaSystemPlatform)
	m.gsSystemEnable = gio.NewSettings(gsSchemaSystemEnable)
	m.gsMediaKey = gio.NewSettings(gsSchemaMediaKey)
	m.gsPower = gio.NewSettings(gsSchemaSessionPower)
	m.wm = wm.NewWm(sessionBus)
	m.keyEvent = keyevent.NewKeyEvent(sysBus)
	m.network = network.NewNetwork(sessionBus)

	m.shortcutManager = shortcuts.NewShortcutManager(m.conn, m.keySymbols, m.handleKeyEvent)

	// when session is locked, we need handle some keyboard function event
	m.lockFront = lockfront.NewLockFront(sessionBus)
	m.lockFront.InitSignalExt(m.sessionSigLoop, true)
	m.lockFront.ConnectChangKey(func(changKey string) {
		m.handleKeyEventFromLockFront(changKey)
	})
	m.lockFront.ConnectVisible(func(visible bool) {
		logger.Debugf("Receive lock front visible: %v", visible)
		if atomic.LoadInt64(&m.waitLockVisibleFlag) != 0 && visible {
			m.waitLockVisibleChan <- visible
			atomic.StoreInt64(&m.waitLockVisibleFlag, 0)
		}
	})

	m.shutdownFront = shutdownfront.NewShutdownFront(sessionBus)
	m.shutdownFront.InitSignalExt(m.sessionSigLoop, true)
	m.shutdownFront.ConnectChangKey(func(changKey string) {
		m.handleKeyEventFromShutdownFront(changKey)
	})

	if _useWayland {
		if shouldUseDDEKwin() {
			logger.Debug("Use DDE KWin")
			m.shortcutManager.AddSpecialToKwin(m.wm)
			m.shortcutManager.AddSystemToKwin(m.gsSystem, m.wm)
			m.shortcutManager.AddMediaToKwin(m.gsMediaKey, m.wm)
			m.shortcutManager.AddKWinForWayland(m.wm)
		} else {
			m.shortcutManager.AddSpecial()
			m.shortcutManager.AddSystem(m.gsSystem, m.gsSystemPlatform, m.gsSystemEnable, m.wm)
			m.shortcutManager.AddMedia(m.gsMediaKey, m.wm)
			m.gsGnomeWM = gio.NewSettings(gsSchemaGnomeWM)
			m.shortcutManager.AddWM(m.gsGnomeWM, m.wm)
		}
	} else {
		m.shortcutManager.AddSystem(m.gsSystem, m.gsSystemPlatform, m.gsSystemEnable, m.wm)
		m.shortcutManager.AddMedia(m.gsMediaKey, m.wm)
		if shouldUseDDEKwin() {
			logger.Debug("Use DDE KWin")
			m.shortcutManager.AddKWin(m.wm)
		} else {
			logger.Debug("Use gnome WM")
			m.gsGnomeWM = gio.NewSettings(gsSchemaGnomeWM)
			m.shortcutManager.AddWM(m.gsGnomeWM, m.wm)
		}
	}

	// init custom shortcuts
	customConfigFilePath := filepath.Join(basedir.GetUserConfigDir(), customConfigFile)
	m.customShortcutManager = shortcuts.NewCustomShortcutManager(customConfigFilePath)
	m.shortcutManager.AddCustom(m.customShortcutManager, m.wm)

	// init controllers
	m.backlightHelper = backlight.NewBacklight(sysBus)
	m.audioController = NewAudioController(sessionBus, m.backlightHelper)
	m.mediaPlayerController = NewMediaPlayerController(m.systemSigLoop, sessionBus)

	m.startManager = sessionmanager.NewStartManager(sessionBus)
	m.airplane = airplanemode.NewAirplaneMode(sysBus)
	m.networkmanager = networkmanager.NewManager(sysBus)
	m.sessionManager = sessionmanager.NewSessionManager(sessionBus)
	m.keyboard = inputdevices.NewKeyboard(sessionBus)
	m.keyboard.InitSignalExt(m.sessionSigLoop, true)
	err := m.keyboard.CurrentLayout().ConnectChanged(func(hasValue bool, layout string) {
		if !hasValue {
			return
		}
		if m.keyboardLayout != layout {
			m.keyboardLayout = layout
			logger.Debug("keyboard layout changed:", layout)
			m.shortcutManager.NotifyLayoutChanged()
		}
	})

	if err != nil {
		logger.Warning("connect CurrentLayout property changed failed:", err)
	}

	m.displayController = NewDisplayController(m.backlightHelper, sessionBus, m)
	m.kbdLightController = NewKbdLightController(m.backlightHelper)
	m.touchPadController = NewTouchPadController(sessionBus)

	m.initSpecialKeycodeMap()
	m.keyEvent.InitSignalExt(m.systemSigLoop, true)
	_, err = m.keyEvent.ConnectKeyEvent(m.handleSpecialKeycode)
	if err != nil {
		logger.Warning(err)
	}

	sysInfo := systeminfo.NewSystemInfo(sysBus)
	dmiInfo, err := sysInfo.DMIInfo().Get(0)
	if err != nil {
		logger.Warning(err)
	} else {
		m.dmiInfo = dmiInfo
	}

	m.clickNum = 0

	m.rfkillState, err = m.airplane.Enabled().Get(0)
	if err != nil {
		logger.Warning(err)
	} else {
		logger.Info("init rfkillState : ", m.rfkillState)
	}

	m.checkDPMSCapable()
	time.AfterFunc(5*time.Second, m.checkPrivacyStatusAfterWakeup)
}

// initDSettings 初始化DSettings配置
// 从DSettings获取键盘背光路径、DPMS关闭延迟等配置
func (m *Manager) initDSettings(bus *dbus.Conn) {
	ds := configManager.NewConfigManager(bus)
	dsPath, err := ds.AcquireManager(0, DSettingsAppID, DSettingsKeyBindingName, "")
	if err != nil {
		logger.Warning(err)
		return
	}

	keybindingDS, err := configManager.NewManager(bus, dsPath)
	if err != nil {
		logger.Warning(err)
		return
	}

	dsControlCenterPath, err := ds.AcquireManager(0, DSettingsControlCenterAppID, DSettingsControlCenterCommoninfo, "")
	if err != nil {
		logger.Warning(err)
		return
	}
	controlCenterDs, err := configManager.NewManager(bus, dsControlCenterPath)
	if err != nil {
		logger.Warning(err)
		return
	}
	getControlCenterMagnifierEnabled := func() {
		v, err := controlCenterDs.Value(0, DSettingsControlCenterMagnifierEnabled)
		if err != nil {
			logger.Warning(err)
			// 如果配置不存在，说明控制中心未升级，则默认支持放大镜
			m.magnifierEnabled = true
			return
		}
		m.magnifierEnabled = v.Value().(bool)
		logger.Info("dsg of org.deepin.dde.control-center.commoninfo magnifierEnabled : ", m.magnifierEnabled)
	}
	getControlCenterMagnifierEnabled()

	getWirelessControlEnableConfig := func() {
		v, err := keybindingDS.Value(0, DSettingsKeyWirelessControlEnable)
		if err != nil {
			logger.Warning(err)
			return
		}
		m.wifiControlEnable = v.Value().(bool)
	}
	getNeedXrandrQConfig := func() {
		v, err := keybindingDS.Value(0, DSettingsKeyNeedXrandrQDevices)
		if err != nil {
			logger.Warning(err)
			return
		}
		itemList := v.Value().([]dbus.Variant)
		for _, i := range itemList {
			m.needXrandrQDevice = append(m.needXrandrQDevice, i.Value().(string))
		}
	}
	getDeviceManagerControlEnableConfig := func() {
		v, err := keybindingDS.Value(0, DSettingsKeyDeviceManagerControlEnable)
		if err != nil {
			logger.Warning(err)
			return
		}
		m.deviceManagerControlEnable = v.Value().(bool)
	}

	getKbBacklightLedConfig := func() {
		v, err := keybindingDS.Value(0, DSettingsKeyKbBacklightLedPath)
		if err != nil {
			logger.Warning(err)
			return
		}
		m.kbBacklightLedPath = v.Value().(string)
		logger.Info("dsg of kbBacklightLedPath value : ", m.kbBacklightLedPath)
	}

	getDelayTriggerDpmsOffDurationConfig := func() {
		v, err := keybindingDS.Value(0, DSettingsKeyDelayTriggerDpmsOffDuration)
		if err != nil {
			logger.Warning(err)
			return
		}
		m.delayTriggerDpmsOffDuration = v.Value().(float64)
	}

	getWirelessControlEnableConfig()
	getNeedXrandrQConfig()
	getDeviceManagerControlEnableConfig()
	getKbBacklightLedConfig()
	getDelayTriggerDpmsOffDurationConfig()

	keybindingDS.InitSignalExt(m.systemSigLoop, true)
	// 监听dsg配置变化
	_, err = keybindingDS.ConnectValueChanged(func(key string) {
		switch key {
		case DSettingsKeyWirelessControlEnable:
			getWirelessControlEnableConfig()
		case DSettingsKeyNeedXrandrQDevices:
			getNeedXrandrQConfig()
		case DSettingsKeyDeviceManagerControlEnable:
			getDeviceManagerControlEnableConfig()
		case DSettingsKeyKbBacklightLedPath:
			getKbBacklightLedConfig()
		}
	})
	if err != nil {
		logger.Warning(err)
	}
}

var kwinSysActionCmdMap = map[string]string{
	"Launcher":              "launcher",               // Super_L Super_R
	"Terminal":              "terminal",               // <Control><Alt>T
	"Terminal Quake Window": "terminal-quake",         //
	"Lock screen":           "lock-screen",            // super+l
	"Shutdown interface":    "logout",                 // ctrl+alt+del
	"File manager":          "file-manager",           // super+e
	"Screenshot":            "screenshot",             // ctrl+alt+a
	"Full screenshot":       "screenshot-fullscreen",  // print
	"Window screenshot":     "screenshot-window",      // alt+print
	"Delay screenshot":      "screenshot-delayed",     // ctrl+print
	"Disable Touchpad":      "disable-touchpad",       //
	"Switch window effects": "wm-switcher",            // alt+tab
	"turn-off-screen":       "Fast Screen Off",        // <Shift><Super>L
	"Deepin Picker":         "color-picker",           // ctrl+alt+v
	"System Monitor":        "system-monitor",         // ctrl+alt+escape
	"Screen Recorder":       "deepin-screen-recorder", // deepin-screen-recorder ctrl+alt+r
	"Desktop AI Assistant":  "ai-assistant",           // ai-assistant [<Super>Q]q
	"Text to Speech":        "text-to-speech",
	"Speech to Text":        "speech-to-text",
	"Clipboard":             "clipboard",
	"Translation":           "translation",
	"Show/Hide the dock":    "show-dock",

	// cmd
	"Calculator":          "calculator",          // XF86Calculator
	"Search":              "search",              // XF86Search
	"Notification Center": "notification-center", // Meta M

	"ScreenshotScroll": "screenshot-scroll",
	"ScreenshotOcr":    "screenshot-ocr",
	"Global Search":    "global-search",
	"Switch monitors":  "switch-monitors",

	"ScriptWayland": "script", // <Shift><Super>Touchpad Off
}

var waylandMediaIdMap = map[string]string{
	"Messenger":         "messenger",           // XF86Messenger
	"Save":              "save",                // XF86Save
	"New":               "new",                 // XF86New
	"WakeUp":            "wake-up",             // XF86WakeUp
	"audio-rewind":      "AudioRewind",         // XF86AudioRewind
	"VolumeMute":        "audio-mute",          // XF86AudioMute  "AudioMute":
	"MonBrightnessUp":   "mon-brightness-up",   // XF86MonBrightnessUp
	"WLAN":              "wlan",                // XF86WLAN
	"AudioMedia":        "audio-media",         // XF86AudioMedia
	"reply":             "Reply",               // XF86Reply
	"favorites":         "Favorites",           // XF86Favorites
	"AudioPlay":         "audio-play",          // XF86AudioPlay
	"AudioMicMute":      "audio-mic-mute",      // XF86AudioMicMute
	"AudioPause":        "audio-pause",         // XF86AudioPause
	"AudioStop":         "audio-stop",          // XF86AudioStop
	"documents":         "Documents",           // XF86Documents
	"game":              "Game",                // XF86Game
	"AudioRecord":       "audio-record",        // XF86AudioRecord
	"Display":           "display",             // XF86Display
	"reload":            "Reload",              // XF86Reload
	"explorer":          "Explorer",            // XF86Explorer
	"calendar":          "Calendar",            // XF86Calendar
	"forward":           "Forward",             // XF86Forward
	"cut":               "Cut",                 // XF86Cut
	"MonBrightnessDown": "mon-brightness-down", // XF86MonBrightnessDown
	"Copy":              "copy",                // XF86Copy
	"Tools":             "tools",               // XF86Tools
	"VolumeUp":          "audio-raise-volume",  // XF86AudioRaiseVolume "AudioRaiseVolume":  "audio-raise-volume",
	"media-close":       "media-Close",         // XF86Close
	"WWW":               "www",                 // XF86WWW
	"HomePage":          "home-page",           // XF86HomePage
	"sleep":             "Sleep",               // XF86Sleep
	"VolumeDown":        "audio-lower-volume",  // XF86AudioLowerVolume  "AudioLowerVolume":  "audio-lower-volume",
	"AudioPrev":         "audio-prev",          // XF86AudioPrev
	"AudioNext":         "audio-next",          // XF86AudioNext
	"Paste":             "paste",               // XF86Paste
	"open":              "Open",                // XF86Open
	"send":              "Send",                // XF86Send
	"my-computer":       "MyComputer",          // XF86MyComputer
	"Mail":              "mail",                // XF86Mail
	"adjust-brightness": "BrightnessAdjust",    // XF86BrightnessAdjust
	"LogOff":            "log-off",             // XF86LogOff
	"pictures":          "Pictures",            // XF86Pictures
	"Terminal":          "terminal",            // XF86Terminal
	"video":             "Video",               // XF86Video
	"Music":             "music",               // XF86Music
	"app-left":          "ApplicationLeft",     // XF86ApplicationLeft
	"app-right":         "ApplicationRight",    // XF86ApplicationRight
	"meeting":           "Meeting",             // XF86Meeting
	"Numlock":           "numlock",
	"Capslock":          "capslock",
	"Switch kbd layout": "switch-kbd-layout",
}

// listenGlobalAccel 监听全局快捷键事件
// 处理来自KWin的全局快捷键信号
// listenGlobalAccel 监听全局快捷键事件
// 参数: sessionBus - 会话DBus连接
// 返回值: 错误信息
func (m *Manager) listenGlobalAccel(sessionBus *dbus.Conn) error {
	err := sessionBus.Object("org.kde.kglobalaccel",
		"/component/kwin").AddMatchSignal("org.kde.kglobalaccel.Component", "globalShortcutPressed").Err
	if err != nil {
		logger.Warning(err)
		return err
	}

	m.sessionSigLoop.AddHandler(&dbusutil.SignalRule{
		Name: "org.kde.kglobalaccel.Component.globalShortcutPressed",
	}, func(sig *dbus.Signal) {
		if len(sig.Body) > 1 {
			const minKeyEventInterval = 200 * time.Millisecond
			now := time.Now()
			duration := now.Sub(m.lastKeyEventTime)
			if 0 < duration && duration < minKeyEventInterval {
				logger.Debug("ignore key event duration:", duration)
				return
			}
			m.lastKeyEventTime = now

			m.shortcutKey = sig.Body[0].(string)
			m.shortcutKeyCmd = sig.Body[1].(string)
			shortId := kwinSysActionCmdMap[m.shortcutKeyCmd]
			if shortId != "" && m.DisabledSystemShortcutsList.Contains(shortId) {
				logger.Warningf("shortcut id: %s is disabled", shortId)
				return
			}
			ok := strings.Compare(string("kwin"), m.shortcutKey)
			if ok == 0 {
				logger.Debug("[global key] get accel sig.Body[1]", m.shortcutKeyCmd)
				if m.shortcutKeyCmd == "" {
					// + 把响应一次的逻辑放到协程外执行，防止协程响应延迟
					m.handleKeyEventByWayland(waylandMediaIdMap[m.shortcutKeyCmd])
				} else {
					m.shortcutCmd = shortcuts.GetSystemActionCmd(kwinSysActionCmdMap[m.shortcutKeyCmd])
					if m.shortcutCmd == "" {
						m.shortcutCmd = m.shortcutManager.WaylandCustomShortCutMap[m.shortcutKeyCmd]
					}
					logger.Debug("Get cmd from WaylandCustomShortCutMap:", m.shortcutCmd)
					if m.shortcutCmd == "" {
						m.handleKeyEventByWayland(waylandMediaIdMap[m.shortcutKeyCmd])
					} else {
						if strings.HasSuffix(m.shortcutCmd, ".desktop") {
							err := m.runDesktopFile(m.shortcutCmd)
							if err != nil {
								logger.Warning(err)
							}
						} else {
							go func() {
								err := m.execCmd(m.shortcutCmd, true)
								if err != nil {
									logger.Warning(err)
								}
							}()
						}
					}
				}
			}
		}
	})

	return nil
}

// listenKeyboardEvent 监听键盘事件
// 参数: systemBus - 系统DBus连接
func (m *Manager) listenKeyboardEvent(systemBus *dbus.Conn) {
	err := systemBus.Object("com.deepin.daemon.Gesture",
		"/com/deepin/daemon/Gesture").AddMatchSignal("com.deepin.daemon.Gesture", "KeyboardEvent").Err
	if err != nil {
		logger.Warning(err)
	}
	m.systemSigLoop.AddHandler(&dbusutil.SignalRule{
		Name: "com.deepin.daemon.Gesture.KeyboardEvent",
	}, func(sig *dbus.Signal) {
		if len(sig.Body) > 1 {
			key := sig.Body[0].(uint32)
			value := sig.Body[1].(uint32)

			if key == CapslockKey && value == KeyPress {
				m.handleKeyEventByWayland("capslock")
			} else if key == NumlockKey && value == KeyPress {
				m.handleKeyEventByWayland("numlock")
			}
		}
	})
}

// 初始化 NumLock 数字锁定键状态
func (m *Manager) initNumLockState(sysBus *dbus.Conn) {
	// 从 gsettings 读取相关设置
	nlState := NumLockState(m.NumLockState.Get())
	saveStateEnabled := m.gsKeyboard.GetBoolean(gsKeySaveNumLockState)
	if nlState == NumLockUnknown {
		// 判断是否是笔记本, 只根据电池状态，有电池则是笔记本。
		isLaptop := false
		sysPower := power.NewPower(sysBus)
		hasBattery, err := sysPower.HasBattery().Get(0)
		if err != nil {
			logger.Warning("failed to get sysPower HasBattery property:", err)
		} else if hasBattery {
			isLaptop = true
		}

		state := NumLockUnknown
		logger.Debug("isLaptop:", isLaptop)
		if isLaptop {
			// 笔记本，默认关闭。
			state = NumLockOff
		} else {
			// 台式机等，默认开启。
			state = NumLockOn
		}

		if saveStateEnabled {
			// 保存新状态到 gsettings
			m.NumLockState.Set(int32(state))
		}

		err = setNumLockState(m.waylandOutputMgr, m.conn, m.keySymbols, state)
		if err != nil {
			logger.Warning("setNumLockState failed:", err)
		}
	} else {
		if saveStateEnabled {
			err := setNumLockState(m.waylandOutputMgr, m.conn, m.keySymbols, nlState)
			if err != nil {
				logger.Warning("setNumLockState failed:", err)
			}
		}
	}

}

// 检查快捷键时间间隔，如果间隔太短返回false，不应该响应快捷键
func (m *Manager) checkKeyEventInterval() bool {
	const minKeyEventInterval = 200 * time.Millisecond
	now := time.Now()
	duration := now.Sub(m.lastKeyEventTime)
	if 0 < duration && duration < minKeyEventInterval {
		logger.Debug("handleKeyEvent ignore key event duration:", duration)
		return false
	}
	m.lastKeyEventTime = now
	return true
}

// handleKeyEventFromLockFront 处理来自锁屏界面的按键事件
// 参数: changKey - 按键标识
func (m *Manager) handleKeyEventFromLockFront(changKey string) {
	if !m.checkKeyEventInterval() {
		return
	}
	logger.Debugf("Receive LockFront ChangKey Event %s", changKey)
	action := shortcuts.GetAction(changKey)

	// numlock/capslock
	if action.Type == shortcuts.ActionTypeShowNumLockOSD ||
		action.Type == shortcuts.ActionTypeShowCapsLockOSD ||
		action.Type == shortcuts.ActionTypeSystemShutdown {
		if handler := m.handlers[int(action.Type)]; handler != nil {
			handler(nil)
		} else {
			logger.Warning("handler is nil")
		}
	} else {
		cmd, ok := action.Arg.(shortcuts.ActionCmd)
		if !ok {
			logger.Warning(errTypeAssertionFail)
		} else {
			if action.Type == shortcuts.ActionTypeAudioCtrl {
				// audio-mute/audio-lower-volume/audio-raise-volume
				if m.audioController != nil {
					if err := m.audioController.ExecCmd(cmd); err != nil {
						logger.Warning(m.audioController.Name(), "Controller exec cmd err:", err)
					}
				}
			} else if action.Type == shortcuts.ActionTypeDisplayCtrl {
				// mon-brightness-up/mon-brightness-down
				if m.displayController != nil {
					if err := m.displayController.ExecCmd(cmd); err != nil {
						logger.Warning(m.displayController.Name(), "Controller exec cmd err:", err)
					}
				}
			} else if action.Type == shortcuts.ActionTypeTouchpadCtrl {
				// touchpad-toggle/touchpad-on/touchpad-off
				if m.touchPadController != nil {
					if err := m.touchPadController.ExecCmd(cmd); err != nil {
						logger.Warning(m.touchPadController.Name(), "Controller exec cmd err:", err)
					}
				}
			}
		}
	}
}

// handleKeyEventByWayland 处理Wayland环境下的按键事件
// 参数: changKey - 按键标识
func (m *Manager) handleKeyEventByWayland(changKey string) {
	action := shortcuts.GetAction(changKey)
	isWaylandGrabed := true
	logger.Debugf("handleKeyEventByWayland: changKey: %s ,action: %v", changKey, action)
	if action.Type == shortcuts.ActionTypeShowNumLockOSD || action.Type == shortcuts.ActionTypeShowCapsLockOSD {
		sessionBus, err := dbus.SessionBus()
		if err != nil {
			return
		}
		sessionObj := sessionBus.Object("org.kde.KWin", "/KWin")
		err = sessionObj.Call("org.kde.KWin.xwaylandGrabed", 0).Store(&isWaylandGrabed)
		if err != nil {
			logger.Warning(err)
			return
		}
		logger.Debug("xwaylandGrabed: ", isWaylandGrabed)
	}
	// numlock/capslock
	if action.Type == shortcuts.ActionTypeSystemShutdown {
		var powerPressAction int32
		systemBus, _ := dbus.SystemBus()
		systemPower := power.NewPower(systemBus)
		onBattery, err := systemPower.OnBattery().Get(0)
		if err != nil {
			logger.Error(err)
		}
		if onBattery {
			powerPressAction = m.gsPower.GetEnum("battery-press-power-button")
		} else {
			powerPressAction = m.gsPower.GetEnum("line-power-press-power-button")
		}
		logger.Debug("powerPressAction:", powerPressAction)
		switch powerPressAction {
		case powerActionShutdown:
			m.systemShutdown()
		case powerActionSuspend:
			systemSuspend()
		case powerActionHibernate:
			m.systemHibernate()
		case powerActionTurnOffScreen:
			m.systemTurnOffScreen()
		case powerActionShowUI:
			cmd := "dde-shutdown"
			go func() {
				err := m.execCmd(cmd, false)
				if err != nil {
					logger.Warning("execCmd error:", err)
				}
			}()
		}
	} else if action.Type == shortcuts.ActionTypeShowControlCenter {
		err := m.execCmd("dbus-send --session --dest=com.deepin.dde.ControlCenter  --print-reply /com/deepin/dde/ControlCenter com.deepin.dde.ControlCenter.Show",
			false)
		if err != nil {
			logger.Warning("failed to show control center:", err)
		}

	} else if action.Type == shortcuts.ActionTypeToggleWireless {
		// 如果已经开启了飞行模式，则不能通过快捷键去控制wifi的使能
		enabled, err := m.airplane.Enabled().Get(0)
		if err != nil {
			logger.Warningf("get airplane enabled failed, err: %v", err)
			return
		}

		if enabled {
			logger.Debug("airplane mode enabled, can not enable wireless by key")
			return
		}

		// check if allow set wireless
		// and check if Wifi shortcut effected by DDE software
		if m.gsMediaKey.GetBoolean(gsKeyUpperLayerWLAN) && m.wifiControlEnable {
			enabled, err := m.airplane.WifiEnabled().Get(0)
			if err != nil {
				logger.Warningf("get wireless enabled failed, err: %v", err)
				return
			}
			// FIXME:  修复NM WiFi无法恢复bug, 使快捷键能够恢复WiFi问题
			if !enabled {
				if devicesJson, err := m.network.Devices().Get(0); err == nil {
					networkDevices := make(map[string][]*networkDevice)
					json.Unmarshal([]byte(devicesJson), &networkDevices)
					for _, wifiDevice := range networkDevices["wireless"] {
						if wifiDevice.InterfaceFlags == 0 {
							// wifi里有未up的网络接口时尝试重置下wifi网络
							logger.Info("rest wifi, because some link down")
							if err := m.networkmanager.WirelessEnabled().Set(0, false); err != nil {
								logger.Warning(err)
							}
							if err := m.networkmanager.WirelessEnabled().Set(0, true); err != nil {
								logger.Warning(err)
							}
							break
						}
					}
				}
			}
			err = m.airplane.EnableWifi(0, !enabled)
			if err != nil {
				logger.Warningf("set wireless enabled failed, err: %v", err)
				return
			}
		}
	} else if action.Type == shortcuts.ActionTypeShowNumLockOSD {
		var state NumLockState
		if !isWaylandGrabed {
			sessionBus, err := dbus.SessionBus()
			if err != nil {
				logger.Warning(err)
				return
			}
			time.Sleep(200 * time.Millisecond) // + 添加200ms延时，保证在dde-system-daemon中先获取状态；
			sessionObj := sessionBus.Object("org.kde.KWin", "/Xkb")
			var ret int32
			err = sessionObj.Call("org.kde.kwin.Xkb.getLeds", 0).Store(&ret)
			if err != nil {
				logger.Warning(err)
				return
			}
			if 0 == (ret & 0x1) {
				state = NumLockOff
			} else {
				state = NumLockOn
			}
			save := m.gsKeyboard.GetBoolean(gsKeySaveNumLockState)

			switch state {
			case NumLockOn:
				if save {
					m.NumLockState.Set(int32(NumLockOn))
				}
				showOSD("NumLockOn")
			case NumLockOff:
				if save {
					m.NumLockState.Set(int32(NumLockOff))
				}
				showOSD("NumLockOff")
			}
		}
	} else if action.Type == shortcuts.ActionTypeShowCapsLockOSD {
		if !m.shouldShowCapsLockOSD() {
			return
		}

		if !isWaylandGrabed {
			var state CapsLockState
			sessionBus, err := dbus.SessionBus()
			if err != nil {
				logger.Warning(err)
				return
			}
			time.Sleep(200 * time.Millisecond) // + 添加200ms延时，保证在dde-system-daemon中先获取状态；
			sessionObj := sessionBus.Object("org.kde.KWin", "/Xkb")
			var ret int32
			err = sessionObj.Call("org.kde.kwin.Xkb.getLeds", 0).Store(&ret)
			if err != nil {
				logger.Warning(err)
				return
			}
			if 0 == (ret & 0x2) {
				state = CapsLockOff
			} else {
				state = CapsLockOn
			}

			switch state {
			case CapsLockOff:
				showOSD("CapsLockOff")
			case CapsLockOn:
				showOSD("CapsLockOn")
			}
		}
	} else if action.Type == shortcuts.ActionTypeSwitchKbdLayout {
		switch m.switchKbdLayoutState {
		case SKLStateNone:
			m.switchKbdLayoutState = SKLStateWait
			go m.sklWait()

		case SKLStateWait:
			m.switchKbdLayoutState = SKLStateOSDShown
			m.terminateSKLWait()
			showOSD("SwitchLayout")

		case SKLStateOSDShown:
			showOSD("SwitchLayout")
		}
	} else {
		cmd, ok := action.Arg.(shortcuts.ActionCmd)
		if !ok {
			logger.Warning(errTypeAssertionFail)
		} else {
			logger.Debug("Exec cmd:", cmd)
			if action.Type == shortcuts.ActionTypeAudioCtrl {
				// audio-mute/audio-lower-volume/audio-raise-volume
				if m.audioController != nil {
					if err := m.audioController.ExecCmd(cmd); err != nil {
						logger.Warning(m.audioController.Name(), "Controller exec cmd err:", err)
					}
				}
			} else if action.Type == shortcuts.ActionTypeDisplayCtrl {
				// mon-brightness-up/mon-brightness-down
				if m.displayController != nil {
					if err := m.displayController.ExecCmd(cmd); err != nil {
						logger.Warning(m.displayController.Name(), "Controller exec cmd err:", err)
					}
				}
			} else if action.Type == shortcuts.ActionTypeTouchpadCtrl {
				// touchpad-toggle/touchpad-on/touchpad-off
				if m.touchPadController != nil {
					if err := m.touchPadController.ExecCmd(cmd); err != nil {
						logger.Warning(m.touchPadController.Name(), "Controller exec cmd err:", err)
					}
				}
			} else if action.Type == shortcuts.ActionTypeSystemShutdown {

			} else if action.Type == shortcuts.ActionTypeMediaPlayerCtrl {
				// 增蓝牙耳机快捷键的处理
				if cmd == shortcuts.MediaPlayerPlay {
					m.clickNum = m.clickNum + 1
					if m.clickNum == 1 {
						time.AfterFunc(time.Millisecond*600, func() {
							m.playMeadiaByHeadphone()
						})
					}
				} else {
					if m.mediaPlayerController != nil {
						err := m.mediaPlayerController.ExecCmd(cmd)
						if err != nil {
							logger.Warning(m.mediaPlayerController.Name(), "Controller exec cmd err:", err)
						}
					}
				}

			}
		}
	}
}

func getMediaPlayAction(num uint32) shortcuts.ActionCmd {
	var cmd shortcuts.ActionCmd = shortcuts.MediaPlayerPlay
	if num == 2 {
		cmd = shortcuts.MediaPlayerNext
	} else if num == 3 {
		cmd = shortcuts.MediaPlayerPrevious
	} else {
		cmd = shortcuts.MediaPlayerPlay
	}
	return cmd
}

// playMeadiaByHeadphone 处理耳机媒体键事件
// 根据点击次数执行不同的媒体操作
func (m *Manager) playMeadiaByHeadphone() {
	cmd := getMediaPlayAction(m.clickNum)
	m.clickNum = 0
	if m.mediaPlayerController != nil {
		err := m.mediaPlayerController.ExecCmd(cmd)
		if err != nil {
			logger.Warning(m.mediaPlayerController.Name(), "Controller exec cmd err:", err)
		}
	}
}

// handleKeyEventFromShutdownFront 处理来自关机界面的按键事件
// 参数: changKey - 按键标识
func (m *Manager) handleKeyEventFromShutdownFront(changKey string) {
	logger.Debugf("handleKeyEvent %s from ShutdownFront", changKey)
	action := shortcuts.GetAction(changKey)
	if action.Type == shortcuts.ActionTypeSystemShutdown {
		if handler := m.handlers[int(action.Type)]; handler != nil {
			handler(nil)
		} else {
			logger.Warning("handler [system shutdown] is nil")
		}
	}
}

// destroy 销毁管理器资源
// 释放所有占用的资源
func (m *Manager) destroy() {
	err := m.service.StopExport(m)
	if err != nil {
		logger.Warning("stop export failed:", err)
	}

	if m.shortcutManager != nil {
		m.shortcutManager.Destroy()
		m.shortcutManager = nil
	}

	// destroy settings
	if m.gsSystem != nil {
		m.gsSystem.Unref()
		m.gsSystem = nil
	}

	if m.gsMediaKey != nil {
		m.gsMediaKey.Unref()
		m.gsMediaKey = nil
	}

	if m.gsGnomeWM != nil {
		m.gsGnomeWM.Unref()
		m.gsGnomeWM = nil
	}

	if m.audioController != nil {
		m.audioController.Destroy()
		m.audioController = nil
	}

	if m.mediaPlayerController != nil {
		m.mediaPlayerController.Destroy()
		m.mediaPlayerController = nil
	}

	if m.keyboard != nil {
		m.keyboard.RemoveHandler(proxy.RemoveAllHandlers)
		m.keyboard = nil
	}

	if m.keyEvent != nil {
		m.keyEvent.RemoveHandler(proxy.RemoveAllHandlers)
		m.keyEvent = nil
	}

	if m.sessionSigLoop != nil {
		m.sessionSigLoop.Stop()
		m.sessionSigLoop = nil
	}

	if m.systemSigLoop != nil {
		m.systemSigLoop.Stop()
		m.systemSigLoop = nil
	}

	if m.conn != nil {
		m.conn.Close()
		m.conn = nil
	}
}

// handleKeyEvent 处理键盘事件
// 根据按下的快捷键执行相应操作
// handleKeyEvent 处理普通按键事件
// 参数: ev - 键盘事件对象
func (m *Manager) handleKeyEvent(ev *shortcuts.KeyEvent) {
	if !m.checkKeyEventInterval() {
		return
	}
	logger.Debugf("handleKeyEvent ev: %#v", ev)
	action := ev.Shortcut.GetAction()
	shortcutId := ev.Shortcut.GetId()
	logger.Debugf("shortcut id: %s, type: %v, action: %#v",
		shortcutId, ev.Shortcut.GetType(), action)
	if ev.Shortcut.GetType() == shortcuts.ShortcutTypeSystem && m.DisabledSystemShortcutsList.Contains(shortcutId) {
		logger.Warningf("shortcut id: %s is disabled", shortcutId)
		return
	}
	if action == nil {
		logger.Warning("action is nil")
		return
	}
	if len(m.handlers) == 0 {
		logger.Warning("handlers is nil")
		return
	}
	if handler := m.handlers[int(action.Type)]; handler != nil {
		handler(ev)
	} else {
		logger.Warning("handler is nil")
	}
}

// emitShortcutSignal 发送快捷键信号
// 参数:
//   signalName - 信号名称(Added/Deleted/Changed)
//   shortcut - 快捷键对象
func (m *Manager) emitShortcutSignal(signalName string, shortcut shortcuts.Shortcut) {
	logger.Debug("emit DBus signal", signalName, shortcut.GetId(), shortcut.GetType())
	err := m.service.Emit(m, signalName, shortcut.GetId(), shortcut.GetType())
	if err != nil {
		logger.Warning(err)
	}
}

// enableListenGSettingsChanged 启用/禁用GSettings监听
// 参数: val - true启用, false禁用
func (m *Manager) enableListenGSettingsChanged(val bool) {
	m.enableListenGSettings = val
}

// listenGSettingsChanged 监听GSettings变更
// 参数:
//   schema - GSettings schema
//   settings - GSettings对象
//   type0 - 快捷键类型
func (m *Manager) listenGSettingsChanged(schema string, settings *gio.Settings, type0 int32) {
	gsettings.ConnectChanged(schema, "*", func(key string) {
		if !m.enableListenGSettings {
			return
		}

		shortcut := m.shortcutManager.GetByIdType(key, type0)
		if shortcut == nil {
			return
		}

		keystrokes := settings.GetStrv(key)
		keystrokesParsed := shortcuts.ParseKeystrokes(keystrokes)
		logger.Debug("listenGSettingsChanged -> ModifyShortcutKeystrokes key : ", key, keystrokesParsed)
		m.shortcutManager.ModifyShortcutKeystrokes(shortcut, keystrokesParsed)
		if len(keystrokesParsed) == 0 && _useWayland {
			ok, err := shortcuts.SetShortForWayland(shortcut, m.wm)
			if !ok {
				logger.Warning("setShortForWayland failed", err)
			}
		}
		m.emitShortcutSignal(shortcutSignalChanged, shortcut)
	})
}

// listenSystemEnableChanged 监听系统快捷键启用状态变更
func (m *Manager) listenSystemEnableChanged() {
	gsettings.ConnectChanged(gsSchemaSystemEnable, "*", func(key string) {
		if !m.enableListenGSettings {
			return
		}

		if m.shortcutManager.CheckSystem(m.gsSystemPlatform, m.gsSystemEnable, key) {
			m.shortcutManager.AddSystemById(m.gsSystem, m.wm, key)
		} else {
			m.shortcutManager.DelSystemById(key)
		}
	})
}

// listenSystemPlatformChanged 监听系统平台快捷键变更
func (m *Manager) listenSystemPlatformChanged() {
	gsettings.ConnectChanged(gsSchemaSystemPlatform, "*", func(key string) {
		if !m.enableListenGSettings {
			return
		}

		if m.shortcutManager.CheckSystem(m.gsSystemPlatform, m.gsSystemEnable, key) {
			m.shortcutManager.AddSystemById(m.gsSystem, m.wm, key)
		} else {
			m.shortcutManager.DelSystemById(key)
		}
	})
}

func runCommand(cmd string) (string, error) {
	result, err := exec.Command("/bin/sh", "-c", cmd).Output()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(result)), err
}

func checkProRunning(serverName string) bool {
	cmd := `ps ux | awk '/` + serverName + `/ && !/awk/ {print $2}'`
	pid, err := runCommand(cmd)
	if err != nil {
		logger.Warning(err)
		return false
	}
	return pid != ""
}

// execCmd 执行命令
// 参数:
//   cmd - 要执行的命令
//   viaStartdde - 是否通过startdde执行
// 返回值: 错误信息
func (m *Manager) execCmd(cmd string, viaStartdde bool) error {
	if cmd == "" {
		logger.Debug("cmd is empty")
		return nil
	}
	if strings.HasPrefix(cmd, "dbus-send ") || !viaStartdde {
		logger.Debug("run cmd:", cmd)
		// #nosec G204
		return exec.Command("/bin/sh", "-c", cmd).Run()
	}

	logger.Debug("startdde run cmd:", cmd)
	return m.startManager.RunCommand(0, "/bin/sh", []string{"-c", cmd})
}

// execCmdWithOutput 执行命令并返回输出结果
// execCmdWithOutput 执行命令并获取输出
// 参数:
//   cmd - 要执行的命令
//   viaStartdde - 是否通过startdde执行
// 返回值: 命令输出和错误信息
func (m *Manager) execCmdWithOutput(cmd string, viaStartdde bool) (string, error) {
	if cmd == "" {
		logger.Debug("cmd is empty")
		return "", nil
	}
	if strings.HasPrefix(cmd, "dbus-send ") || !viaStartdde {
		logger.Debug("run cmd with output:", cmd)
		// #nosec G204
		result, err := exec.Command("/bin/sh", "-c", cmd).Output()
		if err != nil {
			return "", err
		}
		return strings.TrimSpace(string(result)), nil
	}

	logger.Debug("startdde run cmd with output:", cmd)
	// 对于startdde的情况，我们无法直接获取输出，所以返回空字符串
	return "", m.startManager.RunCommand(0, "/bin/sh", []string{"-c", cmd})
}

// handleCheckCamera 处理摄像头检查
// 检查摄像头进程状态并执行相应操作
// 返回值: 错误信息
func (m *Manager) handleCheckCamera() error {
	cmd := "deepin-camera"
	if checkProRunning(cmd) {
		cmd = "killall deepin-camera"
	}
	return m.startManager.RunCommand(0, "/bin/sh", []string{"-c", cmd})
}

// runDesktopFile 运行桌面文件
// 参数: desktop - 桌面文件路径
// 返回值: 错误信息
func (m *Manager) runDesktopFile(desktop string) error {
	return m.startManager.LaunchApp(0, desktop, 0, []string{})
}

// eliminateKeystrokeConflict 解决按键冲突
// 自动解决检测到的快捷键冲突
func (m *Manager) eliminateKeystrokeConflict() {
	for _, ks := range m.shortcutManager.ConflictingKeystrokes {
		shortcut := ks.Shortcut
		logger.Infof("eliminate conflict shortcut: %s keystroke: %s",
			ks.Shortcut.GetUid(), ks)
		err := m.DeleteShortcutKeystroke(shortcut.GetId(), shortcut.GetType(), ks.String())
		if err != nil {
			logger.Warning("delete shortcut keystroke failed:", err)
		}
	}

	m.shortcutManager.ConflictingKeystrokes = nil
	m.shortcutManager.EliminateConflictDone = true
}

// syncMagnifierStatus 同步放大镜状态
// 根据配置启用或禁用KWin的放大镜效果
// syncMagnifierStatus 同步放大镜状态
// 根据配置启用或禁用KWin的放大镜效果
func (m *Manager) syncMagnifierStatus() {
	// 检查当前放大镜是否已加载
	cmd := "gdbus call --session --dest org.kde.KWin --object-path /Effects --method org.kde.kwin.Effects.isEffectLoaded zoom"
	output, err := m.execCmdWithOutput(cmd, false)
	if err != nil {
		logger.Warning(err)
	}

	var loadzoom bool
	outputLower := strings.ToLower(output)
	if strings.Contains(outputLower, "false") {
		loadzoom = false
	} else if strings.Contains(outputLower, "true") {
		loadzoom = true
	}

	// 根据配置和当前状态决定是否需要启用或禁用放大镜
	if m.magnifierEnabled && !loadzoom {
		// 配置启用但当前未加载，需要启用放大镜效果
		cmd := "gdbus call --session --dest org.kde.KWin --object-path /Effects --method org.kde.kwin.Effects.loadEffect zoom"
		output, err := m.execCmdWithOutput(cmd, false)
		if err != nil {
			logger.Warning("Failed to load zoom effect:", err)
		} else {
			logger.Info("Successfully loaded zoom effect, output:", output)
		}
	} else if !m.magnifierEnabled && loadzoom {
		// 配置禁用但当前已加载，需要禁用放大镜效果
		cmd := "gdbus call --session --dest org.kde.KWin --object-path /Effects --method org.kde.kwin.Effects.unloadEffect zoom"
		output, err := m.execCmdWithOutput(cmd, false)
		if err != nil {
			logger.Warning("Failed to unload zoom effect:", err)
		} else {
			logger.Info("Successfully unloaded zoom effect, output:", output)
		}
	} else {
		// 状态已经匹配，无需操作
		logger.Debug("Zoom effect status already matches configuration, magnifierEnabled:", m.magnifierEnabled, "loadzoom:", loadzoom)
	}
}
