1 /*
2  *  SPDX-FileCopyrightText: 2010 Andriy Rysin (rysin@kde.org)
3  *
4  *  SPDX-License-Identifier: GPL-2.0-or-later
5  */
6 
7 
8 #ifndef X11_HELPER_H_
9 #define X11_HELPER_H_
10 
11 #include <QKeySequence>
12 #include <QString>
13 #include <QStringList>
14 #include <QWidget>
15 #include <QX11Info>
16 #include <QAbstractNativeEventFilter>
17 
18 #include <xcb/xcb.h>
19 
20 namespace
21 {
22 typedef struct _xcb_xkb_map_notify_event_t {
23     uint8_t         response_type;
24     uint8_t         xkbType;
25     uint16_t        sequence;
26     xcb_timestamp_t time;
27     uint8_t         deviceID;
28     uint8_t         ptrBtnActions;
29     uint16_t        changed;
30     xcb_keycode_t   minKeyCode;
31     xcb_keycode_t   maxKeyCode;
32     uint8_t         firstType;
33     uint8_t         nTypes;
34     xcb_keycode_t   firstKeySym;
35     uint8_t         nKeySyms;
36     xcb_keycode_t   firstKeyAct;
37     uint8_t         nKeyActs;
38     xcb_keycode_t   firstKeyBehavior;
39     uint8_t         nKeyBehavior;
40     xcb_keycode_t   firstKeyExplicit;
41     uint8_t         nKeyExplicit;
42     xcb_keycode_t   firstModMapKey;
43     uint8_t         nModMapKeys;
44     xcb_keycode_t   firstVModMapKey;
45     uint8_t         nVModMapKeys;
46     uint16_t        virtualMods;
47     uint8_t         pad0[2];
48 } _xcb_xkb_map_notify_event_t;
49 typedef struct _xcb_xkb_state_notify_event_t {
50     uint8_t         response_type;
51     uint8_t         xkbType;
52     uint16_t        sequence;
53     xcb_timestamp_t time;
54     uint8_t         deviceID;
55     uint8_t         mods;
56     uint8_t         baseMods;
57     uint8_t         latchedMods;
58     uint8_t         lockedMods;
59     uint8_t         group;
60     int16_t         baseGroup;
61     int16_t         latchedGroup;
62     uint8_t         lockedGroup;
63     uint8_t         compatState;
64     uint8_t         grabMods;
65     uint8_t         compatGrabMods;
66     uint8_t         lookupMods;
67     uint8_t         compatLoockupMods;
68     uint16_t        ptrBtnState;
69     uint16_t        changed;
70     xcb_keycode_t   keycode;
71     uint8_t         eventType;
72     uint8_t         requestMajor;
73     uint8_t         requestMinor;
74 } _xcb_xkb_state_notify_event_t;
75 typedef union {
76     /* All XKB events share these fields. */
77     struct {
78         uint8_t response_type;
79         uint8_t xkbType;
80         uint16_t sequence;
81         xcb_timestamp_t time;
82         uint8_t deviceID;
83     } any;
84     _xcb_xkb_map_notify_event_t map_notify;
85     _xcb_xkb_state_notify_event_t state_notify;
86 } _xkb_event;
87 }
88 
89 class XEventNotifier : public QObject, public QAbstractNativeEventFilter {
90     Q_OBJECT
91 
92 Q_SIGNALS:
93     void layoutChanged();
94     void layoutMapChanged();
95 
96 public:
97     XEventNotifier();
~XEventNotifier()98     ~XEventNotifier() override {}
99 
100     virtual void start();
101     virtual void stop();
102 
103 protected:
104 //    bool x11Event(XEvent * e);
105     virtual bool processOtherEvents(xcb_generic_event_t* e);
106     virtual bool processXkbEvents(xcb_generic_event_t* e);
107     virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE;
108 
109 private:
110     int registerForXkbEvents(Display* display);
111     bool isXkbEvent(xcb_generic_event_t* event);
112     bool isGroupSwitchEvent(_xkb_event* event);
113     bool isLayoutSwitchEvent(_xkb_event* event);
114 
115     int xkbOpcode;
116 };
117 
118 struct XkbConfig {
119     QString keyboardModel;
120     QStringList layouts;
121     QStringList variants;
122     QStringList options;
123 
isValidXkbConfig124     bool isValid() { return ! layouts.empty(); }
125 };
126 
127 
128 struct LayoutUnit {
129     static const int MAX_LABEL_LENGTH;
130 
131     //TODO: move these to private?
132     QString layout;
133     QString variant;
134 
LayoutUnitLayoutUnit135     LayoutUnit() {}
136     explicit LayoutUnit(const QString& fullLayoutName);
LayoutUnitLayoutUnit137     LayoutUnit(const QString& layout_, const QString& variant_) {
138         layout = layout_;
139         variant = variant_;
140     }
141 
getRawDisplayNameLayoutUnit142     QString getRawDisplayName() const { return displayName; }
getDisplayNameLayoutUnit143     QString getDisplayName() const { return !displayName.isEmpty() ? displayName :  layout; }
setDisplayNameLayoutUnit144     void setDisplayName(const QString& name) { displayName = name; }
145 
setShortcutLayoutUnit146     void setShortcut(const QKeySequence& shortcut) { this->shortcut = shortcut; }
getShortcutLayoutUnit147     QKeySequence getShortcut() const { return shortcut; }
148 
isEmptyLayoutUnit149     bool isEmpty() const { return layout.isEmpty(); }
isValidLayoutUnit150     bool isValid() const { return ! isEmpty(); }
151     bool operator==(const LayoutUnit& layoutItem) const {
152         return layout==layoutItem.layout && variant==layoutItem.variant;
153     }
154     bool operator!=(const LayoutUnit& layoutItem) const {
155         return ! (*this == layoutItem);
156     }
157     QString toString() const;
158 
159 private:
160     QString displayName;
161     QKeySequence shortcut;
162 };
163 
164 struct LayoutSet {
165     QList<LayoutUnit> layouts;
166     LayoutUnit currentLayout;
167 
LayoutSetLayoutSet168     LayoutSet() {}
169 
LayoutSetLayoutSet170     LayoutSet(const LayoutSet& currentLayouts) {
171         this->layouts = currentLayouts.layouts;
172         this->currentLayout = currentLayouts.currentLayout;
173     }
174 
isValidLayoutSet175     bool isValid() const {
176         return currentLayout.isValid() && layouts.contains(currentLayout);
177     }
178 
179     bool operator == (const LayoutSet& currentLayouts) const {
180         return this->layouts == currentLayouts.layouts
181                 && this->currentLayout == currentLayouts.currentLayout;
182     }
183 
184     LayoutSet& operator = (const LayoutSet& currentLayouts) {
185         this->layouts = currentLayouts.layouts;
186         this->currentLayout = currentLayouts.currentLayout;
187         return *this;
188     }
189 
toStringLayoutSet190     QString toString() const {
191         QString str(currentLayout.toString());
192         str += QLatin1String(": ");
193         foreach(const LayoutUnit& layoutUnit, layouts) {
194             str += layoutUnit.toString() + QLatin1Char(' ');
195         }
196         return str;
197     }
198 
toStringLayoutSet199     static QString toString(const QList<LayoutUnit>& layoutUnits) {
200         QString str;
201         foreach(const LayoutUnit& layoutUnit, layoutUnits) {
202             str += layoutUnit.toString() + QLatin1Char(',');
203         }
204         return str;
205     }
206 };
207 
208 class X11Helper
209 {
210 public:
211     static const int MAX_GROUP_COUNT;
212     static const int ARTIFICIAL_GROUP_LIMIT_COUNT;
213 
214     static const char LEFT_VARIANT_STR[];
215     static const char RIGHT_VARIANT_STR[];
216 
217     static bool xkbSupported(int* xkbOpcode);
218 
219     static void switchToNextLayout();
220     static void scrollLayouts(int delta);
221     static bool isDefaultLayout();
222     static bool setDefaultLayout();
223     static bool setLayout(const LayoutUnit& layout);
224     static LayoutUnit getCurrentLayout();
225     static LayoutSet getCurrentLayouts();
226     static QList<LayoutUnit> getLayoutsList();
227     static QStringList getLayoutsListAsString(const QList<LayoutUnit>& layoutsList);
228 
229     enum FetchType { ALL, LAYOUTS_ONLY, MODEL_ONLY };
230     static bool getGroupNames(Display* dpy, XkbConfig* xkbConfig, FetchType fetchType);
231 
232 private:
233     static unsigned int getGroup();
234     static bool setGroup(unsigned int group);
235 };
236 
237 #endif /* X11_HELPER_H_ */
238