1 /*
2 Copyright (c) 2009-2011 Tero Lindeman (kometbomb)
3 
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation
6 files (the "Software"), to deal in the Software without
7 restriction, including without limitation the rights to use,
8 copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following
11 conditions:
12 
13 The above copyright notice and this permission notice shall be
14 included in all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24 */
25 
26 
27 #include "key.h"
28 #include "gui/menu.h"
29 #include <unistd.h>
30 #include <dirent.h>
31 #include <sys/stat.h>
32 #include "action.h"
33 #include "mused.h"
34 #include "keytab.h"
35 #include <string.h>
36 #include "theme.h"
37 
38 extern Mused mused;
39 
40 #define MAX_KEYMAPS 64
41 #define MAX_KEYTRANS 500
42 
43 Menu keymapmenu[MAX_KEYMAPS + 1];
44 extern const Menu prefsmenu[];
45 static KeyTran keytrans[MAX_KEYTRANS];
46 
47 
translate_key_event(SDL_KeyboardEvent * e)48 void translate_key_event(SDL_KeyboardEvent *e)
49 {
50 	const int allowed = KMOD_SHIFT|KMOD_CTRL|KMOD_ALT;
51 
52 	for (int i = 0 ; i < MAX_KEYTRANS && !(keytrans[i].from_key == 0 && keytrans[i].from_mod == 0 && keytrans[i].from_scancode == 0) ; ++i)
53 	{
54 		if ((keytrans[i].focus == mused.focus || keytrans[i].focus == -1) &&
55 			((keytrans[i].type == KEYSYM && e->keysym.sym == keytrans[i].from_key) ||
56 			(keytrans[i].type == SCANCODE && e->keysym.scancode == keytrans[i].from_scancode)) &&
57 			((e->keysym.mod & allowed) == keytrans[i].from_mod))
58 		{
59 			e->keysym.sym = keytrans[i].to_key;
60 			e->keysym.mod = keytrans[i].to_mod;
61 			break;
62 		}
63 	}
64 }
65 
66 
enum_keymaps()67 void enum_keymaps()
68 {
69 	memset(keymapmenu, 0, sizeof(keymapmenu));
70 
71 	// TODO: remove copypastecode and write enum_dir() function that takes a handler
72 
73 	int maps = 0;
74 
75 	keymapmenu[maps].parent = prefsmenu;
76 	keymapmenu[maps].text = strdup("Default");
77 	keymapmenu[maps].action = load_keymap_action;
78 	keymapmenu[maps].p1 = (void*)keymapmenu[maps].text;
79 	++maps;
80 
81 	char path[1000];
82 	snprintf(path, sizeof(path) - 1, "%s/key", query_resource_directory());
83 	DIR *dir = opendir(path);
84 	debug("Enumerating keymaps at %s", path);
85 
86 	if (!dir)
87 	{
88 		warning("Could not enumerate keymaps at %s", path);
89 		return;
90 	}
91 
92 	struct dirent *de = NULL;
93 
94 	while ((de = readdir(dir)) != NULL)
95 	{
96 		char fullpath[1000];
97 
98 		snprintf(fullpath, sizeof(fullpath) - 1, "%s/key/%s", query_resource_directory(), de->d_name);
99 		struct stat attribute;
100 
101 		if (stat(fullpath, &attribute) != -1 && !(attribute.st_mode & S_IFDIR))
102 		{
103 			if (maps >= MAX_KEYMAPS)
104 			{
105 				warning("Maximum keymaps exceeded");
106 				break;
107 			}
108 
109 			keymapmenu[maps].parent = prefsmenu;
110 			keymapmenu[maps].text = strdup(de->d_name);
111 			keymapmenu[maps].action = load_keymap_action;
112 			keymapmenu[maps].p1 = (void*)keymapmenu[maps].text;
113 			++maps;
114 		}
115 	}
116 
117 	debug("Got %d keymaps", maps);
118 
119 	closedir(dir);
120 }
121 
122 
update_keymap_menu()123 void update_keymap_menu()
124 {
125 	for (int i = 0 ; keymapmenu[i].text ; ++i)
126 	{
127 		if (strcmp(mused.keymapname, (char*)keymapmenu[i].p1) == 0)
128 		{
129 			keymapmenu[i].flags |= MENU_BULLET;
130 		}
131 		else
132 			keymapmenu[i].flags &= ~MENU_BULLET;
133 	}
134 }
135 
136 
parse_key(const char * keys,int * key,int * mod,int * scancode)137 int parse_key(const char *keys, int *key, int *mod, int *scancode)
138 {
139 	*mod = 0;
140 	*key = 0;
141 
142 	if (scancode)
143 		*scancode = 0;
144 
145 	char *temp = strdup(keys);
146 	int done = 0;
147 	char *tok = strtok(temp, " \t");
148 
149 	do
150 	{
151 		if (!tok) break;
152 		int found = 0;
153 
154 		for (int i = 0 ; keydefs[i].name ; ++i)
155 		{
156 			if (strcasecmp(tok, keydefs[i].name) == 0)
157 			{
158 				if (*key != 0)
159 				{
160 					warning("More than one key (%s, was %d) specified", tok, *key);
161 					done = 1;
162 				}
163 
164 				*key = keydefs[i].key;
165 				found = 1;
166 				break;
167 			}
168 		}
169 
170 		for (int i = 0 ; moddefs[i].name ; ++i)
171 		{
172 			if (strcasecmp(tok, moddefs[i].name) == 0)
173 			{
174 				*mod |= moddefs[i].key;
175 				found = 1;
176 				break;
177 			}
178 		}
179 
180 		if (scancode)
181 		{
182 			int _scancode = 0;
183 
184 			if (sscanf(tok, "S_%x", &_scancode) == 1)
185 			{
186 				debug("Found scancode %x", _scancode);
187 				*scancode = _scancode;
188 				found = 1;
189 			}
190 		}
191 
192 		if (!found && strlen(tok) > 0)
193 		{
194 			warning("Unknown token %s", tok);
195 			break;
196 		}
197 
198 		tok = strtok(NULL, " \t");
199 	}
200 	while (!done);
201 
202 	free(temp);
203 
204 	if (*key == 0)
205 	{
206 		warning("No keys specified");
207 		*mod = 0;
208 	}
209 
210 	return (*key != 0 || (scancode == NULL || *scancode != 0));
211 }
212 
213 
parse_keys(const char * from,const char * to,KeyTran * tran)214 int parse_keys(const char *from, const char *to, KeyTran *tran)
215 {
216 	if (!parse_key(from, &tran->from_key, &tran->from_mod, &tran->from_scancode)) return 0;
217 
218 	if (!parse_key(to, &tran->to_key, &tran->to_mod, NULL)) return 0;
219 
220 	if (tran->from_scancode)
221 		tran->type = SCANCODE;
222 	else
223 		tran->type = KEYSYM;
224 
225 	return 1;
226 }
227 
228 
load_keymap(const char * name)229 void load_keymap(const char *name)
230 {
231 	memset(keytrans, 0, sizeof(keytrans));
232 
233 	char tmpname[100] = {0};
234 	strncpy(tmpname, name, sizeof(tmpname) - 1);
235 
236 	if (strcmp(name, "Default") == 0)
237 	{
238 		strncpy(mused.keymapname, tmpname, sizeof(mused.keymapname) - 1);
239 		update_keymap_menu();
240 		return;
241 	}
242 
243 	char fullpath[3000] = {0};
244 	snprintf(fullpath, sizeof(fullpath) - 1, "%s/key/%s", query_resource_directory(), tmpname);
245 
246 	strncpy(mused.keymapname, tmpname, sizeof(mused.keymapname) - 1);
247 	update_keymap_menu();
248 
249 	debug("Loading keymap '%s'", fullpath);
250 
251 	FILE *f = fopen(fullpath, "rt");
252 	int trans = 0;
253 
254 	if (f)
255 	{
256 		int lnr = 1;
257 		int focus = -1;
258 
259 		do
260 		{
261 			char line[100], from[100], to[100];
262 			if (trans >= MAX_KEYTRANS)
263 			{
264 				warning("Max keytrans exceeded\n");
265 				break;
266 			}
267 
268 			if (!fgets(line, sizeof(line) - 1, f)) break;
269 
270 			if (line[0] == '#')
271 				continue;
272 			else if (sscanf(line, "%*[[]%64[^]]%*[]]", from) == 1)
273 			{
274 				if (strcasecmp(from, "global") == 0)
275 				{
276 					focus = -1;
277 				}
278 				else if (strcasecmp(from, "pattern") == 0)
279 				{
280 					focus = EDITPATTERN;
281 				}
282 				else if (strcasecmp(from, "sequence") == 0)
283 				{
284 					focus = EDITSEQUENCE;
285 				}
286 				else if (strcasecmp(from, "instrument") == 0)
287 				{
288 					focus = EDITINSTRUMENT;
289 				}
290 				else
291 					warning("Unknown GUI focus \"%s\" on line %d", from, lnr);
292 			}
293 			else if (sscanf(line, "%99[^=]=%99[^\r\n]", from, to) == 2 && parse_keys(from, to, &keytrans[trans]))
294 			{
295 				keytrans[trans].focus = focus;
296 				++trans;
297 			}
298 			else
299 			{
300 				warning("Keymap line %d is malformed", lnr);
301 			}
302 
303 			++lnr;
304 		}
305 		while (1);
306 
307 		fclose(f);
308 	}
309 	else
310 	{
311 		debug("Keymap loading failed");
312 	}
313 
314 	debug("Got %d keytrans", trans);
315 }
316