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