From 879b452cc80dccb6581fea70a671a86994fb3a45 Mon Sep 17 00:00:00 2001
From: yuan_xing <yuanxing@kylinsec.com.cn>
Date: Thu, 29 Feb 2024 17:28:42 +0800
Subject: [PATCH 1/1] fix(keybinding):don't convert normal key if use shift
 modifier in keybinding
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- 如果在快捷键中使用了shift修饰键，则不转换其普通按键,直接使用普通按键的Qt::Key

Relates #30978
---
 README.md                                     |  4 +-
 plugins/keybinding/CMakeLists.txt             |  7 ++-
 plugins/keybinding/utils/custom-line-edit.cpp | 25 +++++++----
 plugins/keybinding/utils/keycode-helper.cpp   | 42 ++++++++++++++++++
 plugins/keybinding/utils/keycode-helper.h     | 23 ++++++++++
 .../keybinding/utils/keycode-translator.cpp   | 44 +++++++++++--------
 plugins/keybinding/utils/keycode-translator.h |  6 +--
 7 files changed, 119 insertions(+), 32 deletions(-)
 create mode 100644 plugins/keybinding/utils/keycode-helper.cpp
 create mode 100644 plugins/keybinding/utils/keycode-helper.h

diff --git a/README.md b/README.md
index 83d25b9..ad47094 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 1. 安装编译依赖
 
    ```bash
-   $ sudo yum install gcc-c++ qt5-qtbase qt5-qtbase-devel qt5-qtbase-gui qt5-qtx11extras qt5-qtx11extras-devel qt5-qtsvg glibc glibc-devel libX11 libX11-devel kiranwidgets-qt5 kiran-widgets-qt5-devel
+   $ sudo yum install gcc-c++ qt5-qtbase qt5-qtbase-devel qt5-qtbase-gui qt5-qtx11extras qt5-qtx11extras-devel qt5-qtsvg glibc glibc-devel libX11 libX11-devel kiranwidgets-qt5 kiran-widgets-qt5-devel qt5-qtbase-static qt5-qtbase-private-devel
    ```
 
 2. **源码根目录下创建**build**目录`mkdir build`
@@ -302,4 +302,4 @@ $ kiran-control-panel
 
 ```bash
 $ kiran-cpanel-launcher --cpanel-plugin 插件安装的desktop文件名
-```
\ No newline at end of file
+```
diff --git a/plugins/keybinding/CMakeLists.txt b/plugins/keybinding/CMakeLists.txt
index bc94de2..d9dc8d1 100644
--- a/plugins/keybinding/CMakeLists.txt
+++ b/plugins/keybinding/CMakeLists.txt
@@ -1,5 +1,7 @@
 set(TARGET_NAME kiran-cpanel-keybinding)
 
+find_package(Qt5 COMPONENTS XkbCommonSupport)
+
 kiran_qt5_add_dbus_interface_ex(KEYBINDING_PROXY
         data/com.kylinsec.Kiran.SessionDaemon.Keybinding.xml
         keybinding_backEnd_proxy
@@ -16,6 +18,7 @@ target_include_directories(${TARGET_NAME} PRIVATE
         ${CMAKE_CURRENT_BINARY_DIR}
         ${CMAKE_CURRENT_SOURCE_DIR}
         ${CMAKE_CURRENT_SOURCE_DIR}/utils
+        ${Qt5XkbCommonSupport_PRIVATE_INCLUDE_DIRS}
         ${KLOG_INCLUDE_DIRS}
         ${KIRAN_WIDGETS_INCLUDE_DIRS}
         ${KIRAN_CC_DAEMON_INCLUDE_DIRS})
@@ -27,8 +30,10 @@ target_link_libraries(${TARGET_NAME}
         Qt5::Svg
         Qt5::Core
         Qt5::Concurrent
+        X11
+        Qt5::XkbCommonSupport
         ${KIRANWIDGETS_LIBRARIES}
         ${KLOG_LIBRARIES})
 
 install(TARGETS ${TARGET_NAME}
-        DESTINATION ${PLUGIN_LIBS_DIR}/)
\ No newline at end of file
+        DESTINATION ${PLUGIN_LIBS_DIR}/)
diff --git a/plugins/keybinding/utils/custom-line-edit.cpp b/plugins/keybinding/utils/custom-line-edit.cpp
index 36250da..6defb3b 100644
--- a/plugins/keybinding/utils/custom-line-edit.cpp
+++ b/plugins/keybinding/utils/custom-line-edit.cpp
@@ -17,6 +17,8 @@
 #include <QInputMethodEvent>
 #include <QPainter>
 #include <QStyleOption>
+#include "keycode-translator.h"
+#include "logging-category.h"
 
 CustomLineEdit::CustomLineEdit(QWidget *parent) : QLineEdit(parent)
 {
@@ -33,18 +35,27 @@ void CustomLineEdit::initUI()
     setFocusPolicy(Qt::ClickFocus);
     setReadOnly(true);
     setObjectName("CustomLineEdit");
-//    setStyleSheet("#CustomLineEdit{border:1px solid #393939;border-radius:6px;padding-left:10px;padding-right:10px;}"
-//                  "#CustomLineEdit:focus{border:1px solid #2eb3ff;}");
+    //    setStyleSheet("#CustomLineEdit{border:1px solid #393939;border-radius:6px;padding-left:10px;padding-right:10px;}"
+    //                  "#CustomLineEdit:focus{border:1px solid #2eb3ff;}");
 }
 
 void CustomLineEdit::keyReleaseEvent(QKeyEvent *event)
 {
     QList<int> keycodes;
+    int qtkey = 0;
 
     if (event->key() == Qt::Key_Backspace && event->modifiers() == Qt::NoModifier)
     {
         return;
     }
+
+    //处理shift修饰的快捷键组合，按键不经过shift转化，将原始按键keycode转化为对应的Qt::Key
+    if (event->key() != 0 && (event->modifiers() & Qt::ShiftModifier))
+    {
+        qtkey = KeycodeTranslator::keycode2QtKey(event->nativeScanCode());
+        KLOG_INFO(qLcKeybinding) << "convert KeyCode:" << event->nativeScanCode() << "to Qt::Key:" << qtkey;
+    }
+
     // no modifier
     if (event->key() != 0 && event->modifiers() == Qt::NoModifier)
     {
@@ -56,14 +67,12 @@ void CustomLineEdit::keyReleaseEvent(QKeyEvent *event)
     else if (event->key() != 0 && event->modifiers() == Qt::ShiftModifier)
     {
         keycodes.append(Qt::Key_Shift);
-        keycodes.append(event->key());
-        //KLOG_INFO() << "shift :" << event->key() << event->modifiers();
+        keycodes.append(qtkey ? qtkey : event->key());
     }
     else if (event->key() != 0 && event->modifiers() == Qt::ControlModifier)
     {
         keycodes.append(Qt::Key_Control);
         keycodes.append(event->key());
-        //KLOG_INFO() << "ctrl :" << event->key() << event->text();
     }
     else if (event->key() != 0 && event->modifiers() == Qt::AltModifier)
     {
@@ -76,7 +85,7 @@ void CustomLineEdit::keyReleaseEvent(QKeyEvent *event)
     {
         keycodes.append(Qt::Key_Control);
         keycodes.append(Qt::Key_Shift);
-        keycodes.append(event->key());
+        keycodes.append(qtkey ? qtkey : event->key());
     }
     else if (event->key() != 0 && event->modifiers() == (Qt::ControlModifier | Qt::AltModifier))
     {
@@ -88,7 +97,7 @@ void CustomLineEdit::keyReleaseEvent(QKeyEvent *event)
     {
         keycodes.append(Qt::Key_Shift);
         keycodes.append(Qt::Key_Alt);
-        keycodes.append(event->key());
+        keycodes.append(qtkey ? qtkey : event->key());
     }
     //three modifier
     else if (event->key() != 0 && event->modifiers() == (Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier))
@@ -96,7 +105,7 @@ void CustomLineEdit::keyReleaseEvent(QKeyEvent *event)
         keycodes.append(Qt::Key_Shift);
         keycodes.append(Qt::Key_Control);
         keycodes.append(Qt::Key_Alt);
-        keycodes.append(event->key());
+        keycodes.append(qtkey ? qtkey : event->key());
     }
     if (keycodes.size() > 0)
     {
diff --git a/plugins/keybinding/utils/keycode-helper.cpp b/plugins/keybinding/utils/keycode-helper.cpp
new file mode 100644
index 0000000..67cb698
--- /dev/null
+++ b/plugins/keybinding/utils/keycode-helper.cpp
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd.
+ * kiran-control-panel is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ *
+ * Author:     yuanxing <yuanxing@kylinsec.com.cn>
+ */
+
+#include "keycode-helper.h"
+
+#include <X11/Xlib.h>
+
+namespace KeycodeHelper
+{
+unsigned long keycode2Keysym(unsigned long keycode)
+{
+    KeySym keysym = NoSymbol;
+    Display *display = QX11Info::display();
+    if (display == nullptr)
+    {
+        KLOG_WARNING(qLcKeybinding) << "can't open display!";
+        return keysym;
+    }
+
+    keysym = XKeycodeToKeysym(display, keycode, 0);
+    if (keysym != NoSymbol)
+    {
+        KLOG_INFO(qLcKeybinding) << "convert KeyCode:" << keycode << "to KeySym:" << keysym;
+    }
+    else
+    {
+        KLOG_INFO(qLcKeybinding) << "no corresponding" << keycode << "KeySym found.";
+    }
+    return keysym;
+}
+}  // namespace KeycodeHelper
diff --git a/plugins/keybinding/utils/keycode-helper.h b/plugins/keybinding/utils/keycode-helper.h
new file mode 100644
index 0000000..849abe0
--- /dev/null
+++ b/plugins/keybinding/utils/keycode-helper.h
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2020 ~ 2021 KylinSec Co., Ltd.
+ * kiran-control-panel is licensed under Mulan PSL v2.
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
+ * You may obtain a copy of Mulan PSL v2 at:
+ *          http://license.coscl.org.cn/MulanPSL2
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+ * See the Mulan PSL v2 for more details.
+ *
+ * Author:     yuanxing <yuanxing@kylinsec.com.cn>
+ */
+
+#pragma once
+
+#include <QX11Info>
+#include "logging-category.h"
+
+namespace KeycodeHelper
+{
+unsigned long keycode2Keysym(unsigned long keycode);
+}  // namespace KeycodeHelper
diff --git a/plugins/keybinding/utils/keycode-translator.cpp b/plugins/keybinding/utils/keycode-translator.cpp
index a8dea85..5b7f40c 100644
--- a/plugins/keybinding/utils/keycode-translator.cpp
+++ b/plugins/keybinding/utils/keycode-translator.cpp
@@ -15,9 +15,11 @@
 #include "keycode-translator.h"
 #include "logging-category.h"
 
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
 #include <qt5-log-i.h>
-#include <QMetaEnum>
 #include <QApplication>
+#include <QMetaEnum>
+#include "keycode-helper.h"
 
 static QMetaEnum keyMetaEnum = QMetaEnum::fromType<KeycodeTranslator::Key>();
 
@@ -89,23 +91,22 @@ static const QMap<QString, QString> MediaKeyMap = {
 KeycodeTranslator::KeycodeTranslator(QObject *parent)
     : QObject(parent)
 {
-
 }
 
 QString KeycodeTranslator::keycode2ReadableString(const QList<int> &keycodes)
 {
     QStringList keyStrings;
 
-    for( int key : keycodes )
+    for (int key : keycodes)
     {
-        if( (key >= Qt::Key_0) && (key <= Qt::Key_9) )
+        if ((key >= Qt::Key_0) && (key <= Qt::Key_9))
         {
-            keyStrings.append(QString::number(key-Qt::Key_0));
+            keyStrings.append(QString::number(key - Qt::Key_0));
             continue;
         }
 
-        const char* keyValue = keyMetaEnum.valueToKey(key);
-        if( keyValue == nullptr )
+        const char *keyValue = keyMetaEnum.valueToKey(key);
+        if (keyValue == nullptr)
         {
             KLOG_WARNING(qLcKeybinding) << "can't convert key:" << key;
             continue;
@@ -113,12 +114,12 @@ QString KeycodeTranslator::keycode2ReadableString(const QList<int> &keycodes)
 
         QString keyStr(keyValue);
         //特殊按键经QMetaEnum翻译之后再经过SpecialKeyMap翻译
-        if( SpecialKeyMap.contains(keyStr.toLower()) )
+        if (SpecialKeyMap.contains(keyStr.toLower()))
         {
             keyStrings.append(SpecialKeyMap.value(keyStr.toLower()));
         }
         //特殊按键 "_" 转换成 " "
-        else if( (key >= Audio_Lower_Volume) && (key <= Audio_Mic_Mute) )
+        else if ((key >= Audio_Lower_Volume) && (key <= Audio_Mic_Mute))
         {
             keyStrings.append(keyStr.split("_").join(" "));
         }
@@ -131,33 +132,40 @@ QString KeycodeTranslator::keycode2ReadableString(const QList<int> &keycodes)
     return keyStrings.join('+');
 }
 
+int KeycodeTranslator::keycode2QtKey(unsigned long keycode)
+{
+    auto keysym = KeycodeHelper::keycode2Keysym(keycode);
+    auto qtkey = QXkbCommon::keysymToQtKey(keysym, Qt::KeyboardModifier::NoModifier);
+    return qtkey;
+}
+
 QString KeycodeTranslator::backendKeyString2Readable(const QString &keyString)
 {
     QString readableString;
 
-    if( keyString.isEmpty() )
+    if (keyString.isEmpty())
     {
         readableString = tr("None");
     }
-    else if( keyString.contains("disable",Qt::CaseInsensitive) )
+    else if (keyString.contains("disable", Qt::CaseInsensitive))
     {
         readableString = tr("disabled");
     }
     else
     {
         QString temp = keyString;
-        temp = temp.replace("<","");
-        temp = temp.replace(">","-");
+        temp = temp.replace("<", "");
+        temp = temp.replace(">", "-");
 #if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
         QStringList keyList = temp.split("-", QString::SkipEmptyParts);
 #else
-        QStringList keyList = temp.split("-",Qt::SkipEmptyParts);
+        QStringList keyList = temp.split("-", Qt::SkipEmptyParts);
 #endif
-        for(int i=0;i<keyList.size();i++)
+        for (int i = 0; i < keyList.size(); i++)
         {
-            if( SpecialKeyMap.contains(keyList.at(i).toLower()) )
+            if (SpecialKeyMap.contains(keyList.at(i).toLower()))
             {
-                keyList.replace(i,SpecialKeyMap.value(keyList.at(i).toLower()));
+                keyList.replace(i, SpecialKeyMap.value(keyList.at(i).toLower()));
             }
             else if (MediaKeyMap.contains(keyList.at(i)))
             {
@@ -174,7 +182,7 @@ QString KeycodeTranslator::backendKeyString2Readable(const QString &keyString)
 QString KeycodeTranslator::readableKeyString2Backend(const QString &keyString)
 {
     QStringList keystrings = keyString.split('+');
-    for( int i=0;i<keystrings.count();i++ )
+    for (int i = 0; i < keystrings.count(); i++)
     {
         QString key = keystrings.at(i);
 
diff --git a/plugins/keybinding/utils/keycode-translator.h b/plugins/keybinding/utils/keycode-translator.h
index 2697b43..40e91f1 100644
--- a/plugins/keybinding/utils/keycode-translator.h
+++ b/plugins/keybinding/utils/keycode-translator.h
@@ -17,7 +17,7 @@
 
 #include <QObject>
 
-class KeycodeTranslator : public  QObject
+class KeycodeTranslator : public QObject
 {
     Q_OBJECT
 public:
@@ -174,9 +174,9 @@ private:
 
 public:
     static QString keycode2ReadableString(const QList<int>& keycodes);
+    static int keycode2QtKey(unsigned long keycode);
     static QString backendKeyString2Readable(const QString& keyString);
     static QString readableKeyString2Backend(const QString& keyString);
 };
 
-
-#endif //KIRAN_CONTROL_PANEL_KEYCODE_TRANSLATOR_H
+#endif  //KIRAN_CONTROL_PANEL_KEYCODE_TRANSLATOR_H
-- 
2.27.0

