]> git.neil.brown.name Git - edlib.git/blob - display-x11-xcb.c
Update the year in some copyright claims
[edlib.git] / display-x11-xcb.c
1 /*
2  * Copyright Neil Brown ©2021-2022 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * X11 display driver for edlib, using xcb, cairopango, libxkbcommon etc.
6  *
7  * A different connection to the server will be created for each
8  * display.  Maybe that can be optimised one day.
9  */
10
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <xcb/xcb.h>
17 #include <stdarg.h>
18 #ifndef __CHECKER__
19 #include <xcb/xkb.h>
20 #else
21 /* xkb.h has a 'long' in an enum :-( */
22 enum {
23         XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY,
24         XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
25         XCB_XKB_NEW_KEYBOARD_NOTIFY,
26         XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
27         XCB_XKB_MAP_PART_MODIFIER_MAP,
28         XCB_XKB_STATE_PART_MODIFIER_LOCK,
29         XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS,
30         XCB_XKB_STATE_PART_GROUP_BASE,
31         XCB_XKB_MAP_PART_KEY_ACTIONS,
32         XCB_XKB_STATE_PART_GROUP_LATCH,
33         XCB_XKB_MAP_PART_VIRTUAL_MODS,
34         XCB_XKB_STATE_PART_GROUP_LOCK,
35         XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP,
36         XCB_XKB_NKN_DETAIL_KEYCODES,
37         XCB_XKB_MAP_PART_KEY_TYPES,
38         XCB_XKB_MAP_PART_KEY_SYMS,
39         XCB_XKB_STATE_PART_MODIFIER_BASE,
40         XCB_XKB_STATE_PART_MODIFIER_LATCH,
41         XCB_XKB_MAP_NOTIFY,
42         XCB_XKB_STATE_NOTIFY,
43 };
44 typedef uint16_t xcb_xkb_device_spec_t;
45 typedef struct xcb_xkb_select_events_details_t {
46         uint16_t affectNewKeyboard;
47         uint16_t newKeyboardDetails;
48         uint16_t affectState;
49         uint16_t stateDetails;
50         /* and other fields */
51 } xcb_xkb_select_events_details_t;
52 typedef struct xcb_xkb_new_keyboard_notify_event_t {
53         uint8_t         deviceID;
54         uint16_t        changed;
55         /* and other fields */
56 } xcb_xkb_new_keyboard_notify_event_t;
57 typedef struct xcb_xkb_state_notify_event_t {
58         uint8_t         deviceID;
59         uint8_t         baseMods;
60         uint8_t         latchedMods;
61         uint8_t         lockedMods;
62         int16_t         baseGroup;
63         int16_t         latchedGroup;
64         uint8_t         lockedGroup;
65         /* and other fields */
66 } xcb_xkb_state_notify_event_t;
67 typedef struct xcb_xkb_map_notify_event_t {
68         uint8_t         deviceID;
69 } xcb_xkb_map_notify_event_t;
70 xcb_void_cookie_t
71 xcb_xkb_select_events_aux_checked(xcb_connection_t              *c,
72                                   xcb_xkb_device_spec_t         deviceSpec,
73                                   uint16_t                      affectWhich,
74                                   uint16_t                      clear,
75                                   uint16_t                      selectAll,
76                                   uint16_t                      affectMap,
77                                   uint16_t                      map,
78                                   const xcb_xkb_select_events_details_t *details);
79
80 #endif
81 #include <xcb/xcbext.h>
82 #include <ctype.h>
83 #include <math.h>
84 #include <locale.h>
85
86 #include <cairo.h>
87 #include <cairo-xcb.h>
88
89 #include <wand/MagickWand.h>
90 #ifdef __CHECKER__
91 // enums confuse sparse...
92 #define MagickBooleanType int
93 #endif
94
95 #ifndef __CHECKER__
96 #include <pango/pango.h>
97 #include <pango/pangocairo.h>
98 #else
99 typedef struct PangoFontDescription {} PangoFontDescription;
100 typedef struct PangoLayout {} PangoLayout;
101 typedef struct PangoContext {} PangoContext;
102 typedef struct PangoFontMetrics {} PangoFontMetrics;
103 typedef struct PangoRectangle { int x,y,width,height;} PangoRectangle;
104 typedef enum { PANGO_STYLE_NORMAL, PANGO_STYLE_OBLIQUE, PANGO_STYLE_ITALIC
105 } PangoStyle;
106 typedef enum { PANGO_VARIANT_NORMAL, PANGO_VARIANT_SMALL_CAPS } PangoVariant;
107 typedef enum { PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_BOLD } PangoWeight;
108 PangoFontDescription *pango_font_description_new(void);
109 void pango_font_description_set_family_static(PangoFontDescription*, char*);
110 void pango_font_description_set_family(PangoFontDescription*, char*);
111 void pango_font_description_set_size(PangoFontDescription*, int);
112 void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
113 void pango_font_description_set_variant(PangoFontDescription*, PangoVariant);
114 void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
115 #define PANGO_SCALE (1024)
116
117 PangoLayout *pango_cairo_create_layout(cairo_t*);
118 void g_object_unref(PangoLayout*);
119 PangoContext *pango_cairo_create_context(cairo_t *);
120 void pango_cairo_show_layout(cairo_t *, PangoLayout *);
121 PangoFontMetrics *pango_context_get_metrics(PangoContext*, PangoFontDescription*, void*);
122 void pango_font_description_free(PangoFontDescription*);
123 int pango_font_metrics_get_approximate_char_width(PangoFontMetrics *);
124 int pango_font_metrics_get_ascent(PangoFontMetrics *);
125 int pango_font_metrics_get_descent(PangoFontMetrics *);
126 void pango_font_metrics_unref(PangoFontMetrics *);
127 PangoContext* pango_layout_get_context(PangoLayout *);
128 int pango_layout_get_baseline(PangoLayout *);
129 void pango_layout_get_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
130 void pango_layout_get_pixel_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
131 void pango_layout_set_font_description(PangoLayout *, PangoFontDescription *);
132 void pango_layout_set_text(PangoLayout*, const char *, int);
133 void pango_layout_xy_to_index(PangoLayout*, int, int, int*, int*);
134 void pango_layout_index_to_pos(PangoLayout*, int, PangoRectangle*);
135 #endif
136
137 #include <xkbcommon/xkbcommon.h>
138 #include <xkbcommon/xkbcommon-compose.h>
139 #include <xkbcommon/xkbcommon-x11.h>
140
141 #undef True
142 #undef False
143 #include "core.h"
144
145 enum my_atoms {
146         a_NONE = 0,
147         a_WM_STATE, a_STATE_FULLSCREEN,
148         a_WM_NAME, a_NET_WM_NAME,
149         a_WM_ICON_NAME, a_NET_WM_ICON_NAME,
150         a_WM_PROTOCOLS, a_WM_DELETE_WINDOW,
151         a_NET_WM_PING,
152         a_WM_CLIENT_MACHINE,
153         a_UTF8_STRING,
154         NR_ATOMS
155 };
156 static char *atom_names[NR_ATOMS] = {
157         [a_NONE]                = "NONE",
158         [a_WM_STATE]            = "_NET_WM_STATE",
159         [a_STATE_FULLSCREEN]    = "_NET_WM_STATE_FULLSCREEN",
160         [a_WM_NAME]             = "WM_NAME",
161         [a_NET_WM_NAME]         = "_NET_WM_NAME",
162         [a_WM_ICON_NAME]        = "WM_ICON_NAME",
163         [a_NET_WM_ICON_NAME]    = "_NET_WM_ICON_NAME",
164         [a_WM_PROTOCOLS]        = "WM_PROTOCOLS",
165         [a_WM_DELETE_WINDOW]    = "WM_DELETE_WINDOW",
166         [a_NET_WM_PING]         = "_NET_WM_PING",
167         [a_WM_CLIENT_MACHINE]   = "WM_CLIENT_MACHINE",
168         [a_UTF8_STRING]         = "UTF8_STRING",
169 };
170
171 struct rgb {
172         double r,g,b;
173 };
174
175 struct xcb_data {
176         xcb_connection_t        *conn safe;
177         char                    *display safe;
178
179         const xcb_setup_t       *setup safe;
180         const xcb_screen_t      *screen safe;
181         xcb_atom_t              atoms[NR_ATOMS];
182
183         long                    last_event;
184         xcb_window_t            win;
185         xcb_visualtype_t        *visual;
186         cairo_t                 *cairo safe;
187         cairo_surface_t         *surface safe;
188         PangoFontDescription    *fd safe;
189         char                    *noclose;
190         int                     charwidth, lineheight;
191         cairo_region_t          *need_update;
192
193         bool                    motion_blocked;
194         bool                    in_focus;
195
196         struct xkb_context      *xkb;
197         uint8_t                 first_xkb_event;
198         int32_t                 xkb_device_id;
199         struct xkb_state        *xkb_state;
200         struct xkb_compose_state *compose_state;
201         struct xkb_compose_table *compose_table;
202         struct xkb_keymap       *xkb_keymap;
203
204         /* FIXME use hash?? */
205         struct panes {
206                 struct panes    *next;
207                 struct pane     *p safe;
208                 cairo_rectangle_int_t r;
209                 cairo_t         *ctx;
210                 struct rgb      bg;
211                 xcb_pixmap_t    draw;
212                 cairo_surface_t *surface;
213                 cairo_region_t  *need_update;
214         } *panes;
215 };
216
217 /* panes->r.x is NEVER_DRAWN if the pane has not been drawn */
218 #define NEVER_DRAWN (-60000)
219
220 static struct map *xcb_map;
221 DEF_LOOKUP_CMD(xcb_handle, xcb_map);
222
223 static struct panes *get_pixmap(struct pane *home safe,
224                                 struct pane *p safe)
225 {
226         struct xcb_data *xd = home->data;
227         struct panes **pp, *ps;
228
229         for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
230                 if (ps->p != p)
231                         continue;
232                 if (ps->r.width == p->w && ps->r.height == p->h)
233                         return ps;
234                 *pp = ps->next;
235                 if (ps->r.x != NEVER_DRAWN) {
236                         if (!xd->need_update)
237                                 xd->need_update = cairo_region_create();
238                         cairo_region_union_rectangle(xd->need_update, &ps->r);
239                 }
240                 if (ps->ctx)
241                         cairo_destroy(ps->ctx);
242                 if (ps->surface)
243                         cairo_surface_destroy(ps->surface);
244                 if (ps->draw)
245                         xcb_free_pixmap(xd->conn, ps->draw);
246                 free(ps);
247                 break;
248         }
249         alloc(ps, pane);
250         ps->p = p;
251         ps->r.x = ps->r.y = NEVER_DRAWN;
252         ps->r.width = p->w;
253         ps->r.height = p->h;
254         ps->bg.r = ps->bg.g = ps->bg.b = 0;
255
256         pane_add_notify(home, p, "Notify:Close");
257         ps->next = *pp;
258         *pp = ps;
259         return ps;
260 }
261
262 static void instantiate_pixmap(struct xcb_data *xd safe,
263                           struct panes *ps safe)
264 {
265         ps->draw = xcb_generate_id(xd->conn);
266         xcb_create_pixmap(xd->conn, xd->screen->root_depth, ps->draw,
267                           xd->win, ps->r.width, ps->r.height);
268         ps->surface = cairo_xcb_surface_create(
269                 xd->conn, ps->draw, xd->visual, ps->r.width, ps->r.height);
270         if (!ps->surface)
271                 goto free_ps;
272         ps->ctx = cairo_create(ps->surface);
273         if (!ps->ctx)
274                 goto free_surface;
275         cairo_set_source_rgb(ps->ctx, ps->bg.r, ps->bg.g, ps->bg.b);
276         cairo_paint(ps->ctx);
277         return;
278
279 free_surface:
280         cairo_surface_destroy(ps->surface);
281         ps->surface = NULL;
282 free_ps:
283         xcb_free_pixmap(xd->conn, ps->draw);
284         ps->draw = 0;
285 }
286
287 static struct panes *find_pixmap(struct xcb_data *xd safe, struct pane *p safe,
288                                  int *xp safe, int *yp safe)
289 {
290         int x = 0, y = 0;
291         struct panes *ret = NULL;
292
293         while (!ret && p->parent != p) {
294                 struct panes *ps;
295                 for (ps = xd->panes; ps ; ps = ps->next)
296                         if (ps->p == p) {
297                                 ret = ps;
298                                 break;
299                         }
300                 if (!ret) {
301                         x += p->x;
302                         y += p->y;
303                         p = p->parent;
304                 }
305         }
306         *xp = x;
307         *yp = y;
308         return ret;
309 }
310
311 static inline double cvt(int i)
312 {
313         return (float)i / 1000.0;
314 }
315
316 static void parse_attrs(
317         struct pane *home safe, const char *cattrs, int scale,
318         struct rgb *fgp, struct rgb *bgp, bool *underline,
319         PangoFontDescription **fdp)
320 {
321         char *attrs = strdup(cattrs ?: "");
322         char *ap = attrs;
323         char *word;
324         char *fg = NULL, *bg = NULL;
325         bool ul = False;
326         bool inv = False;
327         int size = 10*1000;
328         PangoFontDescription *fd = NULL;
329         PangoStyle style = PANGO_STYLE_NORMAL;
330         PangoVariant variant = PANGO_VARIANT_NORMAL;
331         PangoWeight weight = PANGO_WEIGHT_NORMAL;
332
333         if (fdp) {
334                 fd = pango_font_description_new();
335                 *fdp = fd;
336                 pango_font_description_set_family_static(fd, "mono");
337         }
338
339         while ((word = strsep(&ap, ",")) != NULL) {
340                 if (fd && strncmp(word, "family:", 7) == 0)
341                         pango_font_description_set_family(fd, word+7);
342                 if (strcmp(word, "large") == 0)
343                         size = 14 * 1000;
344                 if (strcmp(word, "small") == 0)
345                         size = 9 * 1000;
346                 if (isdigit(word[0])) {
347                         char *end = NULL;
348                         double s = strtod(word, &end);
349                         if (end && end != word && !*end)
350                                 size = trunc(s * 1000.0);
351                         else
352                                 size = 10*1000;
353                 }
354                 if (strcmp(word, "oblique") == 0)
355                         style = PANGO_STYLE_OBLIQUE;
356                 if (strcmp(word, "italic") == 0)
357                         style = PANGO_STYLE_ITALIC;
358                 if (strcmp(word, "normal") == 0)
359                         style = PANGO_STYLE_NORMAL;
360                 if (strcmp(word, "small-caps") == 0)
361                         variant = PANGO_VARIANT_SMALL_CAPS;
362
363                 if (strcmp(word, "bold") == 0)
364                         weight = PANGO_WEIGHT_BOLD;
365                 if (strcmp(word, "nobold") == 0)
366                         weight = PANGO_WEIGHT_NORMAL;
367
368                 if (strncmp(word, "fg:", 3) == 0)
369                         fg = word + 3;
370                 if (strncmp(word, "bg:", 3) == 0)
371                         bg = word + 3;
372                 if (strcmp(word, "inverse") == 0)
373                         inv = True;
374                 if (strcmp(word, "underline") == 0)
375                         ul = True;
376         }
377
378         if (inv) {
379                 char *t = bg;
380                 bg = fg;
381                 fg = t;
382                 if (!fg)
383                         fg = "white";
384                 if (!bg)
385                         bg = "black";
386         } else if (!fg)
387                 fg = "black";
388
389         if (fg && fgp) {
390                 struct call_return ret = call_ret(all, "colour:map", home,
391                                                   0, NULL, fg);
392                 fgp->r = cvt(ret.i);
393                 fgp->g = cvt(ret.i2);
394                 fgp->b = cvt(ret.x);
395         } else if (fgp)
396                 fgp->g = -1;
397         if (bg && bgp) {
398                 struct call_return ret = call_ret(all, "colour:map", home,
399                                                   0, NULL, bg);
400                 bgp->r = cvt(ret.i);
401                 bgp->g = cvt(ret.i2);
402                 bgp->b = cvt(ret.x);
403         } else if (bgp)
404                 bgp->g = -1;
405         if (fd) {
406                 pango_font_description_set_size(fd, size * scale / PANGO_SCALE);
407                 if (style != PANGO_STYLE_NORMAL)
408                         pango_font_description_set_style(fd, style);
409                 if (variant != PANGO_VARIANT_NORMAL)
410                         pango_font_description_set_variant(fd, variant);
411                 if (weight != PANGO_WEIGHT_NORMAL)
412                         pango_font_description_set_weight(fd, weight);
413         }
414         if (underline)
415                 *underline = ul;
416         free(attrs);
417 }
418
419 DEF_CB(cnt_disp)
420 {
421         struct call_return *cr = container_of(ci->comm, struct call_return, c);
422
423         cr->i += 1;
424         return 1;
425 }
426
427 DEF_CMD(xcb_close_display)
428 {
429         /* If this is only display, then refuse to close this one */
430         struct call_return cr;
431         struct xcb_data *xd = ci->home->data;
432         if (xd->noclose) {
433                 call("Message", ci->focus, 0, NULL, xd->noclose);
434                 return 1;
435         }
436         cr.c = cnt_disp;
437         cr.i = 0;
438         call_comm("editor:notify:all-displays", ci->focus, &cr.c);
439         if (cr.i > 1)
440                 pane_close(ci->home);
441         else
442                 call("Message", ci->focus, 0, NULL,
443                      "Cannot close only window.");
444         return 1;
445 }
446
447 DEF_CMD(xcb_set_noclose)
448 {
449         struct xcb_data *xd = ci->home->data;
450
451         free(xd->noclose);
452         xd->noclose = NULL;
453         if (ci->str)
454                 xd->noclose = strdup(ci->str);
455         return 1;
456 }
457
458 DEF_CMD(xcb_external_viewer)
459 {
460         struct xcb_data *xd = ci->home->data;
461         const char *path = ci->str;
462         int pid;
463         int fd;
464
465         if (!path)
466                 return Enoarg;
467         switch (pid = fork()) {
468         case -1:
469                 return Efail;
470         case 0: /* Child */
471                 setenv("DISPLAY", xd->display, 1);
472                 fd = open("/dev/null", O_RDWR);
473                 if (fd) {
474                         dup2(fd, 0);
475                         dup2(fd, 1);
476                         dup2(fd, 2);
477                         if (fd > 2)
478                                 close(fd);
479                 }
480                 execlp("xdg-open", "xdg-open", path, NULL);
481                 exit(1);
482         default: /* parent */
483                 break;
484         }
485         return 1;
486 }
487
488 DEF_CMD(xcb_fullscreen)
489 {
490         struct xcb_data *xd = ci->home->data;
491         xcb_client_message_event_t msg = {};
492
493         msg.response_type = XCB_CLIENT_MESSAGE;
494         msg.format = 32;
495         msg.window = xd->win;
496         msg.type = xd->atoms[a_WM_STATE];
497         if (ci->num > 0)
498                 msg.data.data32[0] = 1; /* ADD */
499         else
500                 msg.data.data32[0] = 0; /* REMOVE */
501         msg.data.data32[1] = xd->atoms[a_STATE_FULLSCREEN];
502         msg.data.data32[2] = 0;
503         msg.data.data32[3] = 1; /* source indicator */
504
505         xcb_send_event(xd->conn, 0, xd->screen->root,
506                        XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
507                        XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
508                        (void*)&msg);
509         xcb_flush(xd->conn);
510         return 1;
511 }
512
513 static void panes_free(struct xcb_data *xd safe)
514 {
515         while (xd->panes) {
516                 struct panes *ps = xd->panes;
517                 xd->panes = ps->next;
518                 if (ps->ctx)
519                         cairo_destroy(ps->ctx);
520                 if (ps->surface)
521                         cairo_surface_destroy(ps->surface);
522                 if (ps->draw)
523                         xcb_free_pixmap(xd->conn, ps->draw);
524                 free(ps);
525         }
526 }
527
528 static void kbd_free(struct xcb_data *xd safe);
529
530 DEF_CMD(xcb_close)
531 {
532         struct xcb_data *xd = ci->home->data;
533
534         xcb_destroy_window(xd->conn, xd->win);
535         kbd_free(xd);
536         panes_free(xd);
537         return 1;
538 }
539
540 DEF_CMD(xcb_free)
541 {
542         struct xcb_data *xd = ci->home->data;
543
544         pango_font_description_free(xd->fd);
545         cairo_destroy(xd->cairo);
546         cairo_device_finish(cairo_surface_get_device(xd->surface));
547         cairo_surface_destroy(xd->surface);
548         free(xd->display);
549         free(xd->noclose);
550         xcb_disconnect(xd->conn);
551         if (xd->need_update)
552                 cairo_region_destroy(xd->need_update);
553         unalloc(xd, pane);
554         return 1;
555 }
556
557 DEF_CMD(xcb_clear)
558 {
559         struct xcb_data *xd = ci->home->data;
560         const char *attr = ci->str;
561         struct panes *src = NULL, *dest;
562         struct rgb bg;
563         int x, y;
564         cairo_rectangle_int_t r;
565
566         if (attr) {
567                 parse_attrs(ci->home, attr, PANGO_SCALE, NULL, &bg, NULL, NULL);
568                 if (bg.g < 0)
569                         bg.r = bg.g = bg.b = 1.0;
570         } else {
571                 src = find_pixmap(xd, ci->focus->parent, &x, &y);
572                 x += ci->focus->x;
573                 y += ci->focus->y;
574                 if (!src)
575                         bg.r = bg.g = bg.b = 1.0;
576                 else if (src->bg.g >= 0)
577                         bg = src->bg;
578                 else if (src->surface == NULL)
579                         bg.r = bg.g = bg.b = 1.0;
580                 else
581                         bg.g = -1;
582         }
583
584         dest = get_pixmap(ci->home, ci->focus);
585         if (!dest)
586                 return 1;
587         if (bg.g >= 0) {
588                 if (dest->ctx) {
589                         cairo_set_source_rgb(dest->ctx, bg.r, bg.g, bg.b);
590                         cairo_paint(dest->ctx);
591                 }
592                 dest->bg = bg;
593         } else if (src) {
594                 if (!dest->ctx)
595                         instantiate_pixmap(xd, dest);
596                 if (dest->ctx) {
597                         cairo_set_source_surface(dest->ctx, src->surface, -x, -y);
598                         cairo_paint(dest->ctx);
599                         dest->bg.g = -1;
600                 }
601         } else
602                 LOG("ERROR neither src or bg");
603         pane_damaged(ci->home, DAMAGED_POSTORDER);
604
605         if (!dest->need_update)
606                 dest->need_update = cairo_region_create();
607         r.x = 0;
608         r.y = 0;
609         r.width = ci->focus->w;
610         r.height = ci->focus->h;
611         cairo_region_union_rectangle(dest->need_update, &r);
612         return 1;
613 }
614
615 DEF_CMD(xcb_text_size)
616 {
617         struct xcb_data *xd = ci->home->data;
618         const char *attr = ci->str2 ?: "";
619         const char *str = ci->str ?: "";
620         int scale = ci->num2;
621         PangoLayout *layout;
622         PangoFontDescription *fd;
623         PangoRectangle log;
624         int baseline;
625         int max_bytes;
626
627         if (scale <= 0)
628                 scale = 1000;
629         parse_attrs(ci->home, attr, scale, NULL, NULL, NULL, &fd);
630         /* If we use an empty string, line-height it wrong */
631         layout = pango_cairo_create_layout(xd->cairo);
632         pango_layout_set_text(layout, *str ? str : "M", -1);
633         pango_layout_set_font_description(layout, fd);
634         pango_layout_get_pixel_extents(layout, NULL, &log);
635         baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
636
637         if (ci->num < 0)
638                 max_bytes = 0;
639         else if (log.width <= ci->num)
640                 max_bytes = strlen(str);
641         else
642                 pango_layout_xy_to_index(layout, PANGO_SCALE*ci->num,
643                                          baseline, &max_bytes, NULL);
644
645         comm_call(ci->comm2, "cb", ci->focus, max_bytes, NULL, NULL,
646                   baseline, NULL, NULL,
647                   str && *str ? log.width : 0,
648                   log.height);
649
650         pango_font_description_free(fd);
651         g_object_unref(layout);
652         return 1;
653 }
654
655 DEF_CMD(xcb_draw_text)
656 {
657         struct xcb_data *xd = ci->home->data;
658         const char *str = ci->str;
659         const char *attr = ci->str2;
660         int scale = 1000;
661         struct panes *ps;
662         cairo_t *ctx;
663         PangoLayout *layout;
664         PangoFontDescription *fd;
665         PangoRectangle log;
666         struct rgb fg, bg;
667         bool ul;
668         int baseline;
669         int xo = 0, yo = 0;
670         int x,y;
671
672         if (!str)
673                 return Enoarg;
674         ps = find_pixmap(xd, ci->focus, &xo, &yo);
675         if (!ps)
676                 return Einval;
677         if (!ps->ctx)
678                 instantiate_pixmap(xd, ps);
679         ps->bg.g = -1;
680         ctx = ps->ctx;
681         if (!ctx)
682                 return Efail;
683
684         pane_damaged(ci->home, DAMAGED_POSTORDER);
685
686         if (ci->num2 > 0)
687                 scale = ci->num2 * 10 / xd->charwidth;
688
689         parse_attrs(ci->home, attr, scale, &fg, &bg, &ul, &fd);
690
691         x = ci->x + xo;
692         y = ci->y + yo;
693         layout = pango_cairo_create_layout(ctx);
694         pango_layout_set_text(layout, str, -1);
695         pango_layout_set_font_description(layout, fd);
696         pango_layout_get_pixel_extents(layout, NULL, &log);
697         baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
698         cairo_save(ctx);
699         if (bg.g >= 0) {
700                 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
701                 cairo_rectangle(ctx, x+log.x, y - baseline + log.y,
702                                 log.width, log.height);
703                 cairo_fill(ctx);
704         }
705         cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
706         if (ul) {
707                 /* Draw an underline */
708                 cairo_rectangle(ctx, x+log.x, y+2+log.y,
709                                 log.width, 1);
710                 cairo_fill(ctx);
711         }
712
713         cairo_move_to(ctx, x, y - baseline);
714         pango_cairo_show_layout(ctx, layout);
715         cairo_stroke(ctx);
716
717         if (ci->num >= 0) {
718                 /* draw a cursor - outline box if not in-focus,
719                  * inverse-video if it is.
720                  */
721                 PangoRectangle curs;
722                 bool in_focus = xd->in_focus;
723                 struct pane *f = ci->focus;
724
725                 pango_layout_index_to_pos(layout, ci->num, &curs);
726                 if (curs.width <= 0) {
727                         /* EOL?*/
728                         pango_layout_set_text(layout, "M", 1);
729                         pango_layout_get_extents(layout, NULL, &log);
730                         curs.width = log.width;
731                 }
732                 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE, y-baseline+curs.y/PANGO_SCALE,
733                                 (curs.width - PANGO_SCALE/2) / PANGO_SCALE,
734                                 (curs.height - PANGO_SCALE/2) / PANGO_SCALE);
735                 cairo_set_line_width(ctx, 1.0);
736                 cairo_stroke(ctx);
737
738                 while (in_focus && f->parent->parent != f &&
739                        f->parent != ci->home) {
740                         if (f->parent->focus != f && f->z >= 0)
741                                 in_focus = False;
742                         f = f->parent;
743                 }
744                 if (in_focus) {
745                         if (fg.g >= 0)
746                                 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
747                         cairo_rectangle(ctx, x+curs.x/PANGO_SCALE,
748                                         y-baseline+curs.y/PANGO_SCALE,
749                                         curs.width / PANGO_SCALE,
750                                         curs.height / PANGO_SCALE);
751                         cairo_fill(ctx);
752                         if (ci->num < (int)strlen(str)) {
753                                 const char *cp = str + ci->num;
754                                 get_utf8(&cp, NULL);
755                                 pango_layout_set_text(layout, str + ci->num,
756                                                       cp - (str + ci->num));
757                                 if (bg.g >= 0)
758                                         cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
759                                 else
760                                         cairo_set_source_rgb(ctx, 1.0, 1.0, 1.0);
761                                 cairo_move_to(ctx,
762                                               x + curs.x / PANGO_SCALE,
763                                               y - baseline + curs.y / PANGO_SCALE);
764                                 pango_cairo_show_layout(ctx, layout);
765                         }
766                 }
767         }
768         cairo_restore(ctx);
769         pango_font_description_free(fd);
770         g_object_unref(layout);
771         return 1;
772 }
773
774 DEF_CMD(xcb_draw_image)
775 {
776         /* 'str' identifies the image. Options are:
777          *     file:filename  - load file from fs
778          *     comm:command   - run command collecting bytes
779          * 'num' is '1' if image should be stretched to fill pane
780          * if 'num is '0', then 'num2' is 'or' of
781          *   0,1,2 for left/middle/right in x direction
782          *   0,4,8 for top/middle/bottom in y direction
783          * only one of these can be used as image will fill pane in other direction.
784          */
785         struct xcb_data *xd = ci->home->data;
786         bool stretch = ci->num == 1;
787         int pos = ci->num2;
788         int w = ci->focus->w, h = ci->focus->h;
789         int x = 0, y = 0;
790         int xo, yo;
791         int stride;
792         struct panes *ps;
793         MagickBooleanType status;
794         MagickWand *wd;
795         int fmt[2];
796         unsigned char *buf;
797         cairo_surface_t *surface;
798
799         if (!ci->str)
800                 return Enoarg;
801         ps = find_pixmap(xd, ci->focus, &xo, &yo);
802         if (!ps)
803                 return Einval;
804         if (!ps->ctx)
805                 instantiate_pixmap(xd, ps);
806         ps->bg.g = -1;
807         if (!ps->ctx)
808                 return Efail;
809         if (strncmp(ci->str, "file:", 5) == 0) {
810                 wd = NewMagickWand();
811                 status = MagickReadImage(wd, ci->str + 5);
812                 if (status == MagickFalse) {
813                         DestroyMagickWand(wd);
814                         return Efail;
815                 }
816         } else if (strncmp(ci->str, "comm:", 5) == 0) {
817                 struct call_return cr;
818                 wd = NewMagickWand();
819                 cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
820                 if (!cr.s) {
821                         DestroyMagickWand(wd);
822                         return Efail;
823                 }
824                 status = MagickReadImageBlob(wd, cr.s, cr.i);
825                 free(cr.s);
826                 if (status == MagickFalse) {
827                         DestroyMagickWand(wd);
828                         return Efail;
829                 }
830         } else
831                 return Einval;
832
833         MagickAutoOrientImage(wd);
834         if (!stretch) {
835                 int ih = MagickGetImageHeight(wd);
836                 int iw = MagickGetImageWidth(wd);
837
838                 if (iw <= 0 || iw <= 0) {
839                         DestroyMagickWand(wd);
840                         return Efail;
841                 }
842                 if (iw * h > ih * w) {
843                         /* Image is wider than space, use less height */
844                         ih = ih * w / iw;
845                         switch(pos & (8+4)) {
846                         case 4: /* center */
847                                 y = (h - ih) / 2; break;
848                         case 8: /* bottom */
849                                 y = h - ih; break;
850                         }
851                         h = ih;
852                 } else {
853                         /* image is too tall, use less width */
854                         iw = iw * h / ih;
855                         switch (pos & (1+2)) {
856                         case 1: /* center */
857                                 x = (w - iw) / 2; break;
858                         case 2: /* right */
859                                 x = w - iw ; break;
860                         }
861                         w = iw;
862                 }
863         }
864         MagickAdaptiveResizeImage(wd, w, h);
865         stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
866         buf = malloc(h * stride);
867         // Cairo expects 32bit values with A in the high byte, then RGB.
868         // Magick provides 8bit values in the order requests.
869         // So depending on byte order, a different string is needed
870
871         fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
872         fmt[1] = 0;
873         MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
874         surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
875                                                       w, h, stride);
876         cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
877         cairo_paint(ps->ctx);
878         cairo_surface_destroy(surface);
879         free(buf);
880         DestroyMagickWand(wd);
881
882         pane_damaged(ci->home, DAMAGED_POSTORDER);
883
884         return 1;
885 }
886
887 static struct panes *sort_split(struct panes *p)
888 {
889         /* consider 'p' to be a list of panes with
890          * ordered subsets (ordered by p->abs_z).
891          * Remove every other such subset and return them
892          * linked together.
893          * If p is ordered, this means we return NULL.
894          */
895         struct panes *ret, **end = &ret;
896         struct panes *next;
897
898         for (; p && p->next; p = next) {
899                 /* If these are not ordered, attach p->next at
900                  * 'end', and make 'end' point to &p->next.
901                  */
902                 next = p->next;
903                 if (p->p->abs_z < next->p->abs_z) {
904                         *end = next;
905                         end = &p->next;
906                 }
907         }
908         *end = NULL;
909         return ret;
910 }
911
912 static struct panes *sort_merge(struct panes *p1, struct panes *p2)
913 {
914         /* merge p1 and p2 and return result */
915         struct panes *ret, **end = &ret;
916         struct panes *prev = NULL;
917
918         while (p1 && p2) {
919                 /* Make p1 the largest (or be added first.
920                  * Then in prev is between them add p2, else p1
921                  */
922                 if (p1->p->abs_z < p2->p->abs_z) {
923                         struct panes *t = p1;
924                         p1 = p2;
925                         p2 = t;
926                 }
927                 if (prev &&
928                     p1->p->abs_z > prev->p->abs_z &&
929                     prev->p->abs_z >= p2->p->abs_z) {
930                         /* p2 is the better choice */
931                         prev = p2;
932                         p2 = p2->next;
933                 } else {
934                         prev = p1;
935                         p1 = p1->next;
936                 }
937                 *end = prev;
938                 end = &prev->next;
939         }
940         if (p1)
941                 *end = p1;
942         else
943                 *end = p2;
944         return ret;
945 }
946
947 DEF_CMD(xcb_refresh_post)
948 {
949         struct xcb_data *xd = ci->home->data;
950         struct panes *ps;
951
952         time_start(TIME_WINDOW);
953         /* First: ensure panes are sorted */
954         while ((ps = sort_split(xd->panes)) != NULL)
955                 xd->panes = sort_merge(xd->panes, ps);
956
957         /* Then merge all update rectanges, checking for movement */
958         if (!xd->need_update)
959                 xd->need_update = cairo_region_create();
960         for (ps = xd->panes; ps ; ps = ps->next)
961         {
962                 struct xy rel;
963
964                 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
965                 if (ps->r.x == NEVER_DRAWN) {
966                         ps->r.x = rel.x;
967                         ps->r.y = rel.y;
968                         cairo_region_union_rectangle(xd->need_update, &ps->r);
969                 } else if (rel.x != ps->r.x || rel.y != ps->r.y) {
970                         /* Moved, so refresh all.
971                          * This rectangle might be too big if it is clipped,
972                          * but that doesn't really matter.
973                          */
974                         cairo_region_union_rectangle(xd->need_update, &ps->r);
975                         ps->r.x = rel.x;
976                         ps->r.y = rel.y;
977                         cairo_region_union_rectangle(xd->need_update, &ps->r);
978                 } else if (ps->need_update) {
979                         cairo_region_translate(ps->need_update, rel.x, rel.y);
980                         cairo_region_union(xd->need_update, ps->need_update);
981                 }
982                 if (ps->need_update)
983                         cairo_region_destroy(ps->need_update);
984                 ps->need_update = NULL;
985         }
986         /* Now copy all panes onto the window where an update is needed */
987         for (ps = xd->panes; ps ; ps = ps->next) {
988                 struct xy rel, lo, hi;
989                 cairo_region_t *cr;
990                 cairo_rectangle_int_t r;
991                 int nr, i;
992
993                 cr = cairo_region_copy(xd->need_update);
994
995                 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
996
997                 cairo_save(xd->cairo);
998                 if (ps->bg.g >= 0)
999                         cairo_set_source_rgb(xd->cairo,
1000                                              ps->bg.r, ps->bg.g, ps->bg.b);
1001                 else
1002                         cairo_set_source_surface(xd->cairo, ps->surface,
1003                                                  rel.x, rel.y);
1004
1005                 lo = pane_mapxy(ps->p, ci->home, 0, 0, True);
1006                 hi = pane_mapxy(ps->p, ci->home, ps->r.width, ps->r.height, True);
1007                 r.x = lo.x; r.y = lo.y;
1008                 r.width = hi.x - lo.x; r.height = hi.y - lo.y;
1009                 cairo_region_intersect_rectangle(cr, &r);
1010                 cairo_region_subtract_rectangle(xd->need_update, &r);
1011                 nr = cairo_region_num_rectangles(cr);
1012                 for (i = 0; i < nr; i++) {
1013                         cairo_region_get_rectangle(cr, i, &r);
1014                         cairo_rectangle(xd->cairo, r.x, r.y, r.width, r.height);
1015                         cairo_fill(xd->cairo);
1016                 }
1017                 cairo_restore(xd->cairo);
1018         }
1019
1020         cairo_region_destroy(xd->need_update);
1021         xd->need_update = NULL;
1022         time_stop(TIME_WINDOW);
1023         xcb_flush(xd->conn);
1024         return 1;
1025 }
1026
1027 DEF_CMD(xcb_refresh_size)
1028 {
1029         /* FIXME: should I consider resizing the window?
1030          * For now, just ensure we redraw everything.
1031          */
1032         struct xcb_data *xd = ci->home->data;
1033         cairo_rectangle_int_t r = {
1034                 .x = 0,
1035                 .y = 0,
1036                 .width = ci->home->w,
1037                 .height = ci->home->h,
1038         };
1039
1040         if (!xd->need_update)
1041                 xd->need_update = cairo_region_create();
1042         cairo_region_union_rectangle(xd->need_update, &r);
1043         /* Ask common code to notify children */
1044         return Efallthrough;
1045 }
1046
1047 DEF_CMD(xcb_pane_close)
1048 {
1049         struct xcb_data *xd = ci->home->data;
1050         struct panes **pp, *ps;
1051
1052         for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
1053                 if (ps->p != ci->focus)
1054                         continue;
1055
1056                 if (!xd->need_update)
1057                         xd->need_update = cairo_region_create();
1058                 if (ps->r.x != NEVER_DRAWN)
1059                         cairo_region_union_rectangle(xd->need_update, &ps->r);
1060
1061                 *pp = ps->next;
1062                 ps->next = NULL;
1063                 if (ps->need_update)
1064                         cairo_region_destroy(ps->need_update);
1065                 cairo_destroy(ps->ctx);
1066                 cairo_surface_destroy(ps->surface);
1067                 xcb_free_pixmap(xd->conn, ps->draw);
1068                 free(ps);
1069                 pane_damaged(ci->home, DAMAGED_POSTORDER);
1070                 break;
1071         }
1072         return 1;
1073 }
1074
1075 DEF_CMD(xcb_notify_display)
1076 {
1077         struct xcb_data *xd = ci->home->data;
1078         comm_call(ci->comm2, "callback:display", ci->home, xd->last_event);
1079         return 1;
1080 }
1081
1082 static void handle_button(struct pane *home safe,
1083                           xcb_button_press_event_t *be safe)
1084 {
1085         struct xcb_data *xd = home->data;
1086         bool press = (be->response_type & 0x7f) == XCB_BUTTON_PRESS;
1087         char mod[2+2+2+1];
1088         char key[2+2+2+9+1+1];
1089
1090         xcb_set_input_focus(xd->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
1091                             xd->win, XCB_CURRENT_TIME);
1092         mod[0] = 0;
1093         if (press) {
1094                 xd->motion_blocked = False;
1095                 if (be->state & XCB_KEY_BUT_MASK_MOD_1)
1096                         strcat(mod, ":A");
1097                 if (be->state & XCB_KEY_BUT_MASK_CONTROL)
1098                         strcat(mod, ":C");
1099                 if (be->state & XCB_KEY_BUT_MASK_SHIFT)
1100                         strcat(mod, ":S");
1101                 strcpy(key, mod);
1102                 strcat(key, ":Press-X");
1103         } else if (be->detail >= 4)
1104                 /* ignore 'release' for scroll wheel */
1105                 return;
1106         else
1107                 strcpy(key, ":Release-X");
1108
1109         key[strlen(key) - 1] = '0' + be->detail;
1110         xd->last_event = time(NULL);
1111         call("Mouse-event", home, be->detail, NULL, key,
1112              press?1:2, NULL, mod,
1113              be->event_x, be->event_y);
1114 }
1115
1116 static void handle_motion(struct pane *home safe,
1117                           xcb_motion_notify_event_t *mne safe)
1118 {
1119         struct xcb_data *xd = home->data;
1120         xcb_query_pointer_cookie_t c;
1121         xcb_query_pointer_reply_t *qpr;
1122         int ret;
1123         int x = mne->event_x, y = mne->event_y;
1124
1125         if (xd->motion_blocked)
1126                 return;
1127         ret = call("Mouse-event", home, 0, NULL, ":Motion",
1128                    3, NULL, NULL, x, y);
1129         if (ret <= 0)
1130                 xd->motion_blocked = True;
1131
1132         /* This doesn't seem to be needed, but the spec says
1133          * I should do this when using POINTER_MOTION_HINT
1134          */
1135         c = xcb_query_pointer(xd->conn, xd->win);
1136         qpr = xcb_query_pointer_reply(xd->conn, c, NULL);
1137         free(qpr);
1138 }
1139
1140 static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
1141 {
1142         struct xcb_data *xd = home->data;
1143         bool in = (fie->response_type & 0x7f) == XCB_FOCUS_IN;
1144         struct pane *p;
1145         struct mark *pt;
1146
1147         xd->in_focus = in;
1148         p = pane_leaf(home);
1149         pt = call_ret(mark, "doc:point", p);
1150         if (pt)
1151                 call("view:changed", p, 0, pt);
1152         if (in)
1153                 call("pane:refocus", home);
1154 }
1155
1156 static bool select_xkb_events_for_device(xcb_connection_t *conn,
1157                                          int32_t device_id)
1158 {
1159         xcb_generic_error_t *error;
1160         xcb_void_cookie_t cookie;
1161
1162         enum {
1163                 required_events =
1164                         (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
1165                          XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
1166                          XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
1167
1168                 required_nkn_details =
1169                         (XCB_XKB_NKN_DETAIL_KEYCODES),
1170
1171                 required_map_parts =
1172                         (XCB_XKB_MAP_PART_KEY_TYPES |
1173                          XCB_XKB_MAP_PART_KEY_SYMS |
1174                          XCB_XKB_MAP_PART_MODIFIER_MAP |
1175                          XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
1176                          XCB_XKB_MAP_PART_KEY_ACTIONS |
1177                          XCB_XKB_MAP_PART_VIRTUAL_MODS |
1178                          XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
1179
1180                 required_state_details =
1181                         (XCB_XKB_STATE_PART_MODIFIER_BASE |
1182                          XCB_XKB_STATE_PART_MODIFIER_LATCH |
1183                          XCB_XKB_STATE_PART_MODIFIER_LOCK |
1184                          XCB_XKB_STATE_PART_GROUP_BASE |
1185                          XCB_XKB_STATE_PART_GROUP_LATCH |
1186                          XCB_XKB_STATE_PART_GROUP_LOCK),
1187         };
1188
1189         static const xcb_xkb_select_events_details_t details = {
1190                 .affectNewKeyboard = required_nkn_details,
1191                 .newKeyboardDetails = required_nkn_details,
1192                 .affectState = required_state_details,
1193                 .stateDetails = required_state_details,
1194         };
1195
1196         cookie = xcb_xkb_select_events_aux_checked(
1197                 conn,
1198                 device_id,
1199                 required_events,        /* affectWhich */
1200                 0,                      /* clear */
1201                 0,                      /* selectAll */
1202                 required_map_parts,     /* affectMap */
1203                 required_map_parts,     /* map */
1204                 &details);              /* details */
1205
1206         error = xcb_request_check(conn, cookie);
1207         if (error) {
1208                 free(error);
1209                 return False;
1210         }
1211
1212         return True;
1213 }
1214
1215 static bool update_keymap(struct xcb_data *xd safe)
1216 {
1217         struct xkb_keymap *new_keymap;
1218         struct xkb_state *new_state;
1219
1220         new_keymap = xkb_x11_keymap_new_from_device(xd->xkb, xd->conn,
1221                                                     xd->xkb_device_id,
1222                                                     XKB_KEYMAP_COMPILE_NO_FLAGS);
1223         if (!new_keymap)
1224                 return False;
1225
1226         new_state = xkb_x11_state_new_from_device(new_keymap, xd->conn,
1227                                                   xd->xkb_device_id);
1228         if (!new_state) {
1229                 xkb_keymap_unref(new_keymap);
1230                 return False;
1231         }
1232
1233         xkb_state_unref(xd->xkb_state);
1234         xkb_keymap_unref(xd->xkb_keymap);
1235         xd->xkb_keymap = new_keymap;
1236         xd->xkb_state = new_state;
1237         return True;
1238 }
1239
1240 static bool kbd_setup(struct xcb_data *xd safe)
1241 {
1242         int ret;
1243         const char *locale;
1244
1245         ret = xkb_x11_setup_xkb_extension(xd->conn,
1246                                           XKB_X11_MIN_MAJOR_XKB_VERSION,
1247                                           XKB_X11_MIN_MINOR_XKB_VERSION,
1248                                           XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
1249                                           NULL, NULL, &xd->first_xkb_event,
1250                                           NULL);
1251
1252         if (!ret)
1253                 return False;
1254         xd->xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1255         if (!xd->xkb)
1256                 return False;
1257         xd->xkb_device_id = xkb_x11_get_core_keyboard_device_id(xd->conn);
1258         if (xd->xkb_device_id == -1)
1259                 return False;
1260
1261         if (!update_keymap(xd))
1262                 return False;
1263
1264         if (!select_xkb_events_for_device(xd->conn, xd->xkb_device_id))
1265                 return False;
1266
1267         locale = setlocale(LC_CTYPE, NULL);
1268         xd->compose_table =
1269                 xkb_compose_table_new_from_locale(xd->xkb, locale,
1270                                                   XKB_COMPOSE_COMPILE_NO_FLAGS);
1271         if (xd->compose_table)
1272                 xd->compose_state =
1273                         xkb_compose_state_new(xd->compose_table,
1274                                               XKB_COMPOSE_STATE_NO_FLAGS);
1275         return True;
1276 }
1277
1278 static void kbd_free(struct xcb_data *xd safe)
1279 {
1280         if (xd->compose_table)
1281                 xkb_compose_table_unref(xd->compose_table);
1282         if (xd->xkb_keymap)
1283                 xkb_keymap_unref(xd->xkb_keymap);
1284         if (xd->xkb)
1285                 xkb_context_unref(xd->xkb);
1286 }
1287
1288 static struct {
1289         char *from safe, *to safe;
1290 } key_map[] = {
1291         { "Return",      ":Enter"},
1292         { "Tab",         ":Tab"},
1293         { "ISO_Left_Tab",":Tab"},
1294         { "Escape",      ":ESC"},
1295         { "Linefeed",    ":LF"},
1296         { "Down",        ":Down"},
1297         { "Up",          ":Up"},
1298         { "Left",        ":Left"},
1299         { "Right",       ":Right"},
1300         { "Home",        ":Home"},
1301         { "End",         ":End"},
1302         { "BackSpace",   ":Backspace"},
1303         { "Delete",      ":Del"},
1304         { "Insert",      ":Ins"},
1305         { "Next",        ":Prior"},
1306         { "Prior",       ":Next"},
1307         { "F1",          ":F1"},
1308         { "F2",          ":F2"},
1309         { "F3",          ":F3"},
1310         { "F4",          ":F4"},
1311         { "F5",          ":F5"},
1312         { "F6",          ":F6"},
1313         { "F7",          ":F7"},
1314         { "F8",          ":F8"},
1315         { "F9",          ":F9"},
1316         { "F10",         ":F11"},
1317         { "F11",         ":F11"},
1318         { "F12",         ":F12"},
1319 };
1320
1321 static void handle_key_press(struct pane *home safe,
1322                              xcb_key_press_event_t *kpe safe)
1323 {
1324         struct xcb_data                 *xd = home->data;
1325         xkb_keycode_t                   keycode = kpe->detail;
1326         xcb_keysym_t                    keysym;
1327         xkb_keysym_t                    sym;
1328         const xkb_keysym_t              *syms;
1329         int                             nsyms;
1330         enum xkb_compose_status         status;
1331         xkb_mod_index_t                 mod;
1332         char                            s[16];
1333         char                            key[16];
1334         char                            mods[32];
1335         bool                            shift=False, ctrl=False, alt=False;
1336
1337         xd->last_event = time(NULL);
1338
1339         keysym = xkb_state_key_get_one_sym(xd->xkb_state,
1340                                            keycode);
1341         if (xd->compose_state)
1342                 xkb_compose_state_feed(xd->compose_state, keysym);
1343         nsyms = xkb_state_key_get_syms(xd->xkb_state, keycode,
1344                                        &syms);
1345         if (nsyms <= 0)
1346                 return;
1347         status = XKB_COMPOSE_NOTHING;
1348         if (xd->compose_state)
1349                 status = xkb_compose_state_get_status(xd->compose_state);
1350         if (status == XKB_COMPOSE_COMPOSING ||
1351             status == XKB_COMPOSE_CANCELLED)
1352                 return;
1353
1354         for (mod = 0; mod < xkb_keymap_num_mods(xd->xkb_keymap); mod++) {
1355                 const char *n;
1356                 if (xkb_state_mod_index_is_active(
1357                             xd->xkb_state, mod,
1358                             XKB_STATE_MODS_EFFECTIVE) <= 0)
1359                         continue;
1360                 /* This does tells me "shift" is consumed for :C:S-l ...
1361                 if (xkb_state_mod_index_is_consumed2(
1362                             xd->xkb_state, keycode, mod,
1363                             XKB_CONSUMED_MODE_XKB))
1364                         continue;
1365                  */
1366                 n = xkb_keymap_mod_get_name(xd->xkb_keymap, mod);
1367                 if (n && strcmp(n, "Shift") == 0)
1368                         shift = True;
1369                 if (n && strcmp(n, "Control") == 0)
1370                         ctrl = True;
1371                 if (n && strcmp(n, "Mod1") == 0)
1372                         alt = True;
1373         }
1374
1375         if (status == XKB_COMPOSE_COMPOSED) {
1376                 sym = xkb_compose_state_get_one_sym(xd->compose_state);
1377                 syms = &sym;
1378                 nsyms = 1;
1379                 s[0] = '-';
1380                 xkb_compose_state_get_utf8(xd->compose_state,
1381                                            s+1, sizeof(s)-1);
1382                 key[0] = 0;
1383                 shift = False;
1384                 ctrl = False;
1385                 /* Mod1 can still apply to a composed char */
1386         } else if (nsyms == 1) {
1387                 unsigned int i;
1388                 sym = xkb_state_key_get_one_sym(xd->xkb_state, keycode);
1389                 syms = &sym;
1390                 s[0] = '-';
1391                 xkb_state_key_get_utf8(xd->xkb_state, keycode,
1392                                        s+1, sizeof(s)-1);
1393                 xkb_keysym_get_name(syms[0], key, sizeof(key));
1394                 for (i = 0; i < ARRAY_SIZE(key_map); i++) {
1395                         if (strcmp(key, key_map[i].from) == 0) {
1396                                 strcpy(s, key_map[i].to);
1397                                 break;
1398                         }
1399                 }
1400                 if (s[0] == '-' && s[1] >= ' ' && s[1] < 0x7f)
1401                         /* Shift is included */
1402                         shift = False;
1403                 if (s[0] == '-' && s[1] && (unsigned char)(s[1]) < ' ') {
1404                         ctrl = True;
1405                         s[1] += '@';
1406                         if (s[1] < 'A' || s[1] > 'Z')
1407                                 shift = False;
1408                 } else if (s[0] == '-' && !s[1] && strcmp(key, "space") == 0) {
1409                         /* 'nul' becomes "C- " (ctrl-space) */
1410                         ctrl = True;
1411                         s[1] = ' ';
1412                         s[2] = 0;
1413                 }
1414         }
1415
1416         if (xd->compose_state &&
1417             (status == XKB_COMPOSE_CANCELLED ||
1418              status == XKB_COMPOSE_COMPOSED))
1419                 xkb_compose_state_reset(xd->compose_state);
1420
1421         if (s[1]) {
1422                 mods[0] = 0;
1423                 if (alt)
1424                         strcat(mods, ":A");
1425                 if (ctrl)
1426                         strcat(mods, ":C");
1427                 if (shift)
1428                         strcat(mods, ":S");
1429                 strcat(mods, s);
1430                 call("Keystroke", home, 0, NULL, mods);
1431         }
1432 }
1433
1434 static void handle_xkb_event(struct pane *home safe,
1435                              xcb_generic_event_t *ev safe)
1436 {
1437         struct xcb_data *xd = home->data;
1438
1439         switch (ev->pad0) {
1440                 xcb_xkb_new_keyboard_notify_event_t     *nkne;
1441                 xcb_xkb_state_notify_event_t            *sne;
1442                 xcb_xkb_map_notify_event_t              *mne;
1443         case XCB_XKB_NEW_KEYBOARD_NOTIFY:
1444                 nkne = (void*)ev;
1445                 if (nkne->deviceID == xd->xkb_device_id &&
1446                     nkne->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
1447                         update_keymap(xd);
1448                 break;
1449         case XCB_XKB_MAP_NOTIFY:
1450                 mne = (void*)ev;
1451                 if (mne->deviceID == xd->xkb_device_id)
1452                         update_keymap(xd);
1453                 break;
1454         case XCB_XKB_STATE_NOTIFY:
1455                 sne = (void*)ev;
1456                 if (sne->deviceID == xd->xkb_device_id)
1457                         xkb_state_update_mask(xd->xkb_state,
1458                                               sne->baseMods,
1459                                               sne->latchedMods,
1460                                               sne->lockedMods,
1461                                               sne->baseGroup,
1462                                               sne->latchedGroup,
1463                                               sne->lockedGroup);
1464                 break;
1465         }
1466 }
1467
1468 static void handle_configure(struct pane *home safe,
1469                              xcb_configure_notify_event_t *cne safe)
1470 {
1471         struct xcb_data *xd = home->data;
1472
1473         pane_resize(home, 0, 0, cne->width, cne->height);
1474         cairo_xcb_surface_set_size(xd->surface, cne->width, cne->height);
1475 }
1476
1477 static void handle_expose(struct pane *home safe,
1478                           xcb_expose_event_t *ee safe)
1479 {
1480         struct xcb_data *xd = home->data;
1481         cairo_rectangle_int_t r = {
1482                 .x = ee->x,
1483                 .y = ee->y,
1484                 .width = ee->width,
1485                 .height = ee->height,
1486         };
1487
1488         if (!xd->need_update)
1489                 xd->need_update = cairo_region_create();
1490         cairo_region_union_rectangle(xd->need_update, &r);
1491         if (ee->count == 0)
1492                 pane_damaged(home, DAMAGED_POSTORDER);
1493 }
1494
1495 static void handle_client_message(struct pane *home safe,
1496                                   xcb_client_message_event_t *cme safe)
1497 {
1498         struct xcb_data *xd = home->data;
1499
1500         if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1501             cme->format == 32 &&
1502             cme->window == xd->win &&
1503             cme->data.data32[0] == xd->atoms[a_WM_DELETE_WINDOW]) {
1504                 pane_call(home, "Display:close", pane_leaf(home));
1505                 return;
1506         }
1507
1508         if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1509             cme->format == 32 &&
1510             cme->window == xd->win &&
1511             cme->data.data32[0] == xd->atoms[a_NET_WM_PING]) {
1512                 cme->window = xd->screen->root;
1513                 xcb_send_event(xd->conn, 0, xd->screen->root,
1514                                XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1515                                XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1516                                (void*)cme);
1517                 return;
1518         }
1519         LOG("x11 %s got unexpected client message type=%d/%d win=%x data=%d",
1520             xd->display,
1521             cme->type, cme->format, cme->window, cme->data.data32[0]);
1522
1523 }
1524
1525 DEF_CMD(xcb_input)
1526 {
1527         struct xcb_data *xd = ci->home->data;
1528         xcb_generic_event_t *ev;
1529         int ret = 1;
1530
1531         if (ci->num < 0)
1532                 /* This is a poll - only return 1 on something happening */
1533                 ret = Efalse;
1534
1535         while ((ev = xcb_poll_for_event(xd->conn)) != NULL) {
1536                 ret = 1;
1537                 switch (ev->response_type & 0x7f) {
1538                 case XCB_KEY_PRESS:
1539                         time_start(TIME_KEY);
1540                         handle_key_press(ci->home, safe_cast (void*)ev);
1541                         time_stop(TIME_KEY);
1542                         break;
1543                 case XCB_KEY_RELEASE:
1544                         /* Ignore for now */
1545                         break;
1546                 case XCB_BUTTON_PRESS:
1547                 case XCB_BUTTON_RELEASE:
1548                         time_start(TIME_KEY);
1549                         handle_button(ci->home, (void*)ev);
1550                         time_stop(TIME_KEY);
1551                         break;
1552                 case XCB_MOTION_NOTIFY:
1553                         time_start(TIME_KEY);
1554                         handle_motion(ci->home, (void*)ev);
1555                         time_stop(TIME_KEY);
1556                         break;
1557                 case XCB_FOCUS_IN:
1558                 case XCB_FOCUS_OUT:
1559                         time_start(TIME_WINDOW);
1560                         handle_focus(ci->home, (void*)ev);
1561                         time_stop(TIME_WINDOW);
1562                         break;
1563                 case XCB_EXPOSE:
1564                         time_start(TIME_WINDOW);
1565                         handle_expose(ci->home, (void*)ev);
1566                         time_stop(TIME_WINDOW);
1567                         break;
1568                 case XCB_CONFIGURE_NOTIFY:
1569                         time_start(TIME_WINDOW);
1570                         handle_configure(ci->home, (void*)ev);
1571                         time_stop(TIME_WINDOW);
1572                         break;
1573                 case XCB_CLIENT_MESSAGE:
1574                         time_start(TIME_WINDOW);
1575                         handle_client_message(ci->home, (void*)ev);
1576                         time_stop(TIME_WINDOW);
1577                         break;
1578                 case XCB_REPARENT_NOTIFY:
1579                         /* Not interested */
1580                         break;
1581                 case XCB_MAP_NOTIFY:
1582                 case XCB_UNMAP_NOTIFY:
1583                 case XCB_MAPPING_NOTIFY:
1584                         /* FIXME what to do?? */
1585                         break;
1586                 case 0:
1587                         /* Don't know what this means, but I get a lot
1588                          * of them so I don't want to log that it was
1589                          * ignored.
1590                          */
1591                         break;
1592                 default:
1593                         if ((ev->response_type & 0x7f) ==
1594                             xd->first_xkb_event) {
1595                                 handle_xkb_event(ci->home, ev);
1596                                 break;
1597                         }
1598                         LOG("Ignored X11 event %d", ev->response_type);
1599                 }
1600                 xcb_flush(xd->conn);
1601         }
1602         if (xcb_connection_has_error(xd->conn))
1603                 pane_close(ci->home);
1604         return ret;
1605 }
1606
1607 static void set_str_prop(struct xcb_data *xd safe,
1608                          enum my_atoms a, const char *str safe)
1609 {
1610         xcb_change_property(xd->conn,
1611                             XCB_PROP_MODE_REPLACE,
1612                             xd->win, xd->atoms[a], XCB_ATOM_STRING,
1613                             8, strlen(str), str);
1614 }
1615
1616 static void set_utf8_prop(struct xcb_data *xd safe,
1617                          enum my_atoms a, const char *str safe)
1618 {
1619         xcb_change_property(xd->conn,
1620                             XCB_PROP_MODE_REPLACE,
1621                             xd->win, xd->atoms[a],
1622                             xd->atoms[a_UTF8_STRING],
1623                             8, strlen(str), str);
1624 }
1625
1626 static void set_atom_prop(struct xcb_data *xd safe,
1627                           enum my_atoms prop, enum my_atoms alist, ...)
1628 {
1629         uint32_t atoms[16];
1630         int anum = 0;
1631         va_list ap;
1632         enum my_atoms a;
1633
1634         atoms[anum++] = xd->atoms[alist];
1635         va_start(ap, alist);
1636         while ((a = va_arg(ap, enum my_atoms)) != a_NONE)
1637                 if (anum < 16)
1638                         atoms[anum++] = xd->atoms[a];
1639         va_end(ap);
1640         xcb_change_property(xd->conn,
1641                             XCB_PROP_MODE_REPLACE,
1642                             xd->win, xd->atoms[prop],
1643                             XCB_ATOM_ATOM,
1644                             32, anum, atoms);
1645 }
1646
1647 static struct pane *xcb_display_init(const char *d safe, struct pane *focus safe)
1648 {
1649         struct xcb_data *xd;
1650         struct pane *p;
1651         xcb_connection_t *conn;
1652         xcb_intern_atom_cookie_t cookies[NR_ATOMS];
1653         xcb_screen_iterator_t iter;
1654         xcb_depth_iterator_t di;
1655         char scale[20];
1656         char hostname[128];
1657         uint32_t valwin[2];
1658         int screen = 0;
1659         int i;
1660         PangoLayout *layout;
1661         PangoRectangle log;
1662         cairo_t *cairo;
1663         cairo_surface_t *surface;
1664         PangoFontDescription *fd;
1665         // FIXME SCALE from environ?? or pango_cairo_context_set_resolution dpi
1666         // 254 * width_in_pixels / width_in_millimeters / 10
1667
1668         conn = safe_cast xcb_connect(d, &screen);
1669         if (xcb_connection_has_error(conn))
1670                 return NULL;
1671
1672         alloc(xd, pane);
1673
1674         xd->motion_blocked = True;
1675         xd->in_focus = True;
1676
1677         xd->conn = conn;
1678         xd->display = strdup(d);
1679         xd->setup = safe_cast xcb_get_setup(conn);
1680         iter = xcb_setup_roots_iterator(xd->setup);
1681         for (i = 0; i < screen; i++)
1682                 xcb_screen_next(&iter);
1683         xd->screen = safe_cast iter.data;
1684
1685         di = xcb_screen_allowed_depths_iterator(xd->screen);
1686         while (di.data && di.data->depth < 24)
1687                 xcb_depth_next(&di);
1688         //?? look for class = TrueColor??
1689         xd->visual = xcb_depth_visuals(di.data);
1690
1691         for (i = 0; i < NR_ATOMS; i++) {
1692                 char *n = atom_names[i];
1693                 if (!n)
1694                         continue;
1695                 cookies[i] = xcb_intern_atom(conn, 0, strlen(n), n);
1696         }
1697
1698         xd->win = xcb_generate_id(conn);
1699         valwin[0] = xd->screen->white_pixel;
1700         valwin[1] = (XCB_EVENT_MASK_KEY_PRESS |
1701                      XCB_EVENT_MASK_KEY_RELEASE |
1702                      XCB_EVENT_MASK_BUTTON_PRESS |
1703                      XCB_EVENT_MASK_BUTTON_RELEASE |
1704                      // XCB_EVENT_MASK_ENTER_WINDOW |
1705                      // XCB_EVENT_MASK_LEAVE_WINDOW |
1706                      XCB_EVENT_MASK_FOCUS_CHANGE |
1707                      XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1708                      XCB_EVENT_MASK_EXPOSURE |
1709                      XCB_EVENT_MASK_BUTTON_MOTION |
1710                      XCB_EVENT_MASK_POINTER_MOTION_HINT |
1711                      0);
1712
1713         xcb_create_window(conn, XCB_COPY_FROM_PARENT, xd->win,
1714                           xd->screen->root,
1715                           0, 0,
1716                           100, 100,
1717                           0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
1718                           xd->screen->root_visual,
1719                           XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
1720                           valwin);
1721         xcb_flush(conn);
1722         kbd_setup(xd);
1723
1724         surface = cairo_xcb_surface_create(
1725                 conn, xd->win, xd->visual, 100, 100);
1726         if (!surface)
1727                 goto abort;
1728         xd->surface = surface;
1729         cairo = safe_cast cairo_create(xd->surface);
1730         if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS)
1731                 goto abort;
1732         xd->cairo = cairo;
1733         fd = pango_font_description_new();
1734         if (!fd)
1735                 goto abort;
1736         xd->fd = fd;
1737         pango_font_description_set_family(xd->fd, "mono");
1738         pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
1739
1740         layout = pango_cairo_create_layout(xd->cairo);
1741         pango_layout_set_font_description(layout, fd);
1742         pango_layout_set_text(layout, "M", 1);
1743         pango_layout_get_pixel_extents(layout, NULL, &log);
1744         g_object_unref(layout);
1745         xd->lineheight = log.height;
1746         xd->charwidth = log.width;
1747
1748         valwin[0] = xd->charwidth * 80;
1749         valwin[1] = xd->lineheight * 26;
1750         xcb_configure_window(conn, xd->win,
1751                              XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
1752                              valwin);
1753         cairo_xcb_surface_set_size(xd->surface, valwin[0], valwin[1]);
1754
1755         /* Now resolve all those cookies */
1756         for (i = 0; i < NR_ATOMS; i++) {
1757                 xcb_intern_atom_reply_t *r;
1758                 r = xcb_intern_atom_reply(conn, cookies[i], NULL);
1759                 if (!r)
1760                         goto abort;
1761                 xd->atoms[i] = r->atom;
1762                 free(r);
1763         }
1764
1765         /* FIXME set:
1766          *
1767          * WM_PROTOCOLS _NET_WM_SYN_REQUEST??
1768          * WM_NORMAL_HINTS WM_HINTS
1769          * WM_CLIENT_MACHINE
1770          */
1771         set_str_prop(xd, a_WM_NAME, "EdLib");
1772         set_utf8_prop(xd, a_NET_WM_NAME, "EdLib");
1773         set_str_prop(xd, a_WM_ICON_NAME, "EdLib");
1774         set_utf8_prop(xd, a_NET_WM_ICON_NAME, "EdLib");
1775         gethostname(hostname, sizeof(hostname));
1776         set_str_prop(xd, a_WM_CLIENT_MACHINE, hostname);
1777         set_atom_prop(xd, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW, a_NET_WM_PING, 0);
1778
1779         /* Configure passive grabs - shift, lock, and control only */
1780         xcb_grab_button(xd->conn, 0, xd->win,
1781                         XCB_EVENT_MASK_BUTTON_PRESS |
1782                         XCB_EVENT_MASK_BUTTON_RELEASE |
1783                         XCB_EVENT_MASK_BUTTON_MOTION,
1784                         XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
1785                         XCB_WINDOW_NONE, XCB_CURSOR_NONE,
1786                         XCB_BUTTON_INDEX_ANY,
1787                         XCB_MOD_MASK_SHIFT |
1788                         XCB_MOD_MASK_LOCK |
1789                         XCB_MOD_MASK_CONTROL);
1790
1791         xcb_map_window(conn, xd->win);
1792         xcb_flush(conn);
1793         p = pane_register(pane_root(focus), 1, &xcb_handle.c, xd);
1794         if (!p)
1795                 goto abort;
1796         pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
1797         call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
1798         call_comm("event:poll", p, &xcb_input);
1799         attr_set_str(&p->attrs, "DISPLAY", d);
1800         snprintf(scale, sizeof(scale), "%dx%d", xd->charwidth, xd->lineheight);
1801         attr_set_str(&p->attrs, "scale:M", scale);
1802         xd->last_event = time(NULL);
1803         call("editor:request:all-displays", p);
1804         return p;
1805 abort:
1806         kbd_free(xd);
1807         cairo_destroy(xd->cairo);
1808         cairo_surface_destroy(xd->surface);
1809         xcb_disconnect(conn);
1810         free(xd->display);
1811         unalloc(xd, pane);
1812         return NULL;
1813 }
1814
1815 DEF_CMD(display_xcb)
1816 {
1817         struct pane *p;
1818         const char *d = ci->str;
1819
1820         if (!d)
1821                 return Enoarg;
1822         p = xcb_display_init(d, ci->focus);
1823         if (p)
1824                 return comm_call(ci->comm2, "cb", p);
1825         return Efail;
1826 }
1827
1828 DEF_CMD(xcb_new_display)
1829 {
1830         struct pane *p;
1831         const char *d = ci->str;
1832
1833         if (!d)
1834                 d = pane_attr_get(ci->focus, "DISPLAY");
1835
1836         if (!d)
1837                 return Enoarg;
1838         p = xcb_display_init(d, ci->focus);
1839         if (p)
1840                 p = call_ret(pane, "editor:activate-display", p);
1841         if (p)
1842                 home_call_ret(pane, ci->focus, "doc:attach-view", p, 1);
1843         if (p)
1844                 comm_call(ci->comm2, "cb", p);
1845         return 1;
1846 }
1847
1848 void edlib_init(struct pane *ed safe)
1849 {
1850         call_comm("global-set-command", ed, &display_xcb, 0, NULL,
1851                   "attach-display-x11");
1852         call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
1853                   "interactive-cmd-x11window");
1854
1855         xcb_map = key_alloc();
1856
1857         key_add(xcb_map, "Display:close", &xcb_close_display);
1858         key_add(xcb_map, "Display:set-noclose", &xcb_set_noclose);
1859         key_add(xcb_map, "Display:external-viewer", &xcb_external_viewer);
1860         key_add(xcb_map, "Display:fullscreen", &xcb_fullscreen);
1861         key_add(xcb_map, "Display:new", &xcb_new_display);
1862
1863         key_add(xcb_map, "Close", &xcb_close);
1864         key_add(xcb_map, "Free", &xcb_free);
1865         key_add(xcb_map, "Draw:clear", &xcb_clear);
1866         key_add(xcb_map, "Draw:text-size", &xcb_text_size);
1867         key_add(xcb_map, "Draw:text", &xcb_draw_text);
1868         key_add(xcb_map, "Draw:image", &xcb_draw_image);
1869         key_add(xcb_map, "Refresh:size", &xcb_refresh_size);
1870         key_add(xcb_map, "Refresh:postorder", &xcb_refresh_post);
1871         key_add(xcb_map, "all-displays", &xcb_notify_display);
1872         key_add(xcb_map, "Notify:Close", &xcb_pane_close);
1873 }