]> git.neil.brown.name Git - edlib.git/blobdiff - display-ncurses.c
TODO: clean out done items.
[edlib.git] / display-ncurses.c
index 1e854f42bcf0190595a49bad9f4f5cd7b5867d19..86995cd44e86ca57bb051f9bc26729dd389e127e 100644 (file)
@@ -64,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;
@@ -128,7 +127,7 @@ static void set_screen(struct pane *p)
                current_screen = NULL;
                return;
        }
-       dd = &p->data;
+       dd = p->data;
        current_dd = dd;
        if (!dd)
                return;
@@ -159,7 +158,7 @@ DEF_CMD(next_evt);
 static bool parse_event(struct pane *p safe);
 static bool prepare_recrep(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        char *name;
 
        name = getenv("EDLIB_RECORD");
@@ -179,7 +178,7 @@ static bool prepare_recrep(struct pane *p safe)
 
 static void close_recrep(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
 
        if (dd->log) {
                fprintf(dd->log, "Close %d\n", dd->clears);
@@ -189,7 +188,7 @@ static void close_recrep(struct pane *p safe)
 
 static void record_key(struct pane *p safe, char *key safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        char q;
 
        if (!dd->log)
@@ -209,7 +208,7 @@ static void record_key(struct pane *p safe, char *key safe)
 
 static void record_mouse(struct pane *p safe, char *key safe, int x, int y)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        char q;
        if (!dd->log)
                return;
@@ -228,7 +227,7 @@ static void record_mouse(struct pane *p safe, char *key safe, int x, int y)
 
 static void record_screen(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        struct md5_state ctx;
        uint16_t buf[CCHARW_MAX+5];
        char out[MD5_DIGEST_SIZE*2+1];
@@ -331,7 +330,7 @@ static char *get_hash(char *line safe, hash_t hash safe)
 
 static bool parse_event(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        char line[80];
 
        line[79] = 0;
@@ -377,7 +376,7 @@ static bool parse_event(struct pane *p safe)
 REDEF_CMD(next_evt)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        int button = 0, type = 0;
 
        dd->input_sleeping = 0;
@@ -442,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;
        }
 
@@ -464,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)
@@ -511,7 +499,7 @@ static void wait_for(struct display_data *dd safe)
 
 DEF_CB(ns_resume)
 {
-       struct display_data *dd = &ci->home->data;
+       struct display_data *dd = ci->home->data;
 
        if (dd->suspended) {
                dd->suspended = False;
@@ -524,7 +512,7 @@ DEF_CB(ns_resume)
 DEF_CMD(nc_external_viewer)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        char *disp = pane_attr_get(p, "DISPLAY");
        char *disp_auth = pane_attr_get(p, "XAUTHORITY");
        char *remote = pane_attr_get(p, "REMOTE_SESSION");
@@ -626,7 +614,7 @@ DEF_CMD(nc_external_viewer)
 
 static void ncurses_stop(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
 
        if (dd->is_xterm) {
                /* disable bracketed-paste */
@@ -651,7 +639,7 @@ static void ncurses_stop(struct pane *p safe)
 
 static void ncurses_end(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
 
        if (dd->did_close)
                return;
@@ -800,9 +788,9 @@ 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;
+       struct display_data *dd = home->data;
        int attr = 0;
        const char *a, *v;
        char *col = NULL;
@@ -811,10 +799,9 @@ static int cvt_attrs(struct pane *p safe, struct pane *home safe,
        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);
@@ -866,15 +853,15 @@ static int make_cursor(int attr)
 
 DEF_CMD(nc_notify_display)
 {
-       struct display_data *dd = &ci->home->data;
+       struct display_data *dd = ci->home->data;
        comm_call(ci->comm2, "callback:display", ci->home, dd->last_event);
        return 1;
 }
 
-DEF_CMD(nc_close)
+DEF_CMD_CLOSED(nc_close)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        ncurses_end(p);
        hash_free(dd);
        fclose(dd->scr_file);
@@ -919,10 +906,11 @@ static PANEL * safe pane_panel(struct pane *p safe, struct pane *home)
 DEF_CMD(nc_clear)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       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;
@@ -978,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;
@@ -1007,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;
@@ -1046,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;
@@ -1057,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);
 
@@ -1180,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;
@@ -1218,7 +1214,7 @@ DEF_CMD(nc_refresh_size)
 DEF_CMD(nc_refresh_post)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        struct pane *p1;
        PANEL *pan, *pan2;
 
@@ -1285,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;
@@ -1301,7 +1297,7 @@ DEF_CMD(nc_refresh_post)
 
 static void ncurses_start(struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        int rows, cols;
 
        start_color();
@@ -1354,11 +1350,13 @@ static struct pane *ncurses_init(struct pane *ed safe,
        p = pane_register(ed, 1, &ncurses_handle.c);
        if (!p)
                return NULL;
-       dd = &p->data;
+       dd = p->data;
        dd->scr = scr;
        dd->scr_file = f;
        dd->is_xterm = (term && strstarts(term, "xterm"));
 
+       attr_set_str(&p->attrs, "Display:pixels", "1x2");
+
        set_screen(p);
 
        ncurses_start(p);
@@ -1387,7 +1385,7 @@ static struct pane *ncurses_init(struct pane *ed safe,
 REDEF_CMD(handle_winch)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        struct winsize size;
        ioctl(fileno(dd->scr_file), TIOCGWINSZ, &size);
        set_screen(p);
@@ -1408,6 +1406,7 @@ DEF_CMD(force_redraw)
        ncurses_start(p);
 
        clearok(curscr, 1);
+       pane_damaged(p, DAMAGED_POSTORDER);
        return 1;
 }
 
@@ -1547,7 +1546,7 @@ static char *find_name (struct namelist *l safe, wint_t c)
 
 static void send_key(int keytype, wint_t c, int alt, struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        char *n;
        char buf[100];/* FIXME */
        char t[5];
@@ -1585,7 +1584,7 @@ static void do_send_mouse(struct pane *p safe, int x, int y, char *cmd safe,
                          int button, char *mod, int type)
 {
        int ret;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
 
        record_mouse(p, cmd, x, y);
        ret = call("Mouse-event", p, button, NULL, cmd, type, NULL, mod, x, y);
@@ -1606,7 +1605,7 @@ static void do_send_mouse(struct pane *p safe, int x, int y, char *cmd safe,
 
 static void send_mouse(MEVENT *mev safe, struct pane *p safe)
 {
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        int x = mev->x;
        int y = mev->y;
        int b;
@@ -1652,7 +1651,7 @@ static void send_mouse(MEVENT *mev safe, struct pane *p safe)
 
 static void paste_start(struct pane *home safe)
 {
-       struct display_data *dd = &home->data;
+       struct display_data *dd = home->data;
 
        dd->paste_start = time(NULL);
        buf_init(&dd->paste_buf);
@@ -1660,7 +1659,7 @@ static void paste_start(struct pane *home safe)
 
 static void paste_flush(struct pane *home safe)
 {
-       struct display_data *dd = &home->data;
+       struct display_data *dd = home->data;
 
        if (!dd->paste_start)
                return;
@@ -1673,7 +1672,7 @@ static void paste_flush(struct pane *home safe)
 
 static bool paste_recv(struct pane *home safe, int is_keycode, wint_t ch)
 {
-       struct display_data *dd = &home->data;
+       struct display_data *dd = home->data;
        time_t now;
        if (dd->paste_start == 0)
                return False;
@@ -1699,7 +1698,7 @@ static bool paste_recv(struct pane *home safe, int is_keycode, wint_t ch)
 
 DEF_CMD(nc_get_paste)
 {
-       struct display_data *dd = &ci->home->data;
+       struct display_data *dd = ci->home->data;
 
        comm_call(ci->comm2, "cb", ci->focus,
                  dd->paste_start, NULL, dd->paste_latest);
@@ -1709,7 +1708,7 @@ DEF_CMD(nc_get_paste)
 REDEF_CMD(input_handle)
 {
        struct pane *p = ci->home;
-       struct display_data *dd = &p->data;
+       struct display_data *dd = p->data;
        static const char paste_seq[] = "\e[200~";
        wint_t c;
        int is_keycode;
@@ -1804,12 +1803,11 @@ DEF_CMD(display_ncurses)
        if (!term)
                term = "xterm-256color";
 
-       p = ncurses_init(ed, tty, term);
-       if (p)
-               p = call_ret(pane, "editor:activate-display", p);
-       if (p && ci->focus != ed)
-               /* Assume ci->focus is a document */
-               p = home_call_ret(pane, ci->focus, "doc:attach-view", p, 1);
+       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);
 
@@ -1822,10 +1820,9 @@ 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, "Draw:clear", &nc_clear);
        key_add(nc_map, "Draw:text-size", &nc_text_size);