1 /*
2  * Copyright (C) 2002-2006 Sergey V. Udaltsov <svu@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include <time.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include <X11/Xmd.h>
25 #include <X11/Xatom.h>
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/keysym.h>
29 
30 #include "config.h"
31 
32 #include "xklavier_private.h"
33 #include "xklavier_private_xmm.h"
34 
35 #define SHORTCUT_OPTION_PREFIX "grp:"
36 
37 const gchar **
xkl_xmm_get_groups_names(XklEngine * engine)38 xkl_xmm_get_groups_names(XklEngine * engine)
39 {
40 	return (const gchar **) xkl_engine_backend(engine, XklXmm,
41 						   current_config).layouts;
42 }
43 
44 const gchar **
xkl_xmm_get_indicators_names(XklEngine * engine)45 xkl_xmm_get_indicators_names(XklEngine * engine)
46 {
47 	return NULL;
48 }
49 
50 void
xkl_xmm_shortcuts_grab(XklEngine * engine)51 xkl_xmm_shortcuts_grab(XklEngine * engine)
52 {
53 	const XmmShortcut *shortcut;
54 	const XmmSwitchOption *option =
55 	    xkl_xmm_shortcut_get_current(engine);
56 
57 	xkl_debug(150, "Found shortcut option: %p\n", option);
58 	if (option == NULL)
59 		return;
60 
61 	shortcut = option->shortcuts;
62 	while (shortcut->keysym != XK_VoidSymbol) {
63 		int keycode =
64 		    XKeysymToKeycode(xkl_engine_get_display(engine),
65 				     shortcut->keysym);
66 		xkl_xmm_grab_ignoring_indicators(engine, keycode,
67 						 shortcut->modifiers);
68 		shortcut++;
69 	}
70 }
71 
72 void
xkl_xmm_shortcuts_ungrab(XklEngine * engine)73 xkl_xmm_shortcuts_ungrab(XklEngine * engine)
74 {
75 	const XmmShortcut *shortcut;
76 	const XmmSwitchOption *option =
77 	    xkl_xmm_shortcut_get_current(engine);
78 
79 	if (option == NULL)
80 		return;
81 
82 	shortcut = option->shortcuts;
83 	while (shortcut->keysym != XK_VoidSymbol) {
84 		int keycode =
85 		    XKeysymToKeycode(xkl_engine_get_display(engine),
86 				     shortcut->keysym);
87 		xkl_xmm_ungrab_ignoring_indicators(engine, keycode,
88 						   shortcut->modifiers);
89 		shortcut++;
90 	}
91 }
92 
93 XmmSwitchOption *
xkl_xmm_shortcut_get_current(XklEngine * engine)94 xkl_xmm_shortcut_get_current(XklEngine * engine)
95 {
96 	const gchar *option_name =
97 	    xkl_xmm_shortcut_get_current_option_name(engine);
98 
99 	xkl_debug(150, "Configured switch option: [%s]\n", option_name);
100 
101 	if (option_name == NULL)
102 		return NULL;
103 
104 	return (XmmSwitchOption *)
105 	    g_hash_table_lookup(xkl_engine_backend
106 				(engine, XklXmm, switch_options),
107 				(gconstpointer) option_name);
108 }
109 
110 const gchar *
xkl_xmm_shortcut_get_current_option_name(XklEngine * engine)111 xkl_xmm_shortcut_get_current_option_name(XklEngine * engine)
112 {
113 	gchar **option =
114 	    xkl_engine_backend(engine, XklXmm, current_config).options;
115 	if (option == NULL)
116 		return NULL;
117 
118 	while (*option != NULL) {
119 		/* starts with "grp:" */
120 		if (strstr(*option, SHORTCUT_OPTION_PREFIX) != NULL) {
121 			return *option + sizeof SHORTCUT_OPTION_PREFIX - 1;
122 		}
123 		option++;
124 	}
125 	return NULL;
126 }
127 
128 const XmmSwitchOption *
xkl_xmm_find_switch_option(XklEngine * engine,gint keycode,guint state,gint * current_shortcut_rv)129 xkl_xmm_find_switch_option(XklEngine * engine, gint keycode,
130 			   guint state, gint * current_shortcut_rv)
131 {
132 	const XmmSwitchOption *rv = xkl_xmm_shortcut_get_current(engine);
133 
134 	if (rv != NULL) {
135 		const XmmShortcut *sc = rv->shortcuts;
136 		while (sc->keysym != XK_VoidSymbol) {
137 			if ((XKeysymToKeycode
138 			     (xkl_engine_get_display(engine),
139 			      sc->keysym) == keycode)
140 			    && ((state & sc->modifiers) == sc->modifiers)) {
141 				return rv;
142 			}
143 			sc++;
144 		}
145 	}
146 	return NULL;
147 }
148 
149 gint
xkl_xmm_resume_listen(XklEngine * engine)150 xkl_xmm_resume_listen(XklEngine * engine)
151 {
152 	if (xkl_engine_is_listening_for(engine, XKLL_MANAGE_LAYOUTS))
153 		xkl_xmm_shortcuts_grab(engine);
154 	return 0;
155 }
156 
157 gint
xkl_xmm_pause_listen(XklEngine * engine)158 xkl_xmm_pause_listen(XklEngine * engine)
159 {
160 	if (xkl_engine_is_listening_for(engine, XKLL_MANAGE_LAYOUTS))
161 		xkl_xmm_shortcuts_ungrab(engine);
162 	return 0;
163 }
164 
165 guint
xkl_xmm_get_max_num_groups(XklEngine * engine)166 xkl_xmm_get_max_num_groups(XklEngine * engine)
167 {
168 	return 0;
169 }
170 
171 guint
xkl_xmm_get_num_groups(XklEngine * engine)172 xkl_xmm_get_num_groups(XklEngine * engine)
173 {
174 	gint rv = 0;
175 	gchar **p =
176 	    xkl_engine_backend(engine, XklXmm, current_config).layouts;
177 	if (p != NULL)
178 		while (*p++ != NULL)
179 			rv++;
180 	return rv;
181 }
182 
183 void
xkl_xmm_free_all_info(XklEngine * engine)184 xkl_xmm_free_all_info(XklEngine * engine)
185 {
186 	gchar *current_rules =
187 	    xkl_engine_backend(engine, XklXmm, current_rules);
188 	if (current_rules != NULL) {
189 		g_free(current_rules);
190 		current_rules = NULL;
191 		xkl_engine_backend(engine, XklXmm, current_rules) = NULL;
192 	}
193 	xkl_config_rec_reset(&xkl_engine_backend
194 			     (engine, XklXmm, current_config));
195 }
196 
197 gboolean
xkl_xmm_if_cached_info_equals_actual(XklEngine * engine)198 xkl_xmm_if_cached_info_equals_actual(XklEngine * engine)
199 {
200 	return FALSE;
201 }
202 
203 gboolean
xkl_xmm_load_all_info(XklEngine * engine)204 xkl_xmm_load_all_info(XklEngine * engine)
205 {
206 	return
207 	    xkl_config_rec_get_full_from_server(&xkl_engine_backend
208 						(engine, XklXmm,
209 						 current_rules),
210 						&xkl_engine_backend(engine,
211 								    XklXmm,
212 								    current_config),
213 						engine);
214 }
215 
216 void
xkl_xmm_get_server_state(XklEngine * engine,XklState * state)217 xkl_xmm_get_server_state(XklEngine * engine, XklState * state)
218 {
219 	unsigned char *propval = NULL;
220 	Atom actual_type;
221 	int actual_format;
222 	unsigned long bytes_remaining;
223 	unsigned long actual_items;
224 	int result;
225 
226 	memset(state, 0, sizeof(*state));
227 
228 	result =
229 	    XGetWindowProperty(xkl_engine_get_display(engine),
230 			       xkl_engine_priv(engine, root_window),
231 			       xkl_engine_backend(engine, XklXmm,
232 						  state_atom), 0L, 1L,
233 			       False, XA_INTEGER, &actual_type,
234 			       &actual_format, &actual_items,
235 			       &bytes_remaining, &propval);
236 
237 	if (Success == result) {
238 		if (actual_format == 32 || actual_items == 1) {
239 			state->group = *(CARD32 *) propval;
240 		} else {
241 			xkl_debug(160,
242 				  "Could not get the xmodmap current group\n");
243 		}
244 		XFree(propval);
245 	} else {
246 		xkl_debug(160,
247 			  "Could not get the xmodmap current group: %d\n",
248 			  result);
249 	}
250 }
251 
252 void
xkl_xmm_actualize_group(XklEngine * engine,gint group)253 xkl_xmm_actualize_group(XklEngine * engine, gint group)
254 {
255 	char cmd[1024];
256 	int res;
257 	const gchar *layout_name = NULL;
258 
259 	if (xkl_xmm_get_num_groups(engine) < group)
260 		return;
261 
262 	layout_name =
263 	    xkl_engine_backend(engine, XklXmm,
264 			       current_config).layouts[group];
265 
266 	g_snprintf(cmd, sizeof cmd,
267 		   "xmodmap %s/xmodmap.%s", XMODMAP_BASE, layout_name);
268 
269 	res = system(cmd);
270 	if (res > 0) {
271 		xkl_debug(0, "xmodmap error %d\n", res);
272 	} else if (res < 0) {
273 		xkl_debug(0, "Could not execute xmodmap: %d\n", res);
274 	}
275 	XSync(xkl_engine_get_display(engine), False);
276 }
277 
278 void
xkl_xmm_lock_group(XklEngine * engine,gint group)279 xkl_xmm_lock_group(XklEngine * engine, gint group)
280 {
281 	CARD32 propval;
282 	Display *display;
283 
284 	if (xkl_xmm_get_num_groups(engine) < group)
285 		return;
286 
287 	/* updating the status property */
288 	propval = group;
289 	display = xkl_engine_get_display(engine);
290 	XChangeProperty(display, xkl_engine_priv(engine, root_window),
291 			xkl_engine_backend(engine, XklXmm, state_atom),
292 			XA_INTEGER, 32, PropModeReplace,
293 			(unsigned char *) &propval, 1);
294 	XSync(display, False);
295 }
296 
297 void
xkl_xmm_set_indicators(XklEngine * engine,const XklState * window_state)298 xkl_xmm_set_indicators(XklEngine * engine, const XklState * window_state)
299 {
300 }
301 
302 
303 gint
xkl_xmm_init(XklEngine * engine)304 xkl_xmm_init(XklEngine * engine)
305 {
306 	Display *display;
307 
308 	xkl_engine_priv(engine, backend_id) = "xmodmap";
309 	xkl_engine_priv(engine, features) =
310 	    XKLF_MULTIPLE_LAYOUTS_SUPPORTED |
311 	    XKLF_REQUIRES_MANUAL_LAYOUT_MANAGEMENT;
312 	xkl_engine_priv(engine, activate_config_rec) =
313 	    xkl_xmm_activate_config_rec;
314 	xkl_engine_priv(engine, init_config_registry) =
315 	    xkl_xmm_init_config_registry;
316 	xkl_engine_priv(engine, load_config_registry) =
317 	    xkl_xmm_load_config_registry;
318 	xkl_engine_priv(engine, write_config_rec_to_file) = NULL;
319 
320 	xkl_engine_priv(engine, get_groups_names) =
321 	    xkl_xmm_get_groups_names;
322 	xkl_engine_priv(engine, get_indicators_names) =
323 	    xkl_xmm_get_indicators_names;
324 	xkl_engine_priv(engine, get_max_num_groups) =
325 	    xkl_xmm_get_max_num_groups;
326 	xkl_engine_priv(engine, get_num_groups) = xkl_xmm_get_num_groups;
327 	xkl_engine_priv(engine, lock_group) = xkl_xmm_lock_group;
328 
329 	xkl_engine_priv(engine, process_x_event) = xkl_xmm_process_x_event;
330 	xkl_engine_priv(engine, process_x_error) = NULL;
331 	xkl_engine_priv(engine, free_all_info) = xkl_xmm_free_all_info;
332 	xkl_engine_priv(engine, if_cached_info_equals_actual) =
333 	    xkl_xmm_if_cached_info_equals_actual;
334 	xkl_engine_priv(engine, load_all_info) = xkl_xmm_load_all_info;
335 	xkl_engine_priv(engine, get_server_state) =
336 	    xkl_xmm_get_server_state;
337 	xkl_engine_priv(engine, pause_listen) = xkl_xmm_pause_listen;
338 	xkl_engine_priv(engine, resume_listen) = xkl_xmm_resume_listen;
339 	xkl_engine_priv(engine, set_indicators) = xkl_xmm_set_indicators;
340 	xkl_engine_priv(engine, finalize) = xkl_xmm_term;
341 
342 	if (getenv("XKL_XMODMAP_DISABLE") != NULL)
343 		return -1;
344 
345 	display = xkl_engine_get_display(engine);
346 	xkl_engine_priv(engine, base_config_atom) =
347 	    XInternAtom(display, "_XMM_NAMES", False);
348 	xkl_engine_priv(engine, backup_config_atom) =
349 	    XInternAtom(display, "_XMM_NAMES_BACKUP", False);
350 
351 	xkl_engine_priv(engine, backend) = g_new0(XklXmm, 1);
352 
353 	xkl_engine_backend(engine, XklXmm, state_atom) =
354 	    XInternAtom(display, "_XMM_STATE", False);
355 
356 	xkl_engine_priv(engine, default_model) = "generic";
357 	xkl_engine_priv(engine, default_layout) = "us";
358 
359 	xkl_xmm_init_switch_options((XklXmm *)
360 				    xkl_engine_priv(engine, backend));
361 
362 	return 0;
363 }
364 
365 void
xkl_xmm_term(XklEngine * engine)366 xkl_xmm_term(XklEngine * engine)
367 {
368 	xkl_xmm_term_switch_options((XklXmm *)
369 				    xkl_engine_priv(engine, backend));
370 }
371