3#include <QLoggingCategory>
4#include <QStandardPaths>
5#include <QTemporaryDir>
6#include "DesktopShortcuts.h"
8static Q_LOGGING_CATEGORY(log,
"Plugin.Hook.Desktop.Shortcut")
13 m_desktopEnv = detectDesktopEnvironment();
14 qDebug(log) <<
"Desktop environment:" << m_desktopEnv;
17 QString tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
18 m_gnomeBackupPath = tempDir +
"/gnome_shortcuts_backup.conf";
19 m_kdeBackupPath = tempDir +
"/kde_shortcuts_backup.conf";
22CDesktopShortcutManager::~CDesktopShortcutManager()
25 if (m_shortcutsDisabled) {
26 qWarning(log) <<
"Shortcuts is disabled, restoring ......";
27 restoreAllShortcuts();
31QString CDesktopShortcutManager::detectDesktopEnvironment()
33 QString xdgDesktop = qEnvironmentVariable(
"XDG_CURRENT_DESKTOP",
"").toLower();
34 QString desktopSession = qEnvironmentVariable(
"DESKTOP_SESSION",
"").toLower();
35 QString gdmSession = qEnvironmentVariable(
"GDMSESSION",
"").toLower();
37 if (xdgDesktop.contains(
"gnome") || desktopSession.contains(
"gnome") || gdmSession.contains(
"gnome")) {
39 }
else if (xdgDesktop.contains(
"kde") || desktopSession.contains(
"plasma") || gdmSession.contains(
"plasma")) {
41 }
else if (xdgDesktop.contains(
"xfce") || desktopSession.contains(
"xfce")) {
43 }
else if (xdgDesktop.contains(
"mate") || desktopSession.contains(
"mate")) {
45 }
else if (xdgDesktop.contains(
"cinnamon") || desktopSession.contains(
"cinnamon")) {
47 }
else if (xdgDesktop.contains(
"pantheon") || desktopSession.contains(
"pantheon")) {
54bool CDesktopShortcutManager::disableAllShortcuts()
56 if (m_shortcutsDisabled) {
57 qDebug(log) <<
"Shortcuts is disabled";
61 m_shortcutsDisabled =
true;
64#if defined(Q_OS_LINUX)
65 if (m_desktopEnv ==
"GNOME") {
66 success = disableGNOMEShortcuts();
67 }
else if (m_desktopEnv ==
"KDE") {
68 success = disableKDEShortcuts();
69 }
else if (m_desktopEnv ==
"XFCE") {
71 qWarning(log) <<
"XFCE Shortcut key disabling is not yet implemented";
73 qWarning(log) <<
"Unsupported desktop environment:" << m_desktopEnv;
79 qDebug(log) <<
"All desktop shortcuts have been disabled";
85bool CDesktopShortcutManager::restoreAllShortcuts()
87 if (!m_shortcutsDisabled) {
88 qDebug(log) <<
"The shortcut key is not disabled, no need to restore.";
92 m_shortcutsDisabled =
false;
95#if defined(Q_OS_LINUX)
96 if (m_desktopEnv ==
"GNOME") {
97 success = restoreGNOMEShortcuts();
99 }
else if (m_desktopEnv ==
"KDE") {
100 success = restoreKDEShortcuts();
102 qWarning(log) <<
"Unsupported desktop environment:" << m_desktopEnv;
108 qDebug(log) <<
"All desktop shortcuts have been restored";
114#if defined(Q_OS_LINUX)
116bool CDesktopShortcutManager::disableGNOMEShortcuts()
118 qDebug(log) <<
"Disable GNOME shortcuts ......";
121 backupGNOMESettings();
124 struct GNOMESetting {
130 QVector<GNOMESetting> disabledSettings = {
132 {
"org.gnome.mutter",
"overlay-key",
"''"},
135 {
"org.gnome.desktop.wm.keybindings",
"close",
"['']"},
136 {
"org.gnome.desktop.wm.keybindings",
"minimize",
"['']"},
137 {
"org.gnome.desktop.wm.keybindings",
"maximize",
"['']"},
138 {
"org.gnome.desktop.wm.keybindings",
"unmaximize",
"['']"},
139 {
"org.gnome.desktop.wm.keybindings",
"begin-move",
"['']"},
140 {
"org.gnome.desktop.wm.keybindings",
"begin-resize",
"['']"},
141 {
"org.gnome.desktop.wm.keybindings",
"toggle-fullscreen",
"['']"},
142 {
"org.gnome.desktop.wm.keybindings",
"toggle-maximized",
"['']"},
143 {
"org.gnome.desktop.wm.keybindings",
"show-desktop",
"['']"},
146 {
"org.gnome.desktop.wm.keybindings",
"switch-to-workspace-left",
"['']"},
147 {
"org.gnome.desktop.wm.keybindings",
"switch-to-workspace-right",
"['']"},
148 {
"org.gnome.desktop.wm.keybindings",
"switch-to-workspace-up",
"['']"},
149 {
"org.gnome.desktop.wm.keybindings",
"switch-to-workspace-down",
"['']"},
150 {
"org.gnome.desktop.wm.keybindings",
"switch-to-workspace-last",
"['']"},
151 {
"org.gnome.desktop.wm.keybindings",
"move-to-workspace-left",
"['']"},
152 {
"org.gnome.desktop.wm.keybindings",
"move-to-workspace-right",
"['']"},
153 {
"org.gnome.desktop.wm.keybindings",
"move-to-workspace-up",
"['']"},
154 {
"org.gnome.desktop.wm.keybindings",
"move-to-workspace-down",
"['']"},
156 {
"org.gnome.desktop.wm.keybindings",
"switch-input-source",
"['']"},
157 {
"org.gnome.desktop.wm.keybindings",
"switch-input-source-backward",
"['']"},
160 {
"org.gnome.shell.keybindings",
"toggle-application-view",
"['']"},
161 {
"org.gnome.shell.keybindings",
"toggle-message-tray",
"['']"},
162 {
"org.gnome.shell.keybindings",
"focus-active-notification",
"['']"},
163 {
"org.gnome.shell.keybindings",
"toggle-overview",
"['']"},
165 {
"org.gnome.shell.keybindings",
"screenshot",
"['']"},
166 {
"org.gnome.shell.keybindings",
"screenshot-window",
"['']"},
167 {
"org.gnome.shell.keybindings",
"show-screenshot-ui",
"['']"},
168 {
"org.gnome.shell.keybindings",
"show-screen-recording-ui",
"['']"},
171 {
"org.gnome.settings-daemon.plugins.media-keys",
"home",
"['']"},
172 {
"org.gnome.settings-daemon.plugins.media-keys",
"email",
"['']"},
173 {
"org.gnome.settings-daemon.plugins.media-keys",
"www",
"['']"},
174 {
"org.gnome.settings-daemon.plugins.media-keys",
"calculator",
"['']"},
175 {
"org.gnome.settings-daemon.plugins.media-keys",
"screensaver",
"['']"},
176 {
"org.gnome.settings-daemon.plugins.media-keys",
"media",
"['']"},
177 {
"org.gnome.settings-daemon.plugins.media-keys",
"volume-up",
"['']"},
178 {
"org.gnome.settings-daemon.plugins.media-keys",
"volume-down",
"['']"},
179 {
"org.gnome.settings-daemon.plugins.media-keys",
"volume-mute",
"['']"},
180 {
"org.gnome.settings-daemon.plugins.media-keys",
"next",
"['']"},
181 {
"org.gnome.settings-daemon.plugins.media-keys",
"previous",
"['']"},
182 {
"org.gnome.settings-daemon.plugins.media-keys",
"play",
"['']"},
183 {
"org.gnome.settings-daemon.plugins.media-keys",
"pause",
"['']"},
184 {
"org.gnome.settings-daemon.plugins.media-keys",
"stop",
"['']"},
187 {
"org.gnome.settings-daemon.plugins.media-keys",
"custom-keybindings",
"['']"},
190 {
"org.gnome.desktop.wm.keybindings",
"panel-main-menu",
"['']"},
191 {
"org.gnome.desktop.wm.keybindings",
"panel-run-dialog",
"['']"},
192 {
"org.gnome.desktop.wm.keybindings",
"switch-applications",
"['']"},
193 {
"org.gnome.desktop.wm.keybindings",
"switch-applications-backward",
"['']"},
194 {
"org.gnome.desktop.wm.keybindings",
"switch-windows",
"['']"},
195 {
"org.gnome.desktop.wm.keybindings",
"switch-windows-backward",
"['']"},
196 {
"org.gnome.desktop.wm.keybindings",
"switch-group",
"['']"},
197 {
"org.gnome.desktop.wm.keybindings",
"switch-group-backward",
"['']"}
202 bool allSuccess =
true;
203 int successCount = 0;
204 int totalCount = disabledSettings.size();
206 for (
const GNOMESetting &setting : disabledSettings) {
207 if (runCommand(
"gsettings", {
"set", setting.schema, setting.key, setting.value})) {
209 qDebug(log) <<
"Disabled:" << setting.schema << setting.key;
211 qCritical(log) <<
"Disable fail:" << setting.schema << setting.key;
216 qDebug(log) << QString(
"GNOME is disabled: %1/%2 Success").arg(successCount).arg(totalCount);
231bool CDesktopShortcutManager::restoreGNOMEShortcuts()
233 qDebug(log) <<
"Restore GNOME shortcuts ......";
235 if (m_gnomeSettings.isEmpty()) {
236 qWarning(log) <<
"No backup settings are available to restore";
240 bool allSuccess =
true;
241 int successCount = 0;
242 int totalCount = m_gnomeSettings.size();
245 for (
auto it = m_gnomeSettings.begin(); it != m_gnomeSettings.end(); ++it) {
246 QStringList parts = it.key().split(
"|");
247 if (parts.size() == 2) {
248 QString schema = parts[0];
249 QString key = parts[1];
250 QString value = it.value().toString();
252 if (runCommand(
"gsettings", {
"set", schema, key, value})) {
254 qDebug(log) <<
"Restored:" << schema << key << value;
256 qCritical(log) <<
"Restore failed:" << schema << key << value;
262 qDebug(log) << QString(
"GNOME shortcut restoration completed: %1/%2 successful").arg(successCount).arg(totalCount);
277bool CDesktopShortcutManager::resetGNOMEShortcuts()
279 qDebug(log) <<
"Reset GNOME shortcuts ......";
281 if (m_gnomeSettings.isEmpty()) {
282 qWarning(log) <<
"No backup settings are available to reset";
286 bool allSuccess =
true;
287 int successCount = 0;
288 int totalCount = m_gnomeSettings.size();
291 for (
auto it = m_gnomeSettings.begin(); it != m_gnomeSettings.end(); ++it) {
292 QStringList parts = it.key().split(
"|");
293 if (parts.size() == 2) {
294 QString schema = parts[0];
295 QString key = parts[1];
297 if (runCommand(
"gsettings", {
"reset", schema, key})) {
299 qDebug(log) <<
"Reset:" << schema << key;
301 qCritical(log) <<
"Reset failed:" << schema << key;
307 qDebug(log) << QString(
"GNOME shortcut reset completed: %1/%2 successful").arg(successCount).arg(totalCount);
322void CDesktopShortcutManager::backupGNOMESettings()
324 qDebug(log) <<
"Backup GNOME settings ......";
326 m_gnomeSettings.clear();
329 QVector<QString> schemas = {
330 "org.gnome.desktop.wm.keybindings",
331 "org.gnome.shell.keybindings",
332 "org.gnome.settings-daemon.plugins.media-keys",
336 for (
const QString &schema : schemas) {
338 QString output = getCommandOutput(
"gsettings", {
"list-keys", schema});
339 if (output.isEmpty()) {
340 qWarning(log) <<
"Unable to retrieve the schema key:" << schema;
344 QStringList keys = output.split(
'\n', Qt::SkipEmptyParts);
345 qDebug(log) << QString(
"Schema %1 has %2 keys").arg(schema).arg(keys.size());
347 foreach (
const QString &key, keys) {
348 QString value = getCommandOutput(
"gsettings", {
"get", schema, key}).trimmed();
349 if (!value.isEmpty()) {
351 QString settingKey = schema +
"|" + key;
352 m_gnomeSettings[settingKey] = value;
353 qDebug(log) <<
"Backup:" << settingKey <<
"=" << value;
355 qWarning(log) <<
"Backup fail:" << schema +
"|" + key;
360 qDebug(log) << QString(
"GNOME settings backup completed, a total of %1 settings were backed up").arg(m_gnomeSettings.size());
364bool CDesktopShortcutManager::disableKDEShortcuts()
366 qDebug(log) <<
"Disable KDE shortcuts ......";
374 QStringList kwinKeys = {
375 "Window Close",
"Window Maximize",
"Window Minimize",
376 "Window Fullscreen",
"Window to Desktop 1",
"Window to Desktop 2",
377 "Switch to Desktop 1",
"Switch to Desktop 2",
"Expose"
380 for (
const QString &key : kwinKeys) {
381 if (!runCommand(
"kwriteconfig5", {
"--file",
"kglobalshortcutsrc",
"--key", key,
"none"})) {
387 if (!runCommand(
"kwriteconfig5", {
"--file",
"kwinrc",
"--group",
"ModifierOnlyShortcuts",
"--key",
"Meta",
""})) {
397bool CDesktopShortcutManager::restoreKDEShortcuts()
399 qDebug(log) <<
"Restore KDE shortcuts ......";
402 QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
404 if (!m_kdeSettings.isEmpty()) {
405 for (
auto it = m_kdeSettings.begin(); it != m_kdeSettings.end(); ++it) {
406 QStringList parts = it.key().split(
"|");
407 if (parts.size() == 3) {
408 QString file = parts[0];
409 QString group = parts[1];
410 QString key = parts[2];
412 if (!runCommand(
"kwriteconfig5", {
"--file", file,
"--group", group,
"--key", key, it.value()})) {
413 qWarning(log) <<
"Failed to restore KDE setting:" << file << group << key;
425void CDesktopShortcutManager::backupKDESettings()
427 qDebug(log) <<
"Backup KDE settings ......";
429 QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
432 QStringList configFiles = {
433 "kglobalshortcutsrc",
438 for (
const QString &file : configFiles) {
439 QString sourcePath = configDir +
"/" + file;
440 if (QFile::exists(sourcePath)) {
441 backupFile(sourcePath, m_kdeBackupPath +
"_" + file);
446 QStringList settings = {
447 "kglobalshortcutsrc|kwin|Window Close",
448 "kglobalshortcutsrc|kwin|Window Maximize",
449 "kglobalshortcutsrc|kwin|Window Minimize",
450 "kwinrc|ModifierOnlyShortcuts|Meta"
453 for (
const QString &setting : settings) {
454 QStringList parts = setting.split(
"|");
455 if (parts.size() == 3) {
456 QString value = getCommandOutput(
"kreadconfig5", {
"--file", parts[0],
"--group", parts[1],
"--key", parts[2]});
457 m_kdeSettings[setting] = value.trimmed();
464bool CDesktopShortcutManager::runCommand(
const QString &program,
const QStringList &args,
int timeout)
467 process.setProgram(program);
469 process.setArguments(args);
475 if (!process.waitForFinished(timeout)) {
476 qWarning(log) <<
"Command timeout:" << program << args;
480 if (process.exitCode() != 0) {
481 QString errorOutput = process.readAllStandardError();
482 qWarning(log) <<
"Command failed:" << program << args
483 <<
"exit code:" << process.exitCode()
484 <<
"failed:" << errorOutput;
488 QString standardOutput = process.readAllStandardOutput();
489 if (!standardOutput.isEmpty()) {
490 qDebug(log) <<
"Command output:" << standardOutput.trimmed();
496QString CDesktopShortcutManager::getCommandOutput(
const QString &program,
const QStringList &args)
499 process.start(program, args);
501 if (process.waitForFinished(5000) && process.exitCode() == 0) {
502 return process.readAllStandardOutput();
508bool CDesktopShortcutManager::backupFile(
const QString &sourcePath,
const QString &backupPath)
510 return QFile::copy(sourcePath, backupPath);
513bool CDesktopShortcutManager::restoreFile(
const QString &backupPath,
const QString &targetPath)
515 if (QFile::exists(targetPath)) {
516 QFile::remove(targetPath);
518 return QFile::copy(backupPath, targetPath);