2 * Copyright Neil Brown ©2015-2021 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * ncurses front end for edlib.
7 * There is currently only support for a single terminal window
8 * which provides a single pane.
10 * Rendering operations are:
11 * draw text with attributes at location
12 * erase with attributes in rectangle
20 #define _XOPEN_SOURCE_EXTENDED
21 #ifndef _DEFAULT_SOURCE
22 #define _DEFAULT_SOURCE
34 #include <sys/ioctl.h>
44 typedef char hash_t[MD5_DIGEST_SIZE*2+1];
48 #undef NCURSES_OK_ADDR
49 #define NCURSES_OK_ADDR(p) ((void*)0 != NCURSES_CAST(const void *, (p)))
59 struct col_hash *col_hash;
63 char *rs1, *rs2, *rs3, *clear;
68 char last_screen[MD5_DIGEST_SIZE*2+1];
69 char next_screen[MD5_DIGEST_SIZE*2+1];
70 /* The next event to generate when idle */
71 enum { DoNil, DoMouse, DoKey, DoCheck, DoClose} next_event;
77 static SCREEN *current_screen;
78 static void ncurses_text(struct pane *p safe, struct pane *display safe,
79 wchar_t ch, int attr, short x, short y, short cursor);
80 static PANEL * safe pane_panel(struct pane *p safe, struct pane *home);
81 DEF_CMD(input_handle);
82 DEF_CMD(handle_winch);
83 static struct map *nc_map;
84 DEF_LOOKUP_CMD(ncurses_handle, nc_map);
86 static struct display_data *current_dd;
87 static void set_screen(struct pane *p)
89 struct display_data *dd;
90 extern void *_nc_globals[100];
92 static int index = -1, offset=0;
95 if (current_screen && index >= 0)
96 _nc_globals[index] = NULL;
97 current_screen = NULL;
104 if (dd->scr == current_screen)
109 for (i=0; i<100; i++)
110 if (_nc_globals[i] < (void*)stdscr &&
111 _nc_globals[i]+4*(sizeof(void*)) >= (void*)stdscr) {
112 /* This is _nc_windowlist */
114 offset = ((void*)stdscr) - _nc_globals[i];
119 current_screen = dd->scr;
121 _nc_globals[index] = ((void*)stdscr) - offset;
127 DEF_CMD(abort_replay);
129 static bool parse_event(struct pane *p safe);
130 static bool prepare_recrep(struct pane *p safe)
132 struct display_data *dd = p->data;
135 name = getenv("EDLIB_RECORD");
137 dd->log = fopen(name, "w");
138 name = getenv("EDLIB_REPLAY");
140 dd->input = fopen(name, "r");
141 if (getenv("EDLIB_PAUSE"))
142 sleep(atoi(getenv("EDLIB_PAUSE")));
150 static void close_recrep(struct pane *p safe)
152 struct display_data *dd = p->data;
155 fprintf(dd->log, "Close\n");
160 static void record_key(struct pane *p safe, char *key safe)
162 struct display_data *dd = p->data;
167 if (!strchr(key, '"'))
169 else if (!strchr(key, '\''))
171 else if (!strchr(key, '/'))
175 fprintf(dd->log, "Key %c%s%c\n", q,key,q);
178 static void record_mouse(struct pane *p safe, char *key safe, int x, int y)
180 struct display_data *dd = p->data;
184 if (!strchr(key, '"'))
186 else if (!strchr(key, '\''))
188 else if (!strchr(key, '/'))
192 fprintf(dd->log, "Mouse %c%s%c %d,%d\n", q,key,q, x, y);
195 static void record_screen(struct pane *p safe)
197 struct display_data *dd = p->data;
198 struct md5_state ctx;
199 uint16_t buf[CCHARW_MAX+5];
200 char out[MD5_DIGEST_SIZE*2+1];
203 if (!dd->log && !(dd->input && dd->next_event == DoCheck))
207 for (r = 0; r < p->h; r++)
208 for (c = 0; c < p->w; c++) {
210 wchar_t wc[CCHARW_MAX+2];
215 mvwin_wch(stdscr, r, c, &cc);
216 getcchar(&cc, wc, &a, &color, NULL);
217 pair_content(color, &fg, &bg);
218 buf[0] = htole16(fg);
219 buf[1] = htole16(bg);
220 for (l = 0; l < CCHARW_MAX && wc[l]; l++)
221 buf[l+3] = htole16(wc[l]);
223 LOG("%d,%d %d:%d:%d:%d %lc", c,r,fg,bg,l,wc[0], wc[0] > ' ' ? wc[0] : '?');
224 md5_update(&ctx, (uint8_t*)buf,
225 (l+3) * sizeof(uint16_t));
227 md5_final_txt(&ctx, out);
229 fprintf(dd->log, "Display %d,%d %s", p->w, p->h, out);
230 strcpy(dd->last_screen, out);
232 fprintf(dd->log, " %d,%d", p->cx, p->cy);
233 fprintf(dd->log, "\n");
235 if (dd->input && dd->next_event == DoCheck) {
236 call_comm("event:free", p, &abort_replay);
237 // if (strcmp(dd->last_screen, dd->next_screen) != 0)
238 // dd->next_event = DoClose;
239 call_comm("editor-on-idle", p, &next_evt);
243 static inline int match(char *line safe, char *w safe)
245 return strncmp(line, w, strlen(w)) == 0;
248 static char *copy_quote(char *line safe, char *buf safe)
254 if (q != '"' && q != '\'' && q != '/')
256 while (*line != q && *line)
264 static char *get_coord(char *line safe, struct xy *co safe)
271 v = strtol(line, &ep, 10);
272 if (!ep || ep == line || *ep != ',')
276 v = strtol(line, &ep, 10);
277 if (!ep || ep == line || (*ep && *ep != ' ' && *ep != '\n'))
283 static char *get_hash(char *line safe, hash_t hash safe)
288 for (i = 0; i < MD5_DIGEST_SIZE*2 && *line; i++)
295 REDEF_CMD(abort_replay)
297 struct display_data *dd = ci->home->data;
299 dd->next_event = DoClose;
300 return next_evt_func(ci);
303 static bool parse_event(struct pane *p safe)
305 struct display_data *dd = p->data;
309 dd->next_event = DoNil;
311 fgets(line, sizeof(line)-1, dd->input) == NULL)
313 else if (match(line, "Key ")) {
314 if (!copy_quote(line+4, dd->event_info))
316 dd->next_event = DoKey;
317 } else if (match(line, "Mouse ")) {
318 char *f = copy_quote(line+6, dd->event_info);
321 f = get_coord(f, &dd->event_pos);
324 dd->next_event = DoMouse;
325 } else if (match(line, "Display ")) {
326 char *f = get_coord(line+8, &dd->event_pos);
329 f = get_hash(f, dd->next_screen);
330 dd->next_event = DoCheck;
331 } else if (match(line, "Close")) {
332 dd->next_event = DoClose;
334 LOG("parse %s", line);
336 if (dd->next_event != DoCheck)
337 call_comm("editor-on-idle", p, &next_evt);
339 call_comm("event:timer", p, &abort_replay, 10*1000);
345 struct pane *p = ci->home;
346 struct display_data *dd = p->data;
347 int button = 0, type = 0;
350 delay = getenv("EDLIB_REPLAY_DELAY");
351 if (delay && dd->next_event != DoCheck)
352 usleep(atoi(delay)*1000);
354 switch(dd->next_event) {
356 record_key(p, dd->event_info);
357 call("Keystroke", p, 0, NULL, dd->event_info);
360 record_mouse(p, dd->event_info, dd->event_pos.x,
362 if (strstr(dd->event_info, ":Press"))
364 else if (strstr(dd->event_info, ":Release"))
366 else if (strstr(dd->event_info, ":Motion"))
368 if (type == 1 || type == 2) {
369 char *e = dd->event_info + strlen(dd->event_info) - 1;
372 call("Mouse-event", p, button, NULL, dd->event_info,
374 dd->event_pos.x, dd->event_pos.y);
377 /* No point checking, just do a diff against new trace log. */
378 /* not; (strcmp(dd->next_screen, dd->last_screen) != 0) */
381 call("event:deactivate", p);
385 call_comm("event:read", p, &input_handle, 0);
386 call_comm("event:signal", p, &handle_winch, SIGWINCH);
393 static inline bool prepare_recrep(struct pane *p safe) {return False;}
394 static inline void record_key(struct pane *p safe, char *key) {}
395 static inline void record_mouse(struct pane *p safe, char *key safe,
397 static inline void record_screen(struct pane *p safe) {}
398 static inline void close_recrep(struct pane *p safe) {}
403 struct call_return *cr = container_of(ci->comm, struct call_return, c);
409 DEF_CMD(nc_close_display)
411 /* If this is only display, then refuse to close this one */
412 struct call_return cr;
413 struct display_data *dd = ci->home->data;
416 call("Message", ci->focus, 0, NULL, dd->noclose);
422 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
424 pane_close(ci->home);
426 call("Message", ci->focus, 0, NULL,
427 "Cannot close only window.");
431 DEF_CMD(nc_set_noclose)
433 struct display_data *dd = ci->home->data;
438 dd->noclose = strdup(ci->str);
442 static int nc_putc(int ch)
445 fputc(ch, current_dd->scr_file);
449 DEF_CMD(nc_external_viewer)
451 struct pane *p = ci->home;
452 struct display_data *dd = p->data;
453 char *disp = pane_attr_get(p, "DISPLAY");
454 char *remote = pane_attr_get(p, "REMOTE_SESSION");
456 const char *path = ci->str;
465 switch (pid = fork()) {
469 setenv("DISPLAY", disp, 1);
470 fd = open("/dev/null", O_RDWR);
478 execlp("xdg-open", "xdg-open", path, NULL);
480 default: /* parent */
481 /* FIXME record pid?? */
486 /* handle no-display case */
487 if (remote && strcmp(remote, "yes") == 0 &&
489 gethostname(buf, sizeof(buf)) == 0) {
490 struct addrinfo *res;
491 const struct addrinfo hints = {
492 .ai_flags = AI_CANONNAME,
494 if (getaddrinfo(buf, NULL, &hints, &res) == 0 &&
495 res && res->ai_canonname)
496 fqdn = strdup(res->ai_canonname);
501 ioctl(fileno(dd->scr_file), FIONREAD, &n);
503 n -= read(fileno(dd->scr_file), buf,
504 n <= (int)sizeof(buf) ? n : (int)sizeof(buf));
507 /* Endwin doesn't seem to reset properly, at least on xfce-terminal.
511 tputs(dd->rs1, 1, nc_putc);
513 tputs(dd->rs2, 1, nc_putc);
515 tputs(dd->rs3, 1, nc_putc);
517 tputs(dd->clear, 1, nc_putc);
518 fflush(dd->scr_file);
520 fprintf(dd->scr_file, "# Consider copy-pasting following\n");
521 if (fqdn && path[0] == '/') {
522 /* File will not be local for the user, so help them copy it. */
523 const char *tmp=ci->str2;
526 fprintf(dd->scr_file, "f=`mktemp --tmpdir %s`; scp %s:%s $f ; ",
530 fprintf(dd->scr_file, "xdg-open %s\n", path);
531 fprintf(dd->scr_file, "# Press Enter to continue\n");
532 n = read(fileno(dd->scr_file), buf, sizeof(buf));
538 static void ncurses_end(struct pane *p safe)
540 struct display_data *dd = p->data;
547 tputs(dd->rs1, 1, nc_putc);
549 tputs(dd->rs2, 1, nc_putc);
551 tputs(dd->rs3, 1, nc_putc);
552 fflush(dd->scr_file);
556 * hash table for colours and pairs
557 * key is r,g,b (0-1000) in 10bit fields,
558 * or fg,bg in 16 bit fields with bit 31 set
559 * content is colour number of colour pair number.
560 * We never delete entries, unless we delete everything.
567 #define COL_KEY(r,g,b) ((r<<20) | (g<<10) | b)
568 #define PAIR_KEY(fg, bg) ((1<<31) | (fg<<16) | bg)
569 #define hash_key(k) ((((k) * 0x61C88647) >> 20) & 0xff)
572 int next_col, next_pair;
573 struct chash *tbl[256];
576 static struct col_hash *safe hash_init(struct display_data *dd safe)
579 dd->col_hash = malloc(sizeof(*dd->col_hash));
580 memset(dd->col_hash, 0, sizeof(*dd->col_hash));
581 dd->col_hash->next_col = 16;
582 dd->col_hash->next_pair = 1;
587 static void hash_free(struct display_data *dd safe)
596 for (h = 0; h < 255; h++)
597 while ((c = ch->tbl[h]) != NULL) {
598 ch->tbl[h] = c->next;
605 static int find_col(struct display_data *dd safe, int rgb[])
607 if (0 /* dynamic colours */) {
608 struct col_hash *ch = hash_init(dd);
609 int k = COL_KEY(rgb[0], rgb[1], rgb[2]);
613 for (c = ch->tbl[h]; c; c = c->next)
616 c = malloc(sizeof(*c));
618 c->content = ch->next_col++;
619 c->next = ch->tbl[h];
621 init_color(c->content, rgb[0], rgb[1], rgb[2]);
624 /* If colour is grey, map to 1 of 26 for 0, 232 to 255, 15
625 * The 24 grey shades have bit values from 8 to 238, so the
626 * gap to white is a little bigger, but that probably doesn't
628 * Otherwise map to 6x6x6 rgb cube from 16
629 * Actual colours are biased bright, at 0,95,135,175,215,255
630 * with a 95 gap at bottom and 40 elsewhere.
631 * So we divide 5 and 2 half ranges, and merge bottom 2.
636 //printf("want %d,%d,%d\n", rgb[0], rgb[1], rgb[2]);
637 if (abs(rgb[0] - rgb[1]) < 10 &&
638 abs(rgb[1] - rgb[2]) < 10) {
639 /* grey - within 1% */
640 int v = (rgb[0] + rgb[1] + rgb[2]) / 3;
642 /* We divide the space in 24 ranges surrounding
643 * the grey values, and 2 half-ranges near black
644 * and white. So add half a range - 1000/50 -
645 * then divide by 1000/25 to get a number from 0 to 25.
647 v = (v + 1000/50) / (1000/25);
649 return 0; /* black */
651 return 15; /* white */
652 //printf(" grey %d\n", v + 231);
653 /* grey shades are from 232 to 255 inclusive */
656 for (h = 0; h < 3; h++) {
659 v = (v + 1000/12) / (1000/6);
660 /* v is from 0 to 6, we want up to 5
661 * with 0 and 1 merged
668 //printf(" color %d\n", c + 16);
673 static int to_pair(struct display_data *dd safe, int fg, int bg)
675 struct col_hash *ch = hash_init(dd);
676 int k = PAIR_KEY(fg, bg);
680 for (c = ch->tbl[h]; c; c = c->next)
683 c = malloc(sizeof(*c));
685 c->content = ch->next_pair++;
686 c->next = ch->tbl[h];
688 init_pair(c->content, fg, bg);
693 static int cvt_attrs(struct pane *p safe, struct pane *home safe,
696 struct display_data *dd = home->data;
701 int fg = COLOR_BLACK;
702 int bg = COLOR_WHITE+8;
707 } while (p->parent != p &&(pan = pane_panel(p, NULL)) == NULL);
709 /* Get 'default colours for this pane - set at clear */
710 int at = getbkgd(panel_window(pan));
711 int pair = PAIR_NUMBER(at);
713 pair_content(pair, &dfg, &dbg);
729 strncpy(tmp, a, c-a);
731 if (strcmp(tmp, "inverse")==0) attr |= A_STANDOUT;
732 else if (strcmp(tmp, "noinverse")==0) attr &= ~A_STANDOUT;
733 else if (strcmp(tmp, "bold")==0) attr |= A_BOLD;
734 else if (strcmp(tmp, "nobold")==0) attr &= ~A_BOLD;
735 else if (strcmp(tmp, "underline")==0) attr |= A_UNDERLINE;
736 else if (strcmp(tmp, "nounderline")==0) attr &= ~A_UNDERLINE;
737 else if (strncmp(tmp, "fg:", 3) == 0) {
738 struct call_return cr =
739 call_ret(all, "colour:map", home,
741 int rgb[3] = {cr.i, cr.i2, cr.x};
742 fg = find_col(dd, rgb);
743 } else if (strncmp(tmp, "bg:", 3) == 0) {
744 struct call_return cr =
745 call_ret(all, "colour:map", home,
747 int rgb[3] = {cr.i, cr.i2, cr.x};
748 bg = find_col(dd, rgb);
752 if (fg != COLOR_BLACK || bg != COLOR_WHITE+8)
753 attr |= COLOR_PAIR(to_pair(dd, fg, bg));
757 static int make_cursor(int attr)
759 return attr ^ A_UNDERLINE;
762 DEF_CMD(nc_notify_display)
764 struct display_data *dd = ci->home->data;
765 comm_call(ci->comm2, "callback:display", ci->home, dd->last_event);
771 struct pane *p = ci->home;
772 struct display_data *dd = p->data;
775 fclose(dd->scr_file);
779 DEF_CMD(nc_pane_close)
783 set_screen(ci->home);
784 while ((pan = panel_above(pan)) != NULL)
785 if (panel_userptr(pan) == ci->focus)
788 WINDOW *win = panel_window(pan);
791 pane_damaged(ci->home, DAMAGED_POSTORDER);
796 static PANEL * safe pane_panel(struct pane *p safe, struct pane *home)
800 while ((pan = panel_above(pan)) != NULL)
801 if (panel_userptr(pan) == p)
807 pan = new_panel(newwin(p->h, p->w, 0, 0));
808 set_panel_userptr(pan, p);
809 pane_add_notify(home, p, "Notify:Close");
816 struct pane *p = ci->home;
817 int attr = cvt_attrs(ci->focus, p, ci->str2?:ci->str);
823 panel = pane_panel(ci->focus, p);
826 win = panel_window(panel);
828 if (h != ci->focus->h || w != ci->focus->w) {
829 wresize(win, ci->focus->h, ci->focus->w);
830 replace_panel(panel, win);
835 pane_damaged(p, DAMAGED_POSTORDER);
839 DEF_CMD(nc_text_size)
841 int max_space = ci->num;
844 const char *str = ci->str;
848 while (str[0] != 0) {
849 wint_t wc = get_utf8(&str, NULL);
857 if (size <= max_space)
858 max_bytes = str - ci->str;
860 return comm_call(ci->comm2, "callback:size", ci->focus,
861 max_bytes, NULL, NULL,
862 0, NULL, NULL, size, 1);
865 DEF_CMD(nc_draw_text)
867 struct pane *p = ci->home;
868 int attr = cvt_attrs(ci->focus, p, ci->str2);
869 int cursor_offset = ci->num;
870 short x = ci->x, y = ci->y;
871 const char *str = ci->str;
876 while (str[0] != 0) {
877 int precurs = str <= ci->str + cursor_offset;
878 wint_t wc = get_utf8(&str, NULL);
880 if (wc == WEOF || wc == WERR)
885 if (precurs && str > ci->str + cursor_offset)
886 ncurses_text(ci->focus, p, wc, attr, x, y, 1);
888 ncurses_text(ci->focus, p, wc, attr, x, y, 0);
891 if (str == ci->str + cursor_offset)
892 ncurses_text(ci->focus, p, ' ', 0, x, y, 1);
893 pane_damaged(p, DAMAGED_POSTORDER);
897 DEF_CMD(nc_refresh_size)
899 struct pane *p = ci->home;
902 getmaxyx(stdscr, p->h, p->w);
907 DEF_CMD(nc_refresh_post)
909 struct pane *p = ci->home;
915 /* Need to ensure stacking order and panel y,x position
916 * is correct. FIXME it would be good if we could skip this
919 pan = panel_above(NULL);
922 p1 = (struct pane*) panel_userptr(pan);
923 for (;(pan2 = panel_above(pan)) != NULL; pan = pan2) {
924 struct pane *p2 = (struct pane*)panel_userptr(pan2);
925 p1 = (struct pane*)panel_userptr(pan);
929 if (p1->abs_z < p2->abs_z)
931 if (p1->abs_z == p2->abs_z &&
934 /* pan needs to be above pan2. All we can do is move it to
935 * the top. Anything that needs to be above it will eventually
939 /* Now the panel below pan might need to be over pan2 too... */
940 pan = panel_below(pan2);
945 /* As we need to crop pane against their parents, we cannot simply
946 * use update_panels(). Instead we copy each to stdscr and refresh
949 for (pan = NULL; (pan = panel_above(pan)) != NULL; ) {
951 struct xy src, dest, destend;
954 p1 = (void*)panel_userptr(pan);
957 dest = pane_mapxy(p1, p, 0, 0, True);
958 destend = pane_mapxy(p1, p, p1->w, p1->h, True);
959 src = pane_mapxy(p1, p, 0, 0, False);
960 src.x = dest.x - src.x;
961 src.y = dest.y - src.y;
962 win = panel_window(pan);
964 /* guard again accessing beyond boundary of win */
965 if (destend.x > dest.x + (w - src.x))
966 destend.x = dest.x + (w - src.x);
967 if (destend.y > dest.y + (h - src.y))
968 destend.y = dest.y - (h - src.y);
969 copywin(win, stdscr, src.y, src.x,
970 dest.y, dest.x, destend.y-1, destend.x-1, 0);
972 /* place the cursor */
975 while (p1 != p && (pan = pane_panel(p1, NULL)) == NULL)
977 if (pan && p1->cx >= 0) {
978 struct xy curs = pane_mapxy(p1, p, p1->cx, p1->cy, False);
979 wmove(stdscr, curs.y, curs.x);
980 } else if (p->cx >= 0)
981 wmove(stdscr, p->cy, p->cx);
983 record_screen(ci->home);
987 static struct pane *ncurses_init(struct pane *ed,
988 const char *tty, const char *term)
992 struct display_data *dd;
999 f = fopen(tty, "r+");
1001 f = fdopen(1, "r+");
1004 scr = newterm(term, f, f);
1011 dd->is_xterm = (term && strncmp(term, "xterm", 5) == 0);
1013 p = pane_register(ed, 1, &ncurses_handle.c, dd);
1021 use_default_colors();
1027 intrflush(stdscr, FALSE);
1028 keypad(stdscr, TRUE);
1029 mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED |
1030 BUTTON2_PRESSED | BUTTON2_RELEASED |
1031 BUTTON3_PRESSED | BUTTON3_RELEASED |
1032 BUTTON4_PRESSED | BUTTON4_RELEASED |
1033 BUTTON5_PRESSED | BUTTON5_RELEASED |
1034 BUTTON_CTRL | BUTTON_SHIFT | BUTTON_ALT |
1035 REPORT_MOUSE_POSITION, NULL);
1038 getmaxyx(stdscr, rows, cols);
1039 pane_resize(p, 0, 0, cols, rows);
1041 area = dd->attr_buf;
1042 dd->rs1 = tgetstr("rs1", &area);
1044 dd->rs1 = tgetstr("is1", &area);
1045 dd->rs2 = tgetstr("rs2", &area);
1047 dd->rs2 = tgetstr("is2", &area);
1048 dd->rs3 = tgetstr("rs3", &area);
1050 dd->rs3 = tgetstr("is3", &area);
1051 dd->clear = tgetstr("clear", &area);
1053 call("editor:request:all-displays", p);
1054 if (!prepare_recrep(p)) {
1055 call_comm("event:read", p, &input_handle, fileno(f));
1057 call_comm("event:signal", p, &handle_winch, SIGWINCH);
1062 REDEF_CMD(handle_winch)
1064 struct pane *p = ci->home;
1065 struct display_data *dd = p->data;
1066 struct winsize size;
1067 ioctl(fileno(dd->scr_file), TIOCGWINSZ, &size);
1069 resize_term(size.ws_row, size.ws_col);
1071 pane_resize(p, 0, 0, size.ws_row, size.ws_col);
1075 DEF_CMD(force_redraw)
1077 struct pane *p = ci->home;
1084 static void ncurses_text(struct pane *p safe, struct pane *display safe,
1085 wchar_t ch, int attr, short x, short y, short cursor)
1094 set_screen(display);
1096 if (pane_has_focus(p->z < 0 ? p->parent : p)) {
1097 /* Cursor is in-focus */
1098 struct xy curs = pane_mapxy(p, display, x, y, False);
1099 display->cx = curs.x;
1100 display->cy = curs.y;
1102 /* Cursor here, but not focus */
1103 attr = make_cursor(attr);
1109 pan = pane_panel(p2, NULL);
1110 while (!pan && p2->parent != p2) {
1112 pan = pane_panel(p2, NULL);
1115 struct xy xy = pane_mapxy(p, p2, x, y, False);
1116 mvwadd_wch(panel_window(pan), xy.y, xy.x, &cc);
1120 static struct namelist {
1124 {KEY_DOWN, ":Down"},
1126 {KEY_LEFT, ":Left"},
1127 {KEY_RIGHT, ":Right"},
1128 {KEY_HOME, ":Home"},
1129 {KEY_BACKSPACE, ":Backspace"},
1130 {KEY_DL, ":DelLine"},
1131 {KEY_IL, ":InsLine"},
1134 {KEY_ENTER, ":Enter"},
1137 {KEY_NPAGE, ":Next"},
1138 {KEY_PPAGE, ":Prior"},
1140 {KEY_SDC, ":S:Del"},
1141 {KEY_SDL, ":S:DelLine"},
1142 {KEY_SEND, ":S:End"},
1143 {KEY_SHOME, ":S:Home"},
1144 {KEY_SLEFT, ":S:Left"},
1145 {KEY_SRIGHT, ":S:Right"},
1146 {KEY_BTAB, ":S:Tab"},
1150 { 0616, ":S:Prior"},
1152 { 01041, ":S:Home"},
1154 { 01066, ":S:Prior"},
1155 { 01015, ":S:Next"},
1157 { 01027, ":A:S:Home"},
1158 { 01022, ":A:S:End"},
1159 { 01046, ":A:S:Prior"},
1160 { 01047, ":A:S:Next"}, // ??
1162 { 01052, ":A:Prior"},
1163 { 01045, ":A:Next"},
1164 { 01026, ":A:Home"},
1167 { 01014, ":A:Down"},
1168 { 01040, ":A:Left"},
1169 { 01057, ":A:Right"},
1200 {'\177', ":Delete"},
1205 static char *find_name (struct namelist *l safe, wint_t c)
1208 for (i = 0; l[i].name; i++)
1214 static void send_key(int keytype, wint_t c, int alt, struct pane *p safe)
1216 struct display_data *dd = p->data;
1218 char buf[100];/* FIXME */
1220 char *a = alt ? ":A" : "";
1222 if (keytype == KEY_CODE_YES) {
1223 n = find_name(key_names, c);
1225 sprintf(buf, "%sNcurs-%o", a, c);
1227 strcat(strcpy(buf, a), n);
1229 n = find_name(char_names, c);
1231 sprintf(buf, "%s%s", a, n);
1232 else if (c < ' ' || c == 0x7f)
1233 sprintf(buf, "%s:C-%c",
1236 sprintf(buf, "%s-%s", a, put_utf8(t, c));
1239 dd->last_event = time(NULL);
1241 call("Keystroke", p, 0, NULL, buf);
1244 static void do_send_mouse(struct pane *p safe, int x, int y, char *cmd safe,
1245 int button, char *mod, int type)
1248 struct display_data *dd = p->data;
1250 record_mouse(p, cmd, x, y);
1251 ret = call("Mouse-event", p, button, NULL, cmd, type, NULL, mod, x, y);
1252 if (type == 1 && !dd->report_position) {
1254 fprintf(dd->scr_file, "\033[?1002h");
1255 fflush(dd->scr_file);
1257 dd->report_position = 1;
1258 } else if (type == 3 && ret <= 0) {
1260 fprintf(dd->scr_file, "\033[?1002l");
1261 fflush(dd->scr_file);
1263 dd->report_position = 0;
1267 static void send_mouse(MEVENT *mev safe, struct pane *p safe)
1269 struct display_data *dd = p->data;
1275 /* MEVENT has lots of bits. We want a few numbers */
1276 for (b = 1 ; b <= (NCURSES_MOUSE_VERSION <= 1 ? 3 : 5); b++) {
1277 mmask_t s = mev->bstate;
1282 if (s & BUTTON_SHIFT) modf |= 1;
1283 if (s & BUTTON_CTRL) modf |= 2;
1284 if (s & BUTTON_ALT) modf |= 4;
1286 case 0: mod = ""; break;
1287 case 1: mod = ":S"; break;
1288 case 2: mod = ":C"; break;
1289 case 3: mod = ":C:S"; break;
1290 case 4: mod = ":A"; break;
1291 case 5: mod = ":A:S"; break;
1292 case 6: mod = ":A:C"; break;
1293 case 7: mod = ":A:C:S"; break;
1295 if (BUTTON_PRESS(s, b))
1296 action = "%s:Press-%d";
1297 else if (BUTTON_RELEASE(s, b)) {
1298 action = "%s:Release-%d";
1299 /* Modifiers only reported on button Press */
1303 snprintf(buf, sizeof(buf), action, mod, b);
1304 dd->last_event = time(NULL);
1305 do_send_mouse(p, x, y, buf, b, mod, BUTTON_PRESS(s,b) ? 1 : 2);
1307 if ((mev->bstate & REPORT_MOUSE_POSITION) &&
1308 dd->report_position)
1309 /* Motion doesn't update last_event */
1310 do_send_mouse(p, x, y, ":Motion", 0, "", 3);
1313 REDEF_CMD(input_handle)
1315 struct pane *p = ci->home;
1319 int have_escape = 0;
1321 if (!(void*)p->data)
1322 /* already closed */
1325 while ((is_keycode = get_wch(&c)) != ERR) {
1326 if (c == KEY_MOUSE) {
1328 while (getmouse(&mev) != ERR)
1329 send_mouse(&mev, p);
1330 } else if (have_escape) {
1331 send_key(is_keycode, c, 1, p);
1333 } else if (c == '\e')
1336 send_key(is_keycode, c, 0, p);
1337 /* Don't know what other code might have done,
1338 * so re-set the screen
1343 send_key(is_keycode, '\e', 0, p);
1347 DEF_CMD(display_ncurses)
1352 term = pane_attr_get(ci->focus, "TERM");
1354 term = getenv("TERM");
1356 term = "xterm-256color";
1358 p = ncurses_init(ci->focus, ci->str, term);
1360 struct pane *p2 = call_ret(pane, "attach-x11selection", p);
1363 return comm_call(ci->comm2, "callback:display", p);
1368 void edlib_init(struct pane *ed safe)
1370 call_comm("global-set-command", ed, &display_ncurses, 0, NULL,
1371 "attach-display-ncurses");
1373 nc_map = key_alloc();
1374 key_add(nc_map, "Display:refresh", &force_redraw);
1375 key_add(nc_map, "Display:close", &nc_close_display);
1376 key_add(nc_map, "Display:set-noclose", &nc_set_noclose);
1377 key_add(nc_map, "Display:external-viewer", &nc_external_viewer);
1378 key_add(nc_map, "Close", &nc_close);
1379 key_add(nc_map, "Free", &edlib_do_free);
1380 key_add(nc_map, "pane-clear", &nc_clear);
1381 key_add(nc_map, "text-size", &nc_text_size);
1382 key_add(nc_map, "Draw:text", &nc_draw_text);
1383 key_add(nc_map, "Refresh:size", &nc_refresh_size);
1384 key_add(nc_map, "Refresh:postorder", &nc_refresh_post);
1385 key_add(nc_map, "all-displays", &nc_notify_display);
1386 key_add(nc_map, "Sig:Winch", &handle_winch);
1387 key_add(nc_map, "Notify:Close", &nc_pane_close);