]> git.neil.brown.name Git - edlib.git/blob - display-ncurses.c
ncurses: call ncurses_end() before closing the pane.
[edlib.git] / display-ncurses.c
1 /*
2  * Copyright Neil Brown ©2015-2021 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * ncurses front end for edlib.
6  *
7  * There is currently only support for a single terminal window
8  * which provides a single pane.
9  *
10  * Rendering operations are:
11  *  draw text with attributes at location
12  *  erase with attributes in rectangle
13  */
14
15 #define RECORD_REPLAY
16
17 #ifndef _XOPEN_SOURCE
18 #define _XOPEN_SOURCE
19 #endif
20 #define _XOPEN_SOURCE_EXTENDED
21 #ifndef _DEFAULT_SOURCE
22 #define _DEFAULT_SOURCE
23 #endif
24
25 #include <stdlib.h>
26 #include <fcntl.h>
27 #include <time.h>
28 #include <curses.h>
29 #include <panel.h>
30 #include <string.h>
31 #include <locale.h>
32 #include <ctype.h>
33 #include <signal.h>
34 #include <sys/ioctl.h>
35 #include <term.h>
36 #include <netdb.h>
37
38 #include "core.h"
39
40 #ifdef RECORD_REPLAY
41 #include <unistd.h>
42 #include <stdio.h>
43 #include "md5.h"
44 typedef char hash_t[MD5_DIGEST_SIZE*2+1];
45 #endif
46
47 #ifdef __CHECKER__
48 #undef NCURSES_OK_ADDR
49 #define NCURSES_OK_ADDR(p) ((void*)0 != NCURSES_CAST(const void *, (p)))
50 #endif
51
52 struct col_hash;
53
54 struct display_data {
55         SCREEN                  *scr;
56         FILE                    *scr_file;
57         int                     is_xterm;
58         char                    *noclose;
59         struct col_hash         *col_hash;
60         int                     report_position;
61         long                    last_event;
62
63         bool                    did_close;
64
65         struct buf              paste_buf;
66         time_t                  paste_start;
67         char                    *paste_latest;
68         int                     paste_pending;
69
70         char                    *rs1, *rs2, *rs3, *clear;
71         char                    attr_buf[1024];
72         #ifdef RECORD_REPLAY
73         FILE                    *log;
74         FILE                    *input;
75         int                     input_sleeping;
76         char                    last_screen[MD5_DIGEST_SIZE*2+1];
77         char                    next_screen[MD5_DIGEST_SIZE*2+1];
78         /* The next event to generate when idle */
79         enum { DoNil, DoMouse, DoKey, DoCheck, DoClose} next_event;
80         char                    event_info[30];
81         struct xy               event_pos;
82
83         int                     clears; /* counts of Draw:clear events */
84         #endif
85 };
86
87 static SCREEN *current_screen;
88 static void ncurses_text(struct pane *p safe, struct pane *display safe,
89                          wchar_t ch, int attr, short x, short y, short cursor);
90 static PANEL * safe pane_panel(struct pane *p safe, struct pane *home);
91 DEF_CMD(input_handle);
92 DEF_CMD(handle_winch);
93 static struct map *nc_map;
94 DEF_LOOKUP_CMD(ncurses_handle, nc_map);
95
96 static struct display_data *current_dd;
97 static void set_screen(struct pane *p)
98 {
99         struct display_data *dd;
100         extern void *_nc_globals[100];
101         int i;
102         static int index = -1, offset=0;
103
104         if (!p) {
105                 if (current_screen && index >= 0)
106                         _nc_globals[index] = NULL;
107                 current_screen = NULL;
108                 return;
109         }
110         dd = p->data;
111         current_dd = dd;
112         if (!dd)
113                 return;
114         if (dd->scr == current_screen)
115                 return;
116
117         if (index == -1) {
118                 index = -2;
119                 for (i=0; i<100; i++)
120                         if (_nc_globals[i] < (void*)stdscr &&
121                             _nc_globals[i]+4*(sizeof(void*)) >= (void*)stdscr) {
122                                 /* This is _nc_windowlist */
123                                 index = i;
124                                 offset = ((void*)stdscr) - _nc_globals[i];
125                         }
126         }
127
128         set_term(dd->scr);
129         current_screen = dd->scr;
130         if (index >= 0) {
131                 _nc_globals[index] = ((void*)stdscr) - offset;
132         }
133 }
134
135 #ifdef RECORD_REPLAY
136 DEF_CMD(next_evt);
137
138 static bool parse_event(struct pane *p safe);
139 static bool prepare_recrep(struct pane *p safe)
140 {
141         struct display_data *dd = p->data;
142         char *name;
143
144         name = getenv("EDLIB_RECORD");
145         if (name)
146                 dd->log = fopen(name, "w");
147         name = getenv("EDLIB_REPLAY");
148         if (name)
149                 dd->input = fopen(name, "r");
150         if (getenv("EDLIB_PAUSE"))
151                 sleep(atoi(getenv("EDLIB_PAUSE")));
152         if (dd->input) {
153                 parse_event(p);
154                 return True;
155         }
156         return False;
157 }
158
159 static void close_recrep(struct pane *p safe)
160 {
161         struct display_data *dd = p->data;
162
163         if (dd->log) {
164                 fprintf(dd->log, "Close %d\n", dd->clears);
165                 fclose(dd->log);
166         }
167 }
168
169 static void record_key(struct pane *p safe, char *key safe)
170 {
171         struct display_data *dd = p->data;
172         char q;
173
174         if (!dd->log)
175                 return;
176         if (!strchr(key, '"'))
177                 q = '"';
178         else if (!strchr(key, '\''))
179                 q = '\'';
180         else if (!strchr(key, '/'))
181                 q = '/';
182         else
183                 return;
184         fprintf(dd->log, "Key %c%s%c\n", q,key,q);
185         fflush(dd->log);
186 }
187
188 static void record_mouse(struct pane *p safe, char *key safe, int x, int y)
189 {
190         struct display_data *dd = p->data;
191         char q;
192         if (!dd->log)
193                 return;
194         if (!strchr(key, '"'))
195                 q = '"';
196         else if (!strchr(key, '\''))
197                 q = '\'';
198         else if (!strchr(key, '/'))
199                 q = '/';
200         else
201                 return;
202         fprintf(dd->log, "Mouse %c%s%c %d,%d\n", q,key,q, x, y);
203         fflush(dd->log);
204 }
205
206 static void record_screen(struct pane *p safe)
207 {
208         struct display_data *dd = p->data;
209         struct md5_state ctx;
210         uint16_t buf[CCHARW_MAX+5];
211         char out[MD5_DIGEST_SIZE*2+1];
212         int r,c;
213
214         if (!dd->log && !(dd->input && dd->next_event == DoCheck))
215                 return;
216         set_screen(p);
217         md5_init(&ctx);
218         for (r = 0; r < p->h; r++)
219                 for (c = 0; c < p->w; c++) {
220                         cchar_t cc;
221                         wchar_t wc[CCHARW_MAX+2];
222                         attr_t a;
223                         short color, fg, bg;
224                         int l;
225
226                         mvwin_wch(stdscr, r, c, &cc);
227                         getcchar(&cc, wc, &a, &color, NULL);
228                         pair_content(color, &fg, &bg);
229                         buf[0] = htole16(fg);
230                         buf[1] = htole16(bg);
231                         for (l = 0; l < CCHARW_MAX && wc[l]; l++)
232                                 buf[l+3] = htole16(wc[l]);
233                         buf[2] = htole16(l);
234                         LOG("%d,%d %d:%d:%d:%d %lc", c,r,fg,bg,l,wc[0], wc[0] > ' ' ? wc[0] : '?');
235                         md5_update(&ctx, (uint8_t*)buf,
236                                    (l+3) * sizeof(uint16_t));
237                 }
238         md5_final_txt(&ctx, out);
239         if (dd->log) {
240                 fprintf(dd->log, "Display %d,%d %s", p->w, p->h, out);
241                 strcpy(dd->last_screen, out);
242                 if (p->cx >= 0)
243                         fprintf(dd->log, " %d,%d", p->cx, p->cy);
244                 fprintf(dd->log, "\n");
245                 fflush(dd->log);
246         }
247         if (dd->input && dd->input_sleeping) {
248                 char *delay = getenv("EDLIB_REPLAY_DELAY");
249                 call_comm("event:free", p, &next_evt);
250                 if (delay)
251                         call_comm("event:timer", p, &next_evt, atoi(delay));
252                 else
253                         call_comm("editor-on-idle", p, &next_evt);
254         }
255 }
256
257 static inline int match(char *line safe, char *w safe)
258 {
259         return strncmp(line, w, strlen(w)) == 0;
260 }
261
262 static char *copy_quote(char *line safe, char *buf safe)
263 {
264         char q;
265         while (*line == ' ')
266                 line++;
267         q = *line++;
268         if (q != '"' && q != '\'' && q != '/')
269                 return NULL;
270         while (*line != q && *line)
271                 *buf++ = *line++;
272         if (!*line)
273                 return NULL;
274         *buf = '\0';
275         return line+1;
276 }
277
278 static char *get_coord(char *line safe, struct xy *co safe)
279 {
280         long v;
281         char *ep;
282
283         while (*line == ' ')
284                 line ++;
285         v = strtol(line, &ep, 10);
286         if (!ep || ep == line || *ep != ',')
287                 return NULL;
288         co->x = v;
289         line = ep+1;
290         v = strtol(line, &ep, 10);
291         if (!ep || ep == line || (*ep && *ep != ' ' && *ep != '\n'))
292                 return NULL;
293         co->y = v;
294         return ep;
295 }
296
297 static char *get_hash(char *line safe, hash_t hash safe)
298 {
299         int i;
300         while (*line == ' ')
301                 line++;
302         for (i = 0; i < MD5_DIGEST_SIZE*2 && *line; i++)
303                 hash[i] = *line++;
304         if (!*line)
305                 return NULL;
306         return line;
307 }
308
309 static bool parse_event(struct pane *p safe)
310 {
311         struct display_data *dd = p->data;
312         char line[80];
313
314         line[79] = 0;
315         dd->next_event = DoNil;
316         if (!dd->input ||
317             fgets(line, sizeof(line)-1, dd->input) == NULL)
318                 line[0]=0;
319         else if (match(line, "Key ")) {
320                 if (!copy_quote(line+4, dd->event_info))
321                         return False;
322                 dd->next_event = DoKey;
323         } else if (match(line, "Mouse ")) {
324                 char *f = copy_quote(line+6, dd->event_info);
325                 if (!f)
326                         return False;
327                 f = get_coord(f, &dd->event_pos);
328                 if (!f)
329                         return False;
330                 dd->next_event = DoMouse;
331         } else if (match(line, "Display ")) {
332                 char *f = get_coord(line+8, &dd->event_pos);
333                 if (!f)
334                         return False;
335                 f = get_hash(f, dd->next_screen);
336                 dd->next_event = DoCheck;
337         } else if (match(line, "Close")) {
338                 dd->next_event = DoClose;
339         }
340         LOG("parse %s", line);
341
342         dd->input_sleeping = 1;
343         if (dd->next_event != DoCheck) {
344                 char *delay = getenv("EDLIB_REPLAY_DELAY");
345                 if (delay)
346                         call_comm("event:timer", p, &next_evt, atoi(delay));
347                 else
348                         call_comm("editor-on-idle", p, &next_evt);
349         } else
350                 call_comm("event:timer", p, &next_evt, 10*1000);
351         return True;
352 }
353
354 REDEF_CMD(next_evt)
355 {
356         struct pane *p = ci->home;
357         struct display_data *dd = p->data;
358         int button = 0, type = 0;
359
360         dd->input_sleeping = 0;
361         switch(dd->next_event) {
362         case DoKey:
363                 record_key(p, dd->event_info);
364                 call("Keystroke", p, 0, NULL, dd->event_info);
365                 break;
366         case DoMouse:
367                 record_mouse(p, dd->event_info, dd->event_pos.x,
368                              dd->event_pos.y);
369                 if (strstr(dd->event_info, ":Press"))
370                         type = 1;
371                 else if (strstr(dd->event_info, ":Release"))
372                         type = 2;
373                 else if (strstr(dd->event_info, ":Motion"))
374                         type = 3;
375                 if (type == 1 || type == 2) {
376                         char *e = dd->event_info + strlen(dd->event_info) - 1;
377                         button = atoi(e);
378                 }
379                 call("Mouse-event", p, button, NULL, dd->event_info,
380                      type, NULL, NULL,
381                      dd->event_pos.x, dd->event_pos.y);
382                 break;
383         case DoCheck:
384                 /* No point checking, just do a diff against new trace log. */
385                 /* not; (strcmp(dd->next_screen, dd->last_screen) != 0) */
386                 break;
387         case DoClose:
388                 call("event:deactivate", p);
389                 pane_close(p);
390                 return 1;
391         case DoNil:
392                 call_comm("event:read", p, &input_handle, 0);
393                 call_comm("event:signal", p, &handle_winch, SIGWINCH);
394                 return 1;
395         }
396         parse_event(p);
397         return 1;
398 }
399 #else
400 static inline bool  prepare_recrep(struct pane *p safe) {return False;}
401 static inline void record_key(struct pane *p safe, char *key) {}
402 static inline void record_mouse(struct pane *p safe, char *key safe,
403                                 int x, int y) {}
404 static inline void record_screen(struct pane *p safe) {}
405 static inline void close_recrep(struct pane *p safe) {}
406 #endif
407
408 DEF_CB(cnt_disp)
409 {
410         struct call_return *cr = container_of(ci->comm, struct call_return, c);
411
412         cr->i += 1;
413         return 1;
414 }
415
416 static void ncurses_end(struct pane *p safe);
417
418 DEF_CMD(nc_close_display)
419 {
420         /* If this is only display, then refuse to close this one */
421         struct call_return cr;
422         struct display_data *dd = ci->home->data;
423
424         if (dd->noclose) {
425                 call("Message", ci->focus, 0, NULL, dd->noclose);
426                 return 1;
427         }
428
429         cr.c = cnt_disp;
430         cr.i = 0;
431         call_comm("editor:notify:all-displays", ci->focus, &cr.c);
432         if (cr.i > 1) {
433                 /* Need to call ncurses_end() before we send a Notify:Close
434                  * notification, else server exists too early
435                  */
436                 ncurses_end(ci->home);
437                 pane_close(ci->home);
438         } else
439                 call("Message", ci->focus, 0, NULL,
440                      "Cannot close only window.");
441         return 1;
442 }
443
444 DEF_CMD(nc_set_noclose)
445 {
446         struct display_data *dd = ci->home->data;
447
448         free(dd->noclose);
449         dd->noclose = NULL;
450         if (ci->str)
451                 dd->noclose = strdup(ci->str);
452         return 1;
453 }
454
455 static int nc_putc(int ch)
456 {
457         if (current_dd)
458                 fputc(ch, current_dd->scr_file);
459         return 1;
460 }
461
462 static char *fnormalize(struct pane *p safe, const char *str) safe
463 {
464         char *ret = strsave(p, str);
465         char *cp;
466
467         for (cp = ret ; cp && *cp ; cp++)
468                 if (!isalnum(*cp) &&
469                     !strchr("/_-+=.,@#", *cp))
470                         /* Don't like this char */
471                         *cp = '_';
472         return ret ?: "_";
473 }
474
475 DEF_CMD(nc_external_viewer)
476 {
477         struct pane *p = ci->home;
478         struct display_data *dd = p->data;
479         char *disp = pane_attr_get(p, "DISPLAY");
480         char *remote = pane_attr_get(p, "REMOTE_SESSION");
481         char *fqdn = NULL;
482         const char *path = ci->str;
483         int pid;
484         char buf[100];
485         int n;
486         int fd;
487
488         if (!path)
489                 return Enoarg;
490         if (disp && *disp) {
491                 switch (pid = fork()) {
492                 case -1:
493                         return Efail;
494                 case 0: /* Child */
495                         setenv("DISPLAY", disp, 1);
496                         fd = open("/dev/null", O_RDWR);
497                         if (fd) {
498                                 dup2(fd, 0);
499                                 dup2(fd, 1);
500                                 dup2(fd, 2);
501                                 if (fd > 2)
502                                         close(fd);
503                         }
504                         execlp("xdg-open", "xdg-open", path, NULL);
505                         exit(1);
506                 default: /* parent */
507                         /* FIXME record pid?? */
508                         break;
509                 }
510                 return 1;
511         }
512         /* handle no-display case */
513         if (remote && strcmp(remote, "yes") == 0 &&
514             path[0] == '/' &&
515             gethostname(buf, sizeof(buf)) == 0) {
516                 struct addrinfo *res;
517                 const struct addrinfo hints = {
518                         .ai_flags = AI_CANONNAME,
519                 };
520                 if (getaddrinfo(buf, NULL, &hints, &res) == 0 &&
521                     res && res->ai_canonname)
522                         fqdn = strdup(res->ai_canonname);
523                 freeaddrinfo(res);
524         }
525         set_screen(p);
526         n = 0;
527         ioctl(fileno(dd->scr_file), FIONREAD, &n);
528         if (n)
529                 n -= read(fileno(dd->scr_file), buf,
530                           n <= (int)sizeof(buf) ? n : (int)sizeof(buf));
531         endwin();
532
533         /* Endwin doesn't seem to reset properly, at least on xfce-terminal.
534          * So do it manually
535          */
536         if (dd->rs1)
537                 tputs(dd->rs1, 1, nc_putc);
538         if (dd->rs2)
539                 tputs(dd->rs2, 1, nc_putc);
540         if (dd->rs3)
541                 tputs(dd->rs3, 1, nc_putc);
542         if (dd->clear)
543                 tputs(dd->clear, 1, nc_putc);
544         fflush(dd->scr_file);
545
546         fprintf(dd->scr_file, "# Consider copy-pasting following\n");
547         if (fqdn && path[0] == '/') {
548                 /* File will not be local for the user, so help them copy it. */
549                 const char *tmp = fnormalize(p, ci->str2 ?: "XXXXXX");
550                 const char *fname = fnormalize(p, ci->str);
551
552                 if (strcmp(fname, ci->str) != 0)
553                         /* file name had unusuable chars, need to create safe name */
554                         link(ci->str, fname);
555                 fprintf(dd->scr_file, "f=`mktemp --tmpdir %s`; scp %s:%s $f ; ",
556                         tmp, fqdn, fname);
557                 path = "$f";
558         }
559         fprintf(dd->scr_file, "xdg-open %s\n", path);
560         fprintf(dd->scr_file, "# Press Enter to continue\n");
561         n = read(fileno(dd->scr_file), buf, sizeof(buf));
562         free(fqdn);
563         doupdate();
564         return 1;
565 }
566
567 static void ncurses_end(struct pane *p safe)
568 {
569         struct display_data *dd = p->data;
570
571         if (dd->did_close)
572                 return;
573         dd->did_close = True;
574         set_screen(p);
575         close_recrep(p);
576
577         if (dd->is_xterm) {
578                 /* disable bracketed-paste */
579                 fprintf(dd->scr_file, "\033[?2004l");
580                 fflush(dd->scr_file);
581         }
582         if (dd->paste_start)
583                 free(buf_final(&dd->paste_buf));
584         free(dd->paste_latest);
585         nl();
586         endwin();
587         if (dd->rs1)
588                 tputs(dd->rs1, 1, nc_putc);
589         if (dd->rs2)
590                 tputs(dd->rs2, 1, nc_putc);
591         if (dd->rs3)
592                 tputs(dd->rs3, 1, nc_putc);
593         fflush(dd->scr_file);
594 }
595
596 /*
597  * hash table for colours and pairs
598  * key is r,g,b (0-1000) in 10bit fields,
599  * or fg,bg in 16 bit fields with bit 31 set
600  * content is colour number of colour pair number.
601  * We never delete entries, unless we delete everything.
602  */
603
604 struct chash {
605         struct chash *next;
606         int key, content;
607 };
608 #define COL_KEY(r,g,b) ((r<<20) | (g<<10) | b)
609 #define PAIR_KEY(fg, bg) ((1<<31) | (fg<<16) | bg)
610 #define hash_key(k) ((((k) * 0x61C88647) >> 20) & 0xff)
611
612 struct col_hash {
613         int next_col, next_pair;
614         struct chash *tbl[256];
615 };
616
617 static struct col_hash *safe hash_init(struct display_data *dd safe)
618 {
619         if (!dd->col_hash) {
620                 dd->col_hash = malloc(sizeof(*dd->col_hash));
621                 memset(dd->col_hash, 0, sizeof(*dd->col_hash));
622                 dd->col_hash->next_col = 16;
623                 dd->col_hash->next_pair = 1;
624         }
625         return dd->col_hash;
626 }
627
628 static void hash_free(struct display_data *dd safe)
629 {
630         int h;
631         struct chash *c;
632         struct col_hash *ch;
633
634         ch = dd->col_hash;
635         if (!ch)
636                 return;
637         for (h = 0; h < 255; h++)
638                 while ((c = ch->tbl[h]) != NULL) {
639                         ch->tbl[h] = c->next;
640                         free(c);
641                 }
642         free(ch);
643         dd->col_hash = NULL;
644 }
645
646 static int find_col(struct display_data *dd safe, int rgb[])
647 {
648         if (0 /* dynamic colours */) {
649                 struct col_hash *ch = hash_init(dd);
650                 int k = COL_KEY(rgb[0], rgb[1], rgb[2]);
651                 int h = hash_key(k);
652                 struct chash *c;
653
654                 for (c = ch->tbl[h]; c; c = c->next)
655                         if (c->key == k)
656                                 return c->content;
657                 c = malloc(sizeof(*c));
658                 c->key = k;
659                 c->content = ch->next_col++;
660                 c->next = ch->tbl[h];
661                 ch->tbl[h] = c;
662                 init_color(c->content, rgb[0], rgb[1], rgb[2]);
663                 return c->content;
664         } else {
665                 /* If colour is grey, map to 1 of 26 for 0, 232 to 255, 15
666                  * The 24 grey shades have bit values from 8 to 238, so the
667                  * gap to white is a little bigger, but that probably doesn't
668                  * matter.
669                  * Otherwise map to 6x6x6 rgb cube from 16
670                  * Actual colours are biased bright, at 0,95,135,175,215,255
671                  * with a 95 gap at bottom and 40 elsewhere.
672                  * So we divide 5 and 2 half ranges, and merge bottom 2.
673                  */
674                 int c = 0;
675                 int h;
676
677                 //printf("want %d,%d,%d\n", rgb[0], rgb[1], rgb[2]);
678                 if (abs(rgb[0] - rgb[1]) < 10 &&
679                     abs(rgb[1] - rgb[2]) < 10) {
680                         /* grey - within 1% */
681                         int v = (rgb[0] + rgb[1] + rgb[2]) / 3;
682
683                         /* We divide the space in 24 ranges surrounding
684                          * the grey values, and 2 half-ranges near black
685                          * and white.  So add half a range - 1000/50 -
686                          * then divide by 1000/25 to get a number from 0 to 25.
687                          */
688                         v = (v + 1000/50) / (1000/25);
689                         if (v == 0)
690                                 return 0; /* black */
691                         if (v >= 25)
692                                 return 15; /* white */
693                         //printf(" grey %d\n", v + 231);
694                         /* grey shades are from 232 to 255 inclusive */
695                         return v + 231;
696                 }
697                 for (h = 0; h < 3; h++) {
698                         int v = rgb[h];
699
700                         v = (v + 1000/12) / (1000/6);
701                         /* v is from 0 to 6, we want up to 5
702                          * with 0 and 1 merged
703                          */
704                         if (v)
705                                 v -= 1;
706
707                         c = c * 6 + v;
708                 }
709                 //printf(" color %d\n", c + 16);
710                 return c + 16;
711         }
712 }
713
714 static int to_pair(struct display_data *dd safe, int fg, int bg)
715 {
716         struct col_hash *ch = hash_init(dd);
717         int k = PAIR_KEY(fg, bg);
718         int h = hash_key(k);
719         struct chash *c;
720
721         for (c = ch->tbl[h]; c; c = c->next)
722                 if (c->key == k)
723                         return c->content;
724         c = malloc(sizeof(*c));
725         c->key = k;
726         c->content = ch->next_pair++;
727         c->next = ch->tbl[h];
728         ch->tbl[h] = c;
729         init_pair(c->content, fg, bg);
730         return c->content;
731 }
732
733 static int cvt_attrs(struct pane *p safe, struct pane *home safe,
734                      const char *attrs, bool use_parent)
735 {
736         struct display_data *dd = home->data;
737         int attr = 0;
738         char tmp[40];
739         const char *a;
740         PANEL *pan = NULL;
741         int fg = COLOR_BLACK;
742         int bg = COLOR_WHITE+8;
743
744         set_screen(home);
745         do {
746                 p = p->parent;
747         } while (p->parent != p &&(pan = pane_panel(p, NULL)) == NULL);
748         if (pan && use_parent) {
749                 /* Get 'default colours for this pane - set at clear */
750                 int at = getbkgd(panel_window(pan));
751                 int pair = PAIR_NUMBER(at);
752                 short dfg, dbg;
753                 pair_content(pair, &dfg, &dbg);
754                 if (dfg >= 0)
755                         fg = dfg;
756                 if (dbg >= 0)
757                         bg = dbg;
758         }
759         a = attrs;
760         while (a && *a) {
761                 const char *c;
762                 if (*a == ',') {
763                         a++;
764                         continue;
765                 }
766                 c = strchr(a, ',');
767                 if (!c)
768                         c = a+strlen(a);
769                 strncpy(tmp, a, c-a);
770                 tmp[c-a] = 0;
771                 if (strcmp(tmp, "inverse")==0) attr |= A_STANDOUT;
772                 else if (strcmp(tmp, "noinverse")==0) attr &= ~A_STANDOUT;
773                 else if (strcmp(tmp, "bold")==0) attr |= A_BOLD;
774                 else if (strcmp(tmp, "nobold")==0) attr &= ~A_BOLD;
775                 else if (strcmp(tmp, "underline")==0) attr |= A_UNDERLINE;
776                 else if (strcmp(tmp, "nounderline")==0) attr &= ~A_UNDERLINE;
777                 else if (strncmp(tmp, "fg:", 3) == 0) {
778                         struct call_return cr =
779                                 call_ret(all, "colour:map", home,
780                                          0, NULL, tmp+3);
781                         int rgb[3] = {cr.i, cr.i2, cr.x};
782                         fg = find_col(dd, rgb);
783                 } else if (strncmp(tmp, "bg:", 3) == 0) {
784                         struct call_return cr =
785                                 call_ret(all, "colour:map", home,
786                                          0, NULL, tmp+3);
787                         int rgb[3] = {cr.i, cr.i2, cr.x};
788                         bg = find_col(dd, rgb);
789                 }
790                 a = c;
791         }
792         if (fg != COLOR_BLACK || bg != COLOR_WHITE+8)
793                 attr |= COLOR_PAIR(to_pair(dd, fg, bg));
794         return attr;
795 }
796
797 static int make_cursor(int attr)
798 {
799         return attr ^ A_UNDERLINE;
800 }
801
802 DEF_CMD(nc_notify_display)
803 {
804         struct display_data *dd = ci->home->data;
805         comm_call(ci->comm2, "callback:display", ci->home, dd->last_event);
806         return 1;
807 }
808
809 DEF_CMD(nc_close)
810 {
811         struct pane *p = ci->home;
812         struct display_data *dd = p->data;
813         ncurses_end(p);
814         hash_free(dd);
815         fclose(dd->scr_file);
816         return 1;
817 }
818
819 DEF_CMD(nc_pane_close)
820 {
821         PANEL *pan = NULL;
822
823         set_screen(ci->home);
824         while ((pan = panel_above(pan)) != NULL)
825                 if (panel_userptr(pan) == ci->focus)
826                         break;
827         if (pan) {
828                 WINDOW *win = panel_window(pan);
829                 del_panel(pan);
830                 delwin(win);
831                 pane_damaged(ci->home, DAMAGED_POSTORDER);
832         }
833         return 1;
834 }
835
836 static PANEL * safe pane_panel(struct pane *p safe, struct pane *home)
837 {
838         PANEL *pan = NULL;
839
840         while ((pan = panel_above(pan)) != NULL)
841                 if (panel_userptr(pan) == p)
842                         return pan;
843
844         if (!home)
845                 return pan;
846
847         pan = new_panel(newwin(p->h, p->w, 0, 0));
848         set_panel_userptr(pan, p);
849         pane_add_notify(home, p, "Notify:Close");
850
851         return pan;
852 }
853
854 DEF_CMD(nc_clear)
855 {
856         struct pane *p = ci->home;
857         struct display_data *dd = p->data;
858         int attr = cvt_attrs(ci->focus, p, ci->str, ci->str == NULL);
859         PANEL *panel;
860         WINDOW *win;
861         int w, h;
862
863         set_screen(p);
864         panel = pane_panel(ci->focus, p);
865         if (!panel)
866                 return Efail;
867         win = panel_window(panel);
868         getmaxyx(win, h, w);
869         if (h != ci->focus->h || w != ci->focus->w) {
870                 wresize(win, ci->focus->h, ci->focus->w);
871                 replace_panel(panel, win);
872         }
873         wbkgdset(win, attr);
874         werase(win);
875         dd->clears += 1;
876
877         pane_damaged(p, DAMAGED_POSTORDER);
878         return 1;
879 }
880
881 DEF_CMD(nc_text_size)
882 {
883         int max_space = ci->num;
884         int max_bytes = 0;
885         int size = 0;
886         const char *str = ci->str;
887
888         if (!str)
889                 return Enoarg;
890         while (str[0] != 0) {
891                 wint_t wc = get_utf8(&str, NULL);
892                 int width;
893                 if (wc >= WERR)
894                         break;
895                 width = wcwidth(wc);
896                 if (width < 0)
897                         break;
898                 size += width;
899                 if (size <= max_space)
900                         max_bytes = str - ci->str;
901         }
902         return comm_call(ci->comm2, "callback:size", ci->focus,
903                          max_bytes, NULL, NULL,
904                          0, NULL, NULL, size, 1);
905 }
906
907 DEF_CMD(nc_draw_text)
908 {
909         struct pane *p = ci->home;
910         int attr = cvt_attrs(ci->focus, p, ci->str2, True);
911         int cursor_offset = ci->num;
912         short x = ci->x, y = ci->y;
913         const char *str = ci->str;
914
915         if (!str)
916                 return Enoarg;
917         set_screen(p);
918         while (str[0] != 0) {
919                 int precurs = str <= ci->str + cursor_offset;
920                 wint_t wc = get_utf8(&str, NULL);
921                 int width;
922                 if (wc == WEOF || wc == WERR)
923                         break;
924                 width = wcwidth(wc);
925                 if (width < 0)
926                         break;
927                 if (precurs && str > ci->str + cursor_offset)
928                         ncurses_text(ci->focus, p, wc, attr, x, y, 1);
929                 else
930                         ncurses_text(ci->focus, p, wc, attr, x, y, 0);
931                 x += width;
932         }
933         if (str == ci->str + cursor_offset)
934                 ncurses_text(ci->focus, p, ' ', 0, x, y, 1);
935         pane_damaged(p, DAMAGED_POSTORDER);
936         return 1;
937 }
938
939 DEF_CMD(nc_refresh_size)
940 {
941         struct pane *p = ci->home;
942
943         set_screen(p);
944         getmaxyx(stdscr, p->h, p->w);
945         clearok(curscr, 1);
946         return 0;
947 }
948
949 DEF_CMD(nc_refresh_post)
950 {
951         struct pane *p = ci->home;
952         struct pane *p1;
953         PANEL *pan, *pan2;
954
955         set_screen(p);
956
957         /* Need to ensure stacking order and panel y,x position
958          * is correct.  FIXME it would be good if we could skip this
959          * almost always.
960          */
961         pan = panel_above(NULL);
962         if (!pan)
963                 return 1;
964         p1 = (struct pane*) panel_userptr(pan);
965         for (;(pan2 = panel_above(pan)) != NULL; pan = pan2) {
966                 struct pane *p2 = (struct pane*)panel_userptr(pan2);
967                 p1 = (struct pane*)panel_userptr(pan);
968                 if (!p1 || !p2)
969                         continue;
970
971                 if (p1->abs_z < p2->abs_z)
972                         continue;
973                 if (p1->abs_z == p2->abs_z &&
974                     p1->z <= p2->z)
975                         continue;
976                 /* pan needs to be above pan2.  All we can do is move it to
977                  * the top. Anything that needs to be above it will eventually
978                  * be pushed up too.
979                  */
980                 top_panel(pan);
981                 /* Now the panel below pan might need to be over pan2 too... */
982                 pan = panel_below(pan2);
983                 if (pan)
984                         pan2 = pan;
985         }
986
987         /* As we need to crop pane against their parents, we cannot simply
988          * use update_panels().  Instead we copy each to stdscr and refresh
989          * that.
990          */
991         for (pan = NULL; (pan = panel_above(pan)) != NULL; ) {
992                 WINDOW *win;
993                 struct xy src, dest, destend;
994                 int w, h;
995
996                 p1 = (void*)panel_userptr(pan);
997                 if (!p1)
998                         continue;
999                 dest = pane_mapxy(p1, p, 0, 0, True);
1000                 destend = pane_mapxy(p1, p, p1->w, p1->h, True);
1001                 src = pane_mapxy(p1, p, 0, 0, False);
1002                 src.x = dest.x - src.x;
1003                 src.y = dest.y - src.y;
1004                 win = panel_window(pan);
1005                 getmaxyx(win, h, w);
1006                 /* guard again accessing beyond boundary of win */
1007                 if (destend.x > dest.x + (w - src.x))
1008                         destend.x = dest.x + (w - src.x);
1009                 if (destend.y > dest.y + (h - src.y))
1010                         destend.y = dest.y - (h - src.y);
1011                 copywin(win, stdscr, src.y, src.x,
1012                         dest.y, dest.x, destend.y-1, destend.x-1, 0);
1013         }
1014         /* place the cursor */
1015         p1 = pane_leaf(p);
1016         pan = NULL;
1017         while (p1 != p && (pan = pane_panel(p1, NULL)) == NULL)
1018                 p1 = p1->parent;
1019         if (pan && p1->cx >= 0) {
1020                 struct xy curs = pane_mapxy(p1, p, p1->cx, p1->cy, False);
1021                 wmove(stdscr, curs.y, curs.x);
1022         } else if (p->cx >= 0)
1023                 wmove(stdscr, p->cy, p->cx);
1024         refresh();
1025         record_screen(ci->home);
1026         return 1;
1027 }
1028
1029 static struct pane *ncurses_init(struct pane *ed,
1030                                  const char *tty, const char *term)
1031 {
1032         SCREEN *scr;
1033         struct pane *p;
1034         struct display_data *dd;
1035         int rows, cols;
1036         char *area;
1037         FILE *f;
1038
1039         set_screen(NULL);
1040         if (tty && strcmp(tty, "-") != 0)
1041                 f = fopen(tty, "r+");
1042         else
1043                 f = fdopen(1, "r+");
1044         if (!f)
1045                 return NULL;
1046         scr = newterm(term, f, f);
1047         if (!scr)
1048                 return NULL;
1049
1050         alloc(dd, pane);
1051         dd->scr = scr;
1052         dd->scr_file = f;
1053         dd->is_xterm = (term && strncmp(term, "xterm", 5) == 0);
1054
1055         p = pane_register(ed, 1, &ncurses_handle.c, dd);
1056         if (!p) {
1057                 unalloc(dd, pane);
1058                 return NULL;
1059         }
1060         set_screen(p);
1061
1062         start_color();
1063         use_default_colors();
1064         raw();
1065         noecho();
1066         nonl();
1067         timeout(0);
1068         set_escdelay(100);
1069         intrflush(stdscr, FALSE);
1070         keypad(stdscr, TRUE);
1071         mousemask(BUTTON1_PRESSED | BUTTON1_RELEASED |
1072                   BUTTON2_PRESSED | BUTTON2_RELEASED |
1073                   BUTTON3_PRESSED | BUTTON3_RELEASED |
1074                   BUTTON4_PRESSED | BUTTON4_RELEASED |
1075                   BUTTON5_PRESSED | BUTTON5_RELEASED |
1076                   BUTTON_CTRL | BUTTON_SHIFT | BUTTON_ALT |
1077                   REPORT_MOUSE_POSITION, NULL);
1078         mouseinterval(0);
1079         if (dd->is_xterm) {
1080                 /* Enable bracketed-paste */
1081                 fprintf(dd->scr_file, "\033[?2004h");
1082                 fflush(dd->scr_file);
1083         }
1084
1085         getmaxyx(stdscr, rows, cols);
1086         pane_resize(p, 0, 0, cols, rows);
1087
1088         area = dd->attr_buf;
1089         dd->rs1 = tgetstr("rs1", &area);
1090         if (!dd->rs1)
1091                 dd->rs1 = tgetstr("is1", &area);
1092         dd->rs2 = tgetstr("rs2", &area);
1093         if (!dd->rs2)
1094                 dd->rs2 = tgetstr("is2", &area);
1095         dd->rs3 = tgetstr("rs3", &area);
1096         if (!dd->rs3)
1097                 dd->rs3 = tgetstr("is3", &area);
1098         dd->clear = tgetstr("clear", &area);
1099
1100         call("editor:request:all-displays", p);
1101         if (!prepare_recrep(p)) {
1102                 call_comm("event:read", p, &input_handle, fileno(f));
1103                 if (!tty || strcmp(tty, "-") == 0)
1104                         call_comm("event:signal", p, &handle_winch, SIGWINCH);
1105         }
1106         return p;
1107 }
1108
1109 REDEF_CMD(handle_winch)
1110 {
1111         struct pane *p = ci->home;
1112         struct display_data *dd = p->data;
1113         struct winsize size;
1114         ioctl(fileno(dd->scr_file), TIOCGWINSZ, &size);
1115         set_screen(p);
1116         resize_term(size.ws_row, size.ws_col);
1117
1118         pane_resize(p, 0, 0, size.ws_row, size.ws_col);
1119         return 1;
1120 }
1121
1122 DEF_CMD(force_redraw)
1123 {
1124         struct pane *p = ci->home;
1125
1126         set_screen(p);
1127         clearok(curscr, 1);
1128         return 1;
1129 }
1130
1131 static void ncurses_text(struct pane *p safe, struct pane *display safe,
1132                          wchar_t ch, int attr, short x, short y, short cursor)
1133 {
1134         PANEL *pan;
1135         struct pane *p2;
1136         cchar_t cc = {};
1137
1138         if (x < 0 || y < 0)
1139                 return;
1140
1141         set_screen(display);
1142         if (cursor) {
1143                 if (pane_has_focus(p->z < 0 ? p->parent : p)) {
1144                         /* Cursor is in-focus */
1145                         struct xy curs = pane_mapxy(p, display, x, y, False);
1146                         display->cx = curs.x;
1147                         display->cy = curs.y;
1148                 } else
1149                         /* Cursor here, but not focus */
1150                         attr = make_cursor(attr);
1151         }
1152         cc.attr = attr;
1153         cc.chars[0] = ch;
1154
1155         p2 = p;
1156         pan = pane_panel(p2, NULL);
1157         while (!pan && p2->parent != p2) {
1158                 p2 = p2->parent;
1159                 pan = pane_panel(p2, NULL);
1160         }
1161         if (pan) {
1162                 struct xy xy = pane_mapxy(p, p2, x, y, False);
1163                 mvwadd_wch(panel_window(pan), xy.y, xy.x, &cc);
1164         }
1165 }
1166
1167 static struct namelist {
1168         wint_t key;
1169         char *name;
1170 } key_names[] = {
1171         {KEY_DOWN, ":Down"},
1172         {KEY_UP, ":Up"},
1173         {KEY_LEFT, ":Left"},
1174         {KEY_RIGHT, ":Right"},
1175         {KEY_HOME, ":Home"},
1176         {KEY_BACKSPACE, ":Backspace"},
1177         {KEY_DL, ":DelLine"},
1178         {KEY_IL, ":InsLine"},
1179         {KEY_DC, ":Del"},
1180         {KEY_IC, ":Ins"},
1181         {KEY_ENTER, ":Enter"},
1182         {KEY_END, ":End"},
1183
1184         {KEY_NPAGE, ":Next"},
1185         {KEY_PPAGE, ":Prior"},
1186
1187         {KEY_SDC, ":S:Del"},
1188         {KEY_SDL, ":S:DelLine"},
1189         {KEY_SEND, ":S:End"},
1190         {KEY_SHOME, ":S:Home"},
1191         {KEY_SLEFT, ":S:Left"},
1192         {KEY_SRIGHT, ":S:Right"},
1193         {KEY_BTAB, ":S:Tab"},
1194
1195         {  0521, ":S:Up"},
1196         {  0520, ":S:Down"},
1197         {  0616, ":S:Prior"},
1198         {  0614, ":S:Next"},
1199         { 01041, ":S:Home"},
1200         { 01060, ":S:End"},
1201         { 01066, ":S:Prior"},
1202         { 01015, ":S:Next"},
1203
1204         { 01027, ":A:S:Home"},
1205         { 01022, ":A:S:End"},
1206         { 01046, ":A:S:Prior"},
1207         { 01047, ":A:S:Next"}, // ??
1208
1209         { 01052, ":A:Prior"},
1210         { 01045, ":A:Next"},
1211         { 01026, ":A:Home"},
1212         { 01021, ":A:End"},
1213         { 01065, ":A:Up"},
1214         { 01014, ":A:Down"},
1215         { 01040, ":A:Left"},
1216         { 01057, ":A:Right"},
1217         { 00411, ":F1"},
1218         { 00412, ":F2"},
1219         { 00413, ":F3"},
1220         { 00414, ":F4"},
1221         { 00415, ":F5"},
1222         { 00416, ":F6"},
1223         { 00417, ":F7"},
1224         { 00420, ":F8"},
1225         { 00421, ":F9"},
1226         { 00422, ":F10"},
1227         { 00423, ":F11"},
1228         { 00424, ":F12"},
1229         { 00425, ":S:F1"},
1230         { 00426, ":S:F2"},
1231         { 00427, ":S:F3"},
1232         { 00430, ":S:F4"},
1233         { 00431, ":S:F5"},
1234         { 00432, ":S:F6"},
1235         { 00433, ":S:F7"},
1236         { 00434, ":S:F8"},
1237         { 00435, ":S:F9"},
1238         { 00436, ":S:F10"},
1239         { 00437, ":S:F11"},
1240         { 00440, ":S:F12"},
1241         {0, NULL}
1242 }, char_names[] = {
1243         {'\e', ":ESC"},
1244         {'\n', ":LF"},
1245         {'\r', ":Enter"},
1246         {'\t', ":Tab"},
1247         {'\177', ":Delete"},
1248         {'\0', ":C- "},
1249         {0, NULL}
1250 };
1251
1252 static char *find_name (struct namelist *l safe, wint_t c)
1253 {
1254         int i;
1255         for (i = 0; l[i].name; i++)
1256                 if (l[i].key == c)
1257                         return l[i].name;
1258         return NULL;
1259 }
1260
1261 static void send_key(int keytype, wint_t c, int alt, struct pane *p safe)
1262 {
1263         struct display_data *dd = p->data;
1264         char *n;
1265         char buf[100];/* FIXME */
1266         char t[5];
1267         char *a = alt ? ":A" : "";
1268
1269         if (keytype == KEY_CODE_YES) {
1270                 n = find_name(key_names, c);
1271                 if (!n)
1272                         sprintf(buf, "%sNcurs-%o", a, c);
1273                 else
1274                         strcat(strcpy(buf, a), n);
1275         } else {
1276                 n = find_name(char_names, c);
1277                 if (n)
1278                         sprintf(buf, "%s%s", a, n);
1279                 else if (c < ' ' || c == 0x7f)
1280                         sprintf(buf, "%s:C-%c",
1281                                 a, c ^ 64);
1282                 else
1283                         sprintf(buf, "%s-%s", a, put_utf8(t, c));
1284         }
1285
1286         dd->last_event = time(NULL);
1287         record_key(p, buf);
1288         call("Keystroke", p, 0, NULL, buf);
1289 }
1290
1291 static void do_send_mouse(struct pane *p safe, int x, int y, char *cmd safe,
1292                           int button, char *mod, int type)
1293 {
1294         int ret;
1295         struct display_data *dd = p->data;
1296
1297         record_mouse(p, cmd, x, y);
1298         ret = call("Mouse-event", p, button, NULL, cmd, type, NULL, mod, x, y);
1299         if (type == 1 && !dd->report_position) {
1300                 if (dd->is_xterm) {
1301                         fprintf(dd->scr_file, "\033[?1002h");
1302                         fflush(dd->scr_file);
1303                 }
1304                 dd->report_position = 1;
1305         } else if (type == 3 && ret <= 0) {
1306                 if (dd->is_xterm) {
1307                         fprintf(dd->scr_file, "\033[?1002l");
1308                         fflush(dd->scr_file);
1309                 }
1310                 dd->report_position = 0;
1311         }
1312 }
1313
1314 static void send_mouse(MEVENT *mev safe, struct pane *p safe)
1315 {
1316         struct display_data *dd = p->data;
1317         int x = mev->x;
1318         int y = mev->y;
1319         int b;
1320         char buf[100];
1321
1322         /* MEVENT has lots of bits.  We want a few numbers */
1323         for (b = 1 ; b <= (NCURSES_MOUSE_VERSION <= 1 ? 3 : 5); b++) {
1324                 mmask_t s = mev->bstate;
1325                 char *action;
1326                 int modf = 0;
1327                 char *mod = "";
1328
1329                 if (s & BUTTON_SHIFT) modf |= 1;
1330                 if (s & BUTTON_CTRL)  modf |= 2;
1331                 if (s & BUTTON_ALT)   modf |= 4;
1332                 switch (modf) {
1333                 case 0: mod = ""; break;
1334                 case 1: mod = ":S"; break;
1335                 case 2: mod = ":C"; break;
1336                 case 3: mod = ":C:S"; break;
1337                 case 4: mod = ":A"; break;
1338                 case 5: mod = ":A:S"; break;
1339                 case 6: mod = ":A:C"; break;
1340                 case 7: mod = ":A:C:S"; break;
1341                 }
1342                 if (BUTTON_PRESS(s, b))
1343                         action = "%s:Press-%d";
1344                 else if (BUTTON_RELEASE(s, b)) {
1345                         action = "%s:Release-%d";
1346                         /* Modifiers only reported on button Press */
1347                         mod = "";
1348                 } else
1349                         continue;
1350                 snprintf(buf, sizeof(buf), action, mod, b);
1351                 dd->last_event = time(NULL);
1352                 do_send_mouse(p, x, y, buf, b, mod, BUTTON_PRESS(s,b) ? 1 : 2);
1353         }
1354         if ((mev->bstate & REPORT_MOUSE_POSITION) &&
1355             dd->report_position)
1356                 /* Motion doesn't update last_event */
1357                 do_send_mouse(p, x, y, ":Motion", 0, "", 3);
1358 }
1359
1360 static void paste_start(struct pane *home safe)
1361 {
1362         struct display_data *dd = home->data;
1363
1364         dd->paste_start = time(NULL);
1365         buf_init(&dd->paste_buf);
1366 }
1367
1368 static void paste_flush(struct pane *home safe)
1369 {
1370         struct display_data *dd = home->data;
1371
1372         if (!dd->paste_start)
1373                 return;
1374         free(dd->paste_latest);
1375         dd->paste_latest = buf_final(&dd->paste_buf);
1376         if (dd->paste_buf.len > 0)
1377                 dd->paste_pending = 1;
1378         dd->paste_start = 0;
1379 }
1380
1381 static bool paste_recv(struct pane *home safe, int is_keycode, wint_t ch)
1382 {
1383         struct display_data *dd = home->data;
1384         time_t now;
1385         if (dd->paste_start == 0)
1386                 return False;
1387         now = time(NULL);
1388         if (dd->paste_start < now || dd->paste_start > now + 2 ||
1389             is_keycode != OK || ch == KEY_MOUSE) {
1390                 /* time to close */
1391                 paste_flush(home);
1392                 return False;
1393         }
1394         if (ch == '\r')
1395                 /* I really don't want carriage-returns... */
1396                 ch = '\n';
1397         buf_append(&dd->paste_buf, ch);
1398         if (ch == '~' && dd->paste_buf.len >= 6 &&
1399             strcmp("\e[201~",
1400                    buf_final(&dd->paste_buf) + dd->paste_buf.len - 6) == 0) {
1401                 dd->paste_buf.len -= 6;
1402                 paste_flush(home);
1403         }
1404         return True;
1405 }
1406
1407 DEF_CMD(nc_get_paste)
1408 {
1409         struct display_data *dd = ci->home->data;
1410
1411         comm_call(ci->comm2, "cb", ci->focus,
1412                   dd->paste_start, NULL, dd->paste_latest);
1413         return 1;
1414 }
1415
1416 REDEF_CMD(input_handle)
1417 {
1418         struct pane *p = ci->home;
1419         struct display_data *dd = p->data;
1420         static const char paste_seq[] = "\e[200~";
1421         wint_t c;
1422         int is_keycode;
1423         int have_escape = 0;
1424         int i;
1425
1426         if (!(void*)p->data)
1427                 /* already closed */
1428                 return 0;
1429         set_screen(p);
1430         while ((is_keycode = get_wch(&c)) != ERR) {
1431                 if (paste_recv(p, is_keycode, c))
1432                         continue;
1433                 if (c == KEY_MOUSE) {
1434                         MEVENT mev;
1435                         paste_flush(p);
1436                         while (getmouse(&mev) != ERR) {
1437                                 if (dd->paste_pending &&
1438                                     mev.bstate == REPORT_MOUSE_POSITION) {
1439                                         /* xcfe-terminal is a bit weird.
1440                                          * It captures middle-press to
1441                                          * sanitise the paste, but lets
1442                                          * middle-release though. It comes
1443                                          * here as REPORT_MOUSE_POSTION
1444                                          * and we can use that to find the
1445                                          * position of the paste.
1446                                          * '6' is an unused button, and
1447                                          * ensures lib-input doesn't expect
1448                                          * matching press/release
1449                                          */
1450                                         call("Mouse-event", ci->home,
1451                                              1, NULL, ":Paste",
1452                                              6, NULL, NULL, mev.x, mev.y);
1453                                         dd->paste_pending = 0;
1454                                 }
1455                                 send_mouse(&mev, p);
1456                         }
1457                 } else if (c == (wint_t)paste_seq[have_escape]) {
1458                         have_escape += 1;
1459                         if (!paste_seq[have_escape]) {
1460                                 paste_start(p);
1461                                 have_escape = 0;
1462                         }
1463                 } else if (have_escape == 1) {
1464                         send_key(is_keycode, c, 1, p);
1465                         have_escape = 0;
1466                 } else if (have_escape) {
1467                         send_key(OK, paste_seq[1], 1, p);
1468                         for (i = 2; i < have_escape; i++)
1469                                 send_key(OK, paste_seq[i], 0, p);
1470                         send_key(is_keycode, c, 0, p);
1471                         have_escape = 0;
1472                 } else {
1473                         send_key(is_keycode, c, 0, p);
1474                 }
1475                 /* Don't know what other code might have done,
1476                  * so re-set the screen
1477                  */
1478                 set_screen(p);
1479         }
1480         if (have_escape == 1)
1481                 send_key(is_keycode, '\e', 0, p);
1482         else if (have_escape > 1) {
1483                 send_key(OK, paste_seq[1], 1, p);
1484                 for (i = 2; i < have_escape; i++)
1485                         send_key(OK, paste_seq[i], 0, p);
1486         }
1487         if (dd->paste_pending == 2) {
1488                 /* no mouse event to give postion, so treat as keyboard */
1489                 call("Keystroke", ci->home, 0, NULL, ":Paste");
1490                 dd->paste_pending = 0;
1491         } else if (dd->paste_pending == 1) {
1492                 /* Wait for possible mouse-position update. */
1493                 dd->paste_pending = 2;
1494                 call_comm("event:timer", p, &input_handle, 200);
1495         }
1496         return 1;
1497 }
1498
1499 DEF_CMD(display_ncurses)
1500 {
1501         struct pane *p;
1502         const char *tty = ci->str;
1503         const char *term = ci->str2;
1504
1505         if (!term)
1506                 term = "xterm-256color";
1507
1508         p = ncurses_init(ci->focus, tty, term);
1509         if (p)
1510                 return comm_call(ci->comm2, "callback:display", p);
1511
1512         return Efail;
1513 }
1514
1515 void edlib_init(struct pane *ed safe)
1516 {
1517         call_comm("global-set-command", ed, &display_ncurses, 0, NULL,
1518                   "attach-display-ncurses");
1519
1520         nc_map = key_alloc();
1521         key_add(nc_map, "Display:refresh", &force_redraw);
1522         key_add(nc_map, "Display:close", &nc_close_display);
1523         key_add(nc_map, "Display:set-noclose", &nc_set_noclose);
1524         key_add(nc_map, "Display:external-viewer", &nc_external_viewer);
1525         key_add(nc_map, "Close", &nc_close);
1526         key_add(nc_map, "Free", &edlib_do_free);
1527         key_add(nc_map, "Draw:clear", &nc_clear);
1528         key_add(nc_map, "Draw:text-size", &nc_text_size);
1529         key_add(nc_map, "Draw:text", &nc_draw_text);
1530         key_add(nc_map, "Refresh:size", &nc_refresh_size);
1531         key_add(nc_map, "Refresh:postorder", &nc_refresh_post);
1532         key_add(nc_map, "Paste:get", &nc_get_paste);
1533         key_add(nc_map, "all-displays", &nc_notify_display);
1534         key_add(nc_map, "Sig:Winch", &handle_winch);
1535         key_add(nc_map, "Notify:Close", &nc_pane_close);
1536 }