1 /**
2  * \file ui-event.c
3  * \brief Utility functions relating to UI events
4  *
5  * Copyright (c) 2011 Andi Sidwell
6  *
7  * This work is free software; you can redistribute it and/or modify it
8  * under the terms of either:
9  *
10  * a) the GNU General Public License as published by the Free Software
11  *    Foundation, version 2, or
12  *
13  * b) the "Angband licence":
14  *    This software may be copied and distributed for educational, research,
15  *    and not for profit purposes provided that this copyright and statement
16  *    are included in all such copies.  Other copyrights may also apply.
17  */
18 #include "angband.h"
19 #include "ui-event.h"
20 
21 /**
22  * Map keycodes to their textual equivalent.
23  */
24 static const struct {
25 	keycode_t code;
26 	const char *desc;
27 } mappings[] = {
28 	{ ESCAPE, "Escape" },
29 	{ KC_ENTER, "Enter" },
30 	{ KC_TAB, "Tab" },
31 	{ KC_DELETE, "Delete" },
32 	{ KC_BACKSPACE, "Backspace" },
33 	{ ARROW_DOWN, "Down" },
34 	{ ARROW_LEFT, "Left" },
35 	{ ARROW_RIGHT, "Right" },
36 	{ ARROW_UP, "Up" },
37 	{ KC_F1, "F1" },
38 	{ KC_F2, "F2" },
39 	{ KC_F3, "F3" },
40 	{ KC_F4, "F4" },
41 	{ KC_F5, "F5" },
42 	{ KC_F6, "F6" },
43 	{ KC_F7, "F7" },
44 	{ KC_F8, "F8" },
45 	{ KC_F9, "F9" },
46 	{ KC_F10, "F10" },
47 	{ KC_F11, "F11" },
48 	{ KC_F12, "F12" },
49 	{ KC_F13, "F13" },
50 	{ KC_F14, "F14" },
51 	{ KC_F15, "F15" },
52 	{ KC_HELP, "Help" },
53 	{ KC_HOME, "Home" },
54 	{ KC_PGUP, "PageUp" },
55 	{ KC_END, "End" },
56 	{ KC_PGDOWN, "PageDown" },
57 	{ KC_INSERT, "Insert" },
58 	{ KC_PAUSE, "Pause" },
59 	{ KC_BREAK, "Break" },
60 	{ KC_BEGIN, "Begin" },
61 };
62 
63 
64 /**
65  * Given a string, try and find it in "mappings".
66  */
keycode_find_code(const char * str,size_t len)67 keycode_t keycode_find_code(const char *str, size_t len)
68 {
69 	size_t i;
70 	for (i = 0; i < N_ELEMENTS(mappings); i++) {
71 		if (strncmp(str, mappings[i].desc, len) == 0)
72 			return mappings[i].code;
73 	}
74 	return 0;
75 }
76 
77 
78 /**
79  * Given a keycode, return its textual mapping.
80  */
keycode_find_desc(keycode_t kc)81 const char *keycode_find_desc(keycode_t kc)
82 {
83 	size_t i;
84 	for (i = 0; i < N_ELEMENTS(mappings); i++) {
85 		if (mappings[i].code == kc)
86 			return mappings[i].desc;
87 	}
88 	return NULL;
89 }
90 
91 
92 /**
93  * Given a keycode, return whether it corresponds to a printable character.
94  */
keycode_isprint(keycode_t kc)95 bool keycode_isprint(keycode_t kc)
96 {
97 	/*
98 	 * Exclude ESCAPE (not part of the Unicode standard).  Otherwise,
99 	 * treat the keycode as a Unicode code point.
100 	 */
101 	return kc != ESCAPE && utf32_isprint(kc);
102 }
103 
104 
105 /**
106  * Convert a hexidecimal-digit into a decimal
107  */
dehex(char c)108 static int dehex(char c)
109 {
110 	if (isdigit((unsigned char)c)) return (D2I(c));
111 	if (isalpha((unsigned char)c)) return (A2I(tolower((unsigned char)c)) + 10);
112 	return (0);
113 }
114 
115 /**
116  * Convert an encoding of a set of keypresses into actual keypresses.
117  */
keypress_from_text(struct keypress * buf,size_t len,const char * str)118 void keypress_from_text(struct keypress *buf, size_t len, const char *str)
119 {
120 	size_t cur = 0;
121 	byte mods = 0;
122 
123 	memset(buf, 0, len * sizeof *buf);
124 
125 #define STORE(buffer, pos, mod, cod) \
126 	{ \
127 		int p = (pos); \
128 		keycode_t c = (cod); \
129 		byte m = (mod); \
130 \
131 		if ((m & KC_MOD_CONTROL) && ENCODE_KTRL(c)) { \
132 			m &= ~KC_MOD_CONTROL; \
133 			c = KTRL(c); \
134 		} \
135 \
136 		buffer[p].mods = m; \
137 		buffer[p].code = c; \
138 	}
139 
140 	/* Analyze the "ascii" string */
141 	while (*str && cur < len) {
142 		buf[cur].type = EVT_KBRD;
143 
144 		if (*str == '\\') {
145 			str++;
146 			if (*str == '\0') break;
147 
148 			switch (*str) {
149 				/* Hex-mode */
150 				case 'x': {
151 					if (isxdigit((unsigned char)(*(str + 1))) &&
152 							isxdigit((unsigned char)(*(str + 2)))) {
153 						int v1 = dehex(*++str) * 16;
154 						int v2 = dehex(*++str);
155 						/* store a nice hex digit */
156 						STORE(buf, cur++, mods, v1 + v2);
157 					} else {
158 						/* invalids get ignored */
159 						STORE(buf, cur++, mods, '?');
160 					}
161 					break;
162 				}
163 
164 				case 'a': STORE(buf, cur++, mods, '\a'); break;
165 				case '\\': STORE(buf, cur++, mods, '\\'); break;
166 				case '^': STORE(buf, cur++, mods, '^'); break;
167 				case '[': STORE(buf, cur++, mods, '['); break;
168 				default: STORE(buf, cur++, mods, *str); break;
169 			}
170 
171 			mods = 0;
172 
173 			/* Skip the final char */
174 			str++;
175 		} else if (*str == '[') {
176 			/* parse non-ascii keycodes */
177 			char *end;
178 			keycode_t kc;
179 
180 			if (*str++ == 0) return;
181 
182 			end = strchr(str, (unsigned char) ']');
183 			if (!end) return;
184 
185 			kc = keycode_find_code(str, (size_t) (end - str));
186 			if (!kc) return;
187 
188 			STORE(buf, cur++, mods, kc);
189 			mods = 0;
190 			str = end + 1;
191 		} else if (*str == '{') {
192 			/* Specify modifier for next character */
193 			str++;
194 			if (*str == '\0' || !strchr(str, (unsigned char) '}'))
195 				return;
196 
197 			/* analyze modifier chars */
198 			while (*str != '}') {
199 				switch (*str) {
200 					case '^': mods |= KC_MOD_CONTROL; break;
201 					case 'S': mods |= KC_MOD_SHIFT; break;
202 					case 'A': mods |= KC_MOD_ALT; break;
203 					case 'M': mods |= KC_MOD_META; break;
204 					case 'K': mods |= KC_MOD_KEYPAD; break;
205 					default:
206 						return;
207 				}
208 
209 				str++;
210 			}
211 
212 			/* skip ending bracket */
213 			str++;
214 		} else if (*str == '^') {
215 			mods |= KC_MOD_CONTROL;
216 			str++;
217 		} else {
218 			/* everything else */
219 			STORE(buf, cur++, mods, *str++);
220 			mods = 0;
221 		}
222 	}
223 
224 	/* Terminate */
225 	cur = MIN(cur, len - 1);
226 	buf[cur].type = EVT_NONE;
227 }
228 
229 /**
230  * Convert a string of keypresses into their textual equivalent.
231  */
keypress_to_text(char * buf,size_t len,const struct keypress * src,bool expand_backslash)232 void keypress_to_text(char *buf, size_t len, const struct keypress *src,
233 	bool expand_backslash)
234 {
235 	size_t cur = 0;
236 	size_t end = 0;
237 
238 	while (src[cur].type == EVT_KBRD) {
239 		keycode_t i = src[cur].code;
240 		int mods = src[cur].mods;
241 		const char *desc = keycode_find_desc(i);
242 
243 		/* un-ktrl control characters if they don't have a description */
244 		/* this is so that Tab (^I) doesn't get turned into ^I but gets
245 		 * displayed as [Tab] */
246 		if (i < 0x20 && !desc) {
247 			mods |= KC_MOD_CONTROL;
248 			i = UN_KTRL(i);
249 		}
250 
251 		if (mods) {
252 			if (mods & KC_MOD_CONTROL && !(mods & ~KC_MOD_CONTROL)) {
253 				strnfcat(buf, len, &end, "^");
254 			} else {
255 				strnfcat(buf, len, &end, "{");
256 				if (mods & KC_MOD_CONTROL) strnfcat(buf, len, &end, "^");
257 				if (mods & KC_MOD_SHIFT) strnfcat(buf, len, &end, "S");
258 				if (mods & KC_MOD_ALT) strnfcat(buf, len, &end, "A");
259 				if (mods & KC_MOD_META) strnfcat(buf, len, &end, "M");
260 				if (mods & KC_MOD_KEYPAD) strnfcat(buf, len, &end, "K");
261 				strnfcat(buf, len, &end, "}");
262 			}
263 		}
264 
265 		if (desc) {
266 			strnfcat(buf, len, &end, "[%s]", desc);
267 		} else {
268 			switch (i) {
269 				case '\a': strnfcat(buf, len, &end, "\a"); break;
270 				case '\\': {
271 					if (expand_backslash)
272 						strnfcat(buf, len, &end, "\\\\");
273 					else
274 						strnfcat(buf, len, &end, "\\");
275 					break;
276 				}
277 				case '^': strnfcat(buf, len, &end, "\\^"); break;
278 				case '[': strnfcat(buf, len, &end, "\\["); break;
279 				default: {
280 					if (i < 127)
281 						strnfcat(buf, len, &end, "%c", i);
282 					else
283 						strnfcat(buf, len, &end, "\\x%02x", (int)i);
284 					break;
285 				}
286 			}
287 		}
288 
289 		cur++;
290 	}
291 
292 	/* Terminate */
293 	buf[end] = '\0';
294 }
295 
296 
297 /**
298  * Convert a keypress into something readable.
299  */
keypress_to_readable(char * buf,size_t len,struct keypress src)300 void keypress_to_readable(char *buf, size_t len, struct keypress src)
301 {
302 	size_t end = 0;
303 	keycode_t i = src.code;
304 	int mods = src.mods;
305 	const char *desc = keycode_find_desc(i);
306 
307 	/* un-ktrl control characters if they don't have a description */
308 	/* this is so that Tab (^I) doesn't get turned into ^I but gets
309 	 * displayed as [Tab] */
310 	if (i < 0x20 && !desc) {
311 		mods |= KC_MOD_CONTROL;
312 		i = UN_KTRL(i);
313 	}
314 
315 	if (mods) {
316 		if (mods & KC_MOD_CONTROL && !(mods & ~KC_MOD_CONTROL) &&
317 				i != '^') {
318 			strnfcat(buf, len, &end, "^");
319 		} else {
320 			if (mods & KC_MOD_CONTROL) strnfcat(buf, len, &end, "Control-");
321 			if (mods & KC_MOD_SHIFT) strnfcat(buf, len, &end, "Shift-");
322 			if (mods & KC_MOD_ALT) strnfcat(buf, len, &end, "Alt-");
323 			if (mods & KC_MOD_META) strnfcat(buf, len, &end, "Meta-");
324 			if (mods & KC_MOD_KEYPAD) strnfcat(buf, len, &end, "Keypad-");
325 		}
326 	}
327 
328 	if (desc) {
329 		strnfcat(buf, len, &end, "%s", desc);
330 	} else {
331 		strnfcat(buf, len, &end, "%c", i);
332 	}
333 
334 	/* Terminate */
335 	buf[end] = '\0';
336 }
337 
338 
339 /**
340  * Return whether the given display char matches an entered symbol
341  *
342  * Horrible hack. TODO UTF-8 find some way of entering mb chars
343  */
char_matches_key(wchar_t c,keycode_t key)344 bool char_matches_key(wchar_t c, keycode_t key)
345 {
346 	wchar_t keychar[2];
347 	char k[2] = {'\0', '\0'};
348 
349 	k[0] = (char)key;
350 	text_mbstowcs(keychar, k, 1);
351 	return (c == keychar[0]);
352 }
353