File indexing completed on 2024-04-28 16:55:49
0001 /* 0002 * SPDX-FileCopyrightText: 2022 Aleix Pol i Gonzalez <aleixpol@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "xdgshortcut.h" 0008 #include <QDebug> 0009 #include <QRegularExpression> 0010 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0011 #include <QtXkbCommonSupport/private/qxkbcommon_p.h> 0012 #else 0013 #include <QtGui/private/qxkbcommon_p.h> 0014 #endif 0015 0016 struct Modifier { 0017 const char *xkbModifier; 0018 Qt::KeyboardModifier qtModifier; 0019 }; 0020 0021 std::optional<QKeySequence> XdgShortcut::parse(const QString &shortcutString) 0022 { 0023 static const QHash<QString, Modifier> allowedModifiers = { 0024 {"SHIFT", {XKB_MOD_NAME_SHIFT, Qt::ShiftModifier}}, 0025 {"CAPS", {XKB_MOD_NAME_CAPS, Qt::GroupSwitchModifier}}, 0026 {"CTRL", {XKB_MOD_NAME_CTRL, Qt::ControlModifier}}, 0027 {"ALT", {XKB_MOD_NAME_ALT, Qt::AltModifier}}, 0028 {"NUM", {XKB_MOD_NAME_NUM, Qt::KeypadModifier}}, 0029 {"LOGO", {XKB_MOD_NAME_LOGO, Qt::MetaModifier}}, 0030 }; 0031 0032 xkb_keysym_t identifier = XKB_KEY_NoSymbol; 0033 QStringList modifiers; 0034 0035 QStringView remaining(shortcutString); 0036 while (!remaining.isEmpty()) { 0037 auto nextPlus = remaining.indexOf(QChar('+')); 0038 if (nextPlus == 0) { // ++ or ending with + 0039 qWarning() << "empty modifier"; 0040 return {}; 0041 } 0042 0043 if (nextPlus < 0) { // just the identifier left 0044 // The spec says that the string ends when all the spec'ed characters are over 0045 // Meaning "CTRL+a;Banana" would be an acceptable and parseable string 0046 static QRegularExpression rx(QStringLiteral("^([\\w\\d_]+).*$")); 0047 Q_ASSERT(rx.isValid()); 0048 rx.setPatternOptions(QRegularExpression::UseUnicodePropertiesOption); 0049 QRegularExpressionMatch match = rx.match(remaining); 0050 Q_ASSERT(match.isValid()); 0051 0052 identifier = xkb_keysym_from_name(match.capturedView(1).toUtf8().constData(), XKB_KEYSYM_CASE_INSENSITIVE); 0053 if (identifier == XKB_KEY_NoSymbol) { 0054 qWarning() << "unknown key" << match.capturedView(1); 0055 return false; 0056 } 0057 break; 0058 } else { // A modifier 0059 auto modifier = remaining.left(nextPlus); 0060 if (!allowedModifiers.contains(modifier.toString())) { 0061 qWarning() << "Unknown modifier" << modifier; 0062 return {}; 0063 } 0064 0065 modifiers << modifier.toString(); 0066 remaining = remaining.mid(nextPlus + 1); 0067 } 0068 } 0069 0070 int keys = 0; 0071 for (const QString &modifier : std::as_const(modifiers)) { 0072 keys |= allowedModifiers[modifier].qtModifier; 0073 } 0074 0075 keys |= QXkbCommon::keysymToQtKey(identifier, Qt::NoModifier, nullptr, XKB_KEYCODE_INVALID); 0076 return QKeySequence(keys); 0077 }