1 /* 2 SPDX-FileCopyrightText: 2010 Andriy Rysin <rysin@kde.org> 3 4 SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #ifndef X11_HELPER_H_ 8 #define X11_HELPER_H_ 9 10 #include <QAbstractNativeEventFilter> 11 #include <QKeySequence> 12 #include <QString> 13 #include <QStringList> 14 #include <QWidget> 15 #include <QX11Info> 16 17 #define explicit explicit_is_keyword_in_cpp 18 #include <xcb/xcb.h> 19 #include <xcb/xkb.h> 20 #undef explicit 21 22 namespace 23 { 24 typedef union { 25 /* All XKB events share these fields. */ 26 struct { 27 uint8_t response_type; 28 uint8_t xkbType; 29 uint16_t sequence; 30 xcb_timestamp_t time; 31 uint8_t deviceID; 32 } any; 33 xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify; 34 xcb_xkb_map_notify_event_t map_notify; 35 xcb_xkb_state_notify_event_t state_notify; 36 } _xkb_event; 37 } 38 39 class XEventNotifier : public QObject, public QAbstractNativeEventFilter 40 { 41 Q_OBJECT 42 43 Q_SIGNALS: 44 void layoutChanged(); 45 void layoutMapChanged(); 46 47 public: 48 XEventNotifier(); ~XEventNotifier()49 ~XEventNotifier() override 50 { 51 } 52 53 virtual void start(); 54 virtual void stop(); 55 56 protected: 57 virtual bool processOtherEvents(xcb_generic_event_t *e); 58 virtual bool processXkbEvents(xcb_generic_event_t *e); 59 bool nativeEventFilter(const QByteArray &eventType, void *message, long *) override; 60 61 private: 62 int registerForXkbEvents(Display *display); 63 bool isXkbEvent(xcb_generic_event_t *event); 64 bool isGroupSwitchEvent(_xkb_event *event); 65 bool isLayoutSwitchEvent(_xkb_event *event); 66 67 int xkbOpcode; 68 }; 69 70 struct XkbConfig { 71 QString keyboardModel; 72 QStringList layouts; 73 QStringList variants; 74 QStringList options; 75 isValidXkbConfig76 bool isValid() 77 { 78 return !layouts.empty(); 79 } 80 }; 81 82 class LayoutUnit 83 { 84 public: 85 static const int MAX_LABEL_LENGTH; 86 LayoutUnit()87 LayoutUnit() 88 { 89 } 90 explicit LayoutUnit(const QString &fullLayoutName); LayoutUnit(const QString & layout,const QString & variant)91 LayoutUnit(const QString &layout, const QString &variant) 92 { 93 m_layout = layout; 94 m_variant = variant; 95 } LayoutUnit(const LayoutUnit & other)96 /*explicit*/ LayoutUnit(const LayoutUnit &other) 97 { 98 operator=(other); 99 } 100 101 LayoutUnit &operator=(const LayoutUnit &other) 102 { 103 if (this != &other) { 104 m_layout = other.m_layout; 105 m_variant = other.m_variant; 106 displayName = other.displayName; 107 shortcut = other.shortcut; 108 } 109 return *this; 110 } 111 getRawDisplayName()112 QString getRawDisplayName() const 113 { 114 return displayName; 115 } getDisplayName()116 QString getDisplayName() const 117 { 118 return !displayName.isEmpty() ? displayName : m_layout; 119 } setDisplayName(const QString & name)120 void setDisplayName(const QString &name) 121 { 122 displayName = name; 123 } 124 setShortcut(const QKeySequence & shortcut)125 void setShortcut(const QKeySequence &shortcut) 126 { 127 this->shortcut = shortcut; 128 } getShortcut()129 QKeySequence getShortcut() const 130 { 131 return shortcut; 132 } layout()133 QString layout() const 134 { 135 return m_layout; 136 } setLayout(const QString & layout)137 void setLayout(const QString &layout) 138 { 139 m_layout = layout; 140 } variant()141 QString variant() const 142 { 143 return m_variant; 144 } setVariant(const QString & variant)145 void setVariant(const QString &variant) 146 { 147 m_variant = variant; 148 } 149 isEmpty()150 bool isEmpty() const 151 { 152 return m_layout.isEmpty(); 153 } isValid()154 bool isValid() const 155 { 156 return !isEmpty(); 157 } 158 bool operator==(const LayoutUnit &layoutItem) const 159 { 160 // FIXME: why not compare the other properties? 161 return m_layout == layoutItem.m_layout && m_variant == layoutItem.m_variant; 162 } 163 bool operator!=(const LayoutUnit &layoutItem) const 164 { 165 return !(*this == layoutItem); 166 } 167 QString toString() const; 168 169 private: 170 QString displayName; 171 QKeySequence shortcut; 172 QString m_layout; 173 QString m_variant; 174 }; 175 176 struct LayoutSet { 177 QList<LayoutUnit> layouts; 178 LayoutUnit currentLayout; 179 LayoutSetLayoutSet180 LayoutSet() 181 { 182 } 183 LayoutSetLayoutSet184 LayoutSet(const LayoutSet &other) 185 { 186 operator=(other); 187 } 188 isValidLayoutSet189 bool isValid() const 190 { 191 return currentLayout.isValid() && layouts.contains(currentLayout); 192 } 193 194 bool operator==(const LayoutSet ¤tLayouts) const 195 { 196 return this->layouts == currentLayouts.layouts && this->currentLayout == currentLayouts.currentLayout; 197 } 198 199 LayoutSet &operator=(const LayoutSet ¤tLayouts) 200 { 201 this->layouts = currentLayouts.layouts; 202 this->currentLayout = currentLayouts.currentLayout; 203 return *this; 204 } 205 toStringLayoutSet206 QString toString() const 207 { 208 QString str(currentLayout.toString()); 209 str += QLatin1String(": "); 210 for (const auto &layoutUnit : qAsConst(layouts)) { 211 str += layoutUnit.toString() + QLatin1Char(' '); 212 } 213 return str; 214 } 215 toStringLayoutSet216 static QString toString(const QList<LayoutUnit> &layoutUnits) 217 { 218 QString str; 219 for (const auto &layoutUnit : layoutUnits) { 220 str += layoutUnit.toString() + QLatin1Char(','); 221 } 222 return str; 223 } 224 }; 225 226 class X11Helper 227 { 228 public: 229 static const int MAX_GROUP_COUNT; 230 static const int ARTIFICIAL_GROUP_LIMIT_COUNT; 231 232 static const char LEFT_VARIANT_STR[]; 233 static const char RIGHT_VARIANT_STR[]; 234 235 static bool xkbSupported(int *xkbOpcode); 236 237 static void switchToNextLayout(); 238 static void scrollLayouts(int delta); 239 static bool isDefaultLayout(); 240 static bool setDefaultLayout(); 241 static bool setLayout(const LayoutUnit &layout); 242 static LayoutUnit getCurrentLayout(); 243 static LayoutSet getCurrentLayouts(); 244 static QList<LayoutUnit> getLayoutsList(); 245 static QStringList getLayoutsListAsString(const QList<LayoutUnit> &layoutsList); 246 247 enum FetchType { ALL, LAYOUTS_ONLY, MODEL_ONLY }; 248 static bool getGroupNames(Display *dpy, XkbConfig *xkbConfig, FetchType fetchType); 249 250 static unsigned int getGroup(); 251 static bool setGroup(unsigned int group); 252 }; 253 254 #endif /* X11_HELPER_H_ */ 255