File indexing completed on 2022-09-20 12:22:40

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