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