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