Remove input field from struct run_request
[tig.git] / src / keys.c
bloba5e0487b80b88131ea655217bdfe94c782412327
1 /* Copyright (c) 2006-2014 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/types.h"
16 #include "tig/argv.h"
17 #include "tig/io.h"
18 #include "tig/keys.h"
19 #include "tig/util.h"
21 struct keybinding {
22 struct key_input input;
23 enum request request;
26 static struct keymap generic_keymap = { "generic" };
27 #define is_generic_keymap(keymap) ((keymap) == &generic_keymap)
29 static struct keymap *keymaps = &generic_keymap;
31 struct keymap *
32 get_keymaps(void)
34 return keymaps;
37 void
38 add_keymap(struct keymap *keymap)
40 keymap->next = keymaps;
41 keymaps = keymap;
44 struct keymap *
45 get_keymap(const char *name, size_t namelen)
47 struct keymap *keymap = keymaps;
49 while (keymap) {
50 if (!strncasecmp(keymap->name, name, namelen))
51 return keymap;
52 keymap = keymap->next;
55 return NULL;
58 static bool
59 keybinding_equals(struct key_input *input1, struct key_input *input2, bool *conflict)
61 if (input1->modifiers.control &&
62 input1->modifiers.multibytes &&
63 !memcmp(&input1->modifiers, &input2->modifiers, sizeof(input1->modifiers)) &&
64 strlen(input1->data.bytes) == 1 &&
65 strlen(input2->data.bytes) == 1) {
66 int c1 = input1->data.bytes[0];
67 int c2 = input2->data.bytes[0];
68 bool equals = ascii_toupper(c1) == ascii_toupper(c2);
70 if (equals && c1 != c2)
71 *conflict = TRUE;
72 return equals;
75 return !memcmp(input1, input2, sizeof(*input1));
78 enum status_code
79 add_keybinding(struct keymap *table, enum request request, struct key_input *input)
81 bool conflict = FALSE;
82 size_t i;
84 for (i = 0; i < table->size; i++) {
85 if (keybinding_equals(&table->data[i].input, input, &conflict)) {
86 table->data[i].request = request;
87 return conflict ? ERROR_CTRL_KEY_CONFLICT : SUCCESS;
91 table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
92 if (!table->data)
93 die("Failed to allocate keybinding");
94 table->data[table->size].input = *input;
95 table->data[table->size++].request = request;
96 return SUCCESS;
99 /* Looks for a key binding first in the given map, then in the generic map, and
100 * lastly in the default keybindings. */
101 enum request
102 get_keybinding(struct keymap *keymap, struct key_input *input)
104 size_t i;
106 for (i = 0; i < keymap->size; i++)
107 if (keybinding_equals(&keymap->data[i].input, input, NULL))
108 return keymap->data[i].request;
110 for (i = 0; i < generic_keymap.size; i++)
111 if (keybinding_equals(&generic_keymap.data[i].input, input, NULL))
112 return generic_keymap.data[i].request;
114 return REQ_NONE;
118 struct key {
119 const char *name;
120 int value;
123 static const struct key key_table[] = {
124 { "Enter", KEY_RETURN },
125 { "Space", ' ' },
126 { "Backspace", KEY_BACKSPACE },
127 { "Tab", KEY_TAB },
128 { "Escape", KEY_ESC },
129 { "Left", KEY_LEFT },
130 { "Right", KEY_RIGHT },
131 { "Up", KEY_UP },
132 { "Down", KEY_DOWN },
133 { "Insert", KEY_IC },
134 { "Delete", KEY_DC },
135 { "Hash", '#' },
136 { "Home", KEY_HOME },
137 { "End", KEY_END },
138 { "PageUp", KEY_PPAGE },
139 { "PgUp", KEY_PPAGE },
140 { "PageDown", KEY_NPAGE },
141 { "PgDown", KEY_NPAGE },
142 { "F1", KEY_F(1) },
143 { "F2", KEY_F(2) },
144 { "F3", KEY_F(3) },
145 { "F4", KEY_F(4) },
146 { "F5", KEY_F(5) },
147 { "F6", KEY_F(6) },
148 { "F7", KEY_F(7) },
149 { "F8", KEY_F(8) },
150 { "F9", KEY_F(9) },
151 { "F10", KEY_F(10) },
152 { "F11", KEY_F(11) },
153 { "F12", KEY_F(12) },
157 get_key_value(const char *name, struct key_input *input)
159 int i;
161 memset(input, 0, sizeof(*input));
163 for (i = 0; i < ARRAY_SIZE(key_table); i++)
164 if (!strcasecmp(key_table[i].name, name)) {
165 if (key_table[i].value == ' ') {
166 name = " ";
167 break;
169 if (key_table[i].value == '#') {
170 name = "#";
171 break;
173 input->data.key = key_table[i].value;
174 return OK;
177 if (name[0] == '^' && name[1] == '[') {
178 input->modifiers.escape = 1;
179 name += 2;
180 } else if (name[0] == '^') {
181 input->modifiers.control = 1;
182 name += 1;
185 i = utf8_char_length(name);
186 if (strlen(name) == i && utf8_to_unicode(name, i) != 0) {
187 strncpy(input->data.bytes, name, i);
188 input->modifiers.multibytes = 1;
189 return OK;
192 return ERR;
195 const char *
196 get_key_name(const struct key_input *input)
198 static char buf[SIZEOF_STR];
199 const char *modifier = "";
200 int key;
202 if (!input->modifiers.multibytes) {
203 for (key = 0; key < ARRAY_SIZE(key_table); key++)
204 if (key_table[key].value == input->data.key)
205 return key_table[key].name;
208 if (input->modifiers.escape)
209 modifier = "^[";
210 else if (input->modifiers.control)
211 modifier = "^";
213 if (string_format(buf, "'%s%s'", modifier, input->data.bytes))
214 return buf;
216 return "(no key)";
219 static bool
220 append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
222 const char *sep = *pos > 0 ? ", " : "";
223 const char *keyname = get_key_name(&keybinding->input);
225 return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
228 static bool
229 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
230 struct keymap *keymap, bool all)
232 int i;
234 for (i = 0; i < keymap->size; i++) {
235 if (keymap->data[i].request == request) {
236 if (!append_key(buf, pos, &keymap->data[i]))
237 return FALSE;
238 if (!all)
239 break;
243 return TRUE;
246 const char *
247 get_keys(struct keymap *keymap, enum request request, bool all)
249 static char buf[BUFSIZ];
250 size_t pos = 0;
252 buf[pos] = 0;
254 if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
255 return "Too many keybindings!";
256 if (pos > 0 && !all)
257 return buf;
259 if (!is_generic_keymap(keymap)) {
260 /* Only the generic keymap includes the default keybindings when
261 * listing all keys. */
262 if (all)
263 return buf;
265 if (!append_keymap_request_keys(buf, &pos, request, &generic_keymap, all))
266 return "Too many keybindings!";
267 if (pos)
268 return buf;
271 return buf;
274 static struct run_request *run_request;
275 static size_t run_requests;
277 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
279 enum status_code
280 add_run_request(struct keymap *keymap, struct key_input *input, const char **argv)
282 struct run_request *req;
283 struct run_request_flags flags = {};
285 if (!strchr(":!?@<", *argv[0]))
286 return ERROR_UNKNOWN_REQUEST_NAME;
288 while (*argv[0]) {
289 if (*argv[0] == ':') {
290 flags.internal = 1;
291 argv[0]++;
292 break;
293 } else if (*argv[0] == '@') {
294 flags.silent = 1;
295 } else if (*argv[0] == '?') {
296 flags.confirm = 1;
297 } else if (*argv[0] == '<') {
298 flags.exit = 1;
299 } else if (*argv[0] != '!') {
300 break;
302 argv[0]++;
305 if (!realloc_run_requests(&run_request, run_requests, 1))
306 return ERROR_OUT_OF_MEMORY;
308 if (!argv_copy(&run_request[run_requests].argv, argv))
309 return ERROR_OUT_OF_MEMORY;
311 req = &run_request[run_requests++];
312 req->flags = flags;
313 req->keymap = keymap;
315 return add_keybinding(keymap, REQ_RUN_REQUESTS + run_requests, input);
318 struct run_request *
319 get_run_request(enum request request)
321 if (request <= REQ_RUN_REQUESTS || request > REQ_RUN_REQUESTS + run_requests)
322 return NULL;
323 return &run_request[request - REQ_RUN_REQUESTS - 1];
326 /* vim: set ts=8 sw=8 noexpandtab: */