1 /*
2 * florence - Florence is a simple virtual keyboard for Gnome.
3
4 * Copyright (C) 2012 François Agrech
5
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 #include "system.h"
23 #include "xkeyboard.h"
24 #include "trace.h"
25 #include <gdk/gdkx.h>
26
27 /* liberate groups memory */
xkeyboard_groups_free(struct xkeyboard * xkeyboard)28 void xkeyboard_groups_free(struct xkeyboard *xkeyboard) {
29 START_FUNC
30 while(xkeyboard->groups) {
31 g_free(g_list_first(xkeyboard->groups)->data);
32 xkeyboard->groups=g_list_delete_link(xkeyboard->groups, g_list_first(xkeyboard->groups));
33 }
34 END_FUNC
35 }
36
37 #ifdef ENABLE_XKB
38
39 /* returns TRUE is the symbol is a layout name */
xkeyboard_is_sym(gchar * symbol)40 gboolean xkeyboard_is_sym(gchar *symbol)
41 {
42 START_FUNC
43 gboolean ret=TRUE;
44 static gchar *nonSymbols[]={"group", "inet", "pc", "terminate", "compose", "level", NULL};
45 int i;
46 for(i=0; nonSymbols[i]; i++) {
47 if (!strcmp(symbol, nonSymbols[i])) { ret=FALSE; break; }
48 }
49 END_FUNC
50 return ret;
51 }
52
53 /* append a symbol to the list */
xkeyboard_list_append(gchar * symbol,GList ** list)54 void xkeyboard_list_append(gchar *symbol, GList **list)
55 {
56 START_FUNC
57 gchar *newsymbol=g_malloc(sizeof(gchar)*(strlen(symbol)+1));
58 strcpy(newsymbol, symbol);
59 *list=g_list_append(*list, newsymbol);
60 flo_debug(TRACE_DEBUG, _("new xkb symbol found: <%s>"), newsymbol);
61 END_FUNC
62 }
63
64 /* parse the symbol string from xkb to extract the layout names */
xkeyboard_symParse(gchar * symbols,gint count)65 GList *xkeyboard_symParse(gchar *symbols, gint count)
66 {
67 START_FUNC
68 GList *ret=NULL;
69 gboolean inSymbol=FALSE;
70 gchar curSymbol[32];
71 guint i;
72 gchar ch;
73 gint c=0;
74
75 curSymbol[0]='\0';
76 for (i=0 ; i<strlen(symbols) ; i++) {
77 ch=symbols[i];
78 if (ch=='+') {
79 if (inSymbol) {
80 if (xkeyboard_is_sym(curSymbol)) {
81 xkeyboard_list_append(curSymbol, &ret);
82 c++; if (c>=count) break;
83 }
84 curSymbol[0]='\0';
85 } else inSymbol=TRUE;
86 } else if (inSymbol && (ISALPHA(ch) || ch=='_')) {
87 if (strlen(curSymbol)<31) {
88 curSymbol[strlen(curSymbol)+1]='\0';
89 curSymbol[strlen(curSymbol)]=ch;
90 }
91 } else if (inSymbol) {
92 if (xkeyboard_is_sym(curSymbol))
93 xkeyboard_list_append(curSymbol, &ret);
94 c++; if (c>=count) break;
95 curSymbol[0]='\0';
96 inSymbol=FALSE;
97 }
98 }
99
100 if (inSymbol && curSymbol[0] && xkeyboard_is_sym(curSymbol) && (c<count))
101 xkeyboard_list_append(curSymbol, &ret);
102
103 END_FUNC
104 return ret;
105 }
106
107 /* returns the name of currently selected keyboard layout in XKB */
xkeyboard_layout(struct xkeyboard * xkeyboard)108 void xkeyboard_layout(struct xkeyboard *xkeyboard)
109 {
110 START_FUNC
111 gint i;
112 gint groupCount;
113 Atom* tmpGroupSource=None;
114 Atom curGroupAtom, symNameAtom;
115 gchar *groupName, *symName;
116 Display *disp=(Display *)gdk_x11_get_default_xdisplay();
117 XkbDescRec* kbdDescPtr=XkbAllocKeyboard();
118
119 if (!kbdDescPtr) {
120 flo_warn("Unable to allocate memory to get keyboard description");
121 return;
122 }
123 XkbGetControls(disp, XkbAllControlsMask, kbdDescPtr);
124 XkbGetNames(disp, XkbSymbolsNameMask, kbdDescPtr);
125 XkbGetNames(disp, XkbGroupNamesMask, kbdDescPtr);
126 if (!kbdDescPtr->names) {
127 flo_warn("Unable to get keyboard description");
128 return;
129 }
130
131 /* Count the number of configured groups. */
132 const Atom* groupSource = kbdDescPtr->names->groups;
133 if (kbdDescPtr->ctrls) {
134 groupCount=kbdDescPtr->ctrls->num_groups;
135 } else {
136 groupCount=0;
137 while ((groupCount<XkbNumKbdGroups) && (groupSource[groupCount]!=None))
138 groupCount++;
139 }
140
141 /* There is always at least one group. */
142 if (!groupCount) {
143 groupCount=1;
144 } else {
145 /* Get the group names. */
146 tmpGroupSource=kbdDescPtr->names->groups;
147 for (i=0 ; i<groupCount ; i++) {
148 if ((curGroupAtom=tmpGroupSource[i])!=None) {
149 groupName=XGetAtomName(disp, curGroupAtom);
150 flo_debug(TRACE_DEBUG, _("keyboard layout found: <%s>"), groupName);
151 XFree(groupName);
152 }
153 }
154 }
155
156 /* Get the symbol name and parse it for layout symbols. */
157 symNameAtom=kbdDescPtr->names->symbols;
158 if (symNameAtom!=None) {
159 symName=XGetAtomName(disp, symNameAtom);
160 flo_debug(TRACE_DEBUG, _("keyboard layout symbol name=<%s>"), symName);
161 xkeyboard->groups=xkeyboard_symParse(symName, groupCount);
162 if (!symName) return;
163 XFree(symName);
164 }
165
166 /* Select events */
167 /* XkbSelectEventDetails(disp, XkbUseCoreKbd, XkbStateNotify,
168 XkbAllStateComponentsMask, XkbGroupStateMask); */
169 END_FUNC
170 }
171
172 /* handles events from XKB */
xkeyboard_event_handler(GdkXEvent * xevent,GdkEvent * event,gpointer data)173 GdkFilterReturn xkeyboard_event_handler(GdkXEvent *xevent, GdkEvent *event, gpointer data)
174 {
175 START_FUNC
176 XkbEvent *xkbev;
177 XEvent *ev=(XEvent *)xevent;
178 struct xkeyboard *xkeyboard=(struct xkeyboard *)data;
179 if (ev->xany.type==(xkeyboard->base_event_code+XkbEventCode)) {
180 xkbev=(XkbEvent *)ev;
181 if ((xkbev->any.xkb_type==XkbStateNotify)||(xkbev->any.xkb_type==XkbNewKeyboardNotify)) {
182 if (xkbev->any.xkb_type==XkbNewKeyboardNotify) {
183 xkeyboard_groups_free(xkeyboard);
184 xkeyboard_layout(xkeyboard);
185 }
186 flo_debug(TRACE_DEBUG, _("XKB state notify event received"));
187 if (xkeyboard->user_data) xkeyboard->event_cb(xkeyboard->user_data);
188 }
189 }
190 END_FUNC
191 return GDK_FILTER_CONTINUE;
192 }
193
194 #endif
195
196 /* returns the next layout name */
xkeyboard_next_layout_get(struct xkeyboard * xkeyboard)197 gchar *xkeyboard_next_layout_get(struct xkeyboard *xkeyboard)
198 {
199 START_FUNC
200 #ifdef ENABLE_XKB
201 guint newgroup;
202 XkbStateRec xkbState;
203 Display *disp=(Display *)gdk_x11_get_default_xdisplay();
204 XkbGetState(disp, XkbUseCoreKbd, &xkbState);
205 newgroup=(xkbState.group+1)%g_list_length(xkeyboard->groups);
206 END_FUNC
207 return (gchar *)g_list_nth_data(xkeyboard->groups, newgroup);
208 #else
209 END_FUNC
210 return (gchar *)g_list_nth_data(xkeyboard->groups, 0);
211 #endif
212 }
213
214 /* switch keyboard layout */
xkeyboard_layout_change(struct xkeyboard * xkeyboard)215 void xkeyboard_layout_change(struct xkeyboard *xkeyboard)
216 {
217 START_FUNC
218 #ifdef ENABLE_XKB
219 guint newgroup;
220 XkbStateRec xkbState;
221 Display *disp=(Display *)gdk_x11_get_default_xdisplay();
222 XkbGetState(disp, XkbUseCoreKbd, &xkbState);
223 newgroup=(xkbState.group+1)%g_list_length(xkeyboard->groups);
224 if (XkbLockGroup(disp, XkbUseCoreKbd, newgroup)) {
225 flo_debug(TRACE_DEBUG, _("switching to xkb layout %s"),
226 g_list_nth_data(xkeyboard->groups, newgroup));
227 } else flo_warn(_("Failed to switch xkb layout"));
228 #endif
229 END_FUNC
230 }
231
232 /* register xkb events */
xkeyboard_register_events(struct xkeyboard * xkeyboard,xkeyboard_layout_changed event_cb,gpointer user_data)233 void xkeyboard_register_events(struct xkeyboard *xkeyboard, xkeyboard_layout_changed event_cb, gpointer user_data)
234 {
235 START_FUNC
236 xkeyboard->event_cb=event_cb;
237 xkeyboard->user_data=user_data;
238 END_FUNC
239 }
240
241 /* get keyval according to modifier */
242 /* TODO: cache group state value */
243 /* TODO: --without-xkb is broken */
xkeyboard_getKeyval(struct xkeyboard * xkeyboard,guint code,GdkModifierType mod)244 guint xkeyboard_getKeyval(struct xkeyboard *xkeyboard, guint code, GdkModifierType mod)
245 {
246 START_FUNC
247 guint keyval=0;
248 XkbStateRec xkbState;
249 Display *disp=(Display *)gdk_x11_get_default_xdisplay();
250 XkbGetState(disp, XkbUseCoreKbd, &xkbState);
251 if (!gdk_keymap_translate_keyboard_state(gdk_keymap_get_default(), code, mod, xkbState.group,
252 &keyval, NULL, NULL, NULL)) {
253 keyval=0;
254 }
255 END_FUNC
256 return keyval;
257 }
258
259 /* get the key properties (locker and modifier) from xkb */
xkeyboard_key_properties_get(struct xkeyboard * xkeyboard,guint code,GdkModifierType * mod,gboolean * locker)260 void xkeyboard_key_properties_get(struct xkeyboard *xkeyboard, guint code, GdkModifierType *mod, gboolean *locker)
261 {
262 START_FUNC
263 *locker=FALSE;
264 *mod=0;
265 #ifdef ENABLE_XKB
266 *mod=xkeyboard->xkb_desc->map->modmap[code];
267 if (XkbKeyAction(xkeyboard->xkb_desc, code, 0)) {
268 switch (XkbKeyAction(xkeyboard->xkb_desc, code, 0)->type) {
269 case XkbSA_LockMods:*locker=TRUE; break;
270 case XkbSA_SetMods:*mod=XkbKeyAction(xkeyboard->xkb_desc, code, 0)->mods.mask;
271 break;
272 }
273 }
274 #else
275 gchar *symbolname=gdk_keyval_name(xkeyboard_getKeyval(xkeyboard, code, 0));
276 if (symbolname) {
277 if ((!strcmp(symbolname, "Caps_Lock"))) {
278 *locker=TRUE;
279 *mod=2;
280 } else if ((!strcmp(symbolname, "Num_Lock"))) {
281 *locker=TRUE;
282 *mod=16;
283 } else if ((!strcmp(symbolname, "Shift_L")) ||
284 (!strcmp(symbolname, "Shift_R"))) {
285 *mod=1;
286 } else if ((!strcmp(symbolname, "Alt_L")) ||
287 (!strcmp(symbolname, "Alt_R"))) {
288 *mod=8;
289 } else if ((!strcmp(symbolname, "Control_L")) ||
290 (!strcmp(symbolname, "Control_R"))) {
291 *mod=4;
292 } else if ((!strcmp(symbolname, "ISO_Level3_Shift"))) {
293 *mod=128;
294 }
295 }
296 #endif
297 END_FUNC
298 }
299
300 /* returns a new allocated structure containing data from xkb */
xkeyboard_new()301 struct xkeyboard *xkeyboard_new()
302 {
303 START_FUNC
304 struct xkeyboard *xkeyboard=g_malloc(sizeof(struct xkeyboard));
305 if (!xkeyboard) flo_fatal(_("Unable to allocate memory for xkeyboard data"));
306 memset(xkeyboard, 0, sizeof(struct xkeyboard));
307
308 #ifdef ENABLE_XKB
309 int maj=XkbMajorVersion;
310 int min=XkbMinorVersion;
311 int opcode_rtrn=0, error_rtrn=0;
312 int ret=0;
313
314 /* Check XKB Version */
315 if (!(ret=XkbLibraryVersion(&maj, &min))) {
316 flo_fatal(_("Unable to initialize XKB library. version=%d.%d rc=%d"), maj, min, ret);
317 }
318 if (!(ret=XkbQueryExtension((Display *)gdk_x11_get_default_xdisplay(),
319 &opcode_rtrn, &(xkeyboard->base_event_code), &error_rtrn, &maj, &min))) {
320 flo_fatal(_("Unable to query XKB extension from X server version=%d.%d rc=%d"), maj, min, ret);
321 }
322
323 xkeyboard_layout(xkeyboard);
324 XkbSelectEvents(gdk_x11_display_get_xdisplay(gdk_display_get_default()),
325 XkbUseCoreKbd, XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask);
326 XkbSelectEventDetails(gdk_x11_display_get_xdisplay(gdk_display_get_default()),
327 XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask);
328 gdk_window_add_filter(NULL, xkeyboard_event_handler, xkeyboard);
329
330 /* get the modifier map from xkb */
331 xkeyboard->xkb_desc=XkbGetMap((Display *)gdk_x11_get_default_xdisplay(),
332 XkbKeyActionsMask|XkbModifierMapMask, XkbUseCoreKbd);
333 /* get modifiers state */
334 XkbGetState((Display *)gdk_x11_get_default_xdisplay(), XkbUseCoreKbd, &(xkeyboard->xkb_state));
335 if (!xkeyboard->groups) {
336 flo_warn(_("No xkb group found. Using default us group"));
337 xkeyboard_list_append("us", &(xkeyboard->groups));
338 }
339 #else
340 flo_warn(_("XKB not compiled: startup keyboard sync is disabled. You should make sure all locker keys are released."));
341 #endif
342 END_FUNC
343 return xkeyboard;
344 }
345
346 /* liberate memory used by the modifier map */
xkeyboard_client_map_free(struct xkeyboard * xkeyboard)347 void xkeyboard_client_map_free(struct xkeyboard *xkeyboard)
348 {
349 START_FUNC
350 #ifdef ENABLE_XKB
351 /* Free the modifiers map */
352 XkbFreeClientMap(xkeyboard->xkb_desc, XkbKeyActionsMask|XkbModifierMapMask, True);
353 #endif
354 END_FUNC
355 }
356
357 /* liberate any memory used to record xkb data */
xkeyboard_free(struct xkeyboard * xkeyboard)358 void xkeyboard_free(struct xkeyboard *xkeyboard)
359 {
360 START_FUNC
361 xkeyboard_groups_free(xkeyboard);
362 g_free(xkeyboard);
363 END_FUNC
364 }
365
366