1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
15  *
16  *  Copyright (C) 2006-2016 XNeur Team
17  *
18  */
19 
20 #include <X11/XKBlib.h>
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 
26 #include "xnconfig.h"
27 
28 #include "types.h"
29 #include "utils.h"
30 #include "log.h"
31 #include "list_char.h"
32 
33 #include "window.h"
34 #include "keymap.h"
35 
36 #include "bind_table.h"
37 
38 static struct _bind_table *ubtable;
39 
40 static struct _bind_table *btable;
41 
42 static const char *normal_action_names[] =	{
43 										"Correct/Undo correction", "Transliterate", "Change case", "Preview correction",
44 										"Correct last line",
45 										"Correct selected text", "Transliterate selected text", "Change case of selected text", "Preview correction of selected text",
46 	                                    "Correct clipboard text", "Transliterate clipboard text", "Change case of clipboard text", "Preview correction of clipboard text",
47 										"Switch to layout 1", "Switch to layout 2", "Switch to layout 3", "Switch to layout 4",
48 		                                "Rotate layouts", "Rotate layouts back", "Expand abbreviations", "Autocompletion confirmation",
49 										"Rotate autocompletion", "Insert date"
50 						};
51 
52 static const char *modifier_names[] =	{"Shift", "Control", "Alt", "Super"};
53 
54 extern struct _xneur_config *xconfig;
55 extern struct _window *main_window;
56 
hotkey_concat_bind(struct _xneur_hotkey * hotkey)57 static char* hotkey_concat_bind(struct _xneur_hotkey * hotkey)
58 {
59 	char *text = (char *) malloc((24 + 1 + strlen(hotkey->key)) * sizeof(char));
60 	text[0] = '\0';
61 
62 	int total_modifiers	= sizeof(modifier_names) / sizeof(modifier_names[0]);
63 	for (int i = 0; i < total_modifiers; i++)
64 	{
65 		if ((hotkey->modifiers & (0x1 << i)) == 0)
66 			continue;
67 
68 		strcat(text, modifier_names[i]);
69 		strcat(text, "+");
70 	}
71 
72 	strcat(text, hotkey->key);
73 
74 	return text;
75 }
76 
create_modifier_mask(int modifiers)77 static int create_modifier_mask(int modifiers){
78 	// This function should be single point to edit modifiers
79 	int modifier_mask = 0;
80 	if (modifiers & 0x01)
81 		modifier_mask += 1; // Shift
82 	if (modifiers & 0x02)
83 		modifier_mask += 4; // Control
84 	if (modifiers & 0x04)
85 		modifier_mask += 8; // Alt
86 	if (modifiers & 0x08)
87 		modifier_mask += 64;// Win
88 	return modifier_mask;
89 }
90 
bind_action(enum _hotkey_action action)91 static void bind_action(enum _hotkey_action action)
92 {
93 	btable[action].key_sym       = 0;
94 	btable[action].key_sym_shift = 0;
95 	btable[action].key_code      = 0;
96 	btable[action].modifier_mask = 0;
97 
98 	if (xconfig->actions[action].hotkey.key == NULL)
99 	{
100 		log_message(DEBUG, _("   No key set for action \"%s\""), _(normal_action_names[xconfig->actions[action].action]));
101 		return;
102 	}
103 
104 	btable[action].modifier_mask = create_modifier_mask(xconfig->actions[action].hotkey.modifiers);
105 
106 	KeySym key_sym, key_sym_shift;
107 	key_sym = NoSymbol;
108 	key_sym_shift = NoSymbol;
109 	main_window->keymap->get_keysyms_by_string(main_window->keymap, xconfig->actions[action].hotkey.key, &key_sym, &key_sym_shift);
110 	if (key_sym == NoSymbol)
111 		key_sym = None;
112 	if (key_sym_shift == NoSymbol)
113 		key_sym_shift = key_sym;
114 
115 	btable[action].key_sym = key_sym;
116 	btable[action].key_sym_shift = key_sym_shift;
117 	btable[action].key_code = XKeysymToKeycode(main_window->display, key_sym);
118 
119 	char *key = hotkey_concat_bind (&(xconfig->actions[action].hotkey));
120 	log_message(DEBUG, _("   Action \"%s\" with key \"%s\""), _(normal_action_names[xconfig->actions[action].action]), key);
121 	//log_message(ERROR, _("      KeySym %d (%d) keycode %d"), key_sym, key_sym_shift, ubtable[action].key_code);
122 	if ((key_sym == None) || (key_sym_shift == None))
123 	{
124 		log_message(ERROR, _("      KeySym (or with Shift modifier) is undefined!"), _(normal_action_names[action]), key);
125 
126 	}
127 	if (key != NULL)
128 		free(key);
129 }
130 
bind_user_action(int action)131 static void bind_user_action(int action)
132 {
133 	ubtable[action].key_sym       = 0;
134 	ubtable[action].key_sym_shift = 0;
135 	ubtable[action].key_code      = 0;
136 	ubtable[action].modifier_mask = 0;
137 
138 	if (xconfig->user_actions[action].hotkey.key == NULL)
139 	{
140 		log_message(DEBUG, _("   No key set for action \"%s\""), xconfig->user_actions[action].name);
141 		return;
142 	}
143 
144 	ubtable[action].modifier_mask = create_modifier_mask(xconfig->user_actions[action].hotkey.modifiers);
145 
146 	KeySym key_sym, key_sym_shift;
147 	key_sym = NoSymbol;
148 	key_sym_shift = NoSymbol;
149 	main_window->keymap->get_keysyms_by_string(main_window->keymap, xconfig->user_actions[action].hotkey.key, &key_sym, &key_sym_shift);
150 	if (key_sym == NoSymbol)
151 		key_sym = None;
152 	if (key_sym_shift == NoSymbol)
153 		key_sym_shift = key_sym;
154 
155 	ubtable[action].key_sym = key_sym;
156 	ubtable[action].key_sym_shift = key_sym_shift;
157 	ubtable[action].key_code = XKeysymToKeycode(main_window->display, key_sym);
158 
159 	char *key = hotkey_concat_bind (&(xconfig->user_actions[action].hotkey));
160 	log_message(DEBUG, _("   Action \"%s\" with key \"%s\""), xconfig->user_actions[action].name, key);
161 	//log_message(ERROR, _("      KeySym %d (%d) keycode %d"), key_sym, key_sym_shift, ubtable[action].key_code);
162 	if ((key_sym == None) || (key_sym_shift == None))
163 	{
164 		log_message(ERROR, _("      KeySym (or with Shift modifier) is undefined!"));
165 
166 	}
167 	if (key != NULL)
168 		free(key);
169 }
170 
get_action(KeySym key_sym,int mask)171 enum _hotkey_action get_action(KeySym key_sym, int mask)
172 {
173 	// Reset Caps and Num mask
174 	if (key_sym != XK_Caps_Lock)
175 		mask &= ~LockMask;
176 	if (key_sym != XK_Num_Lock)
177 		mask &= ~Mod2Mask;
178 	if (key_sym != XK_Scroll_Lock)
179 		mask &= ~Mod3Mask;
180 
181 	KeyCode kc = XKeysymToKeycode(main_window->display, key_sym);
182 	if (IsModifierKey(key_sym))
183 	{
184 		if (key_sym == XK_Shift_L || key_sym == XK_Shift_R)
185 			mask -= (1 << 0);
186 		if (key_sym == XK_Caps_Lock)
187 			mask -= (1 << 1);
188 		if (key_sym == XK_Control_L || key_sym == XK_Control_R)
189 			mask -= (1 << 2);
190 		if (key_sym == XK_Alt_L || key_sym == XK_Alt_R)
191 			mask -= (1 << 3);
192 		if (key_sym == XK_Meta_L || key_sym == XK_Meta_R)
193 			mask -= (1 << 4);
194 		if (key_sym == XK_Num_Lock)
195 			mask -= (1 << 5);
196 		if (key_sym == XK_Super_L || key_sym == XK_Super_R)
197 			mask -= (1 << 6);
198 		if (key_sym == XK_Hyper_L || key_sym == XK_Hyper_R || key_sym == XK_ISO_Level3_Shift)
199 			mask += (1 << 7);
200 	}
201 
202 	for (int action = 0; action < xconfig->actions_count; action++)
203 	{
204 		//log_message (ERROR, "A%d---bt:(%d)%d, ac:(%d)%d", action, btable[action].modifier_mask, btable[action].key_code, mask, kc);
205 		//if (btable[action].key_sym != key_sym && btable[action].key_sym_shift != key_sym)
206 		//	continue;
207 		if (btable[action].key_code != kc)
208 			continue;
209 		if (btable[action].modifier_mask == mask)
210 		{
211 			return action;
212 		}
213 	}
214 
215 	return ACTION_NONE;
216 }
217 
bind_actions(void)218 void bind_actions(void)
219 {
220 	log_message(DEBUG, _("Binded hotkeys actions:"));
221 	btable = (struct _bind_table *) malloc(xconfig->actions_count * sizeof(struct _bind_table));
222 	for (int action = 0; action < xconfig->actions_count; action++)
223 		bind_action(action);
224 }
225 
unbind_actions(void)226 void unbind_actions(void)
227 {
228 	if (btable != NULL)
229 		free(btable);
230 	btable = NULL;
231 }
232 
get_user_action(KeySym key_sym,int mask)233 int get_user_action(KeySym key_sym, int mask)
234 {
235 	// Reset Caps and Num mask
236 	if (key_sym != XK_Caps_Lock)
237 		mask &= ~LockMask;
238 	if (key_sym != XK_Num_Lock)
239 		mask &= ~Mod2Mask;
240 	if (key_sym != XK_Scroll_Lock)
241 		mask &= ~Mod3Mask;
242 
243 	KeyCode kc = XKeysymToKeycode(main_window->display, key_sym);
244 	if (IsModifierKey(key_sym))
245 	{
246 		if (key_sym == XK_Shift_L || key_sym == XK_Shift_R)
247 			mask -= (1 << 0);
248 		if (key_sym == XK_Caps_Lock)
249 			mask -= (1 << 1);
250 		if (key_sym == XK_Control_L || key_sym == XK_Control_R)
251 			mask -= (1 << 2);
252 		if (key_sym == XK_Alt_L || key_sym == XK_Alt_R)
253 			mask -= (1 << 3);
254 		if (key_sym == XK_Meta_L || key_sym == XK_Meta_R)
255 			mask -= (1 << 4);
256 		if (key_sym == XK_Num_Lock)
257 			mask -= (1 << 5);
258 		if (key_sym == XK_Super_L || key_sym == XK_Super_R)
259 			mask -= (1 << 6);
260 		if (key_sym == XK_Hyper_L || key_sym == XK_Hyper_R || key_sym == XK_ISO_Level3_Shift)
261 			mask += (1 << 7);
262 	}
263 
264 	for (int action = 0; action < xconfig->user_actions_count; action++)
265 	{
266 		//log_message (ERROR, "U%d---bt:(%d)%d, ac:(%d)%d", action, ubtable[action].modifier_mask, ubtable[action].key_code, mask, kc);
267 		//if (ubtable[action].key_sym != key_sym && ubtable[action].key_sym_shift != key_sym)
268 		//	continue;
269 		if (ubtable[action].key_code != kc)
270 			continue;
271 		if (ubtable[action].modifier_mask == mask)
272 		{
273 			return action;
274 		}
275 	}
276 	return -1;
277 }
278 
bind_user_actions(void)279 void bind_user_actions(void)
280 {
281 	log_message(DEBUG, _("Binded hotkeys user actions:"));
282 	ubtable = (struct _bind_table *) malloc(xconfig->user_actions_count * sizeof(struct _bind_table));
283 	for (int action = 0; action < xconfig->user_actions_count; action++)
284 		bind_user_action(action);
285 }
286 
unbind_user_actions(void)287 void unbind_user_actions(void)
288 {
289 	if (ubtable != NULL)
290 		free(ubtable);
291 	ubtable = NULL;
292 }
293 
grab_action(Window window)294 void grab_action(Window window)
295 {
296 	for (enum _hotkey_action action = 0; action < MAX_HOTKEYS; action++)
297 	{
298 		grab_action_common(btable[action],window);
299 	}
300 }
301 
grab_user_action(Window window)302 void grab_user_action(Window window)
303 {
304 	for (int action = 0; action < xconfig->actions_count; action++)
305 	{
306 		grab_action_common(ubtable[action],window);
307 	}
308 }
309 
grab_action_common(struct _bind_table btaction,Window window)310 void grab_action_common(struct _bind_table btaction, Window window)
311 {
312 	if (window){};
313 	if (btaction.key_sym == 0)
314 		return;
315 
316 	if (IsModifierKey(btaction.key_sym))
317 	    return;
318 
319 	XGrabKey(main_window->display,
320 				XKeysymToKeycode(main_window->display, btaction.key_sym),
321 				btaction.modifier_mask,
322 				DefaultRootWindow(main_window->display),
323 	         	FALSE, GrabModeAsync, GrabModeAsync);
324 
325 	if (main_window->keymap->numlock_mask)
326 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
327 					btaction.modifier_mask | main_window->keymap->numlock_mask,
328 					DefaultRootWindow(main_window->display),
329 					FALSE, GrabModeAsync, GrabModeAsync);
330 
331 	if (main_window->keymap->capslock_mask)
332 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
333 					btaction.modifier_mask | main_window->keymap->capslock_mask,
334 					DefaultRootWindow(main_window->display),
335 					FALSE, GrabModeAsync, GrabModeAsync);
336 
337 	if (main_window->keymap->scrolllock_mask)
338 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
339 					btaction.modifier_mask | main_window->keymap->scrolllock_mask,
340 					DefaultRootWindow(main_window->display),
341 					FALSE, GrabModeAsync, GrabModeAsync);
342 
343 	if (main_window->keymap->numlock_mask && main_window->keymap->capslock_mask)
344 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
345 					btaction.modifier_mask | main_window->keymap->numlock_mask | main_window->keymap->capslock_mask,
346 					DefaultRootWindow(main_window->display),
347 					FALSE, GrabModeAsync, GrabModeAsync);
348 
349 	if (main_window->keymap->numlock_mask && main_window->keymap->scrolllock_mask)
350 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
351 					btaction.modifier_mask | main_window->keymap->numlock_mask | main_window->keymap->scrolllock_mask,
352 					DefaultRootWindow(main_window->display),
353 					FALSE, GrabModeAsync, GrabModeAsync);
354 
355 	if (main_window->keymap->capslock_mask && main_window->keymap->scrolllock_mask)
356 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
357 					btaction.modifier_mask | main_window->keymap->capslock_mask | main_window->keymap->scrolllock_mask,
358 					DefaultRootWindow(main_window->display),
359 					FALSE, GrabModeAsync, GrabModeAsync);
360 
361 	if (main_window->keymap->numlock_mask && main_window->keymap->capslock_mask && main_window->keymap->scrolllock_mask)
362 			XGrabKey (main_window->display, XKeysymToKeycode(main_window->display, btaction.key_sym),
363 					btaction.modifier_mask | main_window->keymap->numlock_mask | main_window->keymap->capslock_mask | main_window->keymap->scrolllock_mask,
364 					DefaultRootWindow(main_window->display),
365 					FALSE, GrabModeAsync, GrabModeAsync);
366 
367 }
368