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