]> git.neil.brown.name Git - edlib.git/blob - display-x11-xcb.c
display-x11-xcb - initial commit
[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 #include <xcb/xcbext.h>
17 #include <ctype.h>
18 #include <math.h>
19
20 #include <cairo.h>
21 #include <cairo-xcb.h>
22
23 #ifndef __CHECKER__
24 #include <pango/pango.h>
25 #include <pango/pangocairo.h>
26 #else
27 typedef struct PangoFontDescription {} PangoFontDescription;
28 typedef struct PangoLayout {} PangoLayout;
29 typedef struct PangoContext {} PangoContext;
30 typedef struct PangoFontMetrics {} PangoFontMetrics;
31 typedef struct PangoRectangle { int x,y,width,height;} PangoRectangle;
32 typedef enum { PANGO_STYLE_NORMAL, PANGO_STYLE_OBLIQUE, PANGO_STYLE_ITALIC
33 } PangoStyle;
34 typedef enum { PANGO_VARIANT_NORMAL, PANGO_VARIANT_SMALL_CAPS } PangoVariant;
35 typedef enum { PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_BOLD } PangoWeight;
36 PangoFontDescription *pango_font_description_new(void);
37 void pango_font_description_set_family_static(PangoFontDescription*, char*);
38 void pango_font_description_set_family(PangoFontDescription*, char*);
39 void pango_font_description_set_size(PangoFontDescription*, int);
40 void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
41 void pango_font_description_set_variant(PangoFontDescription*, PangoVariant);
42 void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
43 #define PANGO_SCALE (1024)
44
45 PangoLayout *pango_cairo_create_layout(cairo_t*);
46 void g_object_unref(PangoLayout*);
47 PangoContext *pango_cairo_create_context(cairo_t *);
48 void pango_cairo_show_layout(cairo_t *, PangoLayout *);
49 PangoFontMetrics *pango_context_get_metrics(PangoContext*, PangoFontDescription*, void*);
50 void pango_font_description_free(PangoFontDescription*);
51 int pango_font_metrics_get_approximate_char_width(PangoFontMetrics *);
52 int pango_font_metrics_get_ascent(PangoFontMetrics *);
53 int pango_font_metrics_get_descent(PangoFontMetrics *);
54 void pango_font_metrics_unref(PangoFontMetrics *);
55 PangoContext* pango_layout_get_context(PangoLayout *);
56 int pango_layout_get_baseline(PangoLayout *);
57 void pango_layout_get_pixel_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
58 void pango_layout_set_font_description(PangoLayout *, PangoFontDescription *);
59 void pango_layout_set_text(PangoLayout*, const char *, int);
60 void pango_layout_xy_to_index(PangoLayout*, int, int, int*, int*);
61
62 #endif
63
64 //#include <xkbcommon/xkbcommon.h>
65 //#include <xkbcommon/xkbcommon-x11.h>
66
67 #undef True
68 #undef False
69 #include "core.h"
70
71 enum my_atoms {
72         a_WM_STATE, a_STATE_ADD, a_STATE_REMOVE, a_STATE_FULLSCREEN,
73         NR_ATOMS
74 };
75 static char *atom_names[NR_ATOMS] = {
76         [a_WM_STATE]            = "_NET_WM_STATE",
77         [a_STATE_ADD]           = "_NET_WM_STATE_ADD",
78         [a_STATE_REMOVE]        = "_NET_WM_STATE_REMOVE",
79         [a_STATE_FULLSCREEN]    = "_NET_WM_STATE_FULLSCREEN",
80 };
81
82 struct xcb_data {
83         xcb_connection_t        *conn safe;
84         char                    *display safe;
85
86         const xcb_setup_t       *setup safe;
87         const xcb_screen_t      *screen safe;
88         xcb_atom_t              atoms[NR_ATOMS];
89
90         long                    last_event;
91         xcb_window_t            win;
92         xcb_visualtype_t        *visual;
93         cairo_t                 *cairo safe;
94         cairo_surface_t         *surface safe;
95         PangoFontDescription    *fd safe;
96         char                    *noclose;
97         int                     charwidth, lineheight;
98
99         bool                    motion_blocked;
100         bool                    in_focus;
101
102 #ifdef USE_XKB
103         struct xkb_context      *xkb;
104         int32_t                 xkb_device_id;
105         struct xkb_state        *xkb_state;
106 #endif
107
108         /* FIXME use hash?? */
109         struct panes {
110                 struct panes    *next;
111                 struct pane     *p safe;
112                 int             w,h;
113                 cairo_t         *ctx safe;
114                 xcb_pixmap_t    draw;
115                 cairo_surface_t *surface safe;
116         }                       *panes;
117 };
118
119 static struct map *xcb_map;
120 DEF_LOOKUP_CMD(xcb_handle, xcb_map);
121
122 static cairo_t *get_pixmap(struct pane *home safe,
123                            struct pane *p safe)
124 {
125         struct xcb_data *xd = home->data;
126         struct panes **pp, *ps;
127         cairo_surface_t *surface;
128         cairo_t *ctx = NULL;
129
130         for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
131                 if (ps->p != p)
132                         continue;
133                 if (ps->w == p->w && ps->h == p->h)
134                         return ps->ctx;
135                 *pp = ps->next;
136                 cairo_destroy(ps->ctx);
137                 cairo_surface_destroy(ps->surface);
138                 xcb_free_pixmap(xd->conn, ps->draw);
139                 free(ps);
140                 break;
141         }
142         alloc(ps, pane);
143         ps->p = p;
144         ps->w = p->w;
145         ps->h = p->h;
146         ps->draw = xcb_generate_id(xd->conn);
147         xcb_create_pixmap(xd->conn, xd->screen->root_depth, ps->draw,
148                           xd->win, p->w, p->h);
149         surface = cairo_xcb_surface_create(
150                 xd->conn, ps->draw, xd->visual, p->w, p->h);
151         if (!surface)
152                 goto free_ps;
153         ctx = cairo_create(surface);
154         if (!ctx)
155                 goto free_surface;
156         ps->ctx = ctx;
157         ps->surface = surface;
158
159         pane_add_notify(home, p, "Notify:Close");
160         ps->next = *pp;
161         *pp = ps;
162         return ps->ctx;
163 free_surface:
164         cairo_surface_destroy(surface);
165 free_ps:
166         xcb_free_pixmap(xd->conn, ps->draw);
167         unalloc(ps, pane);
168         return NULL;
169 }
170
171 static struct panes *find_pixmap(struct xcb_data *xd safe, struct pane *p safe,
172                                  int *xp safe, int *yp safe)
173 {
174         int x = 0, y = 0;
175         struct panes *ret = NULL;
176
177         while (!ret && p->parent != p) {
178                 struct panes *ps;
179                 for (ps = xd->panes; ps ; ps = ps->next)
180                         if (ps->p == p) {
181                                 ret = ps;
182                                 break;
183                         }
184                 if (!ret) {
185                         x += p->x;
186                         y += p->y;
187                         p = p->parent;
188                 }
189         }
190         *xp = x;
191         *yp = y;
192         return ret;
193 }
194
195 struct rgb {
196         double r,g,b;
197 };
198
199 static inline double cvt(int i)
200 {
201         return (float)i / 1000.0;
202 }
203
204 static void parse_attrs(
205         struct pane *home safe, const char *cattrs, int scale,
206         struct rgb *fgp, struct rgb *bgp, bool *underline,
207         PangoFontDescription **fdp)
208 {
209         char *attrs = strdup(cattrs ?: "");
210         char *ap = attrs;
211         char *word;
212         char *fg = NULL, *bg = NULL;
213         bool ul = False;
214         bool inv = False;
215         int size = 10*1000;
216         PangoFontDescription *fd = NULL;
217         PangoStyle style = PANGO_STYLE_NORMAL;
218         PangoVariant variant = PANGO_VARIANT_NORMAL;
219         PangoWeight weight = PANGO_WEIGHT_NORMAL;
220
221         if (fdp) {
222                 fd = pango_font_description_new();
223                 *fdp = fd;
224                 pango_font_description_set_family_static(fd, "mono");
225         }
226
227         while ((word = strsep(&ap, ",")) != NULL) {
228                 if (fd && strncmp(word, "family:", 7) == 0)
229                         pango_font_description_set_family(fd, word+7);
230                 if (strcmp(word, "large") == 0)
231                         size = 14 * 1000;
232                 if (strcmp(word, "small") == 0)
233                         size = 9 * 1000;
234                 if (isdigit(word[0])) {
235                         char *end = NULL;
236                         double s = strtod(word, &end);
237                         if (end && end != word && !*end)
238                                 size = trunc(s * 1000.0);
239                         else
240                                 size = 10*1000;
241                 }
242                 if (strcmp(word, "oblique") == 0)
243                         style = PANGO_STYLE_OBLIQUE;
244                 if (strcmp(word, "italic") == 0)
245                         style = PANGO_STYLE_ITALIC;
246                 if (strcmp(word, "normal") == 0)
247                         style = PANGO_STYLE_NORMAL;
248                 if (strcmp(word, "small-caps") == 0)
249                         variant = PANGO_VARIANT_SMALL_CAPS;
250
251                 if (strcmp(word, "bold") == 0)
252                         weight = PANGO_WEIGHT_BOLD;
253                 if (strcmp(word, "nobold") == 0)
254                         weight = PANGO_WEIGHT_NORMAL;
255
256                 if (strncmp(word, "fg:", 3) == 0)
257                         fg = word + 3;
258                 if (strncmp(word, "bg:", 3) == 0)
259                         bg = word + 3;
260                 if (strcmp(word, "inverse") == 0)
261                         inv = True;
262                 if (strcmp(word, "underline") == 0)
263                         ul = True;
264         }
265
266         if (inv) {
267                 char *t = bg;
268                 bg = fg;
269                 fg = t;
270                 if (!fg)
271                         fg = "white";
272                 if (!bg)
273                         bg = "black";
274         } else if (!fg)
275                 fg = "black";
276
277         if (fg && fgp) {
278                 struct call_return ret = call_ret(all, "colour:map", home,
279                                                   0, NULL, fg);
280                 fgp->r = cvt(ret.i);
281                 fgp->g = cvt(ret.i2);
282                 fgp->b = cvt(ret.x);
283         } else if (fgp)
284                 fgp->g = -1;
285         if (bg && bgp) {
286                 struct call_return ret = call_ret(all, "colour:map", home,
287                                                   0, NULL, bg);
288                 bgp->r = cvt(ret.i);
289                 bgp->g = cvt(ret.i2);
290                 bgp->b = cvt(ret.x);
291         } else if (bgp)
292                 bgp->g = -1;
293         if (fd) {
294                 pango_font_description_set_size(fd, size * scale / PANGO_SCALE);
295                 if (style != PANGO_STYLE_NORMAL)
296                         pango_font_description_set_style(fd, style);
297                 if (variant != PANGO_VARIANT_NORMAL)
298                         pango_font_description_set_variant(fd, variant);
299                 if (weight != PANGO_WEIGHT_NORMAL)
300                         pango_font_description_set_weight(fd, weight);
301         }
302         if (underline)
303                 *underline = ul;
304         free(attrs);
305 }
306
307 DEF_CB(cnt_disp)
308 {
309         struct call_return *cr = container_of(ci->comm, struct call_return, c);
310
311         cr->i += 1;
312         return 1;
313 }
314
315 DEF_CMD(xcb_close_display)
316 {
317         /* If this is only display, then refuse to close this one */
318         struct call_return cr;
319         struct xcb_data *xd = ci->home->data;
320         if (xd->noclose) {
321                 call("Message", ci->focus, 0, NULL, xd->noclose);
322                 return 1;
323         }
324         cr.c = cnt_disp;
325         cr.i = 0;
326         call_comm("editor:notify:all-displays", ci->focus, &cr.c);
327         if (cr.i > 1)
328                 pane_close(ci->home);
329         else
330                 call("Message", ci->focus, 0, NULL,
331                      "Cannot close only window.");
332         return 1;
333 }
334
335 DEF_CMD(xcb_set_noclose)
336 {
337         struct xcb_data *xd = ci->home->data;
338
339         free(xd->noclose);
340         xd->noclose = NULL;
341         if (ci->str)
342                 xd->noclose = strdup(ci->str);
343         return 1;
344 }
345
346 DEF_CMD(xcb_external_viewer)
347 {
348         //struct xcb_data *xd = ci->home->data;
349         //FIXME
350         return 1;
351 }
352
353 DEF_CMD(xcb_fullscreen)
354 {
355         struct xcb_data *xd = ci->home->data;
356         xcb_client_message_event_t msg = {};
357
358         msg.response_type = XCB_CLIENT_MESSAGE;
359         msg.format = 32;
360         msg.window = xd->win;
361         msg.type = xd->atoms[a_WM_STATE];
362         if (ci->num > 0)
363                 msg.data.data32[0] = xd->atoms[a_STATE_ADD];
364         else
365                 msg.data.data32[0] = xd->atoms[a_STATE_REMOVE];
366         msg.data.data32[1] = xd->atoms[a_STATE_FULLSCREEN];
367         msg.data.data32[2] = 0;
368         msg.data.data32[3] = 1; /* source indicator */
369
370         xcb_send_event(xd->conn, 0, xd->screen->root,
371                        XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
372                        XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
373                        (void*)msg.data.data8);
374
375         return 1;
376 }
377
378 DEF_CMD(xcb_close)
379 {
380         struct xcb_data *xd = ci->home->data;
381         xcb_destroy_window(xd->conn, xd->win);
382         xcb_disconnect(xd->conn);
383         /* free stuff */
384         return 1;
385 }
386
387 DEF_CMD(xcb_clear)
388 {
389         struct xcb_data *xd = ci->home->data;
390         const char *attr = ci->str;
391         struct panes *src = NULL;
392         cairo_t *pm;
393         struct rgb bg;
394         int x, y;
395
396         if (attr)
397                 parse_attrs(ci->home, attr, PANGO_SCALE, NULL, &bg, NULL, NULL);
398         else {
399                 src = find_pixmap(xd, ci->focus->parent, &x, &y);
400                 bg.r = bg.g = bg.b = 1.0;
401         }
402
403         pm = get_pixmap(ci->home, ci->focus);
404         if (!pm)
405                 return 1;
406         if (src) {
407                 cairo_set_source_surface(pm, src->surface, -x - ci->focus->x,
408                                          -y - ci->focus->y);
409                 cairo_paint(pm);
410         } else {
411                 cairo_set_source_rgb(pm, bg.r, bg.g, bg.b);
412                 cairo_rectangle(pm, 0.0, 0.0,
413                                 (double)ci->focus->w, (double)ci->focus->h);
414                 cairo_fill(pm);
415         }
416         pane_damaged(ci->home, DAMAGED_POSTORDER);
417         return 1;
418 }
419
420 DEF_CMD(xcb_text_size)
421 {
422         struct xcb_data *xd = ci->home->data;
423         const char *attr = ci->str2 ?: "";
424         const char *str = ci->str ?: "";
425         int scale = ci->num2;
426         PangoLayout *layout;
427         PangoFontDescription *fd;
428         PangoRectangle log;
429         int baseline;
430         int max_bytes;
431
432         if (scale <= 0)
433                 scale = 1000;
434         parse_attrs(ci->home, attr, scale, NULL, NULL, NULL, &fd);
435         /* If we use an empty string, line-height it wrong */
436         layout = pango_cairo_create_layout(xd->cairo);
437         pango_layout_set_text(layout, *str ? str : "M", -1);
438         pango_layout_set_font_description(layout, fd);
439         pango_layout_get_pixel_extents(layout, NULL, &log);
440         baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
441
442         if (ci->num < 0)
443                 max_bytes = 0;
444         else if (log.width <= ci->num)
445                 max_bytes = strlen(str);
446         else
447                 pango_layout_xy_to_index(layout, 1000*ci->num,
448                                          baseline, &max_bytes, NULL);
449
450         comm_call(ci->comm2, "cb", ci->focus, max_bytes, NULL, NULL,
451                   baseline, NULL, NULL,
452                   str && *str ? log.width : 0,
453                   log.height);
454
455         pango_font_description_free(fd);
456         g_object_unref(layout);
457         return 1;
458 }
459
460 DEF_CMD(xcb_draw_text)
461 {
462         struct xcb_data *xd = ci->home->data;
463         const char *str = ci->str;
464         const char *attr = ci->str2;
465         int scale = 1000;
466         struct panes *ps;
467         cairo_t *ctx;
468         PangoLayout *layout;
469         PangoFontDescription *fd;
470         PangoRectangle log;
471         struct rgb fg, bg;
472         bool ul;
473         int baseline;
474         int xo = 0, yo = 0;
475         int x,y;
476
477         if (!str)
478                 return Enoarg;
479         ps = find_pixmap(xd, ci->focus, &xo, &yo);
480         if (!ps)
481                 return Einval;
482         ctx = ps->ctx;
483
484         pane_damaged(ci->home, DAMAGED_POSTORDER);
485
486         if (ci->num2 > 0)
487                 scale = ci->num2 * 10 / xd->charwidth;
488
489         parse_attrs(ci->home, attr, scale, &fg, &bg, &ul, &fd);
490
491         x = ci->x + xo;
492         y = ci->y + yo;
493         layout = pango_cairo_create_layout(ctx);
494         pango_layout_set_text(layout, str, -1);
495         pango_layout_set_font_description(layout, fd);
496         pango_layout_get_pixel_extents(layout, NULL, &log);
497         baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
498         if (bg.g >= 0) {
499                 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
500                 cairo_rectangle(ctx, x+log.x, y - baseline + log.y,
501                                 log.width, log.height);
502                 cairo_fill(ctx);
503         }
504         cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
505         if (ul) {
506                 /* Draw an underline */
507                 cairo_rectangle(ctx, x+log.x, y+2+log.y,
508                                 log.width, 1);
509                 cairo_fill(ctx);
510         }
511
512         cairo_move_to(ctx, x, y - baseline);
513         pango_cairo_show_layout(ctx, layout);
514         cairo_stroke(ctx);
515
516         if (ci->num >= 0) {
517                 /* draw a cursor - outline box if not in-focus,
518                  * inverse-video if it is.
519                  */
520         }
521         pango_font_description_free(fd);
522         g_object_unref(layout);
523         return 1;
524 }
525
526 DEF_CMD(xcb_draw_image)
527 {
528         //struct xcb_data *xd = ci->home->data;
529         //FIXME
530         return 1;
531 }
532
533 static struct panes *sort_split(struct panes *p)
534 {
535         /* consider 'p' to be a list of panes with
536          * ordered subsets (ordered by p->abs_z).
537          * Remove every other such subset and return them
538          * linked together.
539          * If p is ordered, this means we return NULL.
540          */
541         struct panes *ret, **end = &ret;
542         struct panes *next;
543
544         for (; p && p->next; p = next) {
545                 /* If these are not ordered, attach p->next at
546                  * 'end', and make 'end' point to &p->next.
547                  */
548                 next = p->next;
549                 if (p->p->abs_z <= next->p->abs_z)
550                         continue;
551                 *end = next;
552                 end = &p->next;
553         }
554         *end = NULL;
555         return ret;
556 }
557
558 static struct panes *sort_merge(struct panes *p1, struct panes *p2)
559 {
560         /* merge p1 and p2 and return result */
561         struct panes *ret, **end = &ret;
562         int lastz = -100;
563
564         while (p1 && p2) {
565                 /* if both arg large or smaller than lastz, choose
566                  * least, else choose largest
567                  */
568                 struct panes *lo, *hi, *choice;
569                 if (p1->p->abs_z <= p2->p->abs_z) {
570                         lo = p1; hi = p2;
571                 } else {
572                         lo = p2; hi = p1;
573                 }
574                 if (lo->p->abs_z >= lastz || hi->p->abs_z <= lastz)
575                         choice = lo;
576                 else
577                         choice = hi;
578                 *end = choice;
579                 end = &choice->next;
580                 if (choice == p1)
581                         p1 = p1->next;
582                 else
583                         p2 = p2->next;
584         }
585         if (p1)
586                 *end = p1;
587         else
588                 *end = p2;
589         return ret;
590 }
591
592 DEF_CMD(xcb_refresh_post)
593 {
594         struct xcb_data *xd = ci->home->data;
595         struct panes *ps;
596
597         time_start(TIME_WINDOW);
598         /* First: ensure panes are sorted */
599         while ((ps = sort_split(xd->panes)) != NULL)
600                 xd->panes = sort_merge(xd->panes, ps);
601
602         /* Now copy all panes onto the window */
603         for (ps = xd->panes; ps; ps = ps->next) {
604                 double lox, hix, loy, hiy;
605                 struct xy rel, lo, hi;
606
607                 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
608                 lo = pane_mapxy(ps->p, ci->home, 0, 0, True);
609                 hi = pane_mapxy(ps->p, ci->home, ps->p->w, ps->p->h, True);
610                 lox = lo.x; loy = lo.y;
611                 hix = hi.x; hiy = hi.y;
612                 cairo_save(xd->cairo);
613                 cairo_set_source_surface(xd->cairo, ps->surface,
614                                          rel.x, rel.y);
615                 cairo_rectangle(xd->cairo, lox, loy, hix, hiy);
616                 cairo_fill(xd->cairo);
617                 cairo_restore(xd->cairo);
618         }
619         time_stop(TIME_WINDOW);
620         xcb_flush(xd->conn);
621         return 1;
622 }
623
624 DEF_CMD(xcb_pane_close)
625 {
626         struct xcb_data *xd = ci->home->data;
627         struct panes **pp, *ps;
628
629         for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
630                 if (ps->p != ci->focus)
631                         continue;
632                 *pp = ps->next;
633                 ps->next = NULL;
634                 cairo_destroy(ps->ctx);
635                 cairo_surface_destroy(ps->surface);
636                 xcb_free_pixmap(xd->conn, ps->draw);
637                 free(ps);
638                 pane_damaged(ci->home, DAMAGED_POSTORDER);
639                 break;
640         }
641         return 1;
642 }
643
644 DEF_CMD(xcb_notify_display)
645 {
646         struct xcb_data *xd = ci->home->data;
647         comm_call(ci->comm2, "callback:display", ci->home, xd->last_event);
648         return 1;
649 }
650
651 static void handle_button(struct pane *home safe,
652                           xcb_button_press_event_t *be safe)
653 {
654         struct xcb_data *xd = home->data;
655         bool press = (be->response_type & 0x7f) == XCB_BUTTON_PRESS;
656         char mod[2+2+2+1];
657         char key[2+2+2+9+1+1];
658
659         mod[0] = 0;
660         if (press) {
661                 xd->motion_blocked = False;
662                 if (be->state & XCB_KEY_BUT_MASK_MOD_1)
663                         strcat(mod, ":A");
664                 if (be->state & XCB_KEY_BUT_MASK_CONTROL)
665                         strcat(mod, ":C");
666                 if (be->state & XCB_KEY_BUT_MASK_SHIFT)
667                         strcat(mod, ":S");
668                 strcpy(key, mod);
669                 strcat(key, ":Press-X");
670         } else
671                 strcpy(key, ":Release-X");
672         key[strlen(key) - 1] = '0' + be->detail;
673         xd->last_event = time(NULL);
674         call("Mouse-event", home, be->detail, NULL, key,
675              press?1:2, NULL, mod,
676              be->event_x, be->event_y);
677 }
678
679 static void handle_motion(struct pane *home safe,
680                           xcb_motion_notify_event_t *mne safe)
681 {
682         struct xcb_data *xd = home->data;
683         xcb_query_pointer_cookie_t c;
684         xcb_query_pointer_reply_t *qpr;
685         int ret;
686         int x = mne->event_x, y = mne->event_y;
687
688         if (xd->motion_blocked)
689                 return;
690         ret = call("Mouse-event", home, 0, NULL, ":Motion",
691                    3, NULL, NULL, x, y);
692         if (ret <= 0)
693                 xd->motion_blocked = True;
694         c = xcb_query_pointer(xd->conn, xd->win);
695         qpr = xcb_query_pointer_reply(xd->conn, c, NULL);
696         free(qpr);
697 }
698
699 static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
700 {
701         struct xcb_data *xd = home->data;
702         bool in = (fie->response_type & 0x7f) == XCB_FOCUS_IN;
703         struct pane *p;
704         struct mark *pt;
705
706         xd->in_focus = in;
707         p = pane_leaf(home);
708         pt = call_ret(mark, "doc:point", p);
709         if (pt)
710                 call("view:changed", p, 0, pt);
711         if (in)
712                 call("pane:refocus", home);
713 }
714
715 static void handle_key(struct pane *home safe, xcb_key_press_event_t *kpe safe)
716 {
717         //struct xcb_data *xd = home->data;
718 #ifdef USE_XKB
719         xcb_key_press_event_t           *kpe;
720         xkb_keysym_t                    keysym;
721         char                            name[64];
722         xkb_state_update_key(xd->xkb_state, kpe->keycode,
723                              XKB_KEY_DOWN);
724         keysym = xkb_state_key_get_one_sym(xd->xkb_state,
725                                            kpe->keycode);
726         xkb_keysym_get_name(keysym, name, sizeof(name));
727         xkb_state_key_get_utf8(xd->xkb_state, kpe->keycode,
728                                name, sizeof(name));
729 #endif
730         if (kpe->detail == 24)
731                 call("event:deactivate", home);
732         //XKeyEvent xke;
733         //KeySym keysym;
734         //char buf[40];
735         //XLookupString(&xke, &buf, sizeof(buf), &keysym, NULL);
736 }
737
738 static void handle_configure(struct pane *home safe,
739                              xcb_configure_notify_event_t *cne safe)
740 {
741         struct xcb_data *xd = home->data;
742
743         pane_resize(home, 0, 0, cne->width, cne->height);
744         cairo_xcb_surface_set_size(xd->surface, cne->width, cne->height);
745 }
746
747 DEF_CMD(xcb_input)
748 {
749         struct xcb_data *xd = ci->home->data;
750         xcb_generic_event_t *ev;
751
752         while ((ev = xcb_poll_for_event(xd->conn)) != NULL) {
753                 switch (ev->response_type & 0x7f) {
754                 case XCB_KEY_PRESS:
755                 case XCB_KEY_RELEASE:
756                         time_start(TIME_KEY);
757                         handle_key(ci->home, safe_cast (void*)ev);
758                         time_stop(TIME_KEY);
759                         break;
760                 case XCB_BUTTON_PRESS:
761                 case XCB_BUTTON_RELEASE:
762                         time_start(TIME_KEY);
763                         handle_button(ci->home, (void*)ev);
764                         time_stop(TIME_KEY);
765                         break;
766                 case XCB_MOTION_NOTIFY:
767                         time_start(TIME_KEY);
768                         handle_motion(ci->home, (void*)ev);
769                         time_stop(TIME_KEY);
770                         break;
771                 case XCB_FOCUS_IN:
772                 case XCB_FOCUS_OUT:
773                         time_start(TIME_WINDOW);
774                         handle_focus(ci->home, (void*)ev);
775                         time_stop(TIME_WINDOW);
776                         break;
777                 case XCB_EXPOSE:
778                         pane_damaged(ci->home, DAMAGED_POSTORDER);
779                         break;
780                 case XCB_CONFIGURE_NOTIFY:
781                         time_start(TIME_WINDOW);
782                         handle_configure(ci->home, (void*)ev);
783                         time_stop(TIME_WINDOW);
784                         break;
785                 default:
786                         LOG("ignored %x", ev->response_type);
787                 }
788                 xcb_flush(xd->conn);
789         }
790         return 1;
791 }
792
793 static struct pane *xcb_display_init(const char *d safe, struct pane *focus safe)
794 {
795         struct xcb_data *xd;
796         struct pane *p;
797         xcb_connection_t *conn;
798         xcb_intern_atom_cookie_t cookies[NR_ATOMS];
799         xcb_screen_iterator_t iter;
800         xcb_depth_iterator_t di;
801         char scale[20];
802         uint32_t valwin[2];
803         int screen = 0;
804         int i;
805         PangoLayout *layout;
806         PangoRectangle log;
807         cairo_t *cairo;
808         cairo_surface_t *surface;
809         PangoFontDescription *fd;
810         // FIXME SCALE from environ?? or pango_cairo_context_set_resolution dpi
811         // 254 * width_in_pixels / width_in_millimeters / 10
812
813         conn = xcb_connect(d, &screen);
814         if (!conn)
815                 return NULL;
816
817         alloc(xd, pane);
818
819         xd->motion_blocked = True;
820         xd->in_focus = True;
821
822         xd->conn = conn;
823         xd->display = strdup(d);
824         xd->setup = safe_cast xcb_get_setup(conn);
825         iter = xcb_setup_roots_iterator(xd->setup);
826         for (i = 0; i < screen; i++)
827                 xcb_screen_next(&iter);
828         xd->screen = safe_cast iter.data;
829
830         di = xcb_screen_allowed_depths_iterator(xd->screen);
831         while (di.data && di.data->depth < 24)
832                 xcb_depth_next(&di);
833         //?? look for class = TrueColor??
834         xd->visual = xcb_depth_visuals(di.data);
835
836         for (i = 0; i < NR_ATOMS; i++) {
837                 char *n = atom_names[i];
838                 if (!n)
839                         continue;
840                 cookies[i] = xcb_intern_atom(conn, 0, strlen(n), n);
841         }
842
843         xd->win = xcb_generate_id(conn);
844         valwin[0] = xd->screen->white_pixel;
845         valwin[1] = (XCB_EVENT_MASK_KEY_PRESS |
846                      XCB_EVENT_MASK_KEY_RELEASE |
847                      XCB_EVENT_MASK_BUTTON_PRESS |
848                      XCB_EVENT_MASK_BUTTON_RELEASE |
849                      // XCB_EVENT_MASK_ENTER_WINDOW |
850                      // XCB_EVENT_MASK_LEAVE_WINDOW |
851                      XCB_EVENT_MASK_STRUCTURE_NOTIFY |
852                      XCB_EVENT_MASK_EXPOSURE |
853                      XCB_EVENT_MASK_POINTER_MOTION |
854                      // FIXME XCB_EVENT_MASK_POINTER_MOTION_HINT |
855                      0);
856
857         xcb_create_window(conn, XCB_COPY_FROM_PARENT, xd->win,
858                           xd->screen->root,
859                           0, 0,
860                           100, 100,
861                           0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
862                           xd->screen->root_visual,
863                           XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
864                           valwin);
865         xcb_flush(conn);
866
867         surface = cairo_xcb_surface_create(
868                 conn, xd->win, xd->visual, 100, 100);
869         if (!surface)
870                 goto abort;
871         xd->surface = surface;
872         cairo = cairo_create(xd->surface);
873         if (!cairo)
874                 goto abort;
875         xd->cairo = cairo;
876         fd = pango_font_description_new();
877         if (!fd)
878                 goto abort;
879         xd->fd = fd;
880         pango_font_description_set_family(xd->fd, "mono");
881         pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
882
883         layout = pango_cairo_create_layout(xd->cairo);
884         pango_layout_set_font_description(layout, fd);
885         pango_layout_set_text(layout, "M", 1);
886         pango_layout_get_pixel_extents(layout, NULL, &log);
887         g_object_unref(layout);
888         xd->lineheight = log.height;
889         xd->charwidth = log.width;
890
891         valwin[0] = xd->charwidth * 80;
892         valwin[1] = xd->lineheight * 26;
893         xcb_configure_window(conn, xd->win,
894                              XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
895                              valwin);
896         cairo_xcb_surface_set_size(xd->surface, valwin[0], valwin[1]);
897
898         /* Now resolve all those cookies */
899         for (i = 0; i < NR_ATOMS; i++) {
900                 xcb_intern_atom_reply_t *r;
901                 r = xcb_intern_atom_reply(conn, cookies[i], NULL);
902                 if (!r)
903                         goto abort;
904                 xd->atoms[i] = r->atom;
905                 free(r);
906         }
907
908 #ifdef USE_XKB
909         xd->xkb = safe_cast xkb_context_new(XCB_CONTEXT_NO_FLAGS);
910         xd->xkb_device_id = xkb_x11_get_core_keyboard_device_id(conn);
911         xd->xkb_state = xkb_x11_state_new_from_device(xd->xkb_keymap, conn,
912                                                       xd->xkb_device_id);
913 #endif
914         /* FIXME set:
915          * WM_NAME
916          * WM_PROTOCOLS WM_DELETE_WINDOW WM_TAKE_FOCUS _NET_WM_PING  _NET_WM_SYN_REQUEST??
917          * WM_NORMAL_HINTS WM_HINTS
918          * WM_CLIENT_MACHINE
919          */
920         xcb_map_window(conn, xd->win);
921         xcb_flush(conn);
922         p = pane_register(pane_root(focus), 1, &xcb_handle.c, xd);
923         if (!p)
924                 goto abort;
925         pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
926         call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
927         attr_set_str(&p->attrs, "DISPLAY", d);
928         snprintf(scale, sizeof(scale), "%dx%d", xd->charwidth, xd->lineheight);
929         attr_set_str(&p->attrs, "scale:M", scale);
930         //attr_set_int(&p->attrs, "scale", 2000);
931         return p;
932 abort:
933         cairo_destroy(xd->cairo);
934         cairo_surface_destroy(xd->surface);
935         xcb_disconnect(conn);
936         // FIXME free stuff;
937         unalloc(xd, pane);
938         return NULL;
939 }
940
941 DEF_CMD(display_xcb)
942 {
943         struct pane *p;
944         const char *d = ci->str;
945
946         if (!d)
947                 return Enoarg;
948         p = xcb_display_init(d, ci->focus);
949         if (p)
950                 return comm_call(ci->comm2, "cb", p);
951         return Efail;
952 }
953
954 DEF_CMD(xcb_new_display)
955 {
956         struct pane *p;
957         char *d = pane_attr_get(ci->focus, "DISPLAY");
958
959         if (!d)
960                 return Enoarg;
961         p = xcb_display_init(d, ci->focus);
962         if (p)
963                 p = call_ret(pane, "editor:activate-display", p);
964         if (p)
965                 home_call(ci->focus, "doc:attach-view", p, 1);
966         return 1;
967 }
968
969 void edlib_init(struct pane *ed safe)
970 {
971         call_comm("global-set-command", ed, &display_xcb, 0, NULL,
972                   "attach-display-x11");
973         call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
974                   "interactive-cmd-x11window");
975
976         xcb_map = key_alloc();
977
978         key_add(xcb_map, "Display:close", &xcb_close_display);
979         key_add(xcb_map, "Display:set-noclose", &xcb_set_noclose);
980         key_add(xcb_map, "Display:external-viewer", &xcb_external_viewer);
981         key_add(xcb_map, "Display:fullscreen", &xcb_fullscreen);
982         key_add(xcb_map, "Display:new", &xcb_new_display);
983
984         key_add(xcb_map, "Close", &xcb_close);
985         key_add(xcb_map, "Free", &edlib_do_free);
986         key_add(xcb_map, "Draw:clear", &xcb_clear);
987         key_add(xcb_map, "Draw:text-size", &xcb_text_size);
988         key_add(xcb_map, "Draw:text", &xcb_draw_text);
989         key_add(xcb_map, "Draw:image", &xcb_draw_image);
990         key_add(xcb_map, "Refresh:postorder", &xcb_refresh_post);
991         key_add(xcb_map, "all-displays", &xcb_notify_display);
992         key_add(xcb_map, "Notify:Close", &xcb_pane_close);
993 }