3#include <QRegularExpression>
4#include <QLoggingCategory>
5#include <QStandardPaths>
6#include <QTemporaryDir>
7#include "DesktopShortcuts.h"
9static Q_LOGGING_CATEGORY(log,
"Plugin.Hook.Desktop.Shortcut")
14 qDebug(log) << Q_FUNC_INFO;
15 m_desktopEnv = detectDesktopEnvironment();
16 qDebug(log) <<
"Desktop environment:" << m_desktopEnv;
19 QString tempDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
20 m_gnomeBackupPath = tempDir +
"/gnome_shortcuts_backup.conf";
21 m_kdeBackupPath = tempDir +
"/kde_shortcuts_backup.conf";
24CDesktopShortcutManager::~CDesktopShortcutManager()
26 qDebug(log) << Q_FUNC_INFO;
28 if (m_shortcutsDisabled) {
29 qWarning(log) <<
"Shortcuts is disabled, restoring ......";
30 restoreAllShortcuts();
34QString CDesktopShortcutManager::detectDesktopEnvironment()
36 QString xdgDesktop = qEnvironmentVariable(
"XDG_CURRENT_DESKTOP",
"").toLower();
37 QString desktopSession = qEnvironmentVariable(
"DESKTOP_SESSION",
"").toLower();
38 QString gdmSession = qEnvironmentVariable(
"GDMSESSION",
"").toLower();
40 if (xdgDesktop.contains(
"gnome") || desktopSession.contains(
"gnome") || gdmSession.contains(
"gnome")) {
42 }
else if (xdgDesktop.contains(
"kde") || desktopSession.contains(
"plasma") || gdmSession.contains(
"plasma")) {
44 }
else if (xdgDesktop.contains(
"xfce") || desktopSession.contains(
"xfce")) {
46 }
else if (xdgDesktop.contains(
"mate") || desktopSession.contains(
"mate")) {
48 }
else if (xdgDesktop.contains(
"cinnamon") || desktopSession.contains(
"cinnamon")) {
50 }
else if (xdgDesktop.contains(
"pantheon") || desktopSession.contains(
"pantheon")) {
57bool CDesktopShortcutManager::disableAllShortcuts()
59 if (m_shortcutsDisabled) {
60 qDebug(log) <<
"Shortcuts is disabled";
64 m_shortcutsDisabled =
true;
67#if defined(Q_OS_LINUX)
68 if(
"GNOME" == m_desktopEnv ||
"Cinnamon" == m_desktopEnv ||
"MATE" == m_desktopEnv) {
69 success = disableGNOMEShortcuts();
70 }
else if (m_desktopEnv ==
"KDE") {
71 success = disableKDEShortcuts();
72 }
else if (m_desktopEnv ==
"XFCE") {
73 success = disableXFCEShortcuts();
75 qWarning(log) <<
"Unsupported desktop environment:" << m_desktopEnv;
81 qInfo(log) <<
"All desktop shortcuts have been disabled";
87bool CDesktopShortcutManager::restoreAllShortcuts()
89 if (!m_shortcutsDisabled) {
90 qDebug(log) <<
"The shortcut key is not disabled, no need to restore.";
94 m_shortcutsDisabled =
false;
97#if defined(Q_OS_LINUX)
98 if (m_desktopEnv ==
"GNOME") {
99 success = restoreGNOMEShortcuts();
101 }
else if (m_desktopEnv ==
"KDE") {
102 success = restoreKDEShortcuts();
103 }
else if (m_desktopEnv ==
"XFCE") {
104 success = restoreXFCEShortcuts();
106 qWarning(log) <<
"Unsupported desktop environment:" << m_desktopEnv;
112 qInfo(log) <<
"All desktop shortcuts have been restored";
118#if defined(Q_OS_LINUX)
125bool CDesktopShortcutManager::disableGNOMEShortcuts()
127 qDebug(log) <<
"Disable GNOME shortcuts ......";
130 backupGNOMESettings();
133 bool bAllSuccess =
true;
134 int nSuccessCount = 0;
136 for (
auto it = m_gnomeSettings.begin(); it != m_gnomeSettings.end(); ++it) {
137 QString szSchema = it.key();
138 auto keys = it.value();
139 for(
auto itKey = keys.begin(); itKey != keys.end(); itKey++) {
141 QString szKey = itKey.key();
143 if(
"org.gnome.mutter" == szSchema &&
"overlay-key" == szKey) {
144 if (runCommand(
"gsettings", {
"set", szSchema, szKey,
"''"})) {
148 qCritical(log) <<
"Disabled failed:" << szSchema +
": " + szKey +
" = " +
"''";
154 QString szValue = itKey.value();
159 if(!szValue.contains(QRegularExpression(
"\\[.*\\]")))
continue;
192 szValue.replace(QRegularExpression(
"\\[([^\\]]*)\\]"),
"[]");
193 if (runCommand(
"gsettings", {
"set", szSchema, szKey, szValue})) {
197 qCritical(log) <<
"Disabled failed:" << szSchema +
": " + szKey +
" = " + szValue;
203 qDebug(log) << QString(
"GNOME is disabled: %1/%2 Success").arg(nSuccessCount).arg(nTotalCount);
212bool CDesktopShortcutManager::restoreGNOMEShortcuts()
214 qDebug(log) <<
"Restore GNOME shortcuts ......";
216 if (m_gnomeSettings.isEmpty()) {
217 qWarning(log) <<
"No backup settings are available to restore";
221 bool bAllSuccess =
true;
222 int nSuccessCount = 0;
226 for (
auto it = m_gnomeSettings.begin(); it != m_gnomeSettings.end(); ++it) {
227 QString szSchema = it.key();
228 auto keys = it.value();
229 for(
auto itKey = keys.begin(); itKey != keys.end(); itKey++) {
231 QString szKey = itKey.key();
232 QString szValue = itKey.value();
233 if (runCommand(
"gsettings", {
"set", szSchema, szKey, szValue})) {
237 qCritical(log) <<
"Restore failed:" << szSchema +
": " + szKey +
" = " + szValue;
243 qDebug(log) << QString(
"GNOME shortcut restoration completed: %1/%2 successful").arg(nSuccessCount).arg(nTotalCount);
262bool CDesktopShortcutManager::resetGNOMEShortcuts()
264 qDebug(log) <<
"Reset GNOME shortcuts ......";
266 if (m_gnomeSettings.isEmpty()) {
267 qWarning(log) <<
"No backup settings are available to reset";
271 bool bAllSuccess =
true;
272 int nSuccessCount = 0;
276 for (
auto it = m_gnomeSettings.begin(); it != m_gnomeSettings.end(); ++it) {
277 QString szSchema = it.key();
278 auto keys = it.value();
279 for(
auto itKey = keys.begin(); itKey != keys.end(); itKey++) {
281 QString szKey = itKey.key();
282 if (runCommand(
"gsettings", {
"reset", szSchema, szKey})) {
286 qCritical(log) <<
"Reset failed:" << szSchema +
": " + szKey;
292 qDebug(log) << QString(
"GNOME shortcut reset completed: %1/%2 successful").arg(nSuccessCount).arg(nTotalCount);
311int CDesktopShortcutManager::backupGNOMESettings()
315 qDebug(log) <<
"Backup GNOME settings ......";
317 m_gnomeSettings.clear();
319 struct GNOMESetting {
323 QVector<GNOMESetting> vKeys = {
325 {
"org.gnome.mutter",
"overlay-key"}
327 foreach (
auto setting, vKeys) {
328 QMap<QString, QString> mKey;
329 QString value = getCommandOutput(
"gsettings", {
"get", setting.schema, setting.key}).trimmed();
330 if (!value.isEmpty()) {
332 mKey.insert(setting.key, value);
335 qWarning(log) <<
"Backup fail:" << setting.schema +
": " + setting.key;
338 m_gnomeSettings.insert(setting.schema, mKey);
341 QVector<QString> vSchemas = {
343 "org.gnome.settings-daemon.plugins.media-keys"
348 QString szSchemas = getCommandOutput(
"gsettings", {
"list-schemas"});
350 QStringList lstSchemas = szSchemas.split(
'\n', Qt::SkipEmptyParts);
351 foreach (
const QString &schema, lstSchemas) {
352 if(!(schema.contains(
"keybindings") || schema.contains(
"media-keys")))
354 vSchemas.append(schema);
357 foreach (
const QString &schema, vSchemas) {
359 QString output = getCommandOutput(
"gsettings", {
"list-keys", schema});
360 if (output.isEmpty()) {
361 qWarning(log) <<
"Unable to retrieve the schema key:" << schema;
365 QMap<QString, QString> vKey;
366 QStringList keys = output.split(
'\n', Qt::SkipEmptyParts);
368 foreach (
const QString &key, keys) {
369 QString value = getCommandOutput(
"gsettings", {
"get", schema, key}).trimmed();
370 if (!value.isEmpty()) {
372 vKey.insert(key, value);
375 qWarning(log) <<
"Backup fail:" << schema +
": " + key;
379 m_gnomeSettings.insert(schema, vKey);
382 qDebug(log) << QString(
"GNOME settings backup completed, a total of %1 settings were backed up").arg(nTotalCount);
390bool CDesktopShortcutManager::disableKDEShortcuts()
392 qDebug(log) <<
"Disable KDE shortcuts ......";
400 QStringList kwinKeys = {
401 "Window Close",
"Window Maximize",
"Window Minimize",
402 "Window Fullscreen",
"Window to Desktop 1",
"Window to Desktop 2",
403 "Switch to Desktop 1",
"Switch to Desktop 2",
"Expose"
406 for (
const QString &key : kwinKeys) {
407 if (!runCommand(
"kwriteconfig5", {
"--file",
"kglobalshortcutsrc",
"--key", key,
"none"})) {
413 if (!runCommand(
"kwriteconfig5", {
"--file",
"kwinrc",
"--group",
"ModifierOnlyShortcuts",
"--key",
"Meta",
""})) {
427bool CDesktopShortcutManager::restoreKDEShortcuts()
429 qDebug(log) <<
"Restore KDE shortcuts ......";
432 QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
434 if (!m_kdeSettings.isEmpty()) {
435 for (
auto it = m_kdeSettings.begin(); it != m_kdeSettings.end(); ++it) {
436 QStringList parts = it.key().split(
"|");
437 if (parts.size() == 3) {
438 QString file = parts[0];
439 QString group = parts[1];
440 QString key = parts[2];
442 if (!runCommand(
"kwriteconfig5", {
"--file", file,
"--group", group,
"--key", key, it.value()})) {
443 qWarning(log) <<
"Failed to restore KDE setting:" << file << group << key;
466int CDesktopShortcutManager::backupKDESettings()
469 qDebug(log) <<
"Backup KDE settings ......";
471 QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
474 QStringList configFiles = {
475 "kglobalshortcutsrc",
480 for (
const QString &file : configFiles) {
481 QString sourcePath = configDir +
"/" + file;
482 if (QFile::exists(sourcePath)) {
483 backupFile(sourcePath, m_kdeBackupPath +
"_" + file);
488 QStringList settings = {
489 "kglobalshortcutsrc|kwin|Window Close",
490 "kglobalshortcutsrc|kwin|Window Maximize",
491 "kglobalshortcutsrc|kwin|Window Minimize",
492 "kwinrc|ModifierOnlyShortcuts|Meta"
495 for (
const QString &setting : settings) {
496 QStringList parts = setting.split(
"|");
497 if (parts.size() == 3) {
498 QString value = getCommandOutput(
"kreadconfig5", {
"--file", parts[0],
"--group", parts[1],
"--key", parts[2]});
499 m_kdeSettings[setting] = value.trimmed();
510bool CDesktopShortcutManager::disableXFCEShortcuts()
512 qDebug(log) <<
"Disable XFCE settings ......";
515 backupXFCEShortcuts();
517 if (m_xfceSettings.isEmpty()) {
518 qWarning(log) <<
"No XFCE settings to disable";
522 bool bAllSuccess =
true;
523 int nSuccessCount = 0;
527 for (
auto it = m_xfceSettings.begin(); it != m_xfceSettings.end(); ++it) {
529 QString
property = it.key();
530 QString value = it.value();
547 args <<
"--channel" <<
"xfce4-keyboard-shortcuts";
551 args <<
"--reset" <<
"--property" << property;
553 if (runCommand(
"xfconf-query", args)) {
555 qDebug(log) <<
"Disabled:" <<
property <<
"=" << value;
559 setArgs <<
"--channel" <<
"xfce4-keyboard-shortcuts"
560 <<
"--property" <<
property
563 if (runCommand(
"xfconf-query", setArgs)) {
565 qDebug(log) <<
"Disabled (set empty):" <<
property <<
"=" << value;
567 qCritical(log) <<
"Disabled failed:" << property;
573 qDebug(log) << QString(
"XFCE shortcuts disabled: %1/%2 success").arg(nSuccessCount).arg(nTotalCount);
610int CDesktopShortcutManager::backupXFCEShortcuts()
613 qDebug(log) <<
"Backup XFCE settings ......";
616 m_xfceSettings.clear();
620 QString output = getCommandOutput(
"xfconf-query", {
"--channel",
"xfce4-keyboard-shortcuts",
"-lv"});
621 if (output.isEmpty()) {
622 qWarning(log) <<
"Unable to get XFCE keyboard shortcuts configuration";
631 QStringList lines = output.split(
'\n', Qt::SkipEmptyParts);
633 foreach(
const QString &line, lines) {
636 QRegularExpression regex(
"^(\"[^\"]*\"|/[^\\s]+)\\s+(.*)$");
637 QRegularExpressionMatch match = regex.match(line);
639 if (match.hasMatch()) {
640 QString
property = match.captured(1);
642 if (property.startsWith(
'"') && property.endsWith(
'"')) {
643 property =
property.mid(1, property.length() - 2);
646 QString value = match.captured(2).trimmed();
649 m_xfceSettings[property] = value;
651 qDebug(log) <<
"Backup:" <<
property <<
" = " << value;
654 qDebug(log) <<
"Skipping line (unable to parse):" << line;
675 qDebug(log) << QString(
"XFCE settings backup completed, a total of %1 settings were backed up").arg(nCount);
683bool CDesktopShortcutManager::restoreXFCEShortcuts()
685 qDebug(log) <<
"Restore XFCE settings ......";
687 if (m_xfceSettings.isEmpty()) {
688 qWarning(log) <<
"No backup settings are available to restore";
692 bool bAllSuccess =
true;
693 int nSuccessCount = 0;
697 for (
auto it = m_xfceSettings.begin(); it != m_xfceSettings.end(); ++it) {
699 QString
property = it.key();
700 QString value = it.value();
712 args <<
"--channel" <<
"xfce4-keyboard-shortcuts"
713 <<
"--property" <<
property
716 if (runCommand(
"xfconf-query", args)) {
718 qDebug(log) <<
"Restored:" <<
property <<
"=" << value;
720 qCritical(log) <<
"Restore failed:" <<
property <<
"=" << value;
725 qDebug(log) << QString(
"XFCE shortcuts restored: %1/%2 success").arg(nSuccessCount).arg(nTotalCount);
735bool CDesktopShortcutManager::runCommand(
const QString &program,
const QStringList &args,
int timeout)
738 process.setProgram(program);
740 process.setArguments(args);
746 if (!process.waitForFinished(timeout)) {
747 qWarning(log) <<
"Command timeout:" << program << args;
751 if (process.exitCode() != 0) {
752 QString errorOutput = process.readAllStandardError();
753 qWarning(log) <<
"Command failed:" << program << args
754 <<
"exit code:" << process.exitCode()
755 <<
"failed:" << errorOutput;
759 QString standardOutput = process.readAllStandardOutput();
760 if (!standardOutput.isEmpty()) {
761 qDebug(log) <<
"Command output:" << standardOutput.trimmed();
767QString CDesktopShortcutManager::getCommandOutput(
const QString &program,
const QStringList &args)
770 process.start(program, args);
772 if (process.waitForFinished(5000) && process.exitCode() == 0) {
773 return process.readAllStandardOutput();
779bool CDesktopShortcutManager::backupFile(
const QString &sourcePath,
const QString &backupPath)
781 return QFile::copy(sourcePath, backupPath);
784bool CDesktopShortcutManager::restoreFile(
const QString &backupPath,
const QString &targetPath)
786 if (QFile::exists(targetPath)) {
787 QFile::remove(targetPath);
789 return QFile::copy(backupPath, targetPath);
Manage desktop shortcut keys.