2 * Copyright Neil Brown ©2021-2022 <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.
21 /* xkb.h has a 'long' in an enum :-( */
23 XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY,
24 XCB_XKB_EVENT_TYPE_MAP_NOTIFY,
25 XCB_XKB_NEW_KEYBOARD_NOTIFY,
26 XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
27 XCB_XKB_MAP_PART_MODIFIER_MAP,
28 XCB_XKB_STATE_PART_MODIFIER_LOCK,
29 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS,
30 XCB_XKB_STATE_PART_GROUP_BASE,
31 XCB_XKB_MAP_PART_KEY_ACTIONS,
32 XCB_XKB_STATE_PART_GROUP_LATCH,
33 XCB_XKB_MAP_PART_VIRTUAL_MODS,
34 XCB_XKB_STATE_PART_GROUP_LOCK,
35 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP,
36 XCB_XKB_NKN_DETAIL_KEYCODES,
37 XCB_XKB_MAP_PART_KEY_TYPES,
38 XCB_XKB_MAP_PART_KEY_SYMS,
39 XCB_XKB_STATE_PART_MODIFIER_BASE,
40 XCB_XKB_STATE_PART_MODIFIER_LATCH,
44 typedef uint16_t xcb_xkb_device_spec_t;
45 typedef struct xcb_xkb_select_events_details_t {
46 uint16_t affectNewKeyboard;
47 uint16_t newKeyboardDetails;
49 uint16_t stateDetails;
50 /* and other fields */
51 } xcb_xkb_select_events_details_t;
52 typedef struct xcb_xkb_new_keyboard_notify_event_t {
55 /* and other fields */
56 } xcb_xkb_new_keyboard_notify_event_t;
57 typedef struct xcb_xkb_state_notify_event_t {
65 /* and other fields */
66 } xcb_xkb_state_notify_event_t;
67 typedef struct xcb_xkb_map_notify_event_t {
69 } xcb_xkb_map_notify_event_t;
71 xcb_xkb_select_events_aux_checked(xcb_connection_t *c,
72 xcb_xkb_device_spec_t deviceSpec,
78 const xcb_xkb_select_events_details_t *details);
81 #include <xcb/xcbext.h>
87 #include <cairo-xcb.h>
89 #include <wand/MagickWand.h>
91 // enums confuse sparse...
92 #define MagickBooleanType int
96 #include <pango/pango.h>
97 #include <pango/pangocairo.h>
99 typedef struct PangoFontDescription {} PangoFontDescription;
100 typedef struct PangoLayout {} PangoLayout;
101 typedef struct PangoContext {} PangoContext;
102 typedef struct PangoFontMetrics {} PangoFontMetrics;
103 typedef struct PangoRectangle { int x,y,width,height;} PangoRectangle;
104 typedef enum { PANGO_STYLE_NORMAL, PANGO_STYLE_OBLIQUE, PANGO_STYLE_ITALIC
106 typedef enum { PANGO_VARIANT_NORMAL, PANGO_VARIANT_SMALL_CAPS } PangoVariant;
107 typedef enum { PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_BOLD } PangoWeight;
108 PangoFontDescription *pango_font_description_new(void);
109 void pango_font_description_set_family_static(PangoFontDescription*, char*);
110 void pango_font_description_set_family(PangoFontDescription*, char*);
111 void pango_font_description_set_size(PangoFontDescription*, int);
112 void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
113 void pango_font_description_set_variant(PangoFontDescription*, PangoVariant);
114 void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
115 #define PANGO_SCALE (1024)
117 PangoLayout *pango_cairo_create_layout(cairo_t*);
118 void g_object_unref(PangoLayout*);
119 PangoContext *pango_cairo_create_context(cairo_t *);
120 void pango_cairo_show_layout(cairo_t *, PangoLayout *);
121 PangoFontMetrics *pango_context_get_metrics(PangoContext*, PangoFontDescription*, void*);
122 void pango_font_description_free(PangoFontDescription*);
123 int pango_font_metrics_get_approximate_char_width(PangoFontMetrics *);
124 int pango_font_metrics_get_ascent(PangoFontMetrics *);
125 int pango_font_metrics_get_descent(PangoFontMetrics *);
126 void pango_font_metrics_unref(PangoFontMetrics *);
127 PangoContext* pango_layout_get_context(PangoLayout *);
128 int pango_layout_get_baseline(PangoLayout *);
129 void pango_layout_get_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
130 void pango_layout_get_pixel_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
131 void pango_layout_set_font_description(PangoLayout *, PangoFontDescription *);
132 void pango_layout_set_text(PangoLayout*, const char *, int);
133 void pango_layout_xy_to_index(PangoLayout*, int, int, int*, int*);
134 void pango_layout_index_to_pos(PangoLayout*, int, PangoRectangle*);
137 #include <xkbcommon/xkbcommon.h>
138 #include <xkbcommon/xkbcommon-compose.h>
139 #include <xkbcommon/xkbcommon-x11.h>
147 a_WM_STATE, a_STATE_FULLSCREEN,
148 a_WM_NAME, a_NET_WM_NAME,
149 a_WM_ICON_NAME, a_NET_WM_ICON_NAME,
150 a_WM_PROTOCOLS, a_WM_DELETE_WINDOW,
156 static char *atom_names[NR_ATOMS] = {
158 [a_WM_STATE] = "_NET_WM_STATE",
159 [a_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN",
160 [a_WM_NAME] = "WM_NAME",
161 [a_NET_WM_NAME] = "_NET_WM_NAME",
162 [a_WM_ICON_NAME] = "WM_ICON_NAME",
163 [a_NET_WM_ICON_NAME] = "_NET_WM_ICON_NAME",
164 [a_WM_PROTOCOLS] = "WM_PROTOCOLS",
165 [a_WM_DELETE_WINDOW] = "WM_DELETE_WINDOW",
166 [a_NET_WM_PING] = "_NET_WM_PING",
167 [a_WM_CLIENT_MACHINE] = "WM_CLIENT_MACHINE",
168 [a_UTF8_STRING] = "UTF8_STRING",
176 xcb_connection_t *conn safe;
179 const xcb_setup_t *setup safe;
180 const xcb_screen_t *screen safe;
181 xcb_atom_t atoms[NR_ATOMS];
185 xcb_visualtype_t *visual;
187 cairo_surface_t *surface safe;
188 PangoFontDescription *fd safe;
190 int charwidth, lineheight;
191 cairo_region_t *need_update;
196 struct xkb_context *xkb;
197 uint8_t first_xkb_event;
198 int32_t xkb_device_id;
199 struct xkb_state *xkb_state;
200 struct xkb_compose_state *compose_state;
201 struct xkb_compose_table *compose_table;
202 struct xkb_keymap *xkb_keymap;
204 /* FIXME use hash?? */
208 cairo_rectangle_int_t r;
212 cairo_surface_t *surface;
213 cairo_region_t *need_update;
217 /* panes->r.x is NEVER_DRAWN if the pane has not been drawn */
218 #define NEVER_DRAWN (-60000)
220 static struct map *xcb_map;
221 DEF_LOOKUP_CMD(xcb_handle, xcb_map);
223 static struct panes *get_pixmap(struct pane *home safe,
226 struct xcb_data *xd = home->data;
227 struct panes **pp, *ps;
229 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
232 if (ps->r.width == p->w && ps->r.height == p->h)
235 if (ps->r.x != NEVER_DRAWN) {
236 if (!xd->need_update)
237 xd->need_update = cairo_region_create();
238 cairo_region_union_rectangle(xd->need_update, &ps->r);
241 cairo_destroy(ps->ctx);
243 cairo_surface_destroy(ps->surface);
245 xcb_free_pixmap(xd->conn, ps->draw);
251 ps->r.x = ps->r.y = NEVER_DRAWN;
254 ps->bg.r = ps->bg.g = ps->bg.b = 0;
256 pane_add_notify(home, p, "Notify:Close");
262 static void instantiate_pixmap(struct xcb_data *xd safe,
263 struct panes *ps safe)
265 ps->draw = xcb_generate_id(xd->conn);
266 xcb_create_pixmap(xd->conn, xd->screen->root_depth, ps->draw,
267 xd->win, ps->r.width, ps->r.height);
268 ps->surface = cairo_xcb_surface_create(
269 xd->conn, ps->draw, xd->visual, ps->r.width, ps->r.height);
272 ps->ctx = cairo_create(ps->surface);
275 cairo_set_source_rgb(ps->ctx, ps->bg.r, ps->bg.g, ps->bg.b);
276 cairo_paint(ps->ctx);
280 cairo_surface_destroy(ps->surface);
283 xcb_free_pixmap(xd->conn, ps->draw);
287 static struct panes *find_pixmap(struct xcb_data *xd safe, struct pane *p safe,
288 int *xp safe, int *yp safe)
291 struct panes *ret = NULL;
293 while (!ret && p->parent != p) {
295 for (ps = xd->panes; ps ; ps = ps->next)
311 static inline double cvt(int i)
313 return (float)i / 1000.0;
316 static void parse_attrs(
317 struct pane *home safe, const char *cattrs, int scale,
318 struct rgb *fgp, struct rgb *bgp, bool *underline,
319 PangoFontDescription **fdp)
321 char *attrs = strdup(cattrs ?: "");
324 char *fg = NULL, *bg = NULL;
328 PangoFontDescription *fd = NULL;
329 PangoStyle style = PANGO_STYLE_NORMAL;
330 PangoVariant variant = PANGO_VARIANT_NORMAL;
331 PangoWeight weight = PANGO_WEIGHT_NORMAL;
334 fd = pango_font_description_new();
336 pango_font_description_set_family_static(fd, "mono");
339 while ((word = strsep(&ap, ",")) != NULL) {
340 if (fd && strncmp(word, "family:", 7) == 0)
341 pango_font_description_set_family(fd, word+7);
342 if (strcmp(word, "large") == 0)
344 if (strcmp(word, "small") == 0)
346 if (isdigit(word[0])) {
348 double s = strtod(word, &end);
349 if (end && end != word && !*end)
350 size = trunc(s * 1000.0);
354 if (strcmp(word, "oblique") == 0)
355 style = PANGO_STYLE_OBLIQUE;
356 if (strcmp(word, "italic") == 0)
357 style = PANGO_STYLE_ITALIC;
358 if (strcmp(word, "normal") == 0)
359 style = PANGO_STYLE_NORMAL;
360 if (strcmp(word, "small-caps") == 0)
361 variant = PANGO_VARIANT_SMALL_CAPS;
363 if (strcmp(word, "bold") == 0)
364 weight = PANGO_WEIGHT_BOLD;
365 if (strcmp(word, "nobold") == 0)
366 weight = PANGO_WEIGHT_NORMAL;
368 if (strncmp(word, "fg:", 3) == 0)
370 if (strncmp(word, "bg:", 3) == 0)
372 if (strcmp(word, "inverse") == 0)
374 if (strcmp(word, "underline") == 0)
390 struct call_return ret = call_ret(all, "colour:map", home,
393 fgp->g = cvt(ret.i2);
398 struct call_return ret = call_ret(all, "colour:map", home,
401 bgp->g = cvt(ret.i2);
406 pango_font_description_set_size(fd, size * scale / PANGO_SCALE);
407 if (style != PANGO_STYLE_NORMAL)
408 pango_font_description_set_style(fd, style);
409 if (variant != PANGO_VARIANT_NORMAL)
410 pango_font_description_set_variant(fd, variant);
411 if (weight != PANGO_WEIGHT_NORMAL)
412 pango_font_description_set_weight(fd, weight);
421 struct call_return *cr = container_of(ci->comm, struct call_return, c);
427 DEF_CMD(xcb_close_display)
429 /* If this is only display, then refuse to close this one */
430 struct call_return cr;
431 struct xcb_data *xd = ci->home->data;
433 call("Message", ci->focus, 0, NULL, xd->noclose);
438 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
440 pane_close(ci->home);
442 call("Message", ci->focus, 0, NULL,
443 "Cannot close only window.");
447 DEF_CMD(xcb_set_noclose)
449 struct xcb_data *xd = ci->home->data;
454 xd->noclose = strdup(ci->str);
458 DEF_CMD(xcb_external_viewer)
460 struct xcb_data *xd = ci->home->data;
461 const char *path = ci->str;
467 switch (pid = fork()) {
471 setenv("DISPLAY", xd->display, 1);
472 fd = open("/dev/null", O_RDWR);
480 execlp("xdg-open", "xdg-open", path, NULL);
482 default: /* parent */
488 DEF_CMD(xcb_fullscreen)
490 struct xcb_data *xd = ci->home->data;
491 xcb_client_message_event_t msg = {};
493 msg.response_type = XCB_CLIENT_MESSAGE;
495 msg.window = xd->win;
496 msg.type = xd->atoms[a_WM_STATE];
498 msg.data.data32[0] = 1; /* ADD */
500 msg.data.data32[0] = 0; /* REMOVE */
501 msg.data.data32[1] = xd->atoms[a_STATE_FULLSCREEN];
502 msg.data.data32[2] = 0;
503 msg.data.data32[3] = 1; /* source indicator */
505 xcb_send_event(xd->conn, 0, xd->screen->root,
506 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
507 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
513 static void panes_free(struct xcb_data *xd safe)
516 struct panes *ps = xd->panes;
517 xd->panes = ps->next;
519 cairo_destroy(ps->ctx);
521 cairo_surface_destroy(ps->surface);
523 xcb_free_pixmap(xd->conn, ps->draw);
528 static void kbd_free(struct xcb_data *xd safe);
532 struct xcb_data *xd = ci->home->data;
534 xcb_destroy_window(xd->conn, xd->win);
542 struct xcb_data *xd = ci->home->data;
544 pango_font_description_free(xd->fd);
545 cairo_destroy(xd->cairo);
546 cairo_device_finish(cairo_surface_get_device(xd->surface));
547 cairo_surface_destroy(xd->surface);
550 xcb_disconnect(xd->conn);
552 cairo_region_destroy(xd->need_update);
559 struct xcb_data *xd = ci->home->data;
560 const char *attr = ci->str;
561 struct panes *src = NULL, *dest;
564 cairo_rectangle_int_t r;
567 parse_attrs(ci->home, attr, PANGO_SCALE, NULL, &bg, NULL, NULL);
569 bg.r = bg.g = bg.b = 1.0;
571 src = find_pixmap(xd, ci->focus->parent, &x, &y);
575 bg.r = bg.g = bg.b = 1.0;
576 else if (src->bg.g >= 0)
578 else if (src->surface == NULL)
579 bg.r = bg.g = bg.b = 1.0;
584 dest = get_pixmap(ci->home, ci->focus);
589 cairo_set_source_rgb(dest->ctx, bg.r, bg.g, bg.b);
590 cairo_paint(dest->ctx);
595 instantiate_pixmap(xd, dest);
597 cairo_set_source_surface(dest->ctx, src->surface, -x, -y);
598 cairo_paint(dest->ctx);
602 LOG("ERROR neither src or bg");
603 pane_damaged(ci->home, DAMAGED_POSTORDER);
605 if (!dest->need_update)
606 dest->need_update = cairo_region_create();
609 r.width = ci->focus->w;
610 r.height = ci->focus->h;
611 cairo_region_union_rectangle(dest->need_update, &r);
615 DEF_CMD(xcb_text_size)
617 struct xcb_data *xd = ci->home->data;
618 const char *attr = ci->str2 ?: "";
619 const char *str = ci->str ?: "";
620 int scale = ci->num2;
622 PangoFontDescription *fd;
629 parse_attrs(ci->home, attr, scale, NULL, NULL, NULL, &fd);
630 /* If we use an empty string, line-height it wrong */
631 layout = pango_cairo_create_layout(xd->cairo);
632 pango_layout_set_text(layout, *str ? str : "M", -1);
633 pango_layout_set_font_description(layout, fd);
634 pango_layout_get_pixel_extents(layout, NULL, &log);
635 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
639 else if (log.width <= ci->num)
640 max_bytes = strlen(str);
642 pango_layout_xy_to_index(layout, PANGO_SCALE*ci->num,
643 baseline, &max_bytes, NULL);
645 comm_call(ci->comm2, "cb", ci->focus, max_bytes, NULL, NULL,
646 baseline, NULL, NULL,
647 str && *str ? log.width : 0,
650 pango_font_description_free(fd);
651 g_object_unref(layout);
655 DEF_CMD(xcb_draw_text)
657 struct xcb_data *xd = ci->home->data;
658 const char *str = ci->str;
659 const char *attr = ci->str2;
664 PangoFontDescription *fd;
674 ps = find_pixmap(xd, ci->focus, &xo, &yo);
678 instantiate_pixmap(xd, ps);
684 pane_damaged(ci->home, DAMAGED_POSTORDER);
687 scale = ci->num2 * 10 / xd->charwidth;
689 parse_attrs(ci->home, attr, scale, &fg, &bg, &ul, &fd);
693 layout = pango_cairo_create_layout(ctx);
694 pango_layout_set_text(layout, str, -1);
695 pango_layout_set_font_description(layout, fd);
696 pango_layout_get_pixel_extents(layout, NULL, &log);
697 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
700 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
701 cairo_rectangle(ctx, x+log.x, y - baseline + log.y,
702 log.width, log.height);
705 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
707 /* Draw an underline */
708 cairo_rectangle(ctx, x+log.x, y+2+log.y,
713 cairo_move_to(ctx, x, y - baseline);
714 pango_cairo_show_layout(ctx, layout);
718 /* draw a cursor - outline box if not in-focus,
719 * inverse-video if it is.
722 bool in_focus = xd->in_focus;
723 struct pane *f = ci->focus;
725 pango_layout_index_to_pos(layout, ci->num, &curs);
726 if (curs.width <= 0) {
728 pango_layout_set_text(layout, "M", 1);
729 pango_layout_get_extents(layout, NULL, &log);
730 curs.width = log.width;
732 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE, y-baseline+curs.y/PANGO_SCALE,
733 (curs.width - PANGO_SCALE/2) / PANGO_SCALE,
734 (curs.height - PANGO_SCALE/2) / PANGO_SCALE);
735 cairo_set_line_width(ctx, 1.0);
738 while (in_focus && f->parent->parent != f &&
739 f->parent != ci->home) {
740 if (f->parent->focus != f && f->z >= 0)
746 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
747 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE,
748 y-baseline+curs.y/PANGO_SCALE,
749 curs.width / PANGO_SCALE,
750 curs.height / PANGO_SCALE);
752 if (ci->num < (int)strlen(str)) {
753 const char *cp = str + ci->num;
755 pango_layout_set_text(layout, str + ci->num,
756 cp - (str + ci->num));
758 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
760 cairo_set_source_rgb(ctx, 1.0, 1.0, 1.0);
762 x + curs.x / PANGO_SCALE,
763 y - baseline + curs.y / PANGO_SCALE);
764 pango_cairo_show_layout(ctx, layout);
769 pango_font_description_free(fd);
770 g_object_unref(layout);
774 DEF_CMD(xcb_draw_image)
776 /* 'str' identifies the image. Options are:
777 * file:filename - load file from fs
778 * comm:command - run command collecting bytes
779 * 'num' is '1' if image should be stretched to fill pane
780 * if 'num is '0', then 'num2' is 'or' of
781 * 0,1,2 for left/middle/right in x direction
782 * 0,4,8 for top/middle/bottom in y direction
783 * only one of these can be used as image will fill pane in other direction.
785 struct xcb_data *xd = ci->home->data;
786 bool stretch = ci->num == 1;
788 int w = ci->focus->w, h = ci->focus->h;
793 MagickBooleanType status;
797 cairo_surface_t *surface;
801 ps = find_pixmap(xd, ci->focus, &xo, &yo);
805 instantiate_pixmap(xd, ps);
809 if (strncmp(ci->str, "file:", 5) == 0) {
810 wd = NewMagickWand();
811 status = MagickReadImage(wd, ci->str + 5);
812 if (status == MagickFalse) {
813 DestroyMagickWand(wd);
816 } else if (strncmp(ci->str, "comm:", 5) == 0) {
817 struct call_return cr;
818 wd = NewMagickWand();
819 cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
821 DestroyMagickWand(wd);
824 status = MagickReadImageBlob(wd, cr.s, cr.i);
826 if (status == MagickFalse) {
827 DestroyMagickWand(wd);
833 MagickAutoOrientImage(wd);
835 int ih = MagickGetImageHeight(wd);
836 int iw = MagickGetImageWidth(wd);
838 if (iw <= 0 || iw <= 0) {
839 DestroyMagickWand(wd);
842 if (iw * h > ih * w) {
843 /* Image is wider than space, use less height */
845 switch(pos & (8+4)) {
847 y = (h - ih) / 2; break;
853 /* image is too tall, use less width */
855 switch (pos & (1+2)) {
857 x = (w - iw) / 2; break;
864 MagickAdaptiveResizeImage(wd, w, h);
865 stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
866 buf = malloc(h * stride);
867 // Cairo expects 32bit values with A in the high byte, then RGB.
868 // Magick provides 8bit values in the order requests.
869 // So depending on byte order, a different string is needed
871 fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
873 MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
874 surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
876 cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
877 cairo_paint(ps->ctx);
878 cairo_surface_destroy(surface);
880 DestroyMagickWand(wd);
882 pane_damaged(ci->home, DAMAGED_POSTORDER);
887 static struct panes *sort_split(struct panes *p)
889 /* consider 'p' to be a list of panes with
890 * ordered subsets (ordered by p->abs_z).
891 * Remove every other such subset and return them
893 * If p is ordered, this means we return NULL.
895 struct panes *ret, **end = &ret;
898 for (; p && p->next; p = next) {
899 /* If these are not ordered, attach p->next at
900 * 'end', and make 'end' point to &p->next.
903 if (p->p->abs_z < next->p->abs_z) {
912 static struct panes *sort_merge(struct panes *p1, struct panes *p2)
914 /* merge p1 and p2 and return result */
915 struct panes *ret, **end = &ret;
916 struct panes *prev = NULL;
919 /* Make p1 the largest (or be added first.
920 * Then in prev is between them add p2, else p1
922 if (p1->p->abs_z < p2->p->abs_z) {
923 struct panes *t = p1;
928 p1->p->abs_z > prev->p->abs_z &&
929 prev->p->abs_z >= p2->p->abs_z) {
930 /* p2 is the better choice */
947 DEF_CMD(xcb_refresh_post)
949 struct xcb_data *xd = ci->home->data;
952 time_start(TIME_WINDOW);
953 /* First: ensure panes are sorted */
954 while ((ps = sort_split(xd->panes)) != NULL)
955 xd->panes = sort_merge(xd->panes, ps);
957 /* Then merge all update rectanges, checking for movement */
958 if (!xd->need_update)
959 xd->need_update = cairo_region_create();
960 for (ps = xd->panes; ps ; ps = ps->next)
964 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
965 if (ps->r.x == NEVER_DRAWN) {
968 cairo_region_union_rectangle(xd->need_update, &ps->r);
969 } else if (rel.x != ps->r.x || rel.y != ps->r.y) {
970 /* Moved, so refresh all.
971 * This rectangle might be too big if it is clipped,
972 * but that doesn't really matter.
974 cairo_region_union_rectangle(xd->need_update, &ps->r);
977 cairo_region_union_rectangle(xd->need_update, &ps->r);
978 } else if (ps->need_update) {
979 cairo_region_translate(ps->need_update, rel.x, rel.y);
980 cairo_region_union(xd->need_update, ps->need_update);
983 cairo_region_destroy(ps->need_update);
984 ps->need_update = NULL;
986 /* Now copy all panes onto the window where an update is needed */
987 for (ps = xd->panes; ps ; ps = ps->next) {
988 struct xy rel, lo, hi;
990 cairo_rectangle_int_t r;
993 cr = cairo_region_copy(xd->need_update);
995 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
997 cairo_save(xd->cairo);
999 cairo_set_source_rgb(xd->cairo,
1000 ps->bg.r, ps->bg.g, ps->bg.b);
1002 cairo_set_source_surface(xd->cairo, ps->surface,
1005 lo = pane_mapxy(ps->p, ci->home, 0, 0, True);
1006 hi = pane_mapxy(ps->p, ci->home, ps->r.width, ps->r.height, True);
1007 r.x = lo.x; r.y = lo.y;
1008 r.width = hi.x - lo.x; r.height = hi.y - lo.y;
1009 cairo_region_intersect_rectangle(cr, &r);
1010 cairo_region_subtract_rectangle(xd->need_update, &r);
1011 nr = cairo_region_num_rectangles(cr);
1012 for (i = 0; i < nr; i++) {
1013 cairo_region_get_rectangle(cr, i, &r);
1014 cairo_rectangle(xd->cairo, r.x, r.y, r.width, r.height);
1015 cairo_fill(xd->cairo);
1017 cairo_restore(xd->cairo);
1020 cairo_region_destroy(xd->need_update);
1021 xd->need_update = NULL;
1022 time_stop(TIME_WINDOW);
1023 xcb_flush(xd->conn);
1027 DEF_CMD(xcb_refresh_size)
1029 /* FIXME: should I consider resizing the window?
1030 * For now, just ensure we redraw everything.
1032 struct xcb_data *xd = ci->home->data;
1033 cairo_rectangle_int_t r = {
1036 .width = ci->home->w,
1037 .height = ci->home->h,
1040 if (!xd->need_update)
1041 xd->need_update = cairo_region_create();
1042 cairo_region_union_rectangle(xd->need_update, &r);
1043 /* Ask common code to notify children */
1044 return Efallthrough;
1047 DEF_CMD(xcb_pane_close)
1049 struct xcb_data *xd = ci->home->data;
1050 struct panes **pp, *ps;
1052 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
1053 if (ps->p != ci->focus)
1056 if (!xd->need_update)
1057 xd->need_update = cairo_region_create();
1058 if (ps->r.x != NEVER_DRAWN)
1059 cairo_region_union_rectangle(xd->need_update, &ps->r);
1063 if (ps->need_update)
1064 cairo_region_destroy(ps->need_update);
1065 cairo_destroy(ps->ctx);
1066 cairo_surface_destroy(ps->surface);
1067 xcb_free_pixmap(xd->conn, ps->draw);
1069 pane_damaged(ci->home, DAMAGED_POSTORDER);
1075 DEF_CMD(xcb_notify_display)
1077 struct xcb_data *xd = ci->home->data;
1078 comm_call(ci->comm2, "callback:display", ci->home, xd->last_event);
1082 static void handle_button(struct pane *home safe,
1083 xcb_button_press_event_t *be safe)
1085 struct xcb_data *xd = home->data;
1086 bool press = (be->response_type & 0x7f) == XCB_BUTTON_PRESS;
1088 char key[2+2+2+9+1+1];
1090 xcb_set_input_focus(xd->conn, XCB_INPUT_FOCUS_POINTER_ROOT,
1091 xd->win, XCB_CURRENT_TIME);
1094 xd->motion_blocked = False;
1095 if (be->state & XCB_KEY_BUT_MASK_MOD_1)
1097 if (be->state & XCB_KEY_BUT_MASK_CONTROL)
1099 if (be->state & XCB_KEY_BUT_MASK_SHIFT)
1102 strcat(key, ":Press-X");
1103 } else if (be->detail >= 4)
1104 /* ignore 'release' for scroll wheel */
1107 strcpy(key, ":Release-X");
1109 key[strlen(key) - 1] = '0' + be->detail;
1110 xd->last_event = time(NULL);
1111 call("Mouse-event", home, be->detail, NULL, key,
1112 press?1:2, NULL, mod,
1113 be->event_x, be->event_y);
1116 static void handle_motion(struct pane *home safe,
1117 xcb_motion_notify_event_t *mne safe)
1119 struct xcb_data *xd = home->data;
1120 xcb_query_pointer_cookie_t c;
1121 xcb_query_pointer_reply_t *qpr;
1123 int x = mne->event_x, y = mne->event_y;
1125 if (xd->motion_blocked)
1127 ret = call("Mouse-event", home, 0, NULL, ":Motion",
1128 3, NULL, NULL, x, y);
1130 xd->motion_blocked = True;
1132 /* This doesn't seem to be needed, but the spec says
1133 * I should do this when using POINTER_MOTION_HINT
1135 c = xcb_query_pointer(xd->conn, xd->win);
1136 qpr = xcb_query_pointer_reply(xd->conn, c, NULL);
1140 static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
1142 struct xcb_data *xd = home->data;
1143 bool in = (fie->response_type & 0x7f) == XCB_FOCUS_IN;
1148 p = pane_leaf(home);
1149 pt = call_ret(mark, "doc:point", p);
1151 call("view:changed", p, 0, pt);
1153 call("pane:refocus", home);
1156 static bool select_xkb_events_for_device(xcb_connection_t *conn,
1159 xcb_generic_error_t *error;
1160 xcb_void_cookie_t cookie;
1164 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
1165 XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
1166 XCB_XKB_EVENT_TYPE_STATE_NOTIFY),
1168 required_nkn_details =
1169 (XCB_XKB_NKN_DETAIL_KEYCODES),
1171 required_map_parts =
1172 (XCB_XKB_MAP_PART_KEY_TYPES |
1173 XCB_XKB_MAP_PART_KEY_SYMS |
1174 XCB_XKB_MAP_PART_MODIFIER_MAP |
1175 XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
1176 XCB_XKB_MAP_PART_KEY_ACTIONS |
1177 XCB_XKB_MAP_PART_VIRTUAL_MODS |
1178 XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP),
1180 required_state_details =
1181 (XCB_XKB_STATE_PART_MODIFIER_BASE |
1182 XCB_XKB_STATE_PART_MODIFIER_LATCH |
1183 XCB_XKB_STATE_PART_MODIFIER_LOCK |
1184 XCB_XKB_STATE_PART_GROUP_BASE |
1185 XCB_XKB_STATE_PART_GROUP_LATCH |
1186 XCB_XKB_STATE_PART_GROUP_LOCK),
1189 static const xcb_xkb_select_events_details_t details = {
1190 .affectNewKeyboard = required_nkn_details,
1191 .newKeyboardDetails = required_nkn_details,
1192 .affectState = required_state_details,
1193 .stateDetails = required_state_details,
1196 cookie = xcb_xkb_select_events_aux_checked(
1199 required_events, /* affectWhich */
1202 required_map_parts, /* affectMap */
1203 required_map_parts, /* map */
1204 &details); /* details */
1206 error = xcb_request_check(conn, cookie);
1215 static bool update_keymap(struct xcb_data *xd safe)
1217 struct xkb_keymap *new_keymap;
1218 struct xkb_state *new_state;
1220 new_keymap = xkb_x11_keymap_new_from_device(xd->xkb, xd->conn,
1222 XKB_KEYMAP_COMPILE_NO_FLAGS);
1226 new_state = xkb_x11_state_new_from_device(new_keymap, xd->conn,
1229 xkb_keymap_unref(new_keymap);
1233 xkb_state_unref(xd->xkb_state);
1234 xkb_keymap_unref(xd->xkb_keymap);
1235 xd->xkb_keymap = new_keymap;
1236 xd->xkb_state = new_state;
1240 static bool kbd_setup(struct xcb_data *xd safe)
1245 ret = xkb_x11_setup_xkb_extension(xd->conn,
1246 XKB_X11_MIN_MAJOR_XKB_VERSION,
1247 XKB_X11_MIN_MINOR_XKB_VERSION,
1248 XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
1249 NULL, NULL, &xd->first_xkb_event,
1254 xd->xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1257 xd->xkb_device_id = xkb_x11_get_core_keyboard_device_id(xd->conn);
1258 if (xd->xkb_device_id == -1)
1261 if (!update_keymap(xd))
1264 if (!select_xkb_events_for_device(xd->conn, xd->xkb_device_id))
1267 locale = setlocale(LC_CTYPE, NULL);
1269 xkb_compose_table_new_from_locale(xd->xkb, locale,
1270 XKB_COMPOSE_COMPILE_NO_FLAGS);
1271 if (xd->compose_table)
1273 xkb_compose_state_new(xd->compose_table,
1274 XKB_COMPOSE_STATE_NO_FLAGS);
1278 static void kbd_free(struct xcb_data *xd safe)
1280 if (xd->compose_table)
1281 xkb_compose_table_unref(xd->compose_table);
1283 xkb_keymap_unref(xd->xkb_keymap);
1285 xkb_context_unref(xd->xkb);
1289 char *from safe, *to safe;
1291 { "Return", ":Enter"},
1293 { "ISO_Left_Tab",":Tab"},
1294 { "Escape", ":ESC"},
1295 { "Linefeed", ":LF"},
1299 { "Right", ":Right"},
1302 { "BackSpace", ":Backspace"},
1303 { "Delete", ":Del"},
1304 { "Insert", ":Ins"},
1305 { "Next", ":Prior"},
1306 { "Prior", ":Next"},
1321 static void handle_key_press(struct pane *home safe,
1322 xcb_key_press_event_t *kpe safe)
1324 struct xcb_data *xd = home->data;
1325 xkb_keycode_t keycode = kpe->detail;
1326 xcb_keysym_t keysym;
1328 const xkb_keysym_t *syms;
1330 enum xkb_compose_status status;
1331 xkb_mod_index_t mod;
1335 bool shift=False, ctrl=False, alt=False;
1337 xd->last_event = time(NULL);
1339 keysym = xkb_state_key_get_one_sym(xd->xkb_state,
1341 if (xd->compose_state)
1342 xkb_compose_state_feed(xd->compose_state, keysym);
1343 nsyms = xkb_state_key_get_syms(xd->xkb_state, keycode,
1347 status = XKB_COMPOSE_NOTHING;
1348 if (xd->compose_state)
1349 status = xkb_compose_state_get_status(xd->compose_state);
1350 if (status == XKB_COMPOSE_COMPOSING ||
1351 status == XKB_COMPOSE_CANCELLED)
1354 for (mod = 0; mod < xkb_keymap_num_mods(xd->xkb_keymap); mod++) {
1356 if (xkb_state_mod_index_is_active(
1358 XKB_STATE_MODS_EFFECTIVE) <= 0)
1360 /* This does tells me "shift" is consumed for :C:S-l ...
1361 if (xkb_state_mod_index_is_consumed2(
1362 xd->xkb_state, keycode, mod,
1363 XKB_CONSUMED_MODE_XKB))
1366 n = xkb_keymap_mod_get_name(xd->xkb_keymap, mod);
1367 if (n && strcmp(n, "Shift") == 0)
1369 if (n && strcmp(n, "Control") == 0)
1371 if (n && strcmp(n, "Mod1") == 0)
1375 if (status == XKB_COMPOSE_COMPOSED) {
1376 sym = xkb_compose_state_get_one_sym(xd->compose_state);
1380 xkb_compose_state_get_utf8(xd->compose_state,
1385 /* Mod1 can still apply to a composed char */
1386 } else if (nsyms == 1) {
1388 sym = xkb_state_key_get_one_sym(xd->xkb_state, keycode);
1391 xkb_state_key_get_utf8(xd->xkb_state, keycode,
1393 xkb_keysym_get_name(syms[0], key, sizeof(key));
1394 for (i = 0; i < ARRAY_SIZE(key_map); i++) {
1395 if (strcmp(key, key_map[i].from) == 0) {
1396 strcpy(s, key_map[i].to);
1400 if (s[0] == '-' && s[1] >= ' ' && s[1] < 0x7f)
1401 /* Shift is included */
1403 if (s[0] == '-' && s[1] && (unsigned char)(s[1]) < ' ') {
1406 if (s[1] < 'A' || s[1] > 'Z')
1408 } else if (s[0] == '-' && !s[1] && strcmp(key, "space") == 0) {
1409 /* 'nul' becomes "C- " (ctrl-space) */
1416 if (xd->compose_state &&
1417 (status == XKB_COMPOSE_CANCELLED ||
1418 status == XKB_COMPOSE_COMPOSED))
1419 xkb_compose_state_reset(xd->compose_state);
1430 call("Keystroke", home, 0, NULL, mods);
1434 static void handle_xkb_event(struct pane *home safe,
1435 xcb_generic_event_t *ev safe)
1437 struct xcb_data *xd = home->data;
1440 xcb_xkb_new_keyboard_notify_event_t *nkne;
1441 xcb_xkb_state_notify_event_t *sne;
1442 xcb_xkb_map_notify_event_t *mne;
1443 case XCB_XKB_NEW_KEYBOARD_NOTIFY:
1445 if (nkne->deviceID == xd->xkb_device_id &&
1446 nkne->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
1449 case XCB_XKB_MAP_NOTIFY:
1451 if (mne->deviceID == xd->xkb_device_id)
1454 case XCB_XKB_STATE_NOTIFY:
1456 if (sne->deviceID == xd->xkb_device_id)
1457 xkb_state_update_mask(xd->xkb_state,
1468 static void handle_configure(struct pane *home safe,
1469 xcb_configure_notify_event_t *cne safe)
1471 struct xcb_data *xd = home->data;
1473 pane_resize(home, 0, 0, cne->width, cne->height);
1474 cairo_xcb_surface_set_size(xd->surface, cne->width, cne->height);
1477 static void handle_expose(struct pane *home safe,
1478 xcb_expose_event_t *ee safe)
1480 struct xcb_data *xd = home->data;
1481 cairo_rectangle_int_t r = {
1485 .height = ee->height,
1488 if (!xd->need_update)
1489 xd->need_update = cairo_region_create();
1490 cairo_region_union_rectangle(xd->need_update, &r);
1492 pane_damaged(home, DAMAGED_POSTORDER);
1495 static void handle_client_message(struct pane *home safe,
1496 xcb_client_message_event_t *cme safe)
1498 struct xcb_data *xd = home->data;
1500 if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1501 cme->format == 32 &&
1502 cme->window == xd->win &&
1503 cme->data.data32[0] == xd->atoms[a_WM_DELETE_WINDOW]) {
1504 pane_call(home, "Display:close", pane_leaf(home));
1508 if (cme->type == xd->atoms[a_WM_PROTOCOLS] &&
1509 cme->format == 32 &&
1510 cme->window == xd->win &&
1511 cme->data.data32[0] == xd->atoms[a_NET_WM_PING]) {
1512 cme->window = xd->screen->root;
1513 xcb_send_event(xd->conn, 0, xd->screen->root,
1514 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
1515 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
1519 LOG("x11 %s got unexpected client message type=%d/%d win=%x data=%d",
1521 cme->type, cme->format, cme->window, cme->data.data32[0]);
1527 struct xcb_data *xd = ci->home->data;
1528 xcb_generic_event_t *ev;
1532 /* This is a poll - only return 1 on something happening */
1535 while ((ev = xcb_poll_for_event(xd->conn)) != NULL) {
1537 switch (ev->response_type & 0x7f) {
1539 time_start(TIME_KEY);
1540 handle_key_press(ci->home, safe_cast (void*)ev);
1541 time_stop(TIME_KEY);
1543 case XCB_KEY_RELEASE:
1544 /* Ignore for now */
1546 case XCB_BUTTON_PRESS:
1547 case XCB_BUTTON_RELEASE:
1548 time_start(TIME_KEY);
1549 handle_button(ci->home, (void*)ev);
1550 time_stop(TIME_KEY);
1552 case XCB_MOTION_NOTIFY:
1553 time_start(TIME_KEY);
1554 handle_motion(ci->home, (void*)ev);
1555 time_stop(TIME_KEY);
1559 time_start(TIME_WINDOW);
1560 handle_focus(ci->home, (void*)ev);
1561 time_stop(TIME_WINDOW);
1564 time_start(TIME_WINDOW);
1565 handle_expose(ci->home, (void*)ev);
1566 time_stop(TIME_WINDOW);
1568 case XCB_CONFIGURE_NOTIFY:
1569 time_start(TIME_WINDOW);
1570 handle_configure(ci->home, (void*)ev);
1571 time_stop(TIME_WINDOW);
1573 case XCB_CLIENT_MESSAGE:
1574 time_start(TIME_WINDOW);
1575 handle_client_message(ci->home, (void*)ev);
1576 time_stop(TIME_WINDOW);
1578 case XCB_REPARENT_NOTIFY:
1579 /* Not interested */
1581 case XCB_MAP_NOTIFY:
1582 case XCB_UNMAP_NOTIFY:
1583 case XCB_MAPPING_NOTIFY:
1584 /* FIXME what to do?? */
1587 /* Don't know what this means, but I get a lot
1588 * of them so I don't want to log that it was
1593 if ((ev->response_type & 0x7f) ==
1594 xd->first_xkb_event) {
1595 handle_xkb_event(ci->home, ev);
1598 LOG("Ignored X11 event %d", ev->response_type);
1600 xcb_flush(xd->conn);
1602 if (xcb_connection_has_error(xd->conn))
1603 pane_close(ci->home);
1607 static void set_str_prop(struct xcb_data *xd safe,
1608 enum my_atoms a, const char *str safe)
1610 xcb_change_property(xd->conn,
1611 XCB_PROP_MODE_REPLACE,
1612 xd->win, xd->atoms[a], XCB_ATOM_STRING,
1613 8, strlen(str), str);
1616 static void set_utf8_prop(struct xcb_data *xd safe,
1617 enum my_atoms a, const char *str safe)
1619 xcb_change_property(xd->conn,
1620 XCB_PROP_MODE_REPLACE,
1621 xd->win, xd->atoms[a],
1622 xd->atoms[a_UTF8_STRING],
1623 8, strlen(str), str);
1626 static void set_atom_prop(struct xcb_data *xd safe,
1627 enum my_atoms prop, enum my_atoms alist, ...)
1634 atoms[anum++] = xd->atoms[alist];
1635 va_start(ap, alist);
1636 while ((a = va_arg(ap, enum my_atoms)) != a_NONE)
1638 atoms[anum++] = xd->atoms[a];
1640 xcb_change_property(xd->conn,
1641 XCB_PROP_MODE_REPLACE,
1642 xd->win, xd->atoms[prop],
1647 static struct pane *xcb_display_init(const char *d safe, struct pane *focus safe)
1649 struct xcb_data *xd;
1651 xcb_connection_t *conn;
1652 xcb_intern_atom_cookie_t cookies[NR_ATOMS];
1653 xcb_screen_iterator_t iter;
1654 xcb_depth_iterator_t di;
1660 PangoLayout *layout;
1663 cairo_surface_t *surface;
1664 PangoFontDescription *fd;
1665 // FIXME SCALE from environ?? or pango_cairo_context_set_resolution dpi
1666 // 254 * width_in_pixels / width_in_millimeters / 10
1668 conn = safe_cast xcb_connect(d, &screen);
1669 if (xcb_connection_has_error(conn))
1674 xd->motion_blocked = True;
1675 xd->in_focus = True;
1678 xd->display = strdup(d);
1679 xd->setup = safe_cast xcb_get_setup(conn);
1680 iter = xcb_setup_roots_iterator(xd->setup);
1681 for (i = 0; i < screen; i++)
1682 xcb_screen_next(&iter);
1683 xd->screen = safe_cast iter.data;
1685 di = xcb_screen_allowed_depths_iterator(xd->screen);
1686 while (di.data && di.data->depth < 24)
1687 xcb_depth_next(&di);
1688 //?? look for class = TrueColor??
1689 xd->visual = xcb_depth_visuals(di.data);
1691 for (i = 0; i < NR_ATOMS; i++) {
1692 char *n = atom_names[i];
1695 cookies[i] = xcb_intern_atom(conn, 0, strlen(n), n);
1698 xd->win = xcb_generate_id(conn);
1699 valwin[0] = xd->screen->white_pixel;
1700 valwin[1] = (XCB_EVENT_MASK_KEY_PRESS |
1701 XCB_EVENT_MASK_KEY_RELEASE |
1702 XCB_EVENT_MASK_BUTTON_PRESS |
1703 XCB_EVENT_MASK_BUTTON_RELEASE |
1704 // XCB_EVENT_MASK_ENTER_WINDOW |
1705 // XCB_EVENT_MASK_LEAVE_WINDOW |
1706 XCB_EVENT_MASK_FOCUS_CHANGE |
1707 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1708 XCB_EVENT_MASK_EXPOSURE |
1709 XCB_EVENT_MASK_BUTTON_MOTION |
1710 XCB_EVENT_MASK_POINTER_MOTION_HINT |
1713 xcb_create_window(conn, XCB_COPY_FROM_PARENT, xd->win,
1717 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
1718 xd->screen->root_visual,
1719 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
1724 surface = cairo_xcb_surface_create(
1725 conn, xd->win, xd->visual, 100, 100);
1728 xd->surface = surface;
1729 cairo = safe_cast cairo_create(xd->surface);
1730 if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS)
1733 fd = pango_font_description_new();
1737 pango_font_description_set_family(xd->fd, "mono");
1738 pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
1740 layout = pango_cairo_create_layout(xd->cairo);
1741 pango_layout_set_font_description(layout, fd);
1742 pango_layout_set_text(layout, "M", 1);
1743 pango_layout_get_pixel_extents(layout, NULL, &log);
1744 g_object_unref(layout);
1745 xd->lineheight = log.height;
1746 xd->charwidth = log.width;
1748 valwin[0] = xd->charwidth * 80;
1749 valwin[1] = xd->lineheight * 26;
1750 xcb_configure_window(conn, xd->win,
1751 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
1753 cairo_xcb_surface_set_size(xd->surface, valwin[0], valwin[1]);
1755 /* Now resolve all those cookies */
1756 for (i = 0; i < NR_ATOMS; i++) {
1757 xcb_intern_atom_reply_t *r;
1758 r = xcb_intern_atom_reply(conn, cookies[i], NULL);
1761 xd->atoms[i] = r->atom;
1767 * WM_PROTOCOLS _NET_WM_SYN_REQUEST??
1768 * WM_NORMAL_HINTS WM_HINTS
1771 set_str_prop(xd, a_WM_NAME, "EdLib");
1772 set_utf8_prop(xd, a_NET_WM_NAME, "EdLib");
1773 set_str_prop(xd, a_WM_ICON_NAME, "EdLib");
1774 set_utf8_prop(xd, a_NET_WM_ICON_NAME, "EdLib");
1775 gethostname(hostname, sizeof(hostname));
1776 set_str_prop(xd, a_WM_CLIENT_MACHINE, hostname);
1777 set_atom_prop(xd, a_WM_PROTOCOLS, a_WM_DELETE_WINDOW, a_NET_WM_PING, 0);
1779 /* Configure passive grabs - shift, lock, and control only */
1780 xcb_grab_button(xd->conn, 0, xd->win,
1781 XCB_EVENT_MASK_BUTTON_PRESS |
1782 XCB_EVENT_MASK_BUTTON_RELEASE |
1783 XCB_EVENT_MASK_BUTTON_MOTION,
1784 XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
1785 XCB_WINDOW_NONE, XCB_CURSOR_NONE,
1786 XCB_BUTTON_INDEX_ANY,
1787 XCB_MOD_MASK_SHIFT |
1789 XCB_MOD_MASK_CONTROL);
1791 xcb_map_window(conn, xd->win);
1793 p = pane_register(pane_root(focus), 1, &xcb_handle.c, xd);
1796 pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
1797 call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
1798 call_comm("event:poll", p, &xcb_input);
1799 attr_set_str(&p->attrs, "DISPLAY", d);
1800 snprintf(scale, sizeof(scale), "%dx%d", xd->charwidth, xd->lineheight);
1801 attr_set_str(&p->attrs, "scale:M", scale);
1802 xd->last_event = time(NULL);
1803 call("editor:request:all-displays", p);
1807 cairo_destroy(xd->cairo);
1808 cairo_surface_destroy(xd->surface);
1809 xcb_disconnect(conn);
1815 DEF_CMD(display_xcb)
1818 const char *d = ci->str;
1822 p = xcb_display_init(d, ci->focus);
1824 return comm_call(ci->comm2, "cb", p);
1828 DEF_CMD(xcb_new_display)
1831 const char *d = ci->str;
1834 d = pane_attr_get(ci->focus, "DISPLAY");
1838 p = xcb_display_init(d, ci->focus);
1840 p = call_ret(pane, "editor:activate-display", p);
1842 home_call_ret(pane, ci->focus, "doc:attach-view", p, 1);
1844 comm_call(ci->comm2, "cb", p);
1848 void edlib_init(struct pane *ed safe)
1850 call_comm("global-set-command", ed, &display_xcb, 0, NULL,
1851 "attach-display-x11");
1852 call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
1853 "interactive-cmd-x11window");
1855 xcb_map = key_alloc();
1857 key_add(xcb_map, "Display:close", &xcb_close_display);
1858 key_add(xcb_map, "Display:set-noclose", &xcb_set_noclose);
1859 key_add(xcb_map, "Display:external-viewer", &xcb_external_viewer);
1860 key_add(xcb_map, "Display:fullscreen", &xcb_fullscreen);
1861 key_add(xcb_map, "Display:new", &xcb_new_display);
1863 key_add(xcb_map, "Close", &xcb_close);
1864 key_add(xcb_map, "Free", &xcb_free);
1865 key_add(xcb_map, "Draw:clear", &xcb_clear);
1866 key_add(xcb_map, "Draw:text-size", &xcb_text_size);
1867 key_add(xcb_map, "Draw:text", &xcb_draw_text);
1868 key_add(xcb_map, "Draw:image", &xcb_draw_image);
1869 key_add(xcb_map, "Refresh:size", &xcb_refresh_size);
1870 key_add(xcb_map, "Refresh:postorder", &xcb_refresh_post);
1871 key_add(xcb_map, "all-displays", &xcb_notify_display);
1872 key_add(xcb_map, "Notify:Close", &xcb_pane_close);