2 * Copyright Neil Brown ©2021-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * X11 display driver for edlib, using xcb, cairopango, libxkbcommon etc.
7 * A different connection to the server will be created for each
8 * display. Maybe that can be optimised one day.
23 /* xkb.h has a 'long' in an enum :-( */
25 XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY,
26 XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
27 XCB_XKB_NEW_KEYBOARD_NOTIFY,
28 XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
29 XCB_XKB_MAP_PART_MODIFIER_MAP,
30 XCB_XKB_STATE_PART_MODIFIER_LOCK,
31 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS,
32 XCB_XKB_STATE_PART_GROUP_BASE,
33 XCB_XKB_MAP_PART_KEY_ACTIONS,
34 XCB_XKB_STATE_PART_GROUP_LATCH,
35 XCB_XKB_MAP_PART_VIRTUAL_MODS,
36 XCB_XKB_STATE_PART_GROUP_LOCK,
37 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP,
38 XCB_XKB_NKN_DETAIL_KEYCODES,
39 XCB_XKB_MAP_PART_KEY_TYPES,
40 XCB_XKB_MAP_PART_KEY_SYMS,
41 XCB_XKB_STATE_PART_MODIFIER_BASE,
42 XCB_XKB_STATE_PART_MODIFIER_LATCH,
46 typedef uint16_t xcb_xkb_device_spec_t;
47 typedef struct xcb_xkb_select_events_details_t {
48 uint16_t affectNewKeyboard;
49 uint16_t newKeyboardDetails;
51 uint16_t stateDetails;
52 /* and other fields */
53 } xcb_xkb_select_events_details_t;
54 typedef struct xcb_xkb_new_keyboard_notify_event_t {
57 /* and other fields */
58 } xcb_xkb_new_keyboard_notify_event_t;
59 typedef struct xcb_xkb_state_notify_event_t {
67 /* and other fields */
68 } xcb_xkb_state_notify_event_t;
69 typedef struct xcb_xkb_map_notify_event_t {
71 } xcb_xkb_map_notify_event_t;
73 xcb_xkb_select_events_aux_checked(xcb_connection_t *c,
74 xcb_xkb_device_spec_t deviceSpec,
80 const xcb_xkb_select_events_details_t *details);
83 #include <xcb/xcbext.h>
89 #include <cairo-xcb.h>
91 #include <wand/MagickWand.h>
93 // enums confuse sparse...
94 #define MagickBooleanType int
98 #include <pango/pango.h>
99 #include <pango/pangocairo.h>
101 typedef struct PangoFontDescription {} PangoFontDescription;
102 typedef struct PangoLayout {} PangoLayout;
103 typedef struct PangoContext {} PangoContext;
104 typedef struct PangoFontMetrics {} PangoFontMetrics;
105 typedef struct PangoRectangle { int x,y,width,height;} PangoRectangle;
106 typedef enum { PANGO_STYLE_NORMAL, PANGO_STYLE_OBLIQUE, PANGO_STYLE_ITALIC
108 typedef enum { PANGO_VARIANT_NORMAL, PANGO_VARIANT_SMALL_CAPS } PangoVariant;
109 typedef enum { PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_BOLD } PangoWeight;
110 PangoFontDescription *pango_font_description_new(void);
111 void pango_font_description_set_family_static(PangoFontDescription*, char*);
112 void pango_font_description_set_family(PangoFontDescription*, char*);
113 void pango_font_description_set_size(PangoFontDescription*, int);
114 void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
115 void pango_font_description_set_variant(PangoFontDescription*, PangoVariant);
116 void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
117 #define PANGO_SCALE (1024)
119 PangoLayout *pango_cairo_create_layout(cairo_t*);
120 void g_object_unref(PangoLayout*);
121 PangoContext *pango_cairo_create_context(cairo_t *);
122 void pango_cairo_show_layout(cairo_t *, PangoLayout *);
123 PangoFontMetrics *pango_context_get_metrics(PangoContext*, PangoFontDescription*, void*);
124 void pango_font_description_free(PangoFontDescription*);
125 int pango_font_metrics_get_approximate_char_width(PangoFontMetrics *);
126 int pango_font_metrics_get_ascent(PangoFontMetrics *);
127 int pango_font_metrics_get_descent(PangoFontMetrics *);
128 void pango_font_metrics_unref(PangoFontMetrics *);
129 PangoContext* pango_layout_get_context(PangoLayout *);
130 int pango_layout_get_baseline(PangoLayout *);
131 void pango_layout_get_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
132 void pango_layout_get_pixel_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
133 void pango_layout_set_font_description(PangoLayout *, PangoFontDescription *);
134 void pango_layout_set_text(PangoLayout*, const char *, int);
135 void pango_layout_xy_to_index(PangoLayout*, int, int, int*, int*);
136 void pango_layout_index_to_pos(PangoLayout*, int, PangoRectangle*);
139 #include <xkbcommon/xkbcommon.h>
140 #include <xkbcommon/xkbcommon-compose.h>
141 #include <xkbcommon/xkbcommon-x11.h>
148 #define PANE_DATA_TYPE struct xcb_data
153 a_WM_STATE, a_STATE_FULLSCREEN,
154 a_WM_NAME, a_NET_WM_NAME,
155 a_WM_ICON_NAME, a_NET_WM_ICON_NAME,
156 a_WM_PROTOCOLS, a_WM_DELETE_WINDOW,
163 static const char *atom_names[NR_ATOMS] = {
165 [a_WM_STATE] = "_NET_WM_STATE",
166 [a_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN",
167 [a_WM_NAME] = "WM_NAME",
168 [a_NET_WM_NAME] = "_NET_WM_NAME",
169 [a_WM_ICON_NAME] = "WM_ICON_NAME",
170 [a_NET_WM_ICON_NAME] = "_NET_WM_ICON_NAME",
171 [a_WM_PROTOCOLS] = "WM_PROTOCOLS",
172 [a_WM_DELETE_WINDOW] = "WM_DELETE_WINDOW",
173 [a_NET_WM_PING] = "_NET_WM_PING",
174 [a_NET_WM_ICON] = "_NET_WM_ICON",
175 [a_WM_CLIENT_MACHINE] = "WM_CLIENT_MACHINE",
176 [a_UTF8_STRING] = "UTF8_STRING",
184 xcb_connection_t *conn safe;
188 const xcb_setup_t *setup safe;
189 const xcb_screen_t *screen safe;
190 xcb_atom_t atoms[NR_ATOMS];
194 xcb_visualtype_t *visual;
196 cairo_surface_t *surface safe;
197 PangoFontDescription *fd safe;
198 int charwidth, lineheight;
199 cairo_region_t *need_update;
204 struct xkb_context *xkb;
205 uint8_t first_xkb_event;
206 int32_t xkb_device_id;
207 struct xkb_state *xkb_state;
208 struct xkb_compose_state *compose_state;
209 struct xkb_compose_table *compose_table;
210 struct xkb_keymap *xkb_keymap;
217 /* FIXME use hash?? */
221 cairo_rectangle_int_t r;
225 cairo_surface_t *surface;
226 cairo_region_t *need_update;
229 #include "core-pane.h"
231 /* panes->r.x is NEVER_DRAWN if the pane has not been drawn */
232 #define NEVER_DRAWN (-60000)
234 static struct map *xcb_map;
235 DEF_LOOKUP_CMD(xcb_handle, xcb_map);
237 static struct panes *get_pixmap(struct pane *home safe,
240 struct xcb_data *xd = home->data;
241 struct panes **pp, *ps;
243 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
246 if (ps->r.width == p->w && ps->r.height == p->h)
249 if (ps->r.x != NEVER_DRAWN) {
250 if (!xd->need_update)
251 xd->need_update = cairo_region_create();
252 cairo_region_union_rectangle(xd->need_update, &ps->r);
255 cairo_destroy(ps->ctx);
257 cairo_surface_destroy(ps->surface);
259 xcb_free_pixmap(xd->conn, ps->draw);
265 ps->r.x = ps->r.y = NEVER_DRAWN;
268 ps->bg.r = ps->bg.g = ps->bg.b = 0;
270 pane_add_notify(home, p, "Notify:Close");
276 static void instantiate_pixmap(struct xcb_data *xd safe,
277 struct panes *ps safe)
279 ps->draw = xcb_generate_id(xd->conn);
280 xcb_create_pixmap(xd->conn, xd->screen->root_depth, ps->draw,
281 xd->win, ps->r.width, ps->r.height);
282 ps->surface = cairo_xcb_surface_create(
283 xd->conn, ps->draw, xd->visual, ps->r.width, ps->r.height);
286 ps->ctx = cairo_create(ps->surface);
289 cairo_set_source_rgb(ps->ctx, ps->bg.r, ps->bg.g, ps->bg.b);
290 cairo_paint(ps->ctx);
294 cairo_surface_destroy(ps->surface);
297 xcb_free_pixmap(xd->conn, ps->draw);
301 static struct panes *find_pixmap(struct xcb_data *xd safe, struct pane *p safe,
302 int *xp safe, int *yp safe)
305 struct panes *ret = NULL;
307 while (!ret && p->parent != p) {
309 for (ps = xd->panes; ps ; ps = ps->next)
325 static inline double cvt(int i)
327 return (float)i / 1000.0;
330 static void parse_attrs(
331 struct pane *home safe, const char *cattrs, int scale,
332 struct rgb *fgp, struct rgb *bgp, bool *underline,
333 PangoFontDescription **fdp)
335 char *attrs = strdup(cattrs ?: "");
338 char *fg = NULL, *bg = NULL;
342 PangoFontDescription *fd = NULL;
343 PangoStyle style = PANGO_STYLE_NORMAL;
344 PangoVariant variant = PANGO_VARIANT_NORMAL;
345 PangoWeight weight = PANGO_WEIGHT_NORMAL;
348 fd = pango_font_description_new();
350 pango_font_description_set_family_static(fd, "monospace");
353 while ((word = strsep(&ap, ",")) != NULL) {
354 if (fd && strstarts(word, "family:"))
355 pango_font_description_set_family(fd, word+7);
356 if (strcmp(word, "large") == 0)
358 if (strcmp(word, "small") == 0)
360 if (isdigit(word[0])) {
362 double s = strtod(word, &end);
363 if (end && end != word && !*end)
364 size = trunc(s * 1000.0);
368 if (strcmp(word, "oblique") == 0)
369 style = PANGO_STYLE_OBLIQUE;
370 if (strcmp(word, "italic") == 0)
371 style = PANGO_STYLE_ITALIC;
372 if (strcmp(word, "normal") == 0)
373 style = PANGO_STYLE_NORMAL;
374 if (strcmp(word, "small-caps") == 0)
375 variant = PANGO_VARIANT_SMALL_CAPS;
377 if (strcmp(word, "bold") == 0)
378 weight = PANGO_WEIGHT_BOLD;
379 if (strcmp(word, "nobold") == 0)
380 weight = PANGO_WEIGHT_NORMAL;
382 if (strstarts(word, "fg:"))
384 if (strstarts(word, "bg:"))
386 if (strcmp(word, "inverse") == 0)
388 if (strcmp(word, "noinverse") == 0)
390 if (strcmp(word, "underline") == 0)
392 if (strcmp(word, "nounderline") == 0)
408 struct call_return ret = call_ret(all, "colour:map", home,
411 fgp->g = cvt(ret.i2);
416 struct call_return ret = call_ret(all, "colour:map", home,
419 bgp->g = cvt(ret.i2);
424 pango_font_description_set_size(fd, PANGO_SCALE * size /1000 * scale / 1000);
425 if (style != PANGO_STYLE_NORMAL)
426 pango_font_description_set_style(fd, style);
427 if (variant != PANGO_VARIANT_NORMAL)
428 pango_font_description_set_variant(fd, variant);
429 if (weight != PANGO_WEIGHT_NORMAL)
430 pango_font_description_set_weight(fd, weight);
439 struct call_return *cr = container_of(ci->comm, struct call_return, c);
445 DEF_CMD_CLOSED(xcb_close_display)
447 /* If this is only display, then refuse to close this one */
448 struct call_return cr;
449 char *nc = pane_attr_get(ci->home, "no-close");
452 call("Message", ci->focus, 0, NULL, nc);
457 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
461 call("Message", ci->focus, 0, NULL,
462 "Cannot close only window.");
466 static void wait_for(struct xcb_data *xd safe)
468 struct pids **pp = &xd->pids;
471 struct pids *p = *pp;
472 if (waitpid(p->pid, NULL, WNOHANG) > 0) {
480 DEF_CMD(xcb_external_viewer)
482 struct xcb_data *xd = ci->home->data;
483 const char *path = ci->str;
490 switch (pid = fork()) {
494 setenv("DISPLAY", xd->display, 1);
496 setenv("XAUTHORITY", xd->disp_auth, 1);
497 fd = open("/dev/null", O_RDWR);
505 execlp("xdg-open", "xdg-open", path, NULL);
507 default: /* parent */
508 p = malloc(sizeof(*p));
518 DEF_CMD(xcb_fullscreen)
520 struct xcb_data *xd = ci->home->data;
521 xcb_client_message_event_t msg = {};
523 msg.response_type = XCB_CLIENT_MESSAGE;
525 msg.window = xd->win;
526 msg.type = xd->atoms[a_WM_STATE];
528 msg.data.data32[0] = 1; /* ADD */
530 msg.data.data32[0] = 0; /* REMOVE */
531 msg.data.data32[1] = xd->atoms[a_STATE_FULLSCREEN];
532 msg.data.data32[2] = 0;
533 msg.data.data32[3] = 1; /* source indicator */
535 xcb_send_event(xd->conn, 0, xd->screen->root,
536 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
537 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
543 static void panes_free(struct xcb_data *xd safe)
546 struct panes *ps = xd->panes;
547 xd->panes = ps->next;
549 cairo_destroy(ps->ctx);
551 cairo_surface_destroy(ps->surface);
553 xcb_free_pixmap(xd->conn, ps->draw);
558 static void kbd_free(struct xcb_data *xd safe);
560 DEF_CMD_CLOSED(xcb_close)
562 struct xcb_data *xd = ci->home->data;
564 xcb_destroy_window(xd->conn, xd->win);
568 pango_font_description_free(xd->fd);
569 cairo_destroy(xd->cairo);
570 cairo_device_finish(cairo_surface_get_device(xd->surface));
571 cairo_surface_destroy(xd->surface);
574 xcb_disconnect(xd->conn);
576 cairo_region_destroy(xd->need_update);
582 struct xcb_data *xd = ci->home->data;
583 const char *attr = ci->str;
584 struct panes *src = NULL, *dest;
587 cairo_rectangle_int_t r;
590 parse_attrs(ci->home, attr, PANGO_SCALE, NULL, &bg, NULL, NULL);
592 bg.r = bg.g = bg.b = 1.0;
594 src = find_pixmap(xd, ci->focus->parent, &x, &y);
598 bg.r = bg.g = bg.b = 1.0;
599 else if (src->bg.g >= 0)
601 else if (src->surface == NULL)
602 bg.r = bg.g = bg.b = 1.0;
607 dest = get_pixmap(ci->home, ci->focus);
612 cairo_set_source_rgb(dest->ctx, bg.r, bg.g, bg.b);
613 cairo_paint(dest->ctx);
618 instantiate_pixmap(xd, dest);
620 cairo_set_source_surface(dest->ctx, src->surface, -x, -y);
621 cairo_paint(dest->ctx);
625 pane_damaged(ci->home, DAMAGED_POSTORDER);
627 if (!dest->need_update)
628 dest->need_update = cairo_region_create();
631 r.width = ci->focus->w;
632 r.height = ci->focus->h;
633 cairo_region_union_rectangle(dest->need_update, &r);
637 DEF_CMD(xcb_text_size)
639 struct xcb_data *xd = ci->home->data;
640 const char *attr = ci->str2 ?: "";
641 const char *str = ci->str ?: "";
642 int scale = ci->num2;
644 PangoFontDescription *fd;
651 if (!utf8_valid(str))
653 parse_attrs(ci->home, attr, scale, NULL, NULL, NULL, &fd);
654 /* If we use an empty string, line-height is wrong */
655 layout = pango_cairo_create_layout(xd->cairo);
656 pango_layout_set_text(layout, *str ? str : "M", -1);
657 pango_layout_set_font_description(layout, fd);
658 pango_layout_get_pixel_extents(layout, NULL, &log);
659 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
663 else if (log.width <= ci->num)
664 max_bytes = strlen(str);
666 pango_layout_xy_to_index(layout, PANGO_SCALE*ci->num,
667 baseline, &max_bytes, NULL);
669 comm_call(ci->comm2, "cb", ci->focus, max_bytes, NULL, NULL,
670 baseline, NULL, NULL,
671 str && *str ? log.width : 0,
674 pango_font_description_free(fd);
675 g_object_unref(layout);
679 DEF_CMD(xcb_draw_text)
681 struct xcb_data *xd = ci->home->data;
682 const char *str = ci->str;
683 const char *attr = ci->str2;
688 PangoFontDescription *fd;
698 ps = find_pixmap(xd, ci->focus, &xo, &yo);
702 instantiate_pixmap(xd, ps);
708 if (!utf8_valid(str))
711 pane_damaged(ci->home, DAMAGED_POSTORDER);
714 scale = ci->num2 * 10 / xd->charwidth;
716 parse_attrs(ci->home, attr, scale, &fg, &bg, &ul, &fd);
720 layout = pango_cairo_create_layout(ctx);
721 pango_layout_set_text(layout, str, -1);
722 pango_layout_set_font_description(layout, fd);
723 pango_layout_get_pixel_extents(layout, NULL, &log);
724 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
727 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
728 cairo_rectangle(ctx, x+log.x, y - baseline + log.y,
729 log.width, log.height);
732 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
734 /* Draw an underline */
735 cairo_rectangle(ctx, x+log.x, y+2+log.y,
740 cairo_move_to(ctx, x, y - baseline);
741 pango_cairo_show_layout(ctx, layout);
745 /* draw a cursor - outline box if not in-focus,
746 * inverse-video if it is.
749 bool in_focus = xd->in_focus;
750 struct pane *f = ci->focus;
751 double cx, cy, cw, ch;
753 pango_layout_index_to_pos(layout, ci->num, &curs);
754 if (curs.width <= 0) {
756 pango_layout_set_text(layout, "M", 1);
757 pango_layout_get_extents(layout, NULL, &log);
758 curs.width = log.width;
761 while (in_focus && f->parent->parent != f &&
762 f->parent != ci->home) {
763 if (f->parent->focus != f && f->z >= 0)
768 /* Just an fg:rectangle around the fg:text */
769 /* Add half to x,y as stroke is either side of the line */
770 cx = x * PANGO_SCALE + curs.x + PANGO_SCALE/2;
771 cy = (y - baseline) * PANGO_SCALE + curs.y + PANGO_SCALE/2;
772 ch = curs.height - PANGO_SCALE;
773 cw = curs.width - PANGO_SCALE;
774 cairo_rectangle(ctx, cx/PANGO_SCALE, cy/PANGO_SCALE,
775 cw/PANGO_SCALE, ch/PANGO_SCALE);
776 cairo_set_line_width(ctx, 1.0);
779 /* solid fd:block with txt in bg color */
781 x+curs.x/PANGO_SCALE,
782 y-baseline+curs.y/PANGO_SCALE,
783 curs.width / PANGO_SCALE,
784 curs.height / PANGO_SCALE);
786 if (ci->num < (int)strlen(str)) {
787 const char *cp = str + ci->num;
789 pango_layout_set_text(layout, str + ci->num,
790 cp - (str + ci->num));
792 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
794 cairo_set_source_rgb(ctx, 1.0, 1.0, 1.0);
796 x + curs.x / PANGO_SCALE,
797 y - baseline + curs.y / PANGO_SCALE);
798 pango_cairo_show_layout(ctx, layout);
803 pango_font_description_free(fd);
804 g_object_unref(layout);
808 DEF_CMD(xcb_draw_image)
810 /* 'str' identifies the image. Options are:
811 * file:filename - load file from fs
812 * comm:command - run command collecting bytes
813 * 'str2' container 'mode' information.
814 * By default the image is placed centrally in the pane
815 * and scaled to use either fully height or fully width.
816 * Various letters modify this:
817 * 'S' - stretch to use full height *and* full width
818 * 'L' - place on left if full width isn't used
819 * 'R' - place on right if full width isn't used
820 * 'T' - place at top if full height isn't used
821 * 'B' - place at bottom if full height isn't used.
823 * Also a suffix ":NNxNN" will be parse and the two numbers used
824 * to give number of rows and cols to overlay on the image for
825 * the purpose of cursor positioning. If these are present and
826 * p->cx,cy are not negative, draw a cursor at p->cx,cy highlighting
829 struct xcb_data *xd = ci->home->data;
830 const char *mode = ci->str2 ?: "";
831 bool stretch = strchr(mode, 'S');
832 int w = ci->focus->w, h = ci->focus->h;
837 MagickBooleanType status;
841 cairo_surface_t *surface;
845 ps = find_pixmap(xd, ci->focus, &xo, &yo);
849 instantiate_pixmap(xd, ps);
853 if (strstarts(ci->str, "file:")) {
854 wd = NewMagickWand();
855 status = MagickReadImage(wd, ci->str + 5);
856 if (status == MagickFalse) {
857 DestroyMagickWand(wd);
860 } else if (strstarts(ci->str, "comm:")) {
861 struct call_return cr;
862 wd = NewMagickWand();
863 cr = call_ret(bytes, ci->str+5, ci->focus);
865 DestroyMagickWand(wd);
868 status = MagickReadImageBlob(wd, cr.s, cr.i);
870 if (status == MagickFalse) {
871 DestroyMagickWand(wd);
877 MagickAutoOrientImage(wd);
879 int ih = MagickGetImageHeight(wd);
880 int iw = MagickGetImageWidth(wd);
882 if (iw <= 0 || iw <= 0) {
883 DestroyMagickWand(wd);
886 if (iw * h > ih * w) {
887 /* Image is wider than space, use less height */
889 if (strchr(mode, 'B'))
892 else if (!strchr(mode, 'T'))
897 /* image is too tall, use less width */
899 if (strchr(mode, 'R'))
902 else if (!strchr(mode, 'L'))
907 MagickAdaptiveResizeImage(wd, w, h);
908 stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
909 buf = malloc(h * stride);
910 // Cairo expects 32bit values with A in the high byte, then RGB.
911 // Magick provides 8bit values in the order requests.
912 // So depending on byte order, a different string is needed
914 fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
916 MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
917 surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
919 cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
920 cairo_paint(ps->ctx);
921 cairo_surface_destroy(surface);
924 if (ci->focus->cx >= 0) {
925 struct pane *p = ci->focus;
927 char *cl = strchr(mode, ':');
928 if (cl && sscanf(cl, ":%dx%d", &cols, &rows) == 2) {
929 cairo_rectangle(ps->ctx, p->cx + xo, p->cy + yo,
931 cairo_set_line_width(ps->ctx, 1.0);
932 cairo_set_source_rgb(ps->ctx, 1.0, 0.0, 0.0);
933 cairo_stroke(ps->ctx);
936 DestroyMagickWand(wd);
938 pane_damaged(ci->home, DAMAGED_POSTORDER);
943 DEF_CMD(xcb_image_size)
945 MagickBooleanType status;
951 if (strstarts(ci->str, "file:")) {
952 wd = NewMagickWand();
953 status = MagickReadImage(wd, ci->str + 5);
954 if (status == MagickFalse) {
955 DestroyMagickWand(wd);
958 } else if (strstarts(ci->str, "comm:")) {
959 struct call_return cr;
960 wd = NewMagickWand();
961 cr = call_ret(bytes, ci->str+5, ci->focus);
963 DestroyMagickWand(wd);
966 status = MagickReadImageBlob(wd, cr.s, cr.i);
968 if (status == MagickFalse) {
969 DestroyMagickWand(wd);
975 MagickAutoOrientImage(wd);
976 ih = MagickGetImageHeight(wd);
977 iw = MagickGetImageWidth(wd);
979 DestroyMagickWand(wd);
980 comm_call(ci->comm2, "callback:size", ci->focus,
981 0, NULL, NULL, 0, NULL, NULL,
986 static struct panes *sort_split(struct panes *p)
988 /* consider 'p' to be a list of panes with
989 * ordered subsets (ordered by p->abs_z).
990 * Remove every other such subset and return them
992 * If p is ordered, this means we return NULL.
994 struct panes *ret, **end = &ret;
997 for (; p && p->next; p = next) {
998 /* If these are not ordered, attach p->next at
999 * 'end', and make 'end' point to &p->next.
1002 if (p->p->abs_z < next->p->abs_z) {
1011 static struct panes *sort_merge(struct panes *p1, struct panes *p2)
1013 /* merge p1 and p2 and return result */
1014 struct panes *ret, **end = &ret;
1015 struct panes *prev = NULL;
1018 /* Make p1 the largest (or be added first.
1019 * Then in prev is between them add p2, else p1
1021 if (p1->p->abs_z < p2->p->abs_z) {
1022 struct panes *t = p1;
1027 p1->p->abs_z > prev->p->abs_z &&
1028 prev->p->abs_z >= p2->p->abs_z) {
1029 /* p2 is the better choice */
1046 DEF_CMD(xcb_refresh_post)
1048 struct xcb_data *xd = ci->home->data;
1051 time_start(TIME_WINDOW);
1052 /* First: ensure panes are sorted */
1053 while ((ps = sort_split(xd->panes)) != NULL)
1054 xd->panes = sort_merge(xd->panes, ps);
1056 /* Then merge all update rectanges, checking for movement */
1057 if (!xd->need_update)
1058 xd->need_update = cairo_region_create();
1059 for (ps = xd->panes; ps ; ps = ps->next)
1063 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
1064 if (ps->r.x == NEVER_DRAWN) {
1067 cairo_region_union_rectangle(xd->need_update, &ps->r);
1068 } else if (rel.x != ps->r.x || rel.y != ps->r.y) {
1069 /* Moved, so refresh all.
1070 * This rectangle might be too big if it is clipped,
1071 * but that doesn't really matter.
1073 cairo_region_union_rectangle(xd->need_update, &ps->r);
1076 cairo_region_union_rectangle(xd->need_update, &ps->r);
1077 } else if (ps->need_update) {
1078 cairo_region_translate(ps->need_update, rel.x, rel.y);
1079 cairo_region_union(xd->need_update, ps->need_update);
1081 if (ps->need_update)
1082 cairo_region_destroy(ps->need_update);
1083 ps->need_update = NULL;
1085 /* Now copy all panes onto the window where an update is needed */
1086 for (ps = xd->panes; ps ; ps = ps->next) {
1087 struct xy rel, lo, hi;
1089 cairo_rectangle_int_t r;
1092 cr = cairo_region_copy(xd->need_update);
1094 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
1096 cairo_save(xd->cairo);
1098 cairo_set_source_rgb(xd->cairo,
1099 ps->bg.r, ps->bg.g, ps->bg.b);
1101 cairo_set_source_surface(xd->cairo, ps->surface,
1104 lo = pane_mapxy(ps->p, ci->home, 0, 0, True);
1105 hi = pane_mapxy(ps->p, ci->home, ps->r.width, ps->r.height, True);
1106 r.x = lo.x; r.y = lo.y;
1107 r.width = hi.x - lo.x; r.height = hi.y - lo.y;
1108 cairo_region_intersect_rectangle(cr, &r);
1109 cairo_region_subtract_rectangle(xd->need_update, &r);
1110 nr = cairo_region_num_rectangles(cr);
1111 for (i = 0; i < nr; i++) {
1112 cairo_region_get_rectangle(cr, i, &r);
1113 cairo_rectangle(xd->cairo, r.x, r.y, r.width, r.height);
1114 cairo_fill(xd->cairo);
1116 cairo_restore(xd->cairo);
1119 cairo_region_destroy(xd->need_update);
1120 xd->need_update = NULL;
1121 time_stop(TIME_WINDOW);
1122 xcb_flush(xd->conn);
1126 DEF_CMD(xcb_refresh_size)
1128 /* FIXME: should I consider resizing the window?
1129 * For now, just ensure we redraw everything.
1131 struct xcb_data *xd = ci->home->data;
1132 cairo_rectangle_int_t r = {
1135 .width = ci->home->w,
1136 .height = ci->home->h,
1139 if (!xd->need_update)
1140 xd->need_update = cairo_region_create();
1141 cairo_region_union_rectangle(xd->need_update, &r);
1142 /* Ask common code to notify children */
1143 return Efallthrough;
1146 DEF_CMD(xcb_pane_close)
1148 struct xcb_data *xd = ci->home->data;
1149 struct panes **pp, *ps;
1151 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
1152 if (ps->p != ci->focus)
1155 if (!xd->need_update)
1156 xd->need_update = cairo_region_create();
1157 if (ps->r.x != NEVER_DRAWN)
1158 cairo_region_union_rectangle(xd->need_update, &ps->r);
1162 if (ps->need_update)
1163 cairo_region_destroy(ps->need_update);
1164 cairo_destroy(ps->ctx);
1165 cairo_surface_destroy(ps->surface);
1166 xcb_free_pixmap(xd->conn, ps->draw);
1168 pane_damaged(ci->home, DAMAGED_POSTORDER);
1174 DEF_CMD(xcb_notify_display)
1176 struct xcb_data *xd = ci->home->data;
1177 comm_call(ci->comm2, "callback:display", ci->home, xd->last_event);
1181 static void handle_button(struct pane *home safe,
1182 xcb_button_press_event_t *be safe)
1184 struct xcb_data *xd = home->data;
1185 bool press = (be->response_type & 0x7f) == XCB_BUTTON_PRESS;
1187 char key[2+2+2+9+1+1];
1189 xcb_set_input_focus(xd->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
1190 xd->win, XCB_CURRENT_TIME);
1193 xd->motion_blocked = False;
1194 if (be->state & XCB_KEY_BUT_MASK_MOD_1)
1196 if (be->state & XCB_KEY_BUT_MASK_CONTROL)
1198 if (be->state & XCB_KEY_BUT_MASK_SHIFT)
1201 strcat(key, ":Press-X");
1202 } else if (be->detail >= 4)
1203 /* ignore 'release' for scroll wheel */
1206 strcpy(key, ":Release-X");
1208 key[strlen(key) - 1] = '0' + be->detail;
1209 xd->last_event = time(NULL);
1210 call("Mouse-event", home, be->detail, NULL, key,
1211 press?1:2, NULL, mod,
1212 be->event_x, be->event_y);
1215 static void handle_motion(struct pane *home safe,
1216 xcb_motion_notify_event_t *mne safe)
1218 struct xcb_data *xd = home->data;
1219 xcb_query_pointer_cookie_t c;
1220 xcb_query_pointer_reply_t *qpr;
1222 int x = mne->event_x, y = mne->event_y;
1224 if (xd->motion_blocked)
1226 ret = call("Mouse-event", home, 0, NULL, ":Motion",
1227 3, NULL, NULL, x, y);
1229 xd->motion_blocked = True;
1231 /* This doesn't seem to be needed, but the spec says
1232 * I should do this when using POINTER_MOTION_HINT
1234 c = xcb_query_pointer(xd->conn, xd->win);
1235 qpr = xcb_query_pointer_reply(xd->conn, c, NULL);
1239 static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
1241 struct xcb_data *xd = home->data;
1242 bool in = (fie->response_type & 0x7f) == XCB_FOCUS_IN;
1247 p = pane_focus(home);
1248 pt = call_ret(mark, "doc:point", p);
1250 call("view:changed", p, 0, pt);
1252 call("pane:refocus", home);
1255 static bool select_xkb_events_for_device(xcb_connection_t *conn,
1258 xcb_generic_error_t *error;
1259 xcb_void_cookie_t cookie;
1263 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
1264 XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
1265 XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
1267 required_nkn_details =
1268 (XCB_XKB_NKN_DETAIL_KEYCODES),
1270 required_map_parts =
1271 (XCB_XKB_MAP_PART_KEY_TYPES |
1272 XCB_XKB_MAP_PART_KEY_SYMS |
1273 XCB_XKB_MAP_PART_MODIFIER_MAP |
1274 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
1275 XCB_XKB_MAP_PART_KEY_ACTIONS |
1276 XCB_XKB_MAP_PART_VIRTUAL_MODS |
1277 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
1279 required_state_details =
1280 (XCB_XKB_STATE_PART_MODIFIER_BASE |
1281 XCB_XKB_STATE_PART_MODIFIER_LATCH |
1282 XCB_XKB_STATE_PART_MODIFIER_LOCK |
1283 XCB_XKB_STATE_PART_GROUP_BASE |
1284 XCB_XKB_STATE_PART_GROUP_LATCH |
1285 XCB_XKB_STATE_PART_GROUP_LOCK),
1288 static const xcb_xkb_select_events_details_t details = {
1289 .affectNewKeyboard = required_nkn_details,
1290 .newKeyboardDetails = required_nkn_details,
1291 .affectState = required_state_details,
1292 .stateDetails = required_state_details,
1295 cookie = xcb_xkb_select_events_aux_checked(
1298 required_events, /* affectWhich */
1301 required_map_parts, /* affectMap */
1302 required_map_parts, /* map */
1303 &details); /* details */
1305 error = xcb_request_check(conn, cookie);
1314 static bool update_keymap(struct xcb_data *xd safe)
1316 struct xkb_keymap *new_keymap;
1317 struct xkb_state *new_state;
1319 new_keymap = xkb_x11_keymap_new_from_device(xd->xkb, xd->conn,
1321 XKB_KEYMAP_COMPILE_NO_FLAGS);
1325 new_state = xkb_x11_state_new_from_device(new_keymap, xd->conn,
1328 xkb_keymap_unref(new_keymap);
1332 xkb_state_unref(xd->xkb_state);
1333 xkb_keymap_unref(xd->xkb_keymap);
1334 xd->xkb_keymap = new_keymap;
1335 xd->xkb_state = new_state;
1339 static bool kbd_setup(struct xcb_data *xd safe)
1344 ret = xkb_x11_setup_xkb_extension(xd->conn,
1345 XKB_X11_MIN_MAJOR_XKB_VERSION,
1346 XKB_X11_MIN_MINOR_XKB_VERSION,
1347 XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
1348 NULL, NULL, &xd->first_xkb_event,
1353 xd->xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1356 xd->xkb_device_id = xkb_x11_get_core_keyboard_device_id(xd->conn);
1357 if (xd->xkb_device_id == -1)
1360 if (!update_keymap(xd))
1363 if (!select_xkb_events_for_device(xd->conn, xd->xkb_device_id))
1366 locale = setlocale(LC_CTYPE, NULL);
1368 xkb_compose_table_new_from_locale(xd->xkb, locale,
1369 XKB_COMPOSE_COMPILE_NO_FLAGS);
1370 if (xd->compose_table)
1372 xkb_compose_state_new(xd->compose_table,
1373 XKB_COMPOSE_STATE_NO_FLAGS);
1377 static void kbd_free(struct xcb_data *xd safe)
1379 if (xd->compose_table)
1380 xkb_compose_table_unref(xd->compose_table);
1382 xkb_keymap_unref(xd->xkb_keymap);
1384 xkb_context_unref(xd->xkb);
1388 char *from safe, *to safe;
1390 { "Return", ":Enter"},
1392 { "ISO_Left_Tab",":Tab"},
1393 { "Escape", ":ESC"},
1394 { "Linefeed", ":LF"},
1398 { "Right", ":Right"},
1401 { "BackSpace", ":Backspace"},
1402 { "Delete", ":Del"},
1403 { "Insert", ":Ins"},
1404 { "Next", ":Prior"},
1405 { "Prior", ":Next"},
1420 static void handle_key_press(struct pane *home safe,
1421 xcb_key_press_event_t *kpe safe)
1423 struct xcb_data *xd = home->data;
1424 xkb_keycode_t keycode = kpe->detail;
1425 xcb_keysym_t keysym;
1427 const xkb_keysym_t *syms;
1429 enum xkb_compose_status status;
1430 xkb_mod_index_t mod;
1434 bool shift=False, ctrl=False, alt=False;
1436 xd->last_event = time(NULL);
1438 keysym = xkb_state_key_get_one_sym(xd->xkb_state,
1440 if (xd->compose_state)
1441 xkb_compose_state_feed(xd->compose_state, keysym);
1442 nsyms = xkb_state_key_get_syms(xd->xkb_state, keycode,
1446 status = XKB_COMPOSE_NOTHING;
1447 if (xd->compose_state)
1448 status = xkb_compose_state_get_status(xd->compose_state);
1449 if (status == XKB_COMPOSE_COMPOSING ||
1450 status == XKB_COMPOSE_CANCELLED)
1453 for (mod = 0; mod < xkb_keymap_num_mods(xd->xkb_keymap); mod++) {
1455 if (xkb_state_mod_index_is_active(
1457 XKB_STATE_MODS_EFFECTIVE) <= 0)
1459 /* This does tells me "shift" is consumed for :C:S-l ...
1460 if (xkb_state_mod_index_is_consumed2(
1461 xd->xkb_state, keycode, mod,
1462 XKB_CONSUMED_MODE_XKB))
1465 n = xkb_keymap_mod_get_name(xd->xkb_keymap, mod);
1466 if (n && strcmp(n, "Shift") == 0)
1468 if (n && strcmp(n, "Control") == 0)
1470 if (n && strcmp(n, "Mod1") == 0)
1474 if (status == XKB_COMPOSE_COMPOSED) {
1475 sym = xkb_compose_state_get_one_sym(xd->compose_state);
1479 xkb_compose_state_get_utf8(xd->compose_state,
1484 /* Mod1 can still apply to a composed char */
1485 } else if (nsyms == 1) {
1487 sym = xkb_state_key_get_one_sym(xd->xkb_state, keycode);
1490 xkb_state_key_get_utf8(xd->xkb_state, keycode,
1492 xkb_keysym_get_name(syms[0], key, sizeof(key));
1493 for (i = 0; i < ARRAY_SIZE(key_map); i++) {
1494 if (strcmp(key, key_map[i].from) == 0) {
1495 strcpy(s, key_map[i].to);
1499 if (s[0] == '-' && s[1] >= ' ' && s[1] < 0x7f)
1500 /* Shift is included */
1502 if (s[0] == '-' && s[1] && (unsigned char)(s[1]) < ' ') {
1505 if (s[1] < 'A' || s[1] > 'Z')
1507 } else if (s[0] == '-' && !s[1] && strcmp(key, "space") == 0) {
1508 /* 'nul' becomes "C- " (ctrl-space) */
1515 if (xd->compose_state &&
1516 (status == XKB_COMPOSE_CANCELLED ||
1517 status == XKB_COMPOSE_COMPOSED))
1518 xkb_compose_state_reset(xd->compose_state);
1529 call("Keystroke", home, 0, NULL, mods);
1533 static void handle_xkb_event(struct pane *home safe,
1534 xcb_generic_event_t *ev safe)
1536 struct xcb_data *xd = home->data;
1539 xcb_xkb_new_keyboard_notify_event_t *nkne;
1540 xcb_xkb_state_notify_event_t *sne;
1541 xcb_xkb_map_notify_event_t *mne;
1542 case XCB_XKB_NEW_KEYBOARD_NOTIFY:
1544 if (nkne->deviceID == xd->xkb_device_id &&
1545 nkne->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
1548 case XCB_XKB_MAP_NOTIFY:
1550 if (mne->deviceID == xd->xkb_device_id)
1553 case XCB_XKB_STATE_NOTIFY:
1555 if (sne->deviceID == xd->xkb_device_id)
1556 xkb_state_update_mask(xd->xkb_state,
1567 static void handle_configure(struct pane *home safe,
1568 xcb_configure_notify_event_t *cne safe)
1570 struct xcb_data *xd = home->data;
1572 pane_resize(home, 0, 0, cne->width, cne->height);
1573 cairo_xcb_surface_set_size(xd->surface, cne->width, cne->height);
1576 static void handle_expose(struct pane *home safe,
1577 xcb_expose_event_t *ee safe)
1579 struct xcb_data *xd = home->data;
1580 cairo_rectangle_int_t r = {
1584 .height = ee->height,
1587 if (!xd->need_update)
1588 xd->need_update = cairo_region_create();
1589 cairo_region_union_rectangle(xd->need_update, &r);
1591 pane_damaged(home, DAMAGED_POSTORDER);
1594 static void handle_client_message(struct pane *home safe,
1595 xcb_client_message_event_t *cme safe)
1597 struct xcb_data *xd = home->data;
1599 if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1600 cme->format == 32 &&
1601 cme->window == xd->win &&
1602 cme->data.data32[0] == xd->atoms[a_WM_DELETE_WINDOW]) {
1603 call("window:close", pane_focus(home));
1607 if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1608 cme->format == 32 &&
1609 cme->window == xd->win &&
1610 cme->data.data32[0] == xd->atoms[a_NET_WM_PING]) {
1611 cme->window = xd->screen->root;
1612 xcb_send_event(xd->conn, 0, xd->screen->root,
1613 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1614 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1618 LOG("x11 %s got unexpected client message type=%d/%d win=%x data=%d",
1620 cme->type, cme->format, cme->window, cme->data.data32[0]);
1626 struct xcb_data *xd = ci->home->data;
1627 xcb_generic_event_t *ev;
1632 /* This is a poll - only return 1 on something happening */
1635 while ((ev = xcb_poll_for_event(xd->conn)) != NULL) {
1637 switch (ev->response_type & 0x7f) {
1639 time_start(TIME_KEY);
1640 handle_key_press(ci->home, safe_cast (void*)ev);
1641 time_stop(TIME_KEY);
1643 case XCB_KEY_RELEASE:
1644 /* Ignore for now */
1646 case XCB_BUTTON_PRESS:
1647 case XCB_BUTTON_RELEASE:
1648 time_start(TIME_KEY);
1649 handle_button(ci->home, (void*)ev);
1650 time_stop(TIME_KEY);
1652 case XCB_MOTION_NOTIFY:
1653 time_start(TIME_KEY);
1654 handle_motion(ci->home, (void*)ev);
1655 time_stop(TIME_KEY);
1659 time_start(TIME_WINDOW);
1660 handle_focus(ci->home, (void*)ev);
1661 time_stop(TIME_WINDOW);
1664 time_start(TIME_WINDOW);
1665 handle_expose(ci->home, (void*)ev);
1666 time_stop(TIME_WINDOW);
1668 case XCB_CONFIGURE_NOTIFY:
1669 time_start(TIME_WINDOW);
1670 handle_configure(ci->home, (void*)ev);
1671 time_stop(TIME_WINDOW);
1673 case XCB_CLIENT_MESSAGE:
1674 time_start(TIME_WINDOW);
1675 handle_client_message(ci->home, (void*)ev);
1676 time_stop(TIME_WINDOW);
1678 case XCB_REPARENT_NOTIFY:
1679 /* Not interested */
1681 case XCB_MAP_NOTIFY:
1682 case XCB_UNMAP_NOTIFY:
1683 case XCB_MAPPING_NOTIFY:
1684 /* FIXME what to do?? */
1687 /* Don't know what this means, but I get a lot
1688 * of them so I don't want to log that it was
1693 if ((ev->response_type & 0x7f) ==
1694 xd->first_xkb_event) {
1695 handle_xkb_event(ci->home, ev);
1698 LOG("Ignored X11 event %d", ev->response_type);
1700 xcb_flush(xd->conn);
1702 if (xcb_connection_has_error(xd->conn)) {
1703 call("window:close", ci->home->parent);
1704 pane_close(ci->home);
1709 static void set_str_prop(struct xcb_data *xd safe,
1710 enum my_atoms a, const char *str safe)
1712 xcb_change_property(xd->conn,
1713 XCB_PROP_MODE_REPLACE,
1714 xd->win, xd->atoms[a], XCB_ATOM_STRING,
1715 8, strlen(str), str);
1718 static void set_utf8_prop(struct xcb_data *xd safe,
1719 enum my_atoms a, const char *str safe)
1721 xcb_change_property(xd->conn,
1722 XCB_PROP_MODE_REPLACE,
1723 xd->win, xd->atoms[a],
1724 xd->atoms[a_UTF8_STRING],
1725 8, strlen(str), str);
1728 static void set_card32_property(struct xcb_data *xd safe,
1730 const uint32_t *data, int cnt)
1732 xcb_change_property(xd->conn,
1733 XCB_PROP_MODE_REPLACE,
1734 xd->win, xd->atoms[a],
1735 XCB_ATOM_CARDINAL, 32,
1739 static void set_atom_prop(struct xcb_data *xd safe,
1740 enum my_atoms prop, enum my_atoms alist, ...)
1747 atoms[anum++] = xd->atoms[alist];
1748 va_start(ap, alist);
1749 while ((a = va_arg(ap, enum my_atoms)) != a_NONE)
1751 atoms[anum++] = xd->atoms[a];
1753 xcb_change_property(xd->conn,
1754 XCB_PROP_MODE_REPLACE,
1755 xd->win, xd->atoms[prop],
1760 static void xcb_load_icon(struct pane *p safe,
1761 struct xcb_data *xd safe,
1767 MagickBooleanType status;
1771 path = call_ret(str, "xdg-find-edlib-file", p, 0, NULL,
1772 file, 0, NULL, "data");
1776 wd = NewMagickWand();
1777 status = MagickReadImage(wd, path);
1779 if (status == MagickFalse)
1782 h = MagickGetImageHeight(wd);
1783 w = MagickGetImageWidth(wd);
1785 data = malloc(sizeof(data[0]) * n);
1790 /* Need host-endian ARGB data */
1791 fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
1793 MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt,
1795 set_card32_property(xd, a_NET_WM_ICON, data, n);
1798 DestroyMagickWand(wd);
1802 static struct pane *xcb_display_init(const char *d safe,
1803 const char *disp_auth,
1804 struct pane *focus safe)
1806 struct xcb_data *xd;
1808 xcb_connection_t *conn;
1809 xcb_intern_atom_cookie_t cookies[NR_ATOMS];
1810 xcb_screen_iterator_t iter;
1811 xcb_depth_iterator_t di;
1817 PangoLayout *layout;
1820 cairo_surface_t *surface;
1821 PangoFontDescription *fd;
1822 // FIXME SCALE from environ?? or pango_cairo_context_set_resolution dpi
1823 // 254 * width_in_pixels / width_in_millimeters / 10
1825 conn = safe_cast xcb_connect_auth(d, disp_auth, &screen);
1826 if (xcb_connection_has_error(conn))
1829 p = pane_register(pane_root(focus), 1, &xcb_handle.c);
1834 xd->motion_blocked = True;
1835 xd->in_focus = True;
1838 xd->display = strdup(d);
1840 xd->disp_auth = strdup(disp_auth);
1841 xd->setup = safe_cast xcb_get_setup(conn);
1842 iter = xcb_setup_roots_iterator(xd->setup);
1843 for (i = 0; i < screen; i++)
1844 xcb_screen_next(&iter);
1845 xd->screen = safe_cast iter.data;
1847 di = xcb_screen_allowed_depths_iterator(xd->screen);
1848 while (di.data && di.data->depth < 24)
1849 xcb_depth_next(&di);
1850 //?? look for class = TrueColor??
1851 xd->visual = xcb_depth_visuals(di.data);
1853 for (i = 0; i < NR_ATOMS; i++) {
1854 const char *n = atom_names[i];
1857 cookies[i] = xcb_intern_atom(conn, 0, strlen(n), n);
1860 xd->win = xcb_generate_id(conn);
1861 valwin[0] = xd->screen->white_pixel;
1862 valwin[1] = (XCB_EVENT_MASK_KEY_PRESS |
1863 XCB_EVENT_MASK_KEY_RELEASE |
1864 XCB_EVENT_MASK_BUTTON_PRESS |
1865 XCB_EVENT_MASK_BUTTON_RELEASE |
1866 // XCB_EVENT_MASK_ENTER_WINDOW |
1867 // XCB_EVENT_MASK_LEAVE_WINDOW |
1868 XCB_EVENT_MASK_FOCUS_CHANGE |
1869 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1870 XCB_EVENT_MASK_EXPOSURE |
1871 XCB_EVENT_MASK_BUTTON_MOTION |
1872 XCB_EVENT_MASK_POINTER_MOTION_HINT |
1875 xcb_create_window(conn, XCB_COPY_FROM_PARENT, xd->win,
1879 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
1880 xd->screen->root_visual,
1881 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
1886 surface = cairo_xcb_surface_create(
1887 conn, xd->win, xd->visual, 100, 100);
1890 xd->surface = surface;
1891 cairo = safe_cast cairo_create(xd->surface);
1892 if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS)
1895 fd = pango_font_description_new();
1899 pango_font_description_set_family(xd->fd, "monospace");
1900 pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
1902 layout = pango_cairo_create_layout(xd->cairo);
1903 pango_layout_set_font_description(layout, fd);
1904 pango_layout_set_text(layout, "M", 1);
1905 pango_layout_get_pixel_extents(layout, NULL, &log);
1906 g_object_unref(layout);
1907 xd->lineheight = log.height;
1908 xd->charwidth = log.width;
1910 valwin[0] = xd->charwidth * 80;
1911 valwin[1] = xd->lineheight * 26;
1912 xcb_configure_window(conn, xd->win,
1913 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
1915 cairo_xcb_surface_set_size(xd->surface, valwin[0], valwin[1]);
1917 /* Now resolve all those cookies */
1918 for (i = 0; i < NR_ATOMS; i++) {
1919 xcb_intern_atom_reply_t *r;
1920 r = xcb_intern_atom_reply(conn, cookies[i], NULL);
1923 xd->atoms[i] = r->atom;
1929 * WM_PROTOCOLS _NET_WM_SYN_REQUEST??
1930 * WM_NORMAL_HINTS WM_HINTS
1933 set_str_prop(xd, a_WM_NAME, "EdLib");
1934 set_utf8_prop(xd, a_NET_WM_NAME, "EdLib");
1935 set_str_prop(xd, a_WM_ICON_NAME, "EdLib");
1936 set_utf8_prop(xd, a_NET_WM_ICON_NAME, "EdLib");
1937 gethostname(hostname, sizeof(hostname));
1938 set_str_prop(xd, a_WM_CLIENT_MACHINE, hostname);
1939 set_atom_prop(xd, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW, a_NET_WM_PING, 0);
1941 /* Configure passive grabs - shift, lock, and control only */
1942 xcb_grab_button(xd->conn, 0, xd->win,
1943 XCB_EVENT_MASK_BUTTON_PRESS |
1944 XCB_EVENT_MASK_BUTTON_RELEASE |
1945 XCB_EVENT_MASK_BUTTON_MOTION,
1946 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
1947 XCB_WINDOW_NONE, XCB_CURSOR_NONE,
1948 XCB_BUTTON_INDEX_ANY,
1949 XCB_MOD_MASK_SHIFT |
1951 XCB_MOD_MASK_CONTROL);
1953 xcb_load_icon(focus, xd, "{COMM}-icon.png");
1954 xcb_map_window(conn, xd->win);
1956 pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
1957 call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
1958 call_comm("event:poll", p, &xcb_input);
1959 attr_set_str(&p->attrs, "DISPLAY", d);
1960 attr_set_str(&p->attrs, "XAUTHORITY", disp_auth);
1961 snprintf(scale, sizeof(scale), "%dx%d", xd->charwidth, xd->lineheight);
1962 attr_set_str(&p->attrs, "scale:M", scale);
1963 xd->last_event = time(NULL);
1964 call("editor:request:all-displays", p);
1965 p = call_ret(pane, "editor:activate-display", p);
1969 cairo_destroy(xd->cairo);
1970 cairo_surface_destroy(xd->surface);
1971 xcb_disconnect(conn);
1973 free(xd->disp_auth);
1977 DEF_CMD(xcb_new_display)
1980 const char *d = ci->str;
1981 const char *disp_auth = ci->str2;
1984 d = pane_attr_get(ci->focus, "DISPLAY");
1986 disp_auth = pane_attr_get(ci->focus, "XAUTHORITY");
1988 disp_auth = getenv("XAUTHORITY");
1992 p = xcb_display_init(d, disp_auth, ci->focus);
1994 home_call_ret(pane, ci->focus, "doc:attach-view", p, 1);
1996 comm_call(ci->comm2, "cb", p);
2000 void edlib_init(struct pane *ed safe)
2002 call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
2003 "attach-display-x11");
2004 call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
2005 "interactive-cmd-x11window");
2007 xcb_map = key_alloc();
2009 key_add(xcb_map, "window:close", &xcb_close_display);
2010 key_add(xcb_map, "window:external-viewer", &xcb_external_viewer);
2011 key_add(xcb_map, "window:fullscreen", &xcb_fullscreen);
2012 key_add(xcb_map, "window:new", &xcb_new_display);
2014 key_add(xcb_map, "Close", &xcb_close);
2015 key_add(xcb_map, "Draw:clear", &xcb_clear);
2016 key_add(xcb_map, "Draw:text-size", &xcb_text_size);
2017 key_add(xcb_map, "Draw:text", &xcb_draw_text);
2018 key_add(xcb_map, "Draw:image", &xcb_draw_image);
2019 key_add(xcb_map, "Draw:image-size", &xcb_image_size);
2020 key_add(xcb_map, "Refresh:size", &xcb_refresh_size);
2021 key_add(xcb_map, "Refresh:postorder", &xcb_refresh_post);
2022 key_add(xcb_map, "all-displays", &xcb_notify_display);
2023 key_add(xcb_map, "Notify:Close", &xcb_pane_close);