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