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.
15 #include "tig/types.h"
22 struct key_input input
;
26 static struct keymap generic_keymap
= { "generic" };
27 #define is_generic_keymap(keymap) ((keymap) == &generic_keymap)
29 static struct keymap
*keymaps
= &generic_keymap
;
38 add_keymap(struct keymap
*keymap
)
40 keymap
->next
= keymaps
;
45 get_keymap(const char *name
, size_t namelen
)
47 struct keymap
*keymap
= keymaps
;
50 if (!strncasecmp(keymap
->name
, name
, namelen
))
52 keymap
= keymap
->next
;
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
)
75 return !memcmp(input1
, input2
, sizeof(*input1
));
79 add_keybinding(struct keymap
*table
, enum request request
, struct key_input
*input
)
81 bool conflict
= FALSE
;
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
));
93 die("Failed to allocate keybinding");
94 table
->data
[table
->size
].input
= *input
;
95 table
->data
[table
->size
++].request
= request
;
99 /* Looks for a key binding first in the given map, then in the generic map, and
100 * lastly in the default keybindings. */
102 get_keybinding(struct keymap
*keymap
, struct key_input
*input
)
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
;
123 static const struct key key_table
[] = {
124 { "Enter", KEY_RETURN
},
126 { "Backspace", KEY_BACKSPACE
},
128 { "Escape", KEY_ESC
},
129 { "Left", KEY_LEFT
},
130 { "Right", KEY_RIGHT
},
132 { "Down", KEY_DOWN
},
133 { "Insert", KEY_IC
},
134 { "Delete", KEY_DC
},
136 { "Home", KEY_HOME
},
138 { "PageUp", KEY_PPAGE
},
139 { "PgUp", KEY_PPAGE
},
140 { "PageDown", KEY_NPAGE
},
141 { "PgDown", KEY_NPAGE
},
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
)
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
== ' ') {
169 if (key_table
[i
].value
== '#') {
173 input
->data
.key
= key_table
[i
].value
;
177 if (name
[0] == '^' && name
[1] == '[') {
178 input
->modifiers
.escape
= 1;
180 } else if (name
[0] == '^') {
181 input
->modifiers
.control
= 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;
196 get_key_name(const struct key_input
*input
)
198 static char buf
[SIZEOF_STR
];
199 const char *modifier
= "";
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
)
210 else if (input
->modifiers
.control
)
213 if (string_format(buf
, "'%s%s'", modifier
, input
->data
.bytes
))
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
);
229 append_keymap_request_keys(char *buf
, size_t *pos
, enum request request
,
230 struct keymap
*keymap
, bool all
)
234 for (i
= 0; i
< keymap
->size
; i
++) {
235 if (keymap
->data
[i
].request
== request
) {
236 if (!append_key(buf
, pos
, &keymap
->data
[i
]))
247 get_keys(struct keymap
*keymap
, enum request request
, bool all
)
249 static char buf
[BUFSIZ
];
254 if (!append_keymap_request_keys(buf
, &pos
, request
, keymap
, all
))
255 return "Too many keybindings!";
259 if (!is_generic_keymap(keymap
)) {
260 /* Only the generic keymap includes the default keybindings when
261 * listing all keys. */
265 if (!append_keymap_request_keys(buf
, &pos
, request
, &generic_keymap
, all
))
266 return "Too many keybindings!";
274 static struct run_request
*run_request
;
275 static size_t run_requests
;
277 DEFINE_ALLOCATOR(realloc_run_requests
, struct run_request
, 8)
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
;
289 if (*argv
[0] == ':') {
293 } else if (*argv
[0] == '@') {
295 } else if (*argv
[0] == '?') {
297 } else if (*argv
[0] == '<') {
299 } else if (*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
++];
313 req
->keymap
= keymap
;
315 return add_keybinding(keymap
, REQ_RUN_REQUESTS
+ run_requests
, input
);
319 get_run_request(enum request request
)
321 if (request
<= REQ_RUN_REQUESTS
|| request
> REQ_RUN_REQUESTS
+ run_requests
)
323 return &run_request
[request
- REQ_RUN_REQUESTS
- 1];
326 /* vim: set ts=8 sw=8 noexpandtab: */