]> git.neil.brown.name Git - edlib.git/blobdiff - display-ncurses.c
TODO: clean out done items.
[edlib.git] / display-ncurses.c
index e3162453c1584f99f5d552be983ef9e92335ec28..86995cd44e86ca57bb051f9bc26729dd389e127e 100644 (file)
@@ -43,6 +43,7 @@
 
 #include <term.h>
 
+#define PANE_DATA_TYPE struct display_data
 #include "core.h"
 
 #ifdef RECORD_REPLAY
@@ -63,7 +64,6 @@ struct display_data {
        SCREEN                  *scr;
        FILE                    *scr_file;
        int                     is_xterm;
-       char                    *noclose;
        struct col_hash         *col_hash;
        int                     report_position;
        long                    last_event;
@@ -101,6 +101,7 @@ struct display_data {
        int                     clears; /* counts of Draw:clear events */
        #endif
 };
+#include "core-pane.h"
 
 static SCREEN *current_screen;
 static void ncurses_text(struct pane *p safe, struct pane *display safe,
@@ -440,10 +441,10 @@ DEF_CMD(nc_close_display)
 {
        /* If this is only display, then refuse to close this one */
        struct call_return cr;
-       struct display_data *dd = ci->home->data;
+       char *nc = pane_attr_get(ci->home, "no-close");
 
-       if (dd->noclose) {
-               call("Message", ci->focus, 0, NULL, dd->noclose);
+       if (nc) {
+               call("Message", ci->focus, 0, NULL, nc);
                return 1;
        }
 
@@ -462,17 +463,6 @@ DEF_CMD(nc_close_display)
        return 1;
 }
 
-DEF_CMD(nc_set_noclose)
-{
-       struct display_data *dd = ci->home->data;
-
-       free(dd->noclose);
-       dd->noclose = NULL;
-       if (ci->str)
-               dd->noclose = strdup(ci->str);
-       return 1;
-}
-
 static int nc_putc(int ch)
 {
        if (current_dd)
@@ -798,21 +788,20 @@ static int to_pair(struct display_data *dd safe, int fg, int bg)
 }
 
 static int cvt_attrs(struct pane *p safe, struct pane *home safe,
-                    const char *attrs, int *pairp safe, bool use_parent)
+                    const char *attrs, int *pairp safe)
 {
        struct display_data *dd = home->data;
        int attr = 0;
-       char tmp[40];
-       const char *a;
+       const char *a, *v;
+       char *col = NULL;
        PANEL *pan = NULL;
        int fg = COLOR_BLACK;
        int bg = COLOR_WHITE+8;
 
        set_screen(home);
-       do {
+       while (p->parent != p &&(pan = pane_panel(p, NULL)) == NULL)
                p = p->parent;
-       } while (p->parent != p &&(pan = pane_panel(p, NULL)) == NULL);
-       if (pan && use_parent) {
+       if (pan) {
                /* Get 'default colours for this pane - set at clear */
                int at = getbkgd(panel_window(pan));
                int pair = PAIR_NUMBER(at);
@@ -823,39 +812,35 @@ static int cvt_attrs(struct pane *p safe, struct pane *home safe,
                if (dbg >= 0)
                        bg = dbg;
        }
-       a = attrs;
-       while (a && *a) {
-               const char *c;
-               if (*a == ',') {
-                       a++;
-                       continue;
-               }
-               c = strchr(a, ',');
-               if (!c)
-                       c = a+strlen(a);
-               strncpy(tmp, a, c-a);
-               tmp[c-a] = 0;
-               if (strcmp(tmp, "inverse")==0) attr |= A_STANDOUT;
-               else if (strcmp(tmp, "noinverse")==0) attr &= ~A_STANDOUT;
-               else if (strcmp(tmp, "bold")==0) attr |= A_BOLD;
-               else if (strcmp(tmp, "nobold")==0) attr &= ~A_BOLD;
-               else if (strcmp(tmp, "underline")==0) attr |= A_UNDERLINE;
-               else if (strcmp(tmp, "nounderline")==0) attr &= ~A_UNDERLINE;
-               else if (strstarts(tmp, "fg:")) {
+
+       foreach_attr(a, v, attrs, NULL) {
+               if (amatch(a, "inverse"))
+                       attr |= A_STANDOUT;
+               else if (amatch(a, "noinverse"))
+                       attr &= ~A_STANDOUT;
+               else if (amatch(a, "bold"))
+                       attr |= A_BOLD;
+               else if (amatch(a, "nobold"))
+                       attr &= ~A_BOLD;
+               else if (amatch(a, "underline"))
+                       attr |= A_UNDERLINE;
+               else if (amatch(a, "nounderline"))
+                       attr &= ~A_UNDERLINE;
+               else if (amatch(a, "fg") && v) {
                        struct call_return cr =
                                call_ret(all, "colour:map", home,
-                                        0, NULL, tmp+3);
+                                        0, NULL, aupdate(&col, v));
                        int rgb[3] = {cr.i, cr.i2, cr.x};
                        fg = find_col(dd, rgb);
-               } else if (strstarts(tmp, "bg:")) {
+               } else if (amatch(a, "bg") && v) {
                        struct call_return cr =
                                call_ret(all, "colour:map", home,
-                                        0, NULL, tmp+3);
+                                        0, NULL, aupdate(&col, v));
                        int rgb[3] = {cr.i, cr.i2, cr.x};
                        bg = find_col(dd, rgb);
                }
-               a = c;
        }
+       free(col);
        if (fg != COLOR_BLACK || bg != COLOR_WHITE+8)
                *pairp = to_pair(dd, fg, bg);
        return attr;
@@ -873,7 +858,7 @@ DEF_CMD(nc_notify_display)
        return 1;
 }
 
-DEF_CMD(nc_close)
+DEF_CMD_CLOSED(nc_close)
 {
        struct pane *p = ci->home;
        struct display_data *dd = p->data;
@@ -924,7 +909,8 @@ DEF_CMD(nc_clear)
        struct display_data *dd = p->data;
        cchar_t cc = {};
        int pair = 0;
-       int attr = cvt_attrs(ci->focus, p, ci->str, &pair, ci->str == NULL);
+       /* default come from parent when clearing pane */
+       int attr = cvt_attrs(ci->focus->parent, p, ci->str, &pair);
        PANEL *panel;
        WINDOW *win;
        int w, h;
@@ -980,7 +966,7 @@ DEF_CMD(nc_draw_text)
 {
        struct pane *p = ci->home;
        int pair = 0;
-       int attr = cvt_attrs(ci->focus, p, ci->str2, &pair, True);
+       int attr = cvt_attrs(ci->focus, p, ci->str2, &pair);
        int cursor_offset = ci->num;
        short x = ci->x, y = ci->y;
        const char *str = ci->str;
@@ -1009,32 +995,124 @@ DEF_CMD(nc_draw_text)
        return 1;
 }
 
+struct di_info {
+       struct command c;
+       MagickWand *wd safe;
+       int x,y,w,h;
+       int xo, yo;
+       struct pane *p safe;
+};
+
+DEF_CB(nc_draw_image_cb)
+{
+       struct di_info *dii = container_of(ci->comm, struct di_info, c);
+       struct display_data *dd = dii->p->data;
+       int i, j;
+       unsigned char *buf;
+
+       switch (ci->key[0]) {
+       case 'w': /* width */
+               return MagickGetImageWidth(dii->wd);
+       case 'h': /* height */
+               return MagickGetImageHeight(dii->wd);
+       case 's': /* scale */
+               MagickResizeImage(dii->wd, ci->num, ci->num2, BoxFilter, 1.0);
+               return 1;
+       case 'c': /* crop or cursor */
+               if (ci->key[1] != 'u') {
+                       /* crop */
+                       dii->x = ci->x;
+                       dii->y = ci->y;
+                       dii->w = ci->num;
+                       dii->h = ci->num2;
+                       return 1;
+               } else {
+                       /* cursor */
+                       /* FIXME this doesn't work because
+                        * render-line knows too much and gets it wrong.
+                        */
+                       ncurses_text(ci->focus, dii->p, 'X', 0,
+                                    to_pair(dd, 0, 0),
+                                    ci->x + dii->xo,
+                                    (ci->y + dii->xo)/2, 1);
+                       return 1;
+               }
+       case 'd': /* draw */
+               if (dii->w <= 0 || dii->h <= 0)
+                       return Efail;
+               buf = malloc(dii->h * dii->w * 4);
+
+               MagickExportImagePixels(dii->wd, dii->x, dii->y,
+                                       dii->w, dii->h,
+                                       "RGBA", CharPixel, buf);
+
+               for (i = 0; i < dii->h; i+= 2) {
+                       static const wint_t hilo = 0x2580; /* L'▀' */
+                       static unsigned char blk[4] = "\0\0\0";
+                       for (j = 0; j < dii->w ; j+= 1) {
+                               unsigned char *p1 = buf + i*dii->w*4 + j*4;
+                               unsigned char *p2 = i + 1 < dii->h ?
+                                       buf + (i+1)*dii->w*4 + j*4 : blk;
+                               int rgb1[3] = { p1[0]*1000/255, p1[1]*1000/255,
+                                              p1[2]*1000/255 };
+                               int rgb2[3] = { p2[0]*1000/255, p2[1]*1000/255,
+                                              p2[2]*1000/255 };
+                               int fg = find_col(dd, rgb1);
+                               int bg = find_col(dd, rgb2);
+
+                               if (p1[3] < 128 || p2[3] < 128) {
+                                       /* transparent */
+                                       cchar_t cc;
+                                       short f,b;
+                                       struct pane *pn2 = ci->focus;
+                                       PANEL *pan = pane_panel(pn2, NULL);
+
+                                       while (!pan && pn2->parent != pn2) {
+                                               pn2 = pn2->parent;
+                                               pan = pane_panel(pn2, NULL);
+                                       }
+                                       if (pan) {
+                                               wgetbkgrnd(panel_window(pan), &cc);
+                                               if (cc.ext_color == 0)
+                                                       /* default.  This is light
+                                                        * gray rather then white,
+                                                        * but I think it is a good
+                                                        * result.
+                                                        */
+                                                       b = COLOR_WHITE;
+                                               else
+                                                       pair_content(cc.ext_color, &f, &b);
+                                               if (p1[3] < 128)
+                                                       fg = b;
+                                               if (p2[3] < 128)
+                                                       bg = b;
+                                       }
+                               }
+                               ncurses_text(ci->focus, dii->p, hilo, 0,
+                                            to_pair(dd, fg, bg),
+                                            ci->num + dii->xo + j,
+                                            (ci->num2 + dii->yo + i)/2,
+                                            0);
+                       }
+               }
+               free(buf);
+               return 1;
+       default:
+               return Efail;
+       }
+}
+
 DEF_CMD(nc_draw_image)
 {
        /* 'str' identifies the image. Options are:
         *     file:filename  - load file from fs
         *     comm:command   - run command collecting bytes
-        * 'num' is '16' if image should be stretched to fill pane
-        * Otherwise it is the 'or' of
-        *   0,1,2 for left/middle/right in x direction
-        *   0,4,8 for top/middle/bottom in y direction
-        * only one of these can be used as image will fill pane
-        * in other direction.
-        * If 'x' and 'y' are both positive, draw cursor box at
-        * p->cx, p->cy of a size so that 'x' will fit across and
-        * 'y' will fit down.
+        * 'str2' and numbers are handled by Draw:scale-image.
         */
        struct pane *p = ci->home;
-       struct display_data *dd = p->data;
-       int x = 0, y = 0;
-       bool stretch = ci->num & 16;
-       int pos = ci->num;
-       int w = ci->focus->w, h = ci->focus->h * 2;
-       int cx = -1, cy = -1;
        MagickBooleanType status;
-       MagickWand *wd;
-       unsigned char *buf;
-       int i, j;
+       MagickWand *wd = NULL;
+       struct di_info dii;
 
        if (!ci->str)
                return Enoarg;
@@ -1048,7 +1126,7 @@ DEF_CMD(nc_draw_image)
        } else if (strstarts(ci->str, "comm:")) {
                struct call_return cr;
                wd = NewMagickWand();
-               cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
+               cr = call_ret(bytes, ci->str+5, ci->focus);
                if (!cr.s) {
                        DestroyMagickWand(wd);
                        return Efail;
@@ -1059,103 +1137,19 @@ DEF_CMD(nc_draw_image)
                        DestroyMagickWand(wd);
                        return Efail;
                }
-       } else
+       }
+
+       if (!wd)
                return Einval;
 
        MagickAutoOrientImage(wd);
-       if (!stretch) {
-               int ih = MagickGetImageHeight(wd);
-               int iw = MagickGetImageWidth(wd);
-
-               if (iw <= 0 || iw <= 0) {
-                       DestroyMagickWand(wd);
-                       return Efail;
-               }
-               if (iw * h > ih * w) {
-                       /* Image is wider than space, use less height */
-                       ih = ih * w / iw;
-                       switch(pos & (8+4)) {
-                       case 4: /* center */
-                               y = (h - ih) / 2; break;
-                       case 8: /* bottom */
-                               y = h - ih; break;
-                       }
-                       /* Keep 'h' even! */
-                       h = ((ih+1)/2) * 2;
-               } else {
-                       /* image is too tall, use less width */
-                       iw = iw * h / ih;
-                       switch (pos & (1+2)) {
-                       case 1: /* center */
-                               x = (w - iw) / 2; break;
-                       case 2: /* right */
-                               x = w - iw ; break;
-                       }
-                       w = iw;
-               }
-       }
-       MagickAdaptiveResizeImage(wd, w, h);
-       buf = malloc(h * w * 4);
-       MagickExportImagePixels(wd, 0, 0, w, h, "RGBA", CharPixel, buf);
-
-       if (ci->x > 0 && ci->y > 0 && ci->focus->cx >= 0) {
-               /* We want a cursor */
-               cx = x + ci->focus->cx;
-               cy = y + ci->focus->cy;
-       }
-       for (i = 0; i < h; i+= 2) {
-               static const wint_t hilo = 0x2580; /* L'▀' */
-               for (j = 0; j < w ; j+= 1) {
-                       unsigned char *p1 = buf + i*w*4 + j*4;
-                       unsigned char *p2 = buf + (i+1)*w*4 + j*4;
-                       int rgb1[3] = { p1[0]*1000/255, p1[1]*1000/255, p1[2]*1000/255 };
-                       int rgb2[3] = { p2[0]*1000/255, p2[1]*1000/255, p2[2]*1000/255 };
-                       int fg = find_col(dd, rgb1);
-                       int bg = find_col(dd, rgb2);
-
-                       if (p1[3] < 128 || p2[3] < 128) {
-                               /* transparent */
-                               cchar_t cc;
-                               short f,b;
-                               struct pane *pn2 = ci->focus;
-                               PANEL *pan = pane_panel(pn2, NULL);
-
-                               while (!pan && pn2->parent != pn2) {
-                                       pn2 = pn2->parent;
-                                       pan = pane_panel(pn2, NULL);
-                               }
-                               if (pan) {
-                                       wgetbkgrnd(panel_window(pan), &cc);
-                                       if (cc.ext_color == 0)
-                                               /* default.  This is light
-                                                * gray rather then white,
-                                                * but I think it is a good
-                                                * result.
-                                                */
-                                               b = COLOR_WHITE;
-                                       else
-                                               pair_content(cc.ext_color, &f, &b);
-                                       if (p1[3] < 128)
-                                               fg = b;
-                                       if (p2[3] < 128)
-                                               bg = b;
-                               }
-                       }
-                       /* FIXME this doesn't work because
-                        * render-line knows too much and gets it wrong.
-                        */
-                       if (cx == x+j && cy == y + (i/2))
-                               ncurses_text(ci->focus, p, 'X', 0,
-                                            to_pair(dd, 0, 0),
-                                            x+j, y+(i/2), 1);
-                       else
-                               ncurses_text(ci->focus, p, hilo, 0,
-                                            to_pair(dd, fg, bg),
-                                            x+j, y+(i/2), 0);
-
-               }
-       }
-       free(buf);
+       dii.c = nc_draw_image_cb;
+       dii.wd = wd;
+       dii.p = p;
+       dii.w = dii.h = dii.x = dii.y = dii.xo = dii.yo = 0;
+       call("Draw:scale-image", ci->focus,
+            ci->num, NULL, NULL, ci->num2, NULL, ci->str2,
+            ci->x, ci->y, &dii.c);
 
        DestroyMagickWand(wd);
 
@@ -1182,7 +1176,7 @@ DEF_CMD(nc_image_size)
        } else if (strstarts(ci->str, "comm:")) {
                struct call_return cr;
                wd = NewMagickWand();
-               cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
+               cr = call_ret(bytes, ci->str+5, ci->focus);
                if (!cr.s) {
                        DestroyMagickWand(wd);
                        return Efail;
@@ -1287,7 +1281,7 @@ DEF_CMD(nc_refresh_post)
                        dest.y, dest.x, destend.y-1, destend.x-1, 0);
        }
        /* place the cursor */
-       p1 = pane_leaf(p);
+       p1 = pane_focus(p);
        pan = NULL;
        while (p1 != p && (pan = pane_panel(p1, NULL)) == NULL)
                p1 = p1->parent;
@@ -1353,16 +1347,16 @@ static struct pane *ncurses_init(struct pane *ed safe,
        if (!scr)
                return NULL;
 
-       alloc(dd, pane);
+       p = pane_register(ed, 1, &ncurses_handle.c);
+       if (!p)
+               return NULL;
+       dd = p->data;
        dd->scr = scr;
        dd->scr_file = f;
        dd->is_xterm = (term && strstarts(term, "xterm"));
 
-       p = pane_register(ed, 1, &ncurses_handle.c, dd);
-       if (!p) {
-               unalloc(dd, pane);
-               return NULL;
-       }
+       attr_set_str(&p->attrs, "Display:pixels", "1x2");
+
        set_screen(p);
 
        ncurses_start(p);
@@ -1412,6 +1406,7 @@ DEF_CMD(force_redraw)
        ncurses_start(p);
 
        clearok(curscr, 1);
+       pane_damaged(p, DAMAGED_POSTORDER);
        return 1;
 }
 
@@ -1527,6 +1522,8 @@ static struct namelist {
        { 00436, ":S:F10"},
        { 00437, ":S:F11"},
        { 00440, ":S:F12"},
+       { 01114, ":Focus-in"},
+       { 01115, ":Focus-out"},
        {0, NULL}
 }, char_names[] = {
        {'\e', ":ESC"},
@@ -1557,8 +1554,12 @@ static void send_key(int keytype, wint_t c, int alt, struct pane *p safe)
 
        if (keytype == KEY_CODE_YES) {
                n = find_name(key_names, c);
-               if (!n)
+               if (!n) {
+                       LOG("Unknown ncurses key 0o%o", c);
                        sprintf(buf, "%sNcurs-%o", a, c);
+               } else if (strstarts(n, ":Focus-"))
+                       /* Ignore focus changes for now */
+                       buf[0] = 0;
                else
                        strcat(strcpy(buf, a), n);
        } else {
@@ -1573,8 +1574,10 @@ static void send_key(int keytype, wint_t c, int alt, struct pane *p safe)
        }
 
        dd->last_event = time(NULL);
-       record_key(p, buf);
-       call("Keystroke", p, 0, NULL, buf);
+       if (buf[0]) {
+               record_key(p, buf);
+               call("Keystroke", p, 0, NULL, buf);
+       }
 }
 
 static void do_send_mouse(struct pane *p safe, int x, int y, char *cmd safe,
@@ -1712,9 +1715,6 @@ REDEF_CMD(input_handle)
        int have_escape = 0;
        int i;
 
-       if (!(void*)p->data)
-               /* already closed */
-               return 0;
        wait_for(dd);
        set_screen(p);
        while ((is_keycode = get_wch(&c)) != ERR) {
@@ -1796,15 +1796,18 @@ REDEF_CMD(input_handle)
 DEF_CMD(display_ncurses)
 {
        struct pane *p;
+       struct pane *ed = pane_root(ci->focus);
        const char *tty = ci->str;
        const char *term = ci->str2;
 
        if (!term)
                term = "xterm-256color";
 
-       p = ncurses_init(ci->focus, tty, term);
-       if (p)
-               p = call_ret(pane, "editor:activate-display", p);
+       p = call_ret(pane, "attach-window-core", ed);
+       if (!p)
+               return Efail;
+
+       p = ncurses_init(p, tty, term);
        if (p)
                return comm_call(ci->comm2, "callback:display", p);
 
@@ -1817,12 +1820,10 @@ void edlib_init(struct pane *ed safe)
                  "attach-display-ncurses");
 
        nc_map = key_alloc();
-       key_add(nc_map, "Display:refresh", &force_redraw);
-       key_add(nc_map, "Display:close", &nc_close_display);
-       key_add(nc_map, "Display:set-noclose", &nc_set_noclose);
-       key_add(nc_map, "Display:external-viewer", &nc_external_viewer);
+       key_add(nc_map, "Window:refresh", &force_redraw);
+       key_add(nc_map, "Window:close", &nc_close_display);
+       key_add(nc_map, "Window:external-viewer", &nc_external_viewer);
        key_add(nc_map, "Close", &nc_close);
-       key_add(nc_map, "Free", &edlib_do_free);
        key_add(nc_map, "Draw:clear", &nc_clear);
        key_add(nc_map, "Draw:text-size", &nc_text_size);
        key_add(nc_map, "Draw:text", &nc_draw_text);