]> git.neil.brown.name Git - edlib.git/blobdiff - display-x11-xcb.c
TODO: clean out done items.
[edlib.git] / display-x11-xcb.c
index 6248ab5fc045b575d37ed4d88f3e4fdd6c5c4d56..3aa0f73c654f47d3bf37d87796d675774c6d5a1b 100644 (file)
@@ -11,6 +11,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdint.h>
 #include <fcntl.h>
 #include <string.h>
 #include <xcb/xcb.h>
@@ -143,6 +144,8 @@ void pango_layout_index_to_pos(PangoLayout*, int, PangoRectangle*);
 
 #undef True
 #undef False
+
+#define PANE_DATA_TYPE struct xcb_data
 #include "core.h"
 
 enum my_atoms {
@@ -152,6 +155,7 @@ enum my_atoms {
        a_WM_ICON_NAME, a_NET_WM_ICON_NAME,
        a_WM_PROTOCOLS, a_WM_DELETE_WINDOW,
        a_NET_WM_PING,
+       a_NET_WM_ICON,
        a_WM_CLIENT_MACHINE,
        a_UTF8_STRING,
        NR_ATOMS
@@ -167,6 +171,7 @@ static const char *atom_names[NR_ATOMS] = {
        [a_WM_PROTOCOLS]        = "WM_PROTOCOLS",
        [a_WM_DELETE_WINDOW]    = "WM_DELETE_WINDOW",
        [a_NET_WM_PING]         = "_NET_WM_PING",
+       [a_NET_WM_ICON]         = "_NET_WM_ICON",
        [a_WM_CLIENT_MACHINE]   = "WM_CLIENT_MACHINE",
        [a_UTF8_STRING]         = "UTF8_STRING",
 };
@@ -190,7 +195,6 @@ struct xcb_data {
        cairo_t                 *cairo safe;
        cairo_surface_t         *surface safe;
        PangoFontDescription    *fd safe;
-       char                    *noclose;
        int                     charwidth, lineheight;
        cairo_region_t          *need_update;
 
@@ -222,6 +226,7 @@ struct xcb_data {
                cairo_region_t  *need_update;
        } *panes;
 };
+#include "core-pane.h"
 
 /* panes->r.x is NEVER_DRAWN if the pane has not been drawn */
 #define NEVER_DRAWN (-60000)
@@ -333,7 +338,7 @@ static void parse_attrs(
        char *fg = NULL, *bg = NULL;
        bool ul = False;
        bool inv = False;
-       int size = 10*1000;
+       int size = 12*1000;
        PangoFontDescription *fd = NULL;
        PangoStyle style = PANGO_STYLE_NORMAL;
        PangoVariant variant = PANGO_VARIANT_NORMAL;
@@ -342,7 +347,7 @@ static void parse_attrs(
        if (fdp) {
                fd = pango_font_description_new();
                *fdp = fd;
-               pango_font_description_set_family_static(fd, "mono");
+               pango_font_description_set_family_static(fd, "monospace");
        }
 
        while ((word = strsep(&ap, ",")) != NULL) {
@@ -380,8 +385,12 @@ static void parse_attrs(
                        bg = word + 3;
                if (strcmp(word, "inverse") == 0)
                        inv = True;
+               if (strcmp(word, "noinverse") == 0)
+                       inv = False;
                if (strcmp(word, "underline") == 0)
                        ul = True;
+               if (strcmp(word, "nounderline") == 0)
+                       ul = False;
        }
 
        if (inv) {
@@ -412,7 +421,7 @@ static void parse_attrs(
        } else if (bgp)
                bgp->g = -1;
        if (fd) {
-               pango_font_description_set_size(fd, size * scale / PANGO_SCALE);
+               pango_font_description_set_size(fd, PANGO_SCALE * size /1000 * scale / 1000);
                if (style != PANGO_STYLE_NORMAL)
                        pango_font_description_set_style(fd, style);
                if (variant != PANGO_VARIANT_NORMAL)
@@ -433,14 +442,14 @@ DEF_CB(cnt_disp)
        return 1;
 }
 
-DEF_CMD(xcb_close_display)
+DEF_CMD_CLOSED(xcb_close_display)
 {
        /* If this is only display, then refuse to close this one */
        struct call_return cr;
-       struct xcb_data *xd = ci->home->data;
+       char *nc = pane_attr_get(ci->home, "no-close");
 
-       if (xd->noclose) {
-               call("Message", ci->focus, 0, NULL, xd->noclose);
+       if (nc) {
+               call("Message", ci->focus, 0, NULL, nc);
                return 1;
        }
        cr.c = cnt_disp;
@@ -454,17 +463,6 @@ DEF_CMD(xcb_close_display)
        return 1;
 }
 
-DEF_CMD(xcb_set_noclose)
-{
-       struct xcb_data *xd = ci->home->data;
-
-       free(xd->noclose);
-       xd->noclose = NULL;
-       if (ci->str)
-               xd->noclose = strdup(ci->str);
-       return 1;
-}
-
 static void wait_for(struct xcb_data *xd safe)
 {
        struct pids **pp = &xd->pids;
@@ -559,19 +557,13 @@ static void panes_free(struct xcb_data *xd safe)
 
 static void kbd_free(struct xcb_data *xd safe);
 
-DEF_CMD(xcb_close)
+DEF_CMD_CLOSED(xcb_close)
 {
        struct xcb_data *xd = ci->home->data;
 
        xcb_destroy_window(xd->conn, xd->win);
        kbd_free(xd);
        panes_free(xd);
-       return 1;
-}
-
-DEF_CMD(xcb_free)
-{
-       struct xcb_data *xd = ci->home->data;
 
        pango_font_description_free(xd->fd);
        cairo_destroy(xd->cairo);
@@ -579,11 +571,9 @@ DEF_CMD(xcb_free)
        cairo_surface_destroy(xd->surface);
        free(xd->display);
        free(xd->disp_auth);
-       free(xd->noclose);
        xcb_disconnect(xd->conn);
        if (xd->need_update)
                cairo_region_destroy(xd->need_update);
-       unalloc(xd, pane);
        return 1;
 }
 
@@ -758,6 +748,7 @@ DEF_CMD(xcb_draw_text)
                PangoRectangle curs;
                bool in_focus = xd->in_focus;
                struct pane *f = ci->focus;
+               double cx, cy, cw, ch;
 
                pango_layout_index_to_pos(layout, ci->num, &curs);
                if (curs.width <= 0) {
@@ -766,11 +757,6 @@ DEF_CMD(xcb_draw_text)
                        pango_layout_get_extents(layout, NULL, &log);
                        curs.width = log.width;
                }
-               cairo_rectangle(ctx, x+curs.x/PANGO_SCALE, y-baseline+curs.y/PANGO_SCALE,
-                               (curs.width - PANGO_SCALE/2) / PANGO_SCALE,
-                               (curs.height - PANGO_SCALE/2) / PANGO_SCALE);
-               cairo_set_line_width(ctx, 1.0);
-               cairo_stroke(ctx);
 
                while (in_focus && f->parent->parent != f &&
                       f->parent != ci->home) {
@@ -778,10 +764,21 @@ DEF_CMD(xcb_draw_text)
                                in_focus = False;
                        f = f->parent;
                }
-               if (in_focus) {
-                       if (fg.g >= 0)
-                               cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
-                       cairo_rectangle(ctx, x+curs.x/PANGO_SCALE,
+               if (!in_focus) {
+                       /* Just an fg:rectangle around the fg:text */
+                       /* Add half to x,y as stroke is either side of the line */
+                       cx = x * PANGO_SCALE + curs.x + PANGO_SCALE/2;
+                       cy = (y - baseline) * PANGO_SCALE + curs.y + PANGO_SCALE/2;
+                       ch = curs.height - PANGO_SCALE;
+                       cw = curs.width - PANGO_SCALE;
+                       cairo_rectangle(ctx, cx/PANGO_SCALE, cy/PANGO_SCALE,
+                                       cw/PANGO_SCALE, ch/PANGO_SCALE);
+                       cairo_set_line_width(ctx, 1.0);
+                       cairo_stroke(ctx);
+               } else {
+                       /* solid fd:block with txt in bg color */
+                       cairo_rectangle(ctx,
+                                       x+curs.x/PANGO_SCALE,
                                        y-baseline+curs.y/PANGO_SCALE,
                                        curs.width / PANGO_SCALE,
                                        curs.height / PANGO_SCALE);
@@ -808,46 +805,102 @@ DEF_CMD(xcb_draw_text)
        return 1;
 }
 
+struct di_info {
+       struct command c;
+       MagickWand *wd safe;
+       int x,y,w,h;
+       int xo, yo;
+       struct panes *ps safe;
+};
+
+DEF_CB(xcb_draw_image_cb)
+{
+       struct di_info *dii = container_of(ci->comm, struct di_info, c);
+       int stride;
+       int fmt[2];
+       unsigned char *buf;
+       cairo_surface_t *surface;
+
+       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 */
+                       cairo_rectangle(dii->ps->ctx,
+                                       ci->x + dii->xo, ci->y + dii->yo,
+                                       ci->num, ci->num2);
+                       cairo_set_line_width(dii->ps->ctx, 1.0);
+                       cairo_set_source_rgb(dii->ps->ctx, 1.0, 0.0, 0.0);
+                       cairo_stroke(dii->ps->ctx);
+                       return 1;
+               }
+       case 'd': /* draw */
+               if (dii->w <= 0 || dii->h <= 0)
+                       return Efail;
+               stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24,
+                                                      dii->w);
+               buf = malloc(dii->h * stride);
+               // Cairo expects 32bit values with A in the high byte, then RGB.
+               // Magick provides 8bit values in the order requests.
+               // So depending on byte order, a different string is needed
+
+               fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
+               fmt[1] = 0;
+               MagickExportImagePixels(dii->wd, dii->x, dii->y,
+                                       dii->w, dii->h,
+                                       (char*)fmt, CharPixel, buf);
+               surface = cairo_image_surface_create_for_data(
+                       buf, CAIRO_FORMAT_ARGB32, dii->w, dii->h, stride);
+               cairo_set_source_surface(dii->ps->ctx, surface,
+                                        ci->num + dii->xo, ci->num2 + dii->yo);
+               cairo_paint(dii->ps->ctx);
+               cairo_surface_destroy(surface);
+               free(buf);
+               return 1;
+       default:
+               return Efail;
+       }
+}
+
 DEF_CMD(xcb_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 xcb_data *xd = ci->home->data;
-       bool stretch = ci->num & 16;
-       int pos = ci->num;
-       int w = ci->focus->w, h = ci->focus->h;
-       int x = 0, y = 0;
-       int xo, yo;
-       int stride;
+       struct di_info dii;
+       MagickWand *wd = NULL;
        struct panes *ps;
-       MagickBooleanType status;
-       MagickWand *wd;
-       int fmt[2];
-       unsigned char *buf;
-       cairo_surface_t *surface;
 
        if (!ci->str)
                return Enoarg;
-       ps = find_pixmap(xd, ci->focus, &xo, &yo);
+       ps = find_pixmap(xd, ci->focus, &dii.xo, &dii.yo);
        if (!ps)
                return Einval;
-       if (!ps->ctx)
-               instantiate_pixmap(xd, ps);
-       ps->bg.g = -1;
-       if (!ps->ctx)
+       dii.ps = ps;
+       if (!dii.ps->ctx)
+               instantiate_pixmap(xd, dii.ps);
+       dii.ps->bg.g = -1;
+       if (!dii.ps->ctx)
                return Efail;
        if (strstarts(ci->str, "file:")) {
+               MagickBooleanType status;
+
                wd = NewMagickWand();
                status = MagickReadImage(wd, ci->str + 5);
                if (status == MagickFalse) {
@@ -855,9 +908,11 @@ DEF_CMD(xcb_draw_image)
                        return Efail;
                }
        } else if (strstarts(ci->str, "comm:")) {
+               MagickBooleanType status;
                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;
@@ -868,65 +923,19 @@ DEF_CMD(xcb_draw_image)
                        DestroyMagickWand(wd);
                        return Efail;
                }
-       } else
+       }
+
+       if (!wd)
                return Einval;
 
        MagickAutoOrientImage(wd);
-       if (!stretch) {
-               int ih = MagickGetImageHeight(wd);
-               int iw = MagickGetImageWidth(wd);
+       dii.c = xcb_draw_image_cb;
+       dii.wd = wd;
+       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);
 
-               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;
-                       }
-                       h = ih;
-               } 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);
-       stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
-       buf = malloc(h * stride);
-       // Cairo expects 32bit values with A in the high byte, then RGB.
-       // Magick provides 8bit values in the order requests.
-       // So depending on byte order, a different string is needed
-
-       fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
-       fmt[1] = 0;
-       MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
-       surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
-                                                     w, h, stride);
-       cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
-       cairo_paint(ps->ctx);
-       cairo_surface_destroy(surface);
-       free(buf);
-
-       if (ci->x > 0 && ci->y > 0 && ci->focus->cx >= 0) {
-               struct pane *p = ci->focus;
-               cairo_rectangle(ps->ctx, p->cx + xo, p->cy + yo,
-                               w/ci->x, h/ci->y);
-               cairo_set_line_width(ps->ctx, 1.0);
-               cairo_set_source_rgb(ps->ctx, 1.0, 0.0, 0.0);
-               cairo_stroke(ps->ctx);
-       }
        DestroyMagickWand(wd);
 
        pane_damaged(ci->home, DAMAGED_POSTORDER);
@@ -952,7 +961,7 @@ DEF_CMD(xcb_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;
@@ -1238,7 +1247,7 @@ static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
        struct mark *pt;
 
        xd->in_focus = in;
-       p = pane_leaf(home);
+       p = pane_focus(home);
        pt = call_ret(mark, "doc:point", p);
        if (pt)
                call("view:changed", p, 0, pt);
@@ -1594,7 +1603,7 @@ static void handle_client_message(struct pane *home safe,
            cme->format == 32 &&
            cme->window == xd->win &&
            cme->data.data32[0] == xd->atoms[a_WM_DELETE_WINDOW]) {
-               call("Display:close", pane_leaf(home));
+               call("Window:close", pane_focus(home));
                return;
        }
 
@@ -1694,7 +1703,7 @@ DEF_CMD(xcb_input)
                xcb_flush(xd->conn);
        }
        if (xcb_connection_has_error(xd->conn)) {
-               call("Display:close", ci->home->parent);
+               call("Window:close", ci->home->parent);
                pane_close(ci->home);
        }
        return ret;
@@ -1719,6 +1728,17 @@ static void set_utf8_prop(struct xcb_data *xd safe,
                            8, strlen(str), str);
 }
 
+static void set_card32_property(struct xcb_data *xd safe,
+                               enum my_atoms a,
+                               const uint32_t *data, int cnt)
+{
+       xcb_change_property(xd->conn,
+                           XCB_PROP_MODE_REPLACE,
+                           xd->win, xd->atoms[a],
+                           XCB_ATOM_CARDINAL, 32,
+                           cnt, data);
+}
+
 static void set_atom_prop(struct xcb_data *xd safe,
                          enum my_atoms prop, enum my_atoms alist, ...)
 {
@@ -1740,6 +1760,48 @@ static void set_atom_prop(struct xcb_data *xd safe,
                            32, anum, atoms);
 }
 
+static void xcb_load_icon(struct pane *p safe,
+                         struct xcb_data *xd safe,
+                         char *file safe)
+{
+       char *path;
+       int h, w, n;
+       unsigned int *data;
+       MagickBooleanType status;
+       MagickWand *wd;
+       uint32_t fmt[2];
+
+       path = call_ret(str, "xdg-find-edlib-file", p, 0, NULL,
+                       file, 0, NULL, "data");
+       if (!path)
+               return;
+
+       wd = NewMagickWand();
+       status = MagickReadImage(wd, path);
+       free(path);
+       if (status == MagickFalse)
+               goto done;
+
+       h = MagickGetImageHeight(wd);
+       w = MagickGetImageWidth(wd);
+       n = 2 + w*h;
+       data = malloc(sizeof(data[0]) * n);
+       if (!data)
+               goto done;
+       data[0] = w;
+       data[1] = h;
+       /* Need host-endian ARGB data */
+       fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
+       fmt[1] = 0;
+       MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt,
+                               CharPixel, data+2);
+       set_card32_property(xd, a_NET_WM_ICON, data, n);
+       free(data);
+done:
+       DestroyMagickWand(wd);
+       return;
+}
+
 static struct pane *xcb_display_init(const char *d safe,
                                     const char *disp_auth,
                                     struct pane *focus safe)
@@ -1767,7 +1829,13 @@ static struct pane *xcb_display_init(const char *d safe,
        if (xcb_connection_has_error(conn))
                return NULL;
 
-       alloc(xd, pane);
+       p = call_ret(pane, "attach-window-core", focus);
+       if (!p)
+               return NULL;
+       p = pane_register(p, 1, &xcb_handle.c);
+       if (!p)
+               return NULL;
+       xd = p->data;
 
        xd->motion_blocked = True;
        xd->in_focus = True;
@@ -1834,7 +1902,7 @@ static struct pane *xcb_display_init(const char *d safe,
        if (!fd)
                goto abort;
        xd->fd = fd;
-       pango_font_description_set_family(xd->fd, "mono");
+       pango_font_description_set_family(xd->fd, "monospace");
        pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
 
        layout = pango_cairo_create_layout(xd->cairo);
@@ -1888,11 +1956,9 @@ static struct pane *xcb_display_init(const char *d safe,
                        XCB_MOD_MASK_LOCK |
                        XCB_MOD_MASK_CONTROL);
 
+       xcb_load_icon(focus, xd, "{COMM}-icon.png");
        xcb_map_window(conn, xd->win);
        xcb_flush(conn);
-       p = pane_register(pane_root(focus), 1, &xcb_handle.c, xd);
-       if (!p)
-               goto abort;
        pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
        call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
        call_comm("event:poll", p, &xcb_input);
@@ -1902,7 +1968,6 @@ static struct pane *xcb_display_init(const char *d safe,
        attr_set_str(&p->attrs, "scale:M", scale);
        xd->last_event = time(NULL);
        call("editor:request:all-displays", p);
-       p = call_ret(pane, "editor:activate-display", p);
        return p;
 abort:
        kbd_free(xd);
@@ -1911,23 +1976,9 @@ abort:
        xcb_disconnect(conn);
        free(xd->display);
        free(xd->disp_auth);
-       unalloc(xd, pane);
        return NULL;
 }
 
-DEF_CMD(display_xcb)
-{
-       struct pane *p;
-       const char *d = ci->str;
-
-       if (!d)
-               return Enoarg;
-       p = xcb_display_init(d, ci->str2, ci->focus);
-       if (p)
-               return comm_call(ci->comm2, "cb", p);
-       return Efail;
-}
-
 DEF_CMD(xcb_new_display)
 {
        struct pane *p;
@@ -1944,8 +1995,9 @@ DEF_CMD(xcb_new_display)
        if (!d)
                return Enoarg;
        p = xcb_display_init(d, disp_auth, ci->focus);
-       if (p)
-               home_call_ret(pane, ci->focus, "doc:attach-view", p, 1);
+       if (strcmp(ci->key, "interactive-cmd-x11window") == 0)
+               p = home_call_ret(pane, p,
+                                 "Window:activate-display", ci->focus);
        if (p)
                comm_call(ci->comm2, "cb", p);
        return 1;
@@ -1953,21 +2005,19 @@ DEF_CMD(xcb_new_display)
 
 void edlib_init(struct pane *ed safe)
 {
-       call_comm("global-set-command", ed, &display_xcb, 0, NULL,
+       call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
                  "attach-display-x11");
        call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
                  "interactive-cmd-x11window");
 
        xcb_map = key_alloc();
 
-       key_add(xcb_map, "Display:close", &xcb_close_display);
-       key_add(xcb_map, "Display:set-noclose", &xcb_set_noclose);
-       key_add(xcb_map, "Display:external-viewer", &xcb_external_viewer);
-       key_add(xcb_map, "Display:fullscreen", &xcb_fullscreen);
-       key_add(xcb_map, "Display:new", &xcb_new_display);
+       key_add(xcb_map, "Window:close", &xcb_close_display);
+       key_add(xcb_map, "Window:external-viewer", &xcb_external_viewer);
+       key_add(xcb_map, "Window:fullscreen", &xcb_fullscreen);
+       key_add(xcb_map, "Window:new", &xcb_new_display);
 
        key_add(xcb_map, "Close", &xcb_close);
-       key_add(xcb_map, "Free", &xcb_free);
        key_add(xcb_map, "Draw:clear", &xcb_clear);
        key_add(xcb_map, "Draw:text-size", &xcb_text_size);
        key_add(xcb_map, "Draw:text", &xcb_draw_text);