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