1 /*
2 * Implementation of a class to get keyboard layout information and change layouts
3 *
4 * Copyright (C) 2008 by Jay Bromley <jbromley@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 3 of the License, or (at your option)
9 * any later version.
10 */
11
12 #include "XKeyboard.h"
13 #include "X11Exception.h"
14 #include <algorithm>
15 #include <cstdlib>
16 #include <cctype>
17 #include <cstring>
18
19
20 #define CHECK_MSG(expression,msg) \
21 \
22 if(!(expression)){ \
23 \
24 std::ostringstream stream; \
25 \
26 stream << __FILE__ << " : " \
27 << __LINE__ << " : " \
28 << "XKeyboard" << " : " \
29 << msg << "[ " << #expression << "]"; \
30 \
31 throw std::runtime_error(stream.str()); \
32 }
33
34
35 namespace kb {
36
XKeyboard()37 XKeyboard::XKeyboard()
38 : _display(0), _deviceId(XkbUseCoreKbd)
39 {
40
41 XkbIgnoreExtension(False);
42
43 char* displayName = strdup("");
44 int eventCode;
45 int errorReturn;
46 int major = XkbMajorVersion;
47 int minor = XkbMinorVersion;
48 int reasonReturn;
49 _display = XkbOpenDisplay(displayName, &eventCode, &errorReturn, &major,
50 &minor, &reasonReturn);
51 switch (reasonReturn) {
52 case XkbOD_BadLibraryVersion:
53 throw X11Exception("Bad XKB library version.");
54 break;
55 case XkbOD_ConnectionRefused:
56 throw X11Exception("Connection to X server refused.");
57 break;
58 case XkbOD_BadServerVersion:
59 throw X11Exception("Bad X11 server version.");
60 break;
61 case XkbOD_NonXkbServer:
62 throw X11Exception("XKB not present.");
63 break;
64 case XkbOD_Success:
65 break;
66 }
67
68 _kbdDescPtr = XkbAllocKeyboard();
69 if (_kbdDescPtr == NULL) {
70 XCloseDisplay(_display);
71 throw X11Exception("Failed to get keyboard description.");
72 }
73
74 _kbdDescPtr->dpy = _display;
75 if (_deviceId != XkbUseCoreKbd) {
76 _kbdDescPtr->device_spec = _deviceId;
77 }
78 }
79
~XKeyboard()80 XKeyboard::~XKeyboard()
81 {
82 if(_kbdDescPtr!=NULL)
83 XkbFreeKeyboard(_kbdDescPtr, 0, True);
84
85 XCloseDisplay(_display);
86 }
87
get_kb_string()88 std::string XKeyboard::get_kb_string()
89 {
90 XkbGetControls(_display, XkbAllControlsMask, _kbdDescPtr);
91 XkbGetNames(_display, XkbSymbolsNameMask, _kbdDescPtr);
92
93 Atom symNameAtom = _kbdDescPtr->names->symbols;
94
95 CHECK_MSG(symNameAtom != None,"Symbol Name is not present");
96
97 char* kbsC = XGetAtomName(_display, symNameAtom);
98
99 CHECK_MSG(kbsC,"Symbol Name pointer is invalid");
100
101 std::string kbs(kbsC);
102 XFree(kbsC);
103
104 CHECK_MSG(!kbs.empty(),"Symbol Name is an empty string");
105
106 return kbs;
107
108 /* StringVector symNames; */
109 /* XkbSymbolParser symParser; */
110 /* symParser.parse(symName, symNames); */
111 /* return symNames; */
112 }
113
wait_event()114 void XKeyboard::wait_event()
115 {
116 CHECK_MSG(_display != 0,"Display is not present");
117
118 Bool bret = XkbSelectEventDetails(_display, XkbUseCoreKbd,
119 XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask);
120
121 CHECK_MSG(bret==True, "Failed to select event details");
122
123 XEvent event;
124 int iret = XNextEvent(_display, &event);
125
126 CHECK_MSG(iret==0,"Failed to retrieve next event with " << iret);
127 }
128
set_group(int groupNum)129 void XKeyboard::set_group(int groupNum)
130 {
131 Bool result = XkbLockGroup(_display, _deviceId, groupNum);
132 CHECK_MSG(result == True,"Failed to set group to: " << groupNum);
133 }
134
get_group() const135 int XKeyboard::get_group() const
136 {
137 XkbStateRec xkbState;
138 XkbGetState(_display, _deviceId, &xkbState);
139 return static_cast<int>(xkbState.group);
140 }
141
142 // returns true if symbol is ok
filter(const string_vector & nonsyms,const std::string & symbol)143 bool filter(const string_vector& nonsyms, const std::string& symbol)
144 {
145 if(symbol.empty())
146 return false;
147
148 // Filter out all prohibited words
149 string_vector::const_iterator r = find(nonsyms.begin(), nonsyms.end(), symbol);
150 if(r != nonsyms.end())
151 return false;
152
153 // Filter out all numbers groups started with number
154 if(isdigit(symbol[0]))
155 return false;
156
157 return true;
158 }
159
parse1(const std::string & symbols,const string_vector & nonsyms)160 string_vector parse1(const std::string& symbols, const string_vector& nonsyms)
161 {
162 bool inSymbol = false;
163 std::string sym;
164 string_vector symlist;
165
166 for (size_t i = 0; i < symbols.size(); i++) {
167 char ch = symbols[i];
168 if (ch == '+') {
169 if (inSymbol && !sym.empty() && filter(nonsyms, sym)) {
170 symlist.push_back(sym);
171 }
172 inSymbol = true;
173 sym.clear();
174 }
175 else if (inSymbol && ch == '(') {
176 inSymbol = false;
177 }
178 else if (inSymbol && (isalpha(static_cast<int>(ch)) || ch == '_')) {
179 sym.append(1, ch);
180 }
181 else {
182 if (inSymbol && !sym.empty() && filter(nonsyms, sym)) {
183 symlist.push_back(sym);
184 }
185 inSymbol = false;
186 }
187 }
188
189 if (inSymbol && !sym.empty() && filter(nonsyms, sym)) {
190 symlist.push_back(sym);
191 }
192
193 return symlist;
194 }
195
safe_push_back(string_vector & v,std::string s,std::string)196 void safe_push_back(string_vector& v, std::string s, std::string /*note*/)
197 {
198 if(s.empty()) return;
199 #ifdef NOT_TEXSTUDIO
200 if(!note.empty()) {
201 s += "(" + note + ")";
202 }
203 #endif
204 v.push_back(s);
205 }
206
goodchar(char ch)207 bool goodchar(char ch)
208 {
209 return (isdigit(ch) || isalpha(static_cast<int>(ch)) || ch == '_' || ch == '-');
210 }
211
parse2(const std::string & symbols,const string_vector & nonsyms)212 string_vector parse2(const std::string& symbols, const string_vector& nonsyms)
213 {
214 enum{ok,skip,broken} state = ok;
215 int paren = 0;
216 std::string sym;
217 // Words between optional '(' ')'
218 std::string note;
219 string_vector symlist;
220
221 for (size_t i = 0; i < symbols.size(); i++) {
222 char ch = symbols[i];
223
224 if (ch == '+') {
225 if (state != broken && paren == 0 && filter(nonsyms, sym)) {
226 safe_push_back(symlist, sym, note);
227 }
228 state = ok;
229 paren = 0;
230 sym.clear();
231 note.clear();
232 }
233 else if (state == ok && ch == '(') {
234 paren++;
235 }
236 else if (state == ok && ch == ')') {
237 paren--;
238 }
239 else if (state == ok && ch == ':') {
240 state = skip;
241 }
242 else if (state == ok && goodchar(ch)) {
243 if (paren == 0)
244 sym.append(1, ch);
245 else
246 note.append(1, ch);
247 }
248 else if(state == ok) {
249 state = broken;
250 }
251 }
252
253 if (state != broken && paren == 0 && filter(nonsyms, sym)) {
254 safe_push_back(symlist, sym, note);
255 }
256
257 return symlist;
258 }
259
parse3(const std::string & symbols,const string_vector & nonsyms)260 string_vector parse3(const std::string& symbols, const string_vector& nonsyms)
261 {
262 enum{ok,skip,broken} state = ok;
263 int paren = 0;
264 std::string sym;
265 // Words between optional '(' ')'
266 std::string note;
267 string_vector symlist;
268
269 for (size_t i = 0; i < symbols.size(); i++) {
270 char ch = symbols[i];
271
272 if (ch == '+' || ch == '_') {
273 if(paren == 0) {
274 if (state != broken && paren == 0 && filter(nonsyms, sym)) {
275 safe_push_back(symlist, sym, note);
276 }
277 state = ok;
278 sym.clear();
279 note.clear();
280 }
281 }
282 else if (state == ok && ch == '(') {
283 paren++;
284 }
285 else if (state == ok && ch == ')') {
286 paren--;
287 }
288 else if (state == ok && ch == ':') {
289 state = skip;
290 }
291 else if (state == ok && goodchar(ch)) {
292 if (paren == 0)
293 sym.append(1, ch);
294 else
295 note.append(1, ch);
296 }
297 else if(state == ok) {
298 state = broken;
299 }
300 }
301
302 if (state != broken && paren == 0 && filter(nonsyms, sym)) {
303 safe_push_back(symlist, sym, note);
304 }
305
306 return symlist;
307 }
308
309 }
310
311 #undef CHECK_MSG
312
313