2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * Core input translation.
6 * This module transalates keystrokes and mouse events into commands.
7 * This involves tracking the current 'mode' state.
11 #define _GNU_SOURCE /* for asprintf */
18 #define PANE_DATA_TYPE struct input_mode
23 const char *mode safe;
24 const char *context safe; /* for status lines */
26 struct pane *focus, *source, *grab;
29 struct timespec last_action;
39 #include "core-pane.h"
41 /* 'head' is 1 more than the last key added. */
42 static void log_add(struct input_mode *im safe,
43 const char *type safe, const char *key safe)
45 free(im->log[im->head]);
46 im->log[im->head] = strconcat(NULL, type, key);
47 im->head = (im->head + 1) % LOGSIZE;
50 static void log_dump(struct pane *p safe)
52 struct input_mode *im = p->data;
62 line = strconcat(p, line, ", ", im->log[i]);
64 line = strconcat(p, im->log[i]);
67 LOG("Input %d: %s", cnt/10, line);
70 } while ((i = (i+1) % LOGSIZE) != im->head);
73 LOG("Input: %s", line);
82 static void report_status(struct pane *focus safe, struct input_mode *im safe)
86 if (im->num == NO_NUMERIC && im->mode[0] == 0)
89 if (im->num == NO_NUMERIC)
90 asprintf(&st, " %s", im->mode);
91 else if (im->num == -NO_NUMERIC)
92 asprintf(&st, " - %s", im->mode);
94 asprintf(&st, " %d %s", im->num, im->mode);
95 call("Message:modal", focus, 0, NULL, st);
100 struct input_mode *im = ci->home->data;
103 free((void*)im->mode);
104 im->mode = strdup(ci->str);
107 free((void*)im->context);
108 im->context = strdup(ci->str2);
109 attr_set_str(&ci->home->attrs, "display-context", im->context);
110 call("window:notify:display-context", ci->home);
112 report_status(ci->focus, im);
118 struct input_mode *im = ci->home->data;
121 report_status(ci->focus, im);
127 struct input_mode *im = ci->home->data;
135 struct input_mode *im = ci->home->data;
138 free((void*)im->mode);
139 im->mode = strdup(ci->str);
142 free((void*)im->context);
143 im->context = strdup(ci->str2);
144 attr_set_str(&ci->home->attrs, "display-context", im->context);
145 call("window:notify:display-context", ci->home);
149 report_status(ci->focus, im);
153 static const char *safe ctrl_map[][2] = {
154 { ":Backspace", ":C-H" },
155 { ":Enter", ":C-M" },
160 { ":A:Backspace",":A:C-H" },
161 { ":A:Enter", ":A:C-M" },
162 { ":A:ESC", ":A:C-[" },
163 { ":A:LF", ":A:C-J" },
164 { ":A:Tab", ":A:C-I" },
165 { ":A:Del", ":A:C-?" },
169 static const char *map_key(const char *key safe)
173 for (i = 0; i < ARRAY_SIZE(ctrl_map) ; i++) {
174 if (strcmp(key, ctrl_map[i][0]) == 0)
175 return ctrl_map[i][1];
183 struct input_mode *im = ci->home->data;
188 const char *mode = im->mode;
195 call("window:notify:Keystroke-notify", ci->home, 0, NULL, ci->str);
196 log_add(im, "K", ci->str);
198 im->mode = strdup("");
199 im->num = NO_NUMERIC;
202 if (im->source != ci->focus) {
203 im->source = ci->focus;
208 if (!im->focus || im->focus->focus) {
209 p = pane_focus(ci->focus);
211 pane_add_notify(ci->home, p, "Notify:Close");
216 im->point = call_ret(mark, "doc:point", p);
219 key = strconcat(ci->home, "K", mode, ci->str);
220 time_starts(ci->home);
221 ret = call(key, p, num, m, NULL, num2);
222 if (ret == Efallthrough && (alt = map_key(ci->str)) != NULL) {
223 key = strconcat(ci->home, "K", mode, alt);
224 ret = call(key, p, num, m, NULL, num2);
228 call("Message:default", ci->focus, 0, NULL,
229 "** Command Failed **");
233 DEF_CMD(keystroke_sequence)
235 struct pane *home = ci->home;
236 const char *c = ci->str;
237 const char *e, *dash;
242 while ((e = strchr(c, ' ')) != NULL) {
244 if (*c != ':' || e == c+1)
246 ret = call("Keystroke", home, 0, NULL,
247 strconcat(home, dash,
248 strnsave(home, c, e - c)));
254 if (*c != ':' || c[1] == '\0')
256 ret = call("Keystroke", home, 0, NULL, strconcat(home, dash, c));
262 static int tspec_diff_ms(struct timespec *a safe, struct timespec *b safe)
264 return ((a->tv_sec - b->tv_sec) * 1000 +
265 (a->tv_nsec - b->tv_nsec) / 1000000);
270 struct input_mode *im = ci->home->data;
281 char *mod; /* :A:C:S modifiers - optional */
282 struct mouse_state *ms = NULL;
284 clock_gettime(CLOCK_MONOTONIC, &now);
289 call("window:notify:Mouse-event-notify", ci->home,
290 ci->num, NULL, ci->str,
292 log_add(im, "M", ci->str);
298 } else if (ci->num2 == 2) {
310 ms = &im->buttons[b];
312 /* FIXME the max movement for a double-click should be
313 * about a char width - maybe something based on scale
315 if (tspec_diff_ms(&now, &ms->last_action) <= 500 &&
316 (abs(ci->x - ms->last_x) +
317 abs(ci->y - ms->last_y)) <= 2)
318 /* FIXME should I let 'release' know that
319 * it was close to the 'press'??
323 /* Too much time or space has elapsed.
324 * This cannot be a click.
331 else if (ms->click_count < 3)
332 ms->click_count += 1;
334 ms->last_action = now;
335 ms->last_x = ci->x; ms->last_y = ci->y;
341 /* FIXME is there any point in this? */
342 xy = pane_mapxy(ci->focus, focus, ci->x, ci->y, False);
344 mode = strsave(ci->home, im->mode);
345 free((void*)im->mode);
346 im->mode = strdup("");
347 im->num = NO_NUMERIC;
350 if (im->grab && !press) {
351 /* Release and Motion should go to the same
352 * place as Press went.
355 xy = pane_mapxy(focus, ci->focus, 0, 0, False);
359 struct pane *t, *chld = NULL;
361 list_for_each_entry(t, &focus->children, siblings) {
364 if (xy.x < t->x || xy.x >= t->x + t->w)
366 if (xy.y < t->y || xy.y >= t->y + t->h)
368 if (chld == NULL || t->z > chld->z)
371 /* descend into chld */
378 if (im->grab != focus) {
380 pane_add_notify(ci->home, focus, "Notify:Close");
385 key = strconcat(ci->home, "M", mode, ci->str);
386 time_starts(ci->home);
387 ret = call(key, focus, num, NULL, NULL, ex, NULL, NULL,
393 /* identify and save modifiers */
396 mod = strdup(ci->str2);
398 char *c = strrchr(ci->str, ':');
400 mod = strndup(ci->str, c - ci->str);
409 else if (ms->click_on_up)
414 /* Try :nPress :(n-1)Press ... (or :nRelease or :nClick)
415 * until something gets a result.
416 * 'n' is T (triple) or D(double) or ""(Single).
417 * If nothing got a result for a Press,, register for
418 * 'click' on release.
421 for (r = ms->click_count; r >= 1 ; r--) {
423 char *mult = "\0\0D\0T" + (r-1)*2;
428 key = strconcat(ci->home, "M", mode, ms->mod, ":", mult,
430 time_starts(ci->home);
431 ret = call(key, focus, num, NULL, NULL, ex,
432 NULL, NULL, xy.x, xy.y);
435 /* If this is 'press', then don't want
436 * click_on_up. If this is release, it
437 * don't matter what we set.
448 struct input_mode *im = ci->home->data;
450 im->grab = ci->focus;
451 pane_add_notify(ci->home, ci->focus, "Notify:Close");
457 struct input_mode *im = ci->home->data;
467 struct input_mode *im = ci->home->data;
469 if (im->focus == ci->focus) {
475 if (im->grab == ci->focus)
482 struct input_mode *im = ci->home->data;
485 for (i = 0; i < 3; i++) {
486 free(im->buttons[i].mod);
487 im->buttons[i].mod = NULL;
489 free((void*)im->mode);
490 free((void*)im->context);
494 static struct map *im_map;
495 static void register_map(void)
499 im_map = key_alloc();
500 key_add(im_map, "Keystroke", &keystroke);
501 key_add(im_map, "Keystroke-sequence", &keystroke_sequence);
502 key_add(im_map, "Mouse-event", &mouse_event);
503 key_add(im_map, "Mouse-grab", &mouse_grab);
504 key_add(im_map, "Mode:set-mode", &set_mode);
505 key_add(im_map, "Mode:set-num", &set_num);
506 key_add(im_map, "Mode:set-num2", &set_num2);
507 key_add(im_map, "Mode:set-all", &set_all);
508 key_add(im_map, "pane:refocus", &refocus);
509 key_add(im_map, "Notify:Close", &close_focus);
510 key_add(im_map, "input:log", &log_input);
511 key_add(im_map, "Close", &input_close);
514 DEF_LOOKUP_CMD(input_handle, im_map);
515 DEF_CMD(input_attach)
517 struct input_mode *im;
522 p = pane_register(ci->focus, 0, &input_handle.c);
526 im->mode = strdup("");
527 im->context = strdup("");
528 im->num = NO_NUMERIC;
531 return comm_call(ci->comm2, "callback:attach", p);
534 void edlib_init(struct pane *ed safe)
536 call_comm("global-set-command", ed, &input_attach, 0, NULL, "attach-input");