File indexing completed on 2024-05-12 17:07:16

0001 /*
0002     SPDX-FileCopyrightText: 2010 Andriy Rysin <rysin@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include <QAbstractNativeEventFilter>
0010 #include <QKeySequence>
0011 #include <QString>
0012 #include <QStringList>
0013 #include <QWidget>
0014 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0015 #include <QX11Info>
0016 #else
0017 #include <QtGui/private/qtx11extras_p.h>
0018 #endif
0019 
0020 #define explicit explicit_is_keyword_in_cpp
0021 #include <xcb/xcb.h>
0022 #include <xcb/xkb.h>
0023 #undef explicit
0024 
0025 namespace
0026 {
0027 typedef union {
0028     /* All XKB events share these fields. */
0029     struct {
0030         uint8_t response_type;
0031         uint8_t xkbType;
0032         uint16_t sequence;
0033         xcb_timestamp_t time;
0034         uint8_t deviceID;
0035     } any;
0036     xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify;
0037     xcb_xkb_map_notify_event_t map_notify;
0038     xcb_xkb_state_notify_event_t state_notify;
0039 } _xkb_event;
0040 }
0041 
0042 class XEventNotifier : public QObject, public QAbstractNativeEventFilter
0043 {
0044     Q_OBJECT
0045 
0046 Q_SIGNALS:
0047     void layoutChanged();
0048     void layoutMapChanged();
0049 
0050 public:
0051     XEventNotifier();
0052     ~XEventNotifier() override
0053     {
0054     }
0055 
0056     virtual void start();
0057     virtual void stop();
0058 
0059 protected:
0060     virtual bool processOtherEvents(xcb_generic_event_t *e);
0061     virtual bool processXkbEvents(xcb_generic_event_t *e);
0062 
0063 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0064     bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
0065 #else
0066     bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
0067 #endif
0068 
0069 private:
0070     int registerForXkbEvents(Display *display);
0071     bool isXkbEvent(xcb_generic_event_t *event);
0072     bool isGroupSwitchEvent(_xkb_event *event);
0073     bool isLayoutSwitchEvent(_xkb_event *event);
0074 
0075     int xkbOpcode;
0076 };
0077 
0078 struct XkbConfig {
0079     QString keyboardModel;
0080     QStringList layouts;
0081     QStringList variants;
0082     QStringList options;
0083 
0084     bool isValid()
0085     {
0086         return !layouts.empty();
0087     }
0088 };
0089 
0090 class LayoutUnit
0091 {
0092 public:
0093     static const int MAX_LABEL_LENGTH;
0094 
0095     LayoutUnit()
0096     {
0097     }
0098     explicit LayoutUnit(const QString &fullLayoutName);
0099     LayoutUnit(const QString &layout, const QString &variant)
0100         : m_layout(layout)
0101         , m_variant(variant)
0102     {
0103     }
0104     /*explicit*/ LayoutUnit(const LayoutUnit &other)
0105     {
0106         operator=(other);
0107     }
0108 
0109     LayoutUnit &operator=(const LayoutUnit &other)
0110     {
0111         if (this != &other) {
0112             m_layout = other.m_layout;
0113             m_variant = other.m_variant;
0114             displayName = other.displayName;
0115             shortcut = other.shortcut;
0116         }
0117         return *this;
0118     }
0119 
0120     QString getRawDisplayName() const
0121     {
0122         return displayName;
0123     }
0124     QString getDisplayName() const
0125     {
0126         return !displayName.isEmpty() ? displayName : m_layout;
0127     }
0128     void setDisplayName(const QString &name)
0129     {
0130         displayName = name;
0131     }
0132 
0133     void setShortcut(const QKeySequence &shortcut)
0134     {
0135         this->shortcut = shortcut;
0136     }
0137     QKeySequence getShortcut() const
0138     {
0139         return shortcut;
0140     }
0141     QString layout() const
0142     {
0143         return m_layout;
0144     }
0145     void setLayout(const QString &layout)
0146     {
0147         m_layout = layout;
0148     }
0149     QString variant() const
0150     {
0151         return m_variant;
0152     }
0153     void setVariant(const QString &variant)
0154     {
0155         m_variant = variant;
0156     }
0157 
0158     bool isEmpty() const
0159     {
0160         return m_layout.isEmpty();
0161     }
0162     bool isValid() const
0163     {
0164         return !isEmpty();
0165     }
0166     bool operator==(const LayoutUnit &layoutItem) const
0167     {
0168         // FIXME: why not compare the other properties?
0169         return m_layout == layoutItem.m_layout && m_variant == layoutItem.m_variant;
0170     }
0171     bool operator!=(const LayoutUnit &layoutItem) const
0172     {
0173         return !(*this == layoutItem);
0174     }
0175     QString toString() const;
0176 
0177 private:
0178     QString displayName;
0179     QKeySequence shortcut;
0180     QString m_layout;
0181     QString m_variant;
0182 };
0183 
0184 struct LayoutSet {
0185     QList<LayoutUnit> layouts;
0186     LayoutUnit currentLayout;
0187 
0188     LayoutSet()
0189     {
0190     }
0191 
0192     LayoutSet(const LayoutSet &other)
0193     {
0194         operator=(other);
0195     }
0196 
0197     bool isValid() const
0198     {
0199         return currentLayout.isValid() && layouts.contains(currentLayout);
0200     }
0201 
0202     bool operator==(const LayoutSet &currentLayouts) const
0203     {
0204         return this->layouts == currentLayouts.layouts && this->currentLayout == currentLayouts.currentLayout;
0205     }
0206 
0207     LayoutSet &operator=(const LayoutSet &currentLayouts)
0208     {
0209         this->layouts = currentLayouts.layouts;
0210         this->currentLayout = currentLayouts.currentLayout;
0211         return *this;
0212     }
0213 
0214     QString toString() const
0215     {
0216         QString str(currentLayout.toString());
0217         str += QLatin1String(": ");
0218         for (const auto &layoutUnit : qAsConst(layouts)) {
0219             str += layoutUnit.toString() + QLatin1Char(' ');
0220         }
0221         return str;
0222     }
0223 
0224     static QString toString(const QList<LayoutUnit> &layoutUnits)
0225     {
0226         QString str;
0227         for (const auto &layoutUnit : layoutUnits) {
0228             str += layoutUnit.toString() + QLatin1Char(',');
0229         }
0230         return str;
0231     }
0232 };
0233 
0234 class X11Helper
0235 {
0236 public:
0237     static const int MAX_GROUP_COUNT;
0238 
0239     static const char LEFT_VARIANT_STR[];
0240     static const char RIGHT_VARIANT_STR[];
0241 
0242     static bool xkbSupported(int *xkbOpcode);
0243 
0244     static void switchToNextLayout();
0245     static void scrollLayouts(int delta);
0246     static bool isDefaultLayout();
0247     static bool setDefaultLayout();
0248     static bool setLayout(const LayoutUnit &layout);
0249     static LayoutUnit getCurrentLayout();
0250     static LayoutSet getCurrentLayouts();
0251     static QList<LayoutUnit> getLayoutsList();
0252     static QStringList getLayoutsListAsString(const QList<LayoutUnit> &layoutsList);
0253 
0254     enum FetchType { ALL, LAYOUTS_ONLY, MODEL_ONLY };
0255     static bool getGroupNames(Display *dpy, XkbConfig *xkbConfig, FetchType fetchType);
0256 
0257     static unsigned int getGroup();
0258     static bool setGroup(unsigned int group);
0259 };