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