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.
22 /* xkb.h has a 'long' in an enum :-( */
24 XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY,
25 XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
26 XCB_XKB_NEW_KEYBOARD_NOTIFY,
27 XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
28 XCB_XKB_MAP_PART_MODIFIER_MAP,
29 XCB_XKB_STATE_PART_MODIFIER_LOCK,
30 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS,
31 XCB_XKB_STATE_PART_GROUP_BASE,
32 XCB_XKB_MAP_PART_KEY_ACTIONS,
33 XCB_XKB_STATE_PART_GROUP_LATCH,
34 XCB_XKB_MAP_PART_VIRTUAL_MODS,
35 XCB_XKB_STATE_PART_GROUP_LOCK,
36 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP,
37 XCB_XKB_NKN_DETAIL_KEYCODES,
38 XCB_XKB_MAP_PART_KEY_TYPES,
39 XCB_XKB_MAP_PART_KEY_SYMS,
40 XCB_XKB_STATE_PART_MODIFIER_BASE,
41 XCB_XKB_STATE_PART_MODIFIER_LATCH,
45 typedef uint16_t xcb_xkb_device_spec_t;
46 typedef struct xcb_xkb_select_events_details_t {
47 uint16_t affectNewKeyboard;
48 uint16_t newKeyboardDetails;
50 uint16_t stateDetails;
51 /* and other fields */
52 } xcb_xkb_select_events_details_t;
53 typedef struct xcb_xkb_new_keyboard_notify_event_t {
56 /* and other fields */
57 } xcb_xkb_new_keyboard_notify_event_t;
58 typedef struct xcb_xkb_state_notify_event_t {
66 /* and other fields */
67 } xcb_xkb_state_notify_event_t;
68 typedef struct xcb_xkb_map_notify_event_t {
70 } xcb_xkb_map_notify_event_t;
72 xcb_xkb_select_events_aux_checked(xcb_connection_t *c,
73 xcb_xkb_device_spec_t deviceSpec,
79 const xcb_xkb_select_events_details_t *details);
82 #include <xcb/xcbext.h>
88 #include <cairo-xcb.h>
90 #include <wand/MagickWand.h>
92 // enums confuse sparse...
93 #define MagickBooleanType int
97 #include <pango/pango.h>
98 #include <pango/pangocairo.h>
100 typedef struct PangoFontDescription {} PangoFontDescription;
101 typedef struct PangoLayout {} PangoLayout;
102 typedef struct PangoContext {} PangoContext;
103 typedef struct PangoFontMetrics {} PangoFontMetrics;
104 typedef struct PangoRectangle { int x,y,width,height;} PangoRectangle;
105 typedef enum { PANGO_STYLE_NORMAL, PANGO_STYLE_OBLIQUE, PANGO_STYLE_ITALIC
107 typedef enum { PANGO_VARIANT_NORMAL, PANGO_VARIANT_SMALL_CAPS } PangoVariant;
108 typedef enum { PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_BOLD } PangoWeight;
109 PangoFontDescription *pango_font_description_new(void);
110 void pango_font_description_set_family_static(PangoFontDescription*, char*);
111 void pango_font_description_set_family(PangoFontDescription*, char*);
112 void pango_font_description_set_size(PangoFontDescription*, int);
113 void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
114 void pango_font_description_set_variant(PangoFontDescription*, PangoVariant);
115 void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
116 #define PANGO_SCALE (1024)
118 PangoLayout *pango_cairo_create_layout(cairo_t*);
119 void g_object_unref(PangoLayout*);
120 PangoContext *pango_cairo_create_context(cairo_t *);
121 void pango_cairo_show_layout(cairo_t *, PangoLayout *);
122 PangoFontMetrics *pango_context_get_metrics(PangoContext*, PangoFontDescription*, void*);
123 void pango_font_description_free(PangoFontDescription*);
124 int pango_font_metrics_get_approximate_char_width(PangoFontMetrics *);
125 int pango_font_metrics_get_ascent(PangoFontMetrics *);
126 int pango_font_metrics_get_descent(PangoFontMetrics *);
127 void pango_font_metrics_unref(PangoFontMetrics *);
128 PangoContext* pango_layout_get_context(PangoLayout *);
129 int pango_layout_get_baseline(PangoLayout *);
130 void pango_layout_get_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
131 void pango_layout_get_pixel_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
132 void pango_layout_set_font_description(PangoLayout *, PangoFontDescription *);
133 void pango_layout_set_text(PangoLayout*, const char *, int);
134 void pango_layout_xy_to_index(PangoLayout*, int, int, int*, int*);
135 void pango_layout_index_to_pos(PangoLayout*, int, PangoRectangle*);
138 #include <xkbcommon/xkbcommon.h>
139 #include <xkbcommon/xkbcommon-compose.h>
140 #include <xkbcommon/xkbcommon-x11.h>
150 a_WM_STATE, a_STATE_FULLSCREEN,
151 a_WM_NAME, a_NET_WM_NAME,
152 a_WM_ICON_NAME, a_NET_WM_ICON_NAME,
153 a_WM_PROTOCOLS, a_WM_DELETE_WINDOW,
159 static char *atom_names[NR_ATOMS] = {
161 [a_WM_STATE] = "_NET_WM_STATE",
162 [a_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN",
163 [a_WM_NAME] = "WM_NAME",
164 [a_NET_WM_NAME] = "_NET_WM_NAME",
165 [a_WM_ICON_NAME] = "WM_ICON_NAME",
166 [a_NET_WM_ICON_NAME] = "_NET_WM_ICON_NAME",
167 [a_WM_PROTOCOLS] = "WM_PROTOCOLS",
168 [a_WM_DELETE_WINDOW] = "WM_DELETE_WINDOW",
169 [a_NET_WM_PING] = "_NET_WM_PING",
170 [a_WM_CLIENT_MACHINE] = "WM_CLIENT_MACHINE",
171 [a_UTF8_STRING] = "UTF8_STRING",
179 xcb_connection_t *conn safe;
183 const xcb_setup_t *setup safe;
184 const xcb_screen_t *screen safe;
185 xcb_atom_t atoms[NR_ATOMS];
189 xcb_visualtype_t *visual;
191 cairo_surface_t *surface safe;
192 PangoFontDescription *fd safe;
194 int charwidth, lineheight;
195 cairo_region_t *need_update;
200 struct xkb_context *xkb;
201 uint8_t first_xkb_event;
202 int32_t xkb_device_id;
203 struct xkb_state *xkb_state;
204 struct xkb_compose_state *compose_state;
205 struct xkb_compose_table *compose_table;
206 struct xkb_keymap *xkb_keymap;
213 /* FIXME use hash?? */
217 cairo_rectangle_int_t r;
221 cairo_surface_t *surface;
222 cairo_region_t *need_update;
226 /* panes->r.x is NEVER_DRAWN if the pane has not been drawn */
227 #define NEVER_DRAWN (-60000)
229 static struct map *xcb_map;
230 DEF_LOOKUP_CMD(xcb_handle, xcb_map);
232 static struct panes *get_pixmap(struct pane *home safe,
235 struct xcb_data *xd = home->data;
236 struct panes **pp, *ps;
238 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
241 if (ps->r.width == p->w && ps->r.height == p->h)
244 if (ps->r.x != NEVER_DRAWN) {
245 if (!xd->need_update)
246 xd->need_update = cairo_region_create();
247 cairo_region_union_rectangle(xd->need_update, &ps->r);
250 cairo_destroy(ps->ctx);
252 cairo_surface_destroy(ps->surface);
254 xcb_free_pixmap(xd->conn, ps->draw);
260 ps->r.x = ps->r.y = NEVER_DRAWN;
263 ps->bg.r = ps->bg.g = ps->bg.b = 0;
265 pane_add_notify(home, p, "Notify:Close");
271 static void instantiate_pixmap(struct xcb_data *xd safe,
272 struct panes *ps safe)
274 ps->draw = xcb_generate_id(xd->conn);
275 xcb_create_pixmap(xd->conn, xd->screen->root_depth, ps->draw,
276 xd->win, ps->r.width, ps->r.height);
277 ps->surface = cairo_xcb_surface_create(
278 xd->conn, ps->draw, xd->visual, ps->r.width, ps->r.height);
281 ps->ctx = cairo_create(ps->surface);
284 cairo_set_source_rgb(ps->ctx, ps->bg.r, ps->bg.g, ps->bg.b);
285 cairo_paint(ps->ctx);
289 cairo_surface_destroy(ps->surface);
292 xcb_free_pixmap(xd->conn, ps->draw);
296 static struct panes *find_pixmap(struct xcb_data *xd safe, struct pane *p safe,
297 int *xp safe, int *yp safe)
300 struct panes *ret = NULL;
302 while (!ret && p->parent != p) {
304 for (ps = xd->panes; ps ; ps = ps->next)
320 static inline double cvt(int i)
322 return (float)i / 1000.0;
325 static void parse_attrs(
326 struct pane *home safe, const char *cattrs, int scale,
327 struct rgb *fgp, struct rgb *bgp, bool *underline,
328 PangoFontDescription **fdp)
330 char *attrs = strdup(cattrs ?: "");
333 char *fg = NULL, *bg = NULL;
337 PangoFontDescription *fd = NULL;
338 PangoStyle style = PANGO_STYLE_NORMAL;
339 PangoVariant variant = PANGO_VARIANT_NORMAL;
340 PangoWeight weight = PANGO_WEIGHT_NORMAL;
343 fd = pango_font_description_new();
345 pango_font_description_set_family_static(fd, "mono");
348 while ((word = strsep(&ap, ",")) != NULL) {
349 if (fd && strncmp(word, "family:", 7) == 0)
350 pango_font_description_set_family(fd, word+7);
351 if (strcmp(word, "large") == 0)
353 if (strcmp(word, "small") == 0)
355 if (isdigit(word[0])) {
357 double s = strtod(word, &end);
358 if (end && end != word && !*end)
359 size = trunc(s * 1000.0);
363 if (strcmp(word, "oblique") == 0)
364 style = PANGO_STYLE_OBLIQUE;
365 if (strcmp(word, "italic") == 0)
366 style = PANGO_STYLE_ITALIC;
367 if (strcmp(word, "normal") == 0)
368 style = PANGO_STYLE_NORMAL;
369 if (strcmp(word, "small-caps") == 0)
370 variant = PANGO_VARIANT_SMALL_CAPS;
372 if (strcmp(word, "bold") == 0)
373 weight = PANGO_WEIGHT_BOLD;
374 if (strcmp(word, "nobold") == 0)
375 weight = PANGO_WEIGHT_NORMAL;
377 if (strncmp(word, "fg:", 3) == 0)
379 if (strncmp(word, "bg:", 3) == 0)
381 if (strcmp(word, "inverse") == 0)
383 if (strcmp(word, "underline") == 0)
399 struct call_return ret = call_ret(all, "colour:map", home,
402 fgp->g = cvt(ret.i2);
407 struct call_return ret = call_ret(all, "colour:map", home,
410 bgp->g = cvt(ret.i2);
415 pango_font_description_set_size(fd, size * scale / PANGO_SCALE);
416 if (style != PANGO_STYLE_NORMAL)
417 pango_font_description_set_style(fd, style);
418 if (variant != PANGO_VARIANT_NORMAL)
419 pango_font_description_set_variant(fd, variant);
420 if (weight != PANGO_WEIGHT_NORMAL)
421 pango_font_description_set_weight(fd, weight);
430 struct call_return *cr = container_of(ci->comm, struct call_return, c);
436 DEF_CMD(xcb_close_display)
438 /* If this is only display, then refuse to close this one */
439 struct call_return cr;
440 struct xcb_data *xd = ci->home->data;
442 call("Message", ci->focus, 0, NULL, xd->noclose);
447 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
449 pane_close(ci->home);
451 call("Message", ci->focus, 0, NULL,
452 "Cannot close only window.");
456 DEF_CMD(xcb_set_noclose)
458 struct xcb_data *xd = ci->home->data;
463 xd->noclose = strdup(ci->str);
467 static void wait_for(struct xcb_data *xd safe)
469 struct pids **pp = &xd->pids;
472 struct pids *p = *pp;
473 if (waitpid(p->pid, NULL, WNOHANG) > 0) {
481 DEF_CMD(xcb_external_viewer)
483 struct xcb_data *xd = ci->home->data;
484 const char *path = ci->str;
491 switch (pid = fork()) {
495 setenv("DISPLAY", xd->display, 1);
497 setenv("XAUTHORITY", xd->disp_auth, 1);
498 fd = open("/dev/null", O_RDWR);
506 execlp("xdg-open", "xdg-open", path, NULL);
508 default: /* parent */
509 p = malloc(sizeof(*p));
519 DEF_CMD(xcb_fullscreen)
521 struct xcb_data *xd = ci->home->data;
522 xcb_client_message_event_t msg = {};
524 msg.response_type = XCB_CLIENT_MESSAGE;
526 msg.window = xd->win;
527 msg.type = xd->atoms[a_WM_STATE];
529 msg.data.data32[0] = 1; /* ADD */
531 msg.data.data32[0] = 0; /* REMOVE */
532 msg.data.data32[1] = xd->atoms[a_STATE_FULLSCREEN];
533 msg.data.data32[2] = 0;
534 msg.data.data32[3] = 1; /* source indicator */
536 xcb_send_event(xd->conn, 0, xd->screen->root,
537 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
538 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
544 static void panes_free(struct xcb_data *xd safe)
547 struct panes *ps = xd->panes;
548 xd->panes = ps->next;
550 cairo_destroy(ps->ctx);
552 cairo_surface_destroy(ps->surface);
554 xcb_free_pixmap(xd->conn, ps->draw);
559 static void kbd_free(struct xcb_data *xd safe);
563 struct xcb_data *xd = ci->home->data;
565 xcb_destroy_window(xd->conn, xd->win);
573 struct xcb_data *xd = ci->home->data;
575 pango_font_description_free(xd->fd);
576 cairo_destroy(xd->cairo);
577 cairo_device_finish(cairo_surface_get_device(xd->surface));
578 cairo_surface_destroy(xd->surface);
582 xcb_disconnect(xd->conn);
584 cairo_region_destroy(xd->need_update);
591 struct xcb_data *xd = ci->home->data;
592 const char *attr = ci->str;
593 struct panes *src = NULL, *dest;
596 cairo_rectangle_int_t r;
599 parse_attrs(ci->home, attr, PANGO_SCALE, NULL, &bg, NULL, NULL);
601 bg.r = bg.g = bg.b = 1.0;
603 src = find_pixmap(xd, ci->focus->parent, &x, &y);
607 bg.r = bg.g = bg.b = 1.0;
608 else if (src->bg.g >= 0)
610 else if (src->surface == NULL)
611 bg.r = bg.g = bg.b = 1.0;
616 dest = get_pixmap(ci->home, ci->focus);
621 cairo_set_source_rgb(dest->ctx, bg.r, bg.g, bg.b);
622 cairo_paint(dest->ctx);
627 instantiate_pixmap(xd, dest);
629 cairo_set_source_surface(dest->ctx, src->surface, -x, -y);
630 cairo_paint(dest->ctx);
634 pane_damaged(ci->home, DAMAGED_POSTORDER);
636 if (!dest->need_update)
637 dest->need_update = cairo_region_create();
640 r.width = ci->focus->w;
641 r.height = ci->focus->h;
642 cairo_region_union_rectangle(dest->need_update, &r);
646 DEF_CMD(xcb_text_size)
648 struct xcb_data *xd = ci->home->data;
649 const char *attr = ci->str2 ?: "";
650 const char *str = ci->str ?: "";
651 int scale = ci->num2;
653 PangoFontDescription *fd;
660 if (!utf8_valid(str))
662 parse_attrs(ci->home, attr, scale, NULL, NULL, NULL, &fd);
663 /* If we use an empty string, line-height is wrong */
664 layout = pango_cairo_create_layout(xd->cairo);
665 pango_layout_set_text(layout, *str ? str : "M", -1);
666 pango_layout_set_font_description(layout, fd);
667 pango_layout_get_pixel_extents(layout, NULL, &log);
668 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
672 else if (log.width <= ci->num)
673 max_bytes = strlen(str);
675 pango_layout_xy_to_index(layout, PANGO_SCALE*ci->num,
676 baseline, &max_bytes, NULL);
678 comm_call(ci->comm2, "cb", ci->focus, max_bytes, NULL, NULL,
679 baseline, NULL, NULL,
680 str && *str ? log.width : 0,
683 pango_font_description_free(fd);
684 g_object_unref(layout);
688 DEF_CMD(xcb_draw_text)
690 struct xcb_data *xd = ci->home->data;
691 const char *str = ci->str;
692 const char *attr = ci->str2;
697 PangoFontDescription *fd;
707 ps = find_pixmap(xd, ci->focus, &xo, &yo);
711 instantiate_pixmap(xd, ps);
717 if (!utf8_valid(str))
720 pane_damaged(ci->home, DAMAGED_POSTORDER);
723 scale = ci->num2 * 10 / xd->charwidth;
725 parse_attrs(ci->home, attr, scale, &fg, &bg, &ul, &fd);
729 layout = pango_cairo_create_layout(ctx);
730 pango_layout_set_text(layout, str, -1);
731 pango_layout_set_font_description(layout, fd);
732 pango_layout_get_pixel_extents(layout, NULL, &log);
733 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
736 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
737 cairo_rectangle(ctx, x+log.x, y - baseline + log.y,
738 log.width, log.height);
741 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
743 /* Draw an underline */
744 cairo_rectangle(ctx, x+log.x, y+2+log.y,
749 cairo_move_to(ctx, x, y - baseline);
750 pango_cairo_show_layout(ctx, layout);
754 /* draw a cursor - outline box if not in-focus,
755 * inverse-video if it is.
758 bool in_focus = xd->in_focus;
759 struct pane *f = ci->focus;
761 pango_layout_index_to_pos(layout, ci->num, &curs);
762 if (curs.width <= 0) {
764 pango_layout_set_text(layout, "M", 1);
765 pango_layout_get_extents(layout, NULL, &log);
766 curs.width = log.width;
768 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE, y-baseline+curs.y/PANGO_SCALE,
769 (curs.width - PANGO_SCALE/2) / PANGO_SCALE,
770 (curs.height - PANGO_SCALE/2) / PANGO_SCALE);
771 cairo_set_line_width(ctx, 1.0);
774 while (in_focus && f->parent->parent != f &&
775 f->parent != ci->home) {
776 if (f->parent->focus != f && f->z >= 0)
782 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
783 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE,
784 y-baseline+curs.y/PANGO_SCALE,
785 curs.width / PANGO_SCALE,
786 curs.height / PANGO_SCALE);
788 if (ci->num < (int)strlen(str)) {
789 const char *cp = str + ci->num;
791 pango_layout_set_text(layout, str + ci->num,
792 cp - (str + ci->num));
794 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
796 cairo_set_source_rgb(ctx, 1.0, 1.0, 1.0);
798 x + curs.x / PANGO_SCALE,
799 y - baseline + curs.y / PANGO_SCALE);
800 pango_cairo_show_layout(ctx, layout);
805 pango_font_description_free(fd);
806 g_object_unref(layout);
810 DEF_CMD(xcb_draw_image)
812 /* 'str' identifies the image. Options are:
813 * file:filename - load file from fs
814 * comm:command - run command collecting bytes
815 * 'num' is '16' if image should be stretched to fill pane
816 * Otherwise it is the 'or' of
817 * 0,1,2 for left/middle/right in x direction
818 * 0,4,8 for top/middle/bottom in y direction
819 * only one of these can be used as image will fill pane
820 * in other direction.
821 * If 'x' and 'y' are both positive, draw cursor box at
822 * p->cx, p->cy of a size so that 'x' will fit across and
825 struct xcb_data *xd = ci->home->data;
826 bool stretch = ci->num & 16;
828 int w = ci->focus->w, h = ci->focus->h;
833 MagickBooleanType status;
837 cairo_surface_t *surface;
841 ps = find_pixmap(xd, ci->focus, &xo, &yo);
845 instantiate_pixmap(xd, ps);
849 if (strncmp(ci->str, "file:", 5) == 0) {
850 wd = NewMagickWand();
851 status = MagickReadImage(wd, ci->str + 5);
852 if (status == MagickFalse) {
853 DestroyMagickWand(wd);
856 } else if (strncmp(ci->str, "comm:", 5) == 0) {
857 struct call_return cr;
858 wd = NewMagickWand();
859 cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
861 DestroyMagickWand(wd);
864 status = MagickReadImageBlob(wd, cr.s, cr.i);
866 if (status == MagickFalse) {
867 DestroyMagickWand(wd);
873 MagickAutoOrientImage(wd);
875 int ih = MagickGetImageHeight(wd);
876 int iw = MagickGetImageWidth(wd);
878 if (iw <= 0 || iw <= 0) {
879 DestroyMagickWand(wd);
882 if (iw * h > ih * w) {
883 /* Image is wider than space, use less height */
885 switch(pos & (8+4)) {
887 y = (h - ih) / 2; break;
893 /* image is too tall, use less width */
895 switch (pos & (1+2)) {
897 x = (w - iw) / 2; break;
904 MagickAdaptiveResizeImage(wd, w, h);
905 stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
906 buf = malloc(h * stride);
907 // Cairo expects 32bit values with A in the high byte, then RGB.
908 // Magick provides 8bit values in the order requests.
909 // So depending on byte order, a different string is needed
911 fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
913 MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
914 surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
916 cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
917 cairo_paint(ps->ctx);
918 cairo_surface_destroy(surface);
921 if (ci->x > 0 && ci->y > 0 && ci->focus->cx >= 0) {
922 struct pane *p = ci->focus;
923 cairo_rectangle(ps->ctx, p->cx + xo, p->cy + yo,
925 cairo_set_line_width(ps->ctx, 1.0);
926 cairo_set_source_rgb(ps->ctx, 1.0, 0.0, 0.0);
927 cairo_stroke(ps->ctx);
929 DestroyMagickWand(wd);
931 pane_damaged(ci->home, DAMAGED_POSTORDER);
936 DEF_CMD(xcb_image_size)
938 MagickBooleanType status;
944 if (strncmp(ci->str, "file:", 5) == 0) {
945 wd = NewMagickWand();
946 status = MagickReadImage(wd, ci->str + 5);
947 if (status == MagickFalse) {
948 DestroyMagickWand(wd);
951 } else if (strncmp(ci->str, "comm:", 5) == 0) {
952 struct call_return cr;
953 wd = NewMagickWand();
954 cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
956 DestroyMagickWand(wd);
959 status = MagickReadImageBlob(wd, cr.s, cr.i);
961 if (status == MagickFalse) {
962 DestroyMagickWand(wd);
968 MagickAutoOrientImage(wd);
969 ih = MagickGetImageHeight(wd);
970 iw = MagickGetImageWidth(wd);
972 DestroyMagickWand(wd);
973 comm_call(ci->comm2, "callback:size", ci->focus,
974 0, NULL, NULL, 0, NULL, NULL,
979 static struct panes *sort_split(struct panes *p)
981 /* consider 'p' to be a list of panes with
982 * ordered subsets (ordered by p->abs_z).
983 * Remove every other such subset and return them
985 * If p is ordered, this means we return NULL.
987 struct panes *ret, **end = &ret;
990 for (; p && p->next; p = next) {
991 /* If these are not ordered, attach p->next at
992 * 'end', and make 'end' point to &p->next.
995 if (p->p->abs_z < next->p->abs_z) {
1004 static struct panes *sort_merge(struct panes *p1, struct panes *p2)
1006 /* merge p1 and p2 and return result */
1007 struct panes *ret, **end = &ret;
1008 struct panes *prev = NULL;
1011 /* Make p1 the largest (or be added first.
1012 * Then in prev is between them add p2, else p1
1014 if (p1->p->abs_z < p2->p->abs_z) {
1015 struct panes *t = p1;
1020 p1->p->abs_z > prev->p->abs_z &&
1021 prev->p->abs_z >= p2->p->abs_z) {
1022 /* p2 is the better choice */
1039 DEF_CMD(xcb_refresh_post)
1041 struct xcb_data *xd = ci->home->data;
1044 time_start(TIME_WINDOW);
1045 /* First: ensure panes are sorted */
1046 while ((ps = sort_split(xd->panes)) != NULL)
1047 xd->panes = sort_merge(xd->panes, ps);
1049 /* Then merge all update rectanges, checking for movement */
1050 if (!xd->need_update)
1051 xd->need_update = cairo_region_create();
1052 for (ps = xd->panes; ps ; ps = ps->next)
1056 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
1057 if (ps->r.x == NEVER_DRAWN) {
1060 cairo_region_union_rectangle(xd->need_update, &ps->r);
1061 } else if (rel.x != ps->r.x || rel.y != ps->r.y) {
1062 /* Moved, so refresh all.
1063 * This rectangle might be too big if it is clipped,
1064 * but that doesn't really matter.
1066 cairo_region_union_rectangle(xd->need_update, &ps->r);
1069 cairo_region_union_rectangle(xd->need_update, &ps->r);
1070 } else if (ps->need_update) {
1071 cairo_region_translate(ps->need_update, rel.x, rel.y);
1072 cairo_region_union(xd->need_update, ps->need_update);
1074 if (ps->need_update)
1075 cairo_region_destroy(ps->need_update);
1076 ps->need_update = NULL;
1078 /* Now copy all panes onto the window where an update is needed */
1079 for (ps = xd->panes; ps ; ps = ps->next) {
1080 struct xy rel, lo, hi;
1082 cairo_rectangle_int_t r;
1085 cr = cairo_region_copy(xd->need_update);
1087 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
1089 cairo_save(xd->cairo);
1091 cairo_set_source_rgb(xd->cairo,
1092 ps->bg.r, ps->bg.g, ps->bg.b);
1094 cairo_set_source_surface(xd->cairo, ps->surface,
1097 lo = pane_mapxy(ps->p, ci->home, 0, 0, True);
1098 hi = pane_mapxy(ps->p, ci->home, ps->r.width, ps->r.height, True);
1099 r.x = lo.x; r.y = lo.y;
1100 r.width = hi.x - lo.x; r.height = hi.y - lo.y;
1101 cairo_region_intersect_rectangle(cr, &r);
1102 cairo_region_subtract_rectangle(xd->need_update, &r);
1103 nr = cairo_region_num_rectangles(cr);
1104 for (i = 0; i < nr; i++) {
1105 cairo_region_get_rectangle(cr, i, &r);
1106 cairo_rectangle(xd->cairo, r.x, r.y, r.width, r.height);
1107 cairo_fill(xd->cairo);
1109 cairo_restore(xd->cairo);
1112 cairo_region_destroy(xd->need_update);
1113 xd->need_update = NULL;
1114 time_stop(TIME_WINDOW);
1115 xcb_flush(xd->conn);
1119 DEF_CMD(xcb_refresh_size)
1121 /* FIXME: should I consider resizing the window?
1122 * For now, just ensure we redraw everything.
1124 struct xcb_data *xd = ci->home->data;
1125 cairo_rectangle_int_t r = {
1128 .width = ci->home->w,
1129 .height = ci->home->h,
1132 if (!xd->need_update)
1133 xd->need_update = cairo_region_create();
1134 cairo_region_union_rectangle(xd->need_update, &r);
1135 /* Ask common code to notify children */
1136 return Efallthrough;
1139 DEF_CMD(xcb_pane_close)
1141 struct xcb_data *xd = ci->home->data;
1142 struct panes **pp, *ps;
1144 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
1145 if (ps->p != ci->focus)
1148 if (!xd->need_update)
1149 xd->need_update = cairo_region_create();
1150 if (ps->r.x != NEVER_DRAWN)
1151 cairo_region_union_rectangle(xd->need_update, &ps->r);
1155 if (ps->need_update)
1156 cairo_region_destroy(ps->need_update);
1157 cairo_destroy(ps->ctx);
1158 cairo_surface_destroy(ps->surface);
1159 xcb_free_pixmap(xd->conn, ps->draw);
1161 pane_damaged(ci->home, DAMAGED_POSTORDER);
1167 DEF_CMD(xcb_notify_display)
1169 struct xcb_data *xd = ci->home->data;
1170 comm_call(ci->comm2, "callback:display", ci->home, xd->last_event);
1174 static void handle_button(struct pane *home safe,
1175 xcb_button_press_event_t *be safe)
1177 struct xcb_data *xd = home->data;
1178 bool press = (be->response_type & 0x7f) == XCB_BUTTON_PRESS;
1180 char key[2+2+2+9+1+1];
1182 xcb_set_input_focus(xd->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
1183 xd->win, XCB_CURRENT_TIME);
1186 xd->motion_blocked = False;
1187 if (be->state & XCB_KEY_BUT_MASK_MOD_1)
1189 if (be->state & XCB_KEY_BUT_MASK_CONTROL)
1191 if (be->state & XCB_KEY_BUT_MASK_SHIFT)
1194 strcat(key, ":Press-X");
1195 } else if (be->detail >= 4)
1196 /* ignore 'release' for scroll wheel */
1199 strcpy(key, ":Release-X");
1201 key[strlen(key) - 1] = '0' + be->detail;
1202 xd->last_event = time(NULL);
1203 call("Mouse-event", home, be->detail, NULL, key,
1204 press?1:2, NULL, mod,
1205 be->event_x, be->event_y);
1208 static void handle_motion(struct pane *home safe,
1209 xcb_motion_notify_event_t *mne safe)
1211 struct xcb_data *xd = home->data;
1212 xcb_query_pointer_cookie_t c;
1213 xcb_query_pointer_reply_t *qpr;
1215 int x = mne->event_x, y = mne->event_y;
1217 if (xd->motion_blocked)
1219 ret = call("Mouse-event", home, 0, NULL, ":Motion",
1220 3, NULL, NULL, x, y);
1222 xd->motion_blocked = True;
1224 /* This doesn't seem to be needed, but the spec says
1225 * I should do this when using POINTER_MOTION_HINT
1227 c = xcb_query_pointer(xd->conn, xd->win);
1228 qpr = xcb_query_pointer_reply(xd->conn, c, NULL);
1232 static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
1234 struct xcb_data *xd = home->data;
1235 bool in = (fie->response_type & 0x7f) == XCB_FOCUS_IN;
1240 p = pane_leaf(home);
1241 pt = call_ret(mark, "doc:point", p);
1243 call("view:changed", p, 0, pt);
1245 call("pane:refocus", home);
1248 static bool select_xkb_events_for_device(xcb_connection_t *conn,
1251 xcb_generic_error_t *error;
1252 xcb_void_cookie_t cookie;
1256 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
1257 XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
1258 XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
1260 required_nkn_details =
1261 (XCB_XKB_NKN_DETAIL_KEYCODES),
1263 required_map_parts =
1264 (XCB_XKB_MAP_PART_KEY_TYPES |
1265 XCB_XKB_MAP_PART_KEY_SYMS |
1266 XCB_XKB_MAP_PART_MODIFIER_MAP |
1267 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
1268 XCB_XKB_MAP_PART_KEY_ACTIONS |
1269 XCB_XKB_MAP_PART_VIRTUAL_MODS |
1270 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
1272 required_state_details =
1273 (XCB_XKB_STATE_PART_MODIFIER_BASE |
1274 XCB_XKB_STATE_PART_MODIFIER_LATCH |
1275 XCB_XKB_STATE_PART_MODIFIER_LOCK |
1276 XCB_XKB_STATE_PART_GROUP_BASE |
1277 XCB_XKB_STATE_PART_GROUP_LATCH |
1278 XCB_XKB_STATE_PART_GROUP_LOCK),
1281 static const xcb_xkb_select_events_details_t details = {
1282 .affectNewKeyboard = required_nkn_details,
1283 .newKeyboardDetails = required_nkn_details,
1284 .affectState = required_state_details,
1285 .stateDetails = required_state_details,
1288 cookie = xcb_xkb_select_events_aux_checked(
1291 required_events, /* affectWhich */
1294 required_map_parts, /* affectMap */
1295 required_map_parts, /* map */
1296 &details); /* details */
1298 error = xcb_request_check(conn, cookie);
1307 static bool update_keymap(struct xcb_data *xd safe)
1309 struct xkb_keymap *new_keymap;
1310 struct xkb_state *new_state;
1312 new_keymap = xkb_x11_keymap_new_from_device(xd->xkb, xd->conn,
1314 XKB_KEYMAP_COMPILE_NO_FLAGS);
1318 new_state = xkb_x11_state_new_from_device(new_keymap, xd->conn,
1321 xkb_keymap_unref(new_keymap);
1325 xkb_state_unref(xd->xkb_state);
1326 xkb_keymap_unref(xd->xkb_keymap);
1327 xd->xkb_keymap = new_keymap;
1328 xd->xkb_state = new_state;
1332 static bool kbd_setup(struct xcb_data *xd safe)
1337 ret = xkb_x11_setup_xkb_extension(xd->conn,
1338 XKB_X11_MIN_MAJOR_XKB_VERSION,
1339 XKB_X11_MIN_MINOR_XKB_VERSION,
1340 XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
1341 NULL, NULL, &xd->first_xkb_event,
1346 xd->xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1349 xd->xkb_device_id = xkb_x11_get_core_keyboard_device_id(xd->conn);
1350 if (xd->xkb_device_id == -1)
1353 if (!update_keymap(xd))
1356 if (!select_xkb_events_for_device(xd->conn, xd->xkb_device_id))
1359 locale = setlocale(LC_CTYPE, NULL);
1361 xkb_compose_table_new_from_locale(xd->xkb, locale,
1362 XKB_COMPOSE_COMPILE_NO_FLAGS);
1363 if (xd->compose_table)
1365 xkb_compose_state_new(xd->compose_table,
1366 XKB_COMPOSE_STATE_NO_FLAGS);
1370 static void kbd_free(struct xcb_data *xd safe)
1372 if (xd->compose_table)
1373 xkb_compose_table_unref(xd->compose_table);
1375 xkb_keymap_unref(xd->xkb_keymap);
1377 xkb_context_unref(xd->xkb);
1381 char *from safe, *to safe;
1383 { "Return", ":Enter"},
1385 { "ISO_Left_Tab",":Tab"},
1386 { "Escape", ":ESC"},
1387 { "Linefeed", ":LF"},
1391 { "Right", ":Right"},
1394 { "BackSpace", ":Backspace"},
1395 { "Delete", ":Del"},
1396 { "Insert", ":Ins"},
1397 { "Next", ":Prior"},
1398 { "Prior", ":Next"},
1413 static void handle_key_press(struct pane *home safe,
1414 xcb_key_press_event_t *kpe safe)
1416 struct xcb_data *xd = home->data;
1417 xkb_keycode_t keycode = kpe->detail;
1418 xcb_keysym_t keysym;
1420 const xkb_keysym_t *syms;
1422 enum xkb_compose_status status;
1423 xkb_mod_index_t mod;
1427 bool shift=False, ctrl=False, alt=False;
1429 xd->last_event = time(NULL);
1431 keysym = xkb_state_key_get_one_sym(xd->xkb_state,
1433 if (xd->compose_state)
1434 xkb_compose_state_feed(xd->compose_state, keysym);
1435 nsyms = xkb_state_key_get_syms(xd->xkb_state, keycode,
1439 status = XKB_COMPOSE_NOTHING;
1440 if (xd->compose_state)
1441 status = xkb_compose_state_get_status(xd->compose_state);
1442 if (status == XKB_COMPOSE_COMPOSING ||
1443 status == XKB_COMPOSE_CANCELLED)
1446 for (mod = 0; mod < xkb_keymap_num_mods(xd->xkb_keymap); mod++) {
1448 if (xkb_state_mod_index_is_active(
1450 XKB_STATE_MODS_EFFECTIVE) <= 0)
1452 /* This does tells me "shift" is consumed for :C:S-l ...
1453 if (xkb_state_mod_index_is_consumed2(
1454 xd->xkb_state, keycode, mod,
1455 XKB_CONSUMED_MODE_XKB))
1458 n = xkb_keymap_mod_get_name(xd->xkb_keymap, mod);
1459 if (n && strcmp(n, "Shift") == 0)
1461 if (n && strcmp(n, "Control") == 0)
1463 if (n && strcmp(n, "Mod1") == 0)
1467 if (status == XKB_COMPOSE_COMPOSED) {
1468 sym = xkb_compose_state_get_one_sym(xd->compose_state);
1472 xkb_compose_state_get_utf8(xd->compose_state,
1477 /* Mod1 can still apply to a composed char */
1478 } else if (nsyms == 1) {
1480 sym = xkb_state_key_get_one_sym(xd->xkb_state, keycode);
1483 xkb_state_key_get_utf8(xd->xkb_state, keycode,
1485 xkb_keysym_get_name(syms[0], key, sizeof(key));
1486 for (i = 0; i < ARRAY_SIZE(key_map); i++) {
1487 if (strcmp(key, key_map[i].from) == 0) {
1488 strcpy(s, key_map[i].to);
1492 if (s[0] == '-' && s[1] >= ' ' && s[1] < 0x7f)
1493 /* Shift is included */
1495 if (s[0] == '-' && s[1] && (unsigned char)(s[1]) < ' ') {
1498 if (s[1] < 'A' || s[1] > 'Z')
1500 } else if (s[0] == '-' && !s[1] && strcmp(key, "space") == 0) {
1501 /* 'nul' becomes "C- " (ctrl-space) */
1508 if (xd->compose_state &&
1509 (status == XKB_COMPOSE_CANCELLED ||
1510 status == XKB_COMPOSE_COMPOSED))
1511 xkb_compose_state_reset(xd->compose_state);
1522 call("Keystroke", home, 0, NULL, mods);
1526 static void handle_xkb_event(struct pane *home safe,
1527 xcb_generic_event_t *ev safe)
1529 struct xcb_data *xd = home->data;
1532 xcb_xkb_new_keyboard_notify_event_t *nkne;
1533 xcb_xkb_state_notify_event_t *sne;
1534 xcb_xkb_map_notify_event_t *mne;
1535 case XCB_XKB_NEW_KEYBOARD_NOTIFY:
1537 if (nkne->deviceID == xd->xkb_device_id &&
1538 nkne->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
1541 case XCB_XKB_MAP_NOTIFY:
1543 if (mne->deviceID == xd->xkb_device_id)
1546 case XCB_XKB_STATE_NOTIFY:
1548 if (sne->deviceID == xd->xkb_device_id)
1549 xkb_state_update_mask(xd->xkb_state,
1560 static void handle_configure(struct pane *home safe,
1561 xcb_configure_notify_event_t *cne safe)
1563 struct xcb_data *xd = home->data;
1565 pane_resize(home, 0, 0, cne->width, cne->height);
1566 cairo_xcb_surface_set_size(xd->surface, cne->width, cne->height);
1569 static void handle_expose(struct pane *home safe,
1570 xcb_expose_event_t *ee safe)
1572 struct xcb_data *xd = home->data;
1573 cairo_rectangle_int_t r = {
1577 .height = ee->height,
1580 if (!xd->need_update)
1581 xd->need_update = cairo_region_create();
1582 cairo_region_union_rectangle(xd->need_update, &r);
1584 pane_damaged(home, DAMAGED_POSTORDER);
1587 static void handle_client_message(struct pane *home safe,
1588 xcb_client_message_event_t *cme safe)
1590 struct xcb_data *xd = home->data;
1592 if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1593 cme->format == 32 &&
1594 cme->window == xd->win &&
1595 cme->data.data32[0] == xd->atoms[a_WM_DELETE_WINDOW]) {
1596 pane_call(home, "Display:close", pane_leaf(home));
1600 if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1601 cme->format == 32 &&
1602 cme->window == xd->win &&
1603 cme->data.data32[0] == xd->atoms[a_NET_WM_PING]) {
1604 cme->window = xd->screen->root;
1605 xcb_send_event(xd->conn, 0, xd->screen->root,
1606 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1607 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1611 LOG("x11 %s got unexpected client message type=%d/%d win=%x data=%d",
1613 cme->type, cme->format, cme->window, cme->data.data32[0]);
1619 struct xcb_data *xd = ci->home->data;
1620 xcb_generic_event_t *ev;
1625 /* This is a poll - only return 1 on something happening */
1628 while ((ev = xcb_poll_for_event(xd->conn)) != NULL) {
1630 switch (ev->response_type & 0x7f) {
1632 time_start(TIME_KEY);
1633 handle_key_press(ci->home, safe_cast (void*)ev);
1634 time_stop(TIME_KEY);
1636 case XCB_KEY_RELEASE:
1637 /* Ignore for now */
1639 case XCB_BUTTON_PRESS:
1640 case XCB_BUTTON_RELEASE:
1641 time_start(TIME_KEY);
1642 handle_button(ci->home, (void*)ev);
1643 time_stop(TIME_KEY);
1645 case XCB_MOTION_NOTIFY:
1646 time_start(TIME_KEY);
1647 handle_motion(ci->home, (void*)ev);
1648 time_stop(TIME_KEY);
1652 time_start(TIME_WINDOW);
1653 handle_focus(ci->home, (void*)ev);
1654 time_stop(TIME_WINDOW);
1657 time_start(TIME_WINDOW);
1658 handle_expose(ci->home, (void*)ev);
1659 time_stop(TIME_WINDOW);
1661 case XCB_CONFIGURE_NOTIFY:
1662 time_start(TIME_WINDOW);
1663 handle_configure(ci->home, (void*)ev);
1664 time_stop(TIME_WINDOW);
1666 case XCB_CLIENT_MESSAGE:
1667 time_start(TIME_WINDOW);
1668 handle_client_message(ci->home, (void*)ev);
1669 time_stop(TIME_WINDOW);
1671 case XCB_REPARENT_NOTIFY:
1672 /* Not interested */
1674 case XCB_MAP_NOTIFY:
1675 case XCB_UNMAP_NOTIFY:
1676 case XCB_MAPPING_NOTIFY:
1677 /* FIXME what to do?? */
1680 /* Don't know what this means, but I get a lot
1681 * of them so I don't want to log that it was
1686 if ((ev->response_type & 0x7f) ==
1687 xd->first_xkb_event) {
1688 handle_xkb_event(ci->home, ev);
1691 LOG("Ignored X11 event %d", ev->response_type);
1693 xcb_flush(xd->conn);
1695 if (xcb_connection_has_error(xd->conn))
1696 pane_close(ci->home);
1700 static void set_str_prop(struct xcb_data *xd safe,
1701 enum my_atoms a, const char *str safe)
1703 xcb_change_property(xd->conn,
1704 XCB_PROP_MODE_REPLACE,
1705 xd->win, xd->atoms[a], XCB_ATOM_STRING,
1706 8, strlen(str), str);
1709 static void set_utf8_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],
1715 xd->atoms[a_UTF8_STRING],
1716 8, strlen(str), str);
1719 static void set_atom_prop(struct xcb_data *xd safe,
1720 enum my_atoms prop, enum my_atoms alist, ...)
1727 atoms[anum++] = xd->atoms[alist];
1728 va_start(ap, alist);
1729 while ((a = va_arg(ap, enum my_atoms)) != a_NONE)
1731 atoms[anum++] = xd->atoms[a];
1733 xcb_change_property(xd->conn,
1734 XCB_PROP_MODE_REPLACE,
1735 xd->win, xd->atoms[prop],
1740 static struct pane *xcb_display_init(const char *d safe,
1741 const char *disp_auth,
1742 struct pane *focus safe)
1744 struct xcb_data *xd;
1746 xcb_connection_t *conn;
1747 xcb_intern_atom_cookie_t cookies[NR_ATOMS];
1748 xcb_screen_iterator_t iter;
1749 xcb_depth_iterator_t di;
1755 PangoLayout *layout;
1758 cairo_surface_t *surface;
1759 PangoFontDescription *fd;
1760 // FIXME SCALE from environ?? or pango_cairo_context_set_resolution dpi
1761 // 254 * width_in_pixels / width_in_millimeters / 10
1763 conn = safe_cast xcb_connect_auth(d, disp_auth, &screen);
1764 if (xcb_connection_has_error(conn))
1769 xd->motion_blocked = True;
1770 xd->in_focus = True;
1773 xd->display = strdup(d);
1775 xd->disp_auth = strdup(disp_auth);
1776 xd->setup = safe_cast xcb_get_setup(conn);
1777 iter = xcb_setup_roots_iterator(xd->setup);
1778 for (i = 0; i < screen; i++)
1779 xcb_screen_next(&iter);
1780 xd->screen = safe_cast iter.data;
1782 di = xcb_screen_allowed_depths_iterator(xd->screen);
1783 while (di.data && di.data->depth < 24)
1784 xcb_depth_next(&di);
1785 //?? look for class = TrueColor??
1786 xd->visual = xcb_depth_visuals(di.data);
1788 for (i = 0; i < NR_ATOMS; i++) {
1789 char *n = atom_names[i];
1792 cookies[i] = xcb_intern_atom(conn, 0, strlen(n), n);
1795 xd->win = xcb_generate_id(conn);
1796 valwin[0] = xd->screen->white_pixel;
1797 valwin[1] = (XCB_EVENT_MASK_KEY_PRESS |
1798 XCB_EVENT_MASK_KEY_RELEASE |
1799 XCB_EVENT_MASK_BUTTON_PRESS |
1800 XCB_EVENT_MASK_BUTTON_RELEASE |
1801 // XCB_EVENT_MASK_ENTER_WINDOW |
1802 // XCB_EVENT_MASK_LEAVE_WINDOW |
1803 XCB_EVENT_MASK_FOCUS_CHANGE |
1804 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1805 XCB_EVENT_MASK_EXPOSURE |
1806 XCB_EVENT_MASK_BUTTON_MOTION |
1807 XCB_EVENT_MASK_POINTER_MOTION_HINT |
1810 xcb_create_window(conn, XCB_COPY_FROM_PARENT, xd->win,
1814 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
1815 xd->screen->root_visual,
1816 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
1821 surface = cairo_xcb_surface_create(
1822 conn, xd->win, xd->visual, 100, 100);
1825 xd->surface = surface;
1826 cairo = safe_cast cairo_create(xd->surface);
1827 if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS)
1830 fd = pango_font_description_new();
1834 pango_font_description_set_family(xd->fd, "mono");
1835 pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
1837 layout = pango_cairo_create_layout(xd->cairo);
1838 pango_layout_set_font_description(layout, fd);
1839 pango_layout_set_text(layout, "M", 1);
1840 pango_layout_get_pixel_extents(layout, NULL, &log);
1841 g_object_unref(layout);
1842 xd->lineheight = log.height;
1843 xd->charwidth = log.width;
1845 valwin[0] = xd->charwidth * 80;
1846 valwin[1] = xd->lineheight * 26;
1847 xcb_configure_window(conn, xd->win,
1848 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
1850 cairo_xcb_surface_set_size(xd->surface, valwin[0], valwin[1]);
1852 /* Now resolve all those cookies */
1853 for (i = 0; i < NR_ATOMS; i++) {
1854 xcb_intern_atom_reply_t *r;
1855 r = xcb_intern_atom_reply(conn, cookies[i], NULL);
1858 xd->atoms[i] = r->atom;
1864 * WM_PROTOCOLS _NET_WM_SYN_REQUEST??
1865 * WM_NORMAL_HINTS WM_HINTS
1868 set_str_prop(xd, a_WM_NAME, "EdLib");
1869 set_utf8_prop(xd, a_NET_WM_NAME, "EdLib");
1870 set_str_prop(xd, a_WM_ICON_NAME, "EdLib");
1871 set_utf8_prop(xd, a_NET_WM_ICON_NAME, "EdLib");
1872 gethostname(hostname, sizeof(hostname));
1873 set_str_prop(xd, a_WM_CLIENT_MACHINE, hostname);
1874 set_atom_prop(xd, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW, a_NET_WM_PING, 0);
1876 /* Configure passive grabs - shift, lock, and control only */
1877 xcb_grab_button(xd->conn, 0, xd->win,
1878 XCB_EVENT_MASK_BUTTON_PRESS |
1879 XCB_EVENT_MASK_BUTTON_RELEASE |
1880 XCB_EVENT_MASK_BUTTON_MOTION,
1881 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
1882 XCB_WINDOW_NONE, XCB_CURSOR_NONE,
1883 XCB_BUTTON_INDEX_ANY,
1884 XCB_MOD_MASK_SHIFT |
1886 XCB_MOD_MASK_CONTROL);
1888 xcb_map_window(conn, xd->win);
1890 p = pane_register(pane_root(focus), 1, &xcb_handle.c, xd);
1893 pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
1894 call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
1895 call_comm("event:poll", p, &xcb_input);
1896 attr_set_str(&p->attrs, "DISPLAY", d);
1897 attr_set_str(&p->attrs, "XAUTHORITY", disp_auth);
1898 snprintf(scale, sizeof(scale), "%dx%d", xd->charwidth, xd->lineheight);
1899 attr_set_str(&p->attrs, "scale:M", scale);
1900 xd->last_event = time(NULL);
1901 call("editor:request:all-displays", p);
1905 cairo_destroy(xd->cairo);
1906 cairo_surface_destroy(xd->surface);
1907 xcb_disconnect(conn);
1909 free(xd->disp_auth);
1914 DEF_CMD(display_xcb)
1917 const char *d = ci->str;
1921 p = xcb_display_init(d, ci->str2, ci->focus);
1923 return comm_call(ci->comm2, "cb", p);
1927 DEF_CMD(xcb_new_display)
1930 const char *d = ci->str;
1931 const char *disp_auth = ci->str2;
1934 d = pane_attr_get(ci->focus, "DISPLAY");
1936 disp_auth = pane_attr_get(ci->focus, "XAUTHORITY");
1938 disp_auth = getenv("XAUTHORITY");
1942 p = xcb_display_init(d, disp_auth, ci->focus);
1944 p = call_ret(pane, "editor:activate-display", p);
1946 home_call_ret(pane, ci->focus, "doc:attach-view", p, 1);
1948 comm_call(ci->comm2, "cb", p);
1952 void edlib_init(struct pane *ed safe)
1954 call_comm("global-set-command", ed, &display_xcb, 0, NULL,
1955 "attach-display-x11");
1956 call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
1957 "interactive-cmd-x11window");
1959 xcb_map = key_alloc();
1961 key_add(xcb_map, "Display:close", &xcb_close_display);
1962 key_add(xcb_map, "Display:set-noclose", &xcb_set_noclose);
1963 key_add(xcb_map, "Display:external-viewer", &xcb_external_viewer);
1964 key_add(xcb_map, "Display:fullscreen", &xcb_fullscreen);
1965 key_add(xcb_map, "Display:new", &xcb_new_display);
1967 key_add(xcb_map, "Close", &xcb_close);
1968 key_add(xcb_map, "Free", &xcb_free);
1969 key_add(xcb_map, "Draw:clear", &xcb_clear);
1970 key_add(xcb_map, "Draw:text-size", &xcb_text_size);
1971 key_add(xcb_map, "Draw:text", &xcb_draw_text);
1972 key_add(xcb_map, "Draw:image", &xcb_draw_image);
1973 key_add(xcb_map, "Draw:image-size", &xcb_image_size);
1974 key_add(xcb_map, "Refresh:size", &xcb_refresh_size);
1975 key_add(xcb_map, "Refresh:postorder", &xcb_refresh_post);
1976 key_add(xcb_map, "all-displays", &xcb_notify_display);
1977 key_add(xcb_map, "Notify:Close", &xcb_pane_close);