2 * Copyright Neil Brown ©2015 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
7 * A keymap maps a key to a command.
8 * Keys are ordered for fast binary-search lookup.
9 * A "key" is an arbitrary string which typically contains
10 * some 'mode' pr efix and some specific tail.
11 * e.g emacs-M-C-Chr-x is Meta-Control-X in emacs mode.
12 * As far as the map is concerned, it is just a lexically order string.
14 * A 'command' is a struct provided by any of various
17 * A range can be stored by setting the lsb of the command pointer at
18 * the start of the range.
19 * When searching for a key we find the first entry that is not less than the target.
20 * If it is an exact match, use it.
21 * If previous entry exists and has the lsb set, then use that command.
23 * So to add a range, the start is entered with lsb set, and the end it entered with
26 * If a key is registered a second time, the new over-rides the old.
27 * This is particularly useful for registering a range, and then some exception.
28 * To delete a key from a range we need to make two ranges, one that ends
29 * just before the new key, one that starts just after.
30 * The 'ends just before' is easy - we just add the new key or range.
31 * The 'starts just after' is managed by entering the same key twice.
32 * The first instance of the key has a 'lsb clear' command and is used for
33 * exact matches. The second instance has 'lsb set' and is used for everything
36 * A 'prefix' can be registered which creates a command which temporarily
37 * enabled the given prefix. It is applied to the next command, but is
38 * discarded after that. This is just a convenience function.
51 struct command **comms;
54 static inline struct command *GETCOMM(struct command *c)
56 return (struct command *)(((unsigned long)c) & ~1UL);
59 static inline int IS_RANGE(struct command *c)
61 return ((unsigned long)c) & 1;
64 static inline struct command *SET_RANGE(struct command *c)
66 return (struct command *)(((unsigned long)c) | 1UL);
69 static int size2alloc(int size)
71 /* Alway multiple of 8. */
72 return ((size-1) | 7) + 1;
75 struct map *key_alloc(void)
77 struct map *m = malloc(sizeof(*m));
78 memset(m, 0, sizeof(*m));
82 void key_free(struct map *m)
89 /* Find first entry >= k */
90 static int key_find(struct map *map, char *k)
95 /* all entries before 'lo' are < k.
96 * all entries at 'hi' or later are >= k.
97 * So when lo==hi, hi is the answer.
100 int mid = (hi + lo)/ 2;
101 if (strcmp(map->keys[mid], k) < 0)
109 void key_add(struct map *map, char *k, struct command *comm)
113 struct command *comm2 = NULL;
119 pos = key_find(map, k);
121 * 1/ match start of range: insert before
122 * 2/ match non-range start: replace
123 * 3/ not in range: insert before like 1
124 * 4/ in a range: insert match and range start.
126 if (pos >= map->size) {
127 /* Insert k,comm - default action */
128 } else if (strcmp(k, map->keys[pos]) == 0) {
129 /* match: need to check if range-start */
130 if (IS_RANGE(map->comms[pos])) {
131 /* Changing the start of a range, insert and exact match */
133 /* replace a non-range */
134 /* FIXME do I need to release the old command */
135 map->comms[pos] = comm;
138 } else if (pos > 0 && IS_RANGE(map->comms[pos-1])) {
139 /* insert within a range.
140 * Add given command as non-range match, and old command
143 comm2 = map->comms[pos-1];
145 /* Not in a range, simple insert */
148 ins_cnt = comm2 ? 2 : 1;
149 size = size2alloc(map->size + ins_cnt);
152 if (size2alloc(map->size) != size) {
153 map->keys = realloc(map->keys, size * sizeof(map->keys[0]));
154 map->comms = realloc(map->comms,
155 size * sizeof(struct command *));
158 memmove(map->keys+pos+ins_cnt, map->keys+pos,
159 (map->size - pos) * sizeof(map->keys[0]));
160 memmove(map->comms+pos+ins_cnt, map->comms+pos,
161 (map->size - pos) * sizeof(struct command *));
163 map->comms[pos] = comm;
165 map->keys[pos+1] = k;
166 map->comms[pos+1] = comm2;
168 map->size += ins_cnt;
171 void key_add_range(struct map *map, char *first, char *last,
172 struct command *comm)
177 if (!comm || strcmp(first, last) >= 0)
180 /* Add the first entry using key_add */
181 key_add(map, first, comm);
182 pos = key_find(map, first);
183 pos2 = key_find(map, last);
185 /* Now 'pos' is a stand-alone entry for 'first'.
186 * If the entry before pos2 is a range start, update to start at 'last',
187 * else discard it, and discard everything else between pos and pos2.
188 * Then insert a stand-alone for 'last' and update 'pos' to be a range-start.
190 if (pos2 - 1 > pos && IS_RANGE(map->comms[pos2-1])) {
191 map->keys[pos2-1] = last;
194 /* Need to insert 'last', and remove extras. so +1 and -(pos2-pos-1); */
195 move_size = 1 - (pos2 - pos - 1);
196 size = size2alloc(map->size + move_size);
197 if (size2alloc(map->size) < size) {
198 map->keys = realloc(map->keys, size * sizeof(map->keys[0]));
199 map->comms = realloc(map->comms,
200 size * sizeof(struct command *));
203 memmove(map->keys+pos2 + move_size, map->keys+pos2,
204 (map->size - pos2) * sizeof(map->keys[0]));
205 memmove(map->comms+pos2+ move_size, map->comms+pos2,
206 (map->size - pos2) * sizeof(struct command *));
208 map->comms[pos] = SET_RANGE(comm);
209 map->keys[pos+1] = last;
210 map->comms[pos+1] = comm;
211 map->size += move_size;
216 void key_del(struct map *map, wint_t k)
220 pos = key_find(map, k);
221 if (pos >= map->size || strcmp(map->keys[pos], k) == 0)
224 memmove(map->keys+pos, map->keys+pos+1,
225 (map->size-pos-1) * sizeof(map->keys[0]));
226 memmove(map->comms+pos, map->comms+pos+1,
227 (map->size-pos-1) * sizeof(struct command *));
237 static int key_prefix(const struct cmd_info *ci)
239 struct modmap *m = container_of(ci->comm, struct modmap, comm);
241 pane_set_mode(ci->home, m->name);
245 struct command *key_register_prefix(char *name)
247 struct modmap *mm = malloc(sizeof(*mm));
249 mm->name = strdup(name);
250 mm->comm.func = key_prefix;
254 struct command *key_lookup_cmd(struct map *m, char *c)
256 int pos = key_find(m, c);
260 if (strcmp(m->keys[pos], c) == 0) {
261 /* Exact match - use this entry */
262 return GETCOMM(m->comms[pos]);
263 } else if (pos > 0 && IS_RANGE(m->comms[pos-1])) {
264 /* In a range, use previous */
265 return GETCOMM(m->comms[pos-1]);
270 int key_lookup(struct map *m, const struct cmd_info *ci)
272 int pos = key_find(m, ci->key);
273 struct command *comm;
277 if (strcmp(m->keys[pos], ci->key) == 0) {
278 /* Exact match - use this entry */
279 comm = GETCOMM(m->comms[pos]);
280 } else if (pos > 0 && IS_RANGE(m->comms[pos-1])) {
281 /* In a range, use previous */
282 comm = GETCOMM(m->comms[pos-1]);
285 ((struct cmd_info*)ci)->comm = comm;
286 return comm->func(ci);
289 int key_lookup_cmd_func(const struct cmd_info *ci)
291 struct lookup_cmd *l = container_of(ci->comm, struct lookup_cmd, c);
292 return key_lookup(*l->m, ci);
295 int key_handle(const struct cmd_info *ci)
297 struct cmd_info *vci = (struct cmd_info*)ci;
302 return ci->comm->func(ci);
304 /* If 'home' is set, search from there, else search
311 while (ret == 0 && p) {
314 vci->comm = p->handle;
315 ret = p->handle->func(ci);
318 /* 'p' might have been destroyed */
325 static int __key_handle_focus(struct cmd_info *ci, int savepoint)
327 /* Handle this in the focus pane, so x,y are irrelevant */
328 struct pane *p = ci->home;
337 if (savepoint && p->pointer)
338 ci->mark = p->pointer;
340 if (!ci->home || !ci->focus)
344 return key_handle(ci);
347 int key_handle_focus(struct cmd_info *ci)
349 return __key_handle_focus(ci, 0);
352 int key_handle_focus_point(struct cmd_info *ci)
354 int ret = __key_handle_focus(ci, 1);
356 call5("Message", ci->focus, 0, NULL, "** Command Failed **", 1);
360 static int __key_handle_xy(struct cmd_info *ci, int savepoint)
362 /* Handle this in child with x,y co-ords */
363 struct pane *p = ci->focus;
368 struct pane *t, *chld = NULL;
370 list_for_each_entry(t, &p->children, siblings) {
371 if (x < t->x || x >= t->x + t->w)
373 if (y < t->y || y >= t->y + t->h)
375 if (chld == NULL || t->z > chld->z)
378 /* descend into chld */
384 if (savepoint && p->pointer)
385 ci->mark = p->pointer;
391 return key_handle(ci);
394 int key_handle_xy(struct cmd_info *ci)
396 return __key_handle_xy(ci, 0);
398 int key_handle_xy_point(struct cmd_info *ci)
400 return __key_handle_xy(ci, 1);