2 * Copyright Neil Brown ©2021 <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.
16 #include <xcb/xcbext.h>
21 #include <cairo-xcb.h>
23 #include <wand/MagickWand.h>
25 // enums confuse sparse...
26 #define MagickBooleanType int
30 #include <pango/pango.h>
31 #include <pango/pangocairo.h>
33 typedef struct PangoFontDescription {} PangoFontDescription;
34 typedef struct PangoLayout {} PangoLayout;
35 typedef struct PangoContext {} PangoContext;
36 typedef struct PangoFontMetrics {} PangoFontMetrics;
37 typedef struct PangoRectangle { int x,y,width,height;} PangoRectangle;
38 typedef enum { PANGO_STYLE_NORMAL, PANGO_STYLE_OBLIQUE, PANGO_STYLE_ITALIC
40 typedef enum { PANGO_VARIANT_NORMAL, PANGO_VARIANT_SMALL_CAPS } PangoVariant;
41 typedef enum { PANGO_WEIGHT_NORMAL, PANGO_WEIGHT_BOLD } PangoWeight;
42 PangoFontDescription *pango_font_description_new(void);
43 void pango_font_description_set_family_static(PangoFontDescription*, char*);
44 void pango_font_description_set_family(PangoFontDescription*, char*);
45 void pango_font_description_set_size(PangoFontDescription*, int);
46 void pango_font_description_set_style(PangoFontDescription*, PangoStyle);
47 void pango_font_description_set_variant(PangoFontDescription*, PangoVariant);
48 void pango_font_description_set_weight(PangoFontDescription*, PangoWeight);
49 #define PANGO_SCALE (1024)
51 PangoLayout *pango_cairo_create_layout(cairo_t*);
52 void g_object_unref(PangoLayout*);
53 PangoContext *pango_cairo_create_context(cairo_t *);
54 void pango_cairo_show_layout(cairo_t *, PangoLayout *);
55 PangoFontMetrics *pango_context_get_metrics(PangoContext*, PangoFontDescription*, void*);
56 void pango_font_description_free(PangoFontDescription*);
57 int pango_font_metrics_get_approximate_char_width(PangoFontMetrics *);
58 int pango_font_metrics_get_ascent(PangoFontMetrics *);
59 int pango_font_metrics_get_descent(PangoFontMetrics *);
60 void pango_font_metrics_unref(PangoFontMetrics *);
61 PangoContext* pango_layout_get_context(PangoLayout *);
62 int pango_layout_get_baseline(PangoLayout *);
63 void pango_layout_get_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
64 void pango_layout_get_pixel_extents(PangoLayout *, PangoRectangle *, PangoRectangle *);
65 void pango_layout_set_font_description(PangoLayout *, PangoFontDescription *);
66 void pango_layout_set_text(PangoLayout*, const char *, int);
67 void pango_layout_xy_to_index(PangoLayout*, int, int, int*, int*);
68 void pango_layout_index_to_pos(PangoLayout*, int, PangoRectangle*);
71 //#include <xkbcommon/xkbcommon.h>
72 //#include <xkbcommon/xkbcommon-x11.h>
79 a_WM_STATE, a_STATE_ADD, a_STATE_REMOVE, a_STATE_FULLSCREEN,
82 static char *atom_names[NR_ATOMS] = {
83 [a_WM_STATE] = "_NET_WM_STATE",
84 [a_STATE_ADD] = "_NET_WM_STATE_ADD",
85 [a_STATE_REMOVE] = "_NET_WM_STATE_REMOVE",
86 [a_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN",
90 xcb_connection_t *conn safe;
93 const xcb_setup_t *setup safe;
94 const xcb_screen_t *screen safe;
95 xcb_atom_t atoms[NR_ATOMS];
99 xcb_visualtype_t *visual;
101 cairo_surface_t *surface safe;
102 PangoFontDescription *fd safe;
104 int charwidth, lineheight;
110 struct xkb_context *xkb;
111 int32_t xkb_device_id;
112 struct xkb_state *xkb_state;
115 /* FIXME use hash?? */
122 cairo_surface_t *surface safe;
126 static struct map *xcb_map;
127 DEF_LOOKUP_CMD(xcb_handle, xcb_map);
129 static cairo_t *get_pixmap(struct pane *home safe,
132 struct xcb_data *xd = home->data;
133 struct panes **pp, *ps;
134 cairo_surface_t *surface;
137 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
140 if (ps->w == p->w && ps->h == p->h)
143 cairo_destroy(ps->ctx);
144 cairo_surface_destroy(ps->surface);
145 xcb_free_pixmap(xd->conn, ps->draw);
153 ps->draw = xcb_generate_id(xd->conn);
154 xcb_create_pixmap(xd->conn, xd->screen->root_depth, ps->draw,
155 xd->win, p->w, p->h);
156 surface = cairo_xcb_surface_create(
157 xd->conn, ps->draw, xd->visual, p->w, p->h);
160 ctx = cairo_create(surface);
164 ps->surface = surface;
166 pane_add_notify(home, p, "Notify:Close");
171 cairo_surface_destroy(surface);
173 xcb_free_pixmap(xd->conn, ps->draw);
178 static struct panes *find_pixmap(struct xcb_data *xd safe, struct pane *p safe,
179 int *xp safe, int *yp safe)
182 struct panes *ret = NULL;
184 while (!ret && p->parent != p) {
186 for (ps = xd->panes; ps ; ps = ps->next)
206 static inline double cvt(int i)
208 return (float)i / 1000.0;
211 static void parse_attrs(
212 struct pane *home safe, const char *cattrs, int scale,
213 struct rgb *fgp, struct rgb *bgp, bool *underline,
214 PangoFontDescription **fdp)
216 char *attrs = strdup(cattrs ?: "");
219 char *fg = NULL, *bg = NULL;
223 PangoFontDescription *fd = NULL;
224 PangoStyle style = PANGO_STYLE_NORMAL;
225 PangoVariant variant = PANGO_VARIANT_NORMAL;
226 PangoWeight weight = PANGO_WEIGHT_NORMAL;
229 fd = pango_font_description_new();
231 pango_font_description_set_family_static(fd, "mono");
234 while ((word = strsep(&ap, ",")) != NULL) {
235 if (fd && strncmp(word, "family:", 7) == 0)
236 pango_font_description_set_family(fd, word+7);
237 if (strcmp(word, "large") == 0)
239 if (strcmp(word, "small") == 0)
241 if (isdigit(word[0])) {
243 double s = strtod(word, &end);
244 if (end && end != word && !*end)
245 size = trunc(s * 1000.0);
249 if (strcmp(word, "oblique") == 0)
250 style = PANGO_STYLE_OBLIQUE;
251 if (strcmp(word, "italic") == 0)
252 style = PANGO_STYLE_ITALIC;
253 if (strcmp(word, "normal") == 0)
254 style = PANGO_STYLE_NORMAL;
255 if (strcmp(word, "small-caps") == 0)
256 variant = PANGO_VARIANT_SMALL_CAPS;
258 if (strcmp(word, "bold") == 0)
259 weight = PANGO_WEIGHT_BOLD;
260 if (strcmp(word, "nobold") == 0)
261 weight = PANGO_WEIGHT_NORMAL;
263 if (strncmp(word, "fg:", 3) == 0)
265 if (strncmp(word, "bg:", 3) == 0)
267 if (strcmp(word, "inverse") == 0)
269 if (strcmp(word, "underline") == 0)
285 struct call_return ret = call_ret(all, "colour:map", home,
288 fgp->g = cvt(ret.i2);
293 struct call_return ret = call_ret(all, "colour:map", home,
296 bgp->g = cvt(ret.i2);
301 pango_font_description_set_size(fd, size * scale / PANGO_SCALE);
302 if (style != PANGO_STYLE_NORMAL)
303 pango_font_description_set_style(fd, style);
304 if (variant != PANGO_VARIANT_NORMAL)
305 pango_font_description_set_variant(fd, variant);
306 if (weight != PANGO_WEIGHT_NORMAL)
307 pango_font_description_set_weight(fd, weight);
316 struct call_return *cr = container_of(ci->comm, struct call_return, c);
322 DEF_CMD(xcb_close_display)
324 /* If this is only display, then refuse to close this one */
325 struct call_return cr;
326 struct xcb_data *xd = ci->home->data;
328 call("Message", ci->focus, 0, NULL, xd->noclose);
333 call_comm("editor:notify:all-displays", ci->focus, &cr.c);
335 pane_close(ci->home);
337 call("Message", ci->focus, 0, NULL,
338 "Cannot close only window.");
342 DEF_CMD(xcb_set_noclose)
344 struct xcb_data *xd = ci->home->data;
349 xd->noclose = strdup(ci->str);
353 DEF_CMD(xcb_external_viewer)
355 //struct xcb_data *xd = ci->home->data;
360 DEF_CMD(xcb_fullscreen)
362 struct xcb_data *xd = ci->home->data;
363 xcb_client_message_event_t msg = {};
365 msg.response_type = XCB_CLIENT_MESSAGE;
367 msg.window = xd->win;
368 msg.type = xd->atoms[a_WM_STATE];
370 msg.data.data32[0] = xd->atoms[a_STATE_ADD];
372 msg.data.data32[0] = xd->atoms[a_STATE_REMOVE];
373 msg.data.data32[1] = xd->atoms[a_STATE_FULLSCREEN];
374 msg.data.data32[2] = 0;
375 msg.data.data32[3] = 1; /* source indicator */
377 xcb_send_event(xd->conn, 0, xd->screen->root,
378 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
379 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
380 (void*)msg.data.data8);
387 struct xcb_data *xd = ci->home->data;
388 xcb_destroy_window(xd->conn, xd->win);
389 xcb_disconnect(xd->conn);
396 struct xcb_data *xd = ci->home->data;
397 const char *attr = ci->str;
398 struct panes *src = NULL;
404 parse_attrs(ci->home, attr, PANGO_SCALE, NULL, &bg, NULL, NULL);
406 src = find_pixmap(xd, ci->focus->parent, &x, &y);
407 bg.r = bg.g = bg.b = 1.0;
410 pm = get_pixmap(ci->home, ci->focus);
414 cairo_set_source_surface(pm, src->surface, -x - ci->focus->x,
418 cairo_set_source_rgb(pm, bg.r, bg.g, bg.b);
419 cairo_rectangle(pm, 0.0, 0.0,
420 (double)ci->focus->w, (double)ci->focus->h);
423 pane_damaged(ci->home, DAMAGED_POSTORDER);
427 DEF_CMD(xcb_text_size)
429 struct xcb_data *xd = ci->home->data;
430 const char *attr = ci->str2 ?: "";
431 const char *str = ci->str ?: "";
432 int scale = ci->num2;
434 PangoFontDescription *fd;
441 parse_attrs(ci->home, attr, scale, NULL, NULL, NULL, &fd);
442 /* If we use an empty string, line-height it wrong */
443 layout = pango_cairo_create_layout(xd->cairo);
444 pango_layout_set_text(layout, *str ? str : "M", -1);
445 pango_layout_set_font_description(layout, fd);
446 pango_layout_get_pixel_extents(layout, NULL, &log);
447 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
451 else if (log.width <= ci->num)
452 max_bytes = strlen(str);
454 pango_layout_xy_to_index(layout, 1000*ci->num,
455 baseline, &max_bytes, NULL);
457 comm_call(ci->comm2, "cb", ci->focus, max_bytes, NULL, NULL,
458 baseline, NULL, NULL,
459 str && *str ? log.width : 0,
462 pango_font_description_free(fd);
463 g_object_unref(layout);
467 DEF_CMD(xcb_draw_text)
469 struct xcb_data *xd = ci->home->data;
470 const char *str = ci->str;
471 const char *attr = ci->str2;
476 PangoFontDescription *fd;
486 ps = find_pixmap(xd, ci->focus, &xo, &yo);
491 pane_damaged(ci->home, DAMAGED_POSTORDER);
494 scale = ci->num2 * 10 / xd->charwidth;
496 parse_attrs(ci->home, attr, scale, &fg, &bg, &ul, &fd);
500 layout = pango_cairo_create_layout(ctx);
501 pango_layout_set_text(layout, str, -1);
502 pango_layout_set_font_description(layout, fd);
503 pango_layout_get_pixel_extents(layout, NULL, &log);
504 baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
506 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
507 cairo_rectangle(ctx, x+log.x, y - baseline + log.y,
508 log.width, log.height);
511 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
513 /* Draw an underline */
514 cairo_rectangle(ctx, x+log.x, y+2+log.y,
519 cairo_move_to(ctx, x, y - baseline);
520 pango_cairo_show_layout(ctx, layout);
524 /* draw a cursor - outline box if not in-focus,
525 * inverse-video if it is.
528 bool in_focus = xd->in_focus;
529 struct pane *f = ci->focus;
531 pango_layout_index_to_pos(layout, ci->num, &curs);
532 if (curs.width <= 0) {
534 pango_layout_set_text(layout, "M", 1);
535 pango_layout_get_extents(layout, NULL, &log);
536 curs.width = log.width;
538 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE, y-baseline+curs.y/PANGO_SCALE,
539 (curs.width - PANGO_SCALE/2) / PANGO_SCALE,
540 (curs.height - PANGO_SCALE/2) / PANGO_SCALE);
541 cairo_set_line_width(ctx, 1.0);
544 while (in_focus && f->parent->parent != f &&
545 f->parent != ci->home) {
546 if (f->parent->focus != f && f->z >= 0)
552 cairo_set_source_rgb(ctx, fg.r, fg.g, fg.b);
553 cairo_rectangle(ctx, x+curs.x/PANGO_SCALE,
554 y-baseline+curs.y/PANGO_SCALE,
555 curs.width / PANGO_SCALE,
556 curs.height / PANGO_SCALE);
558 if (ci->num < (int)strlen(str)) {
559 const char *cp = str + ci->num;
561 pango_layout_set_text(layout, str + ci->num,
562 cp - (str + ci->num));
564 cairo_set_source_rgb(ctx, bg.r, bg.g, bg.b);
566 cairo_set_source_rgb(ctx, 1.0, 1.0, 1.0);
568 x + curs.x / PANGO_SCALE,
569 y - baseline + curs.y / PANGO_SCALE);
570 pango_cairo_show_layout(ctx, layout);
574 pango_font_description_free(fd);
575 g_object_unref(layout);
579 DEF_CMD(xcb_draw_image)
581 /* 'str' identifies the image. Options are:
582 * file:filename - load file from fs
583 * comm:command - run command collecting bytes
584 * 'num' is '1' if image should be stretched to fill pane
585 * if 'num is '0', then 'num2' is 'or' of
586 * 0,1,2 for left/middle/right in x direction
587 * 0,4,8 for top/middle/bottom in y direction
588 * only one of these can be used as image will fill pane in other direction.
590 struct xcb_data *xd = ci->home->data;
591 bool stretch = ci->num == 1;
593 int w = ci->focus->w, h = ci->focus->h;
598 MagickBooleanType status;
602 cairo_surface_t *surface;
606 ps = find_pixmap(xd, ci->focus, &xo, &yo);
609 if (strncmp(ci->str, "file:", 5) == 0) {
610 wd = NewMagickWand();
611 status = MagickReadImage(wd, ci->str + 5);
612 if (status == MagickFalse) {
613 DestroyMagickWand(wd);
616 } else if (strncmp(ci->str, "comm:", 5) == 0) {
617 struct call_return cr;
618 wd = NewMagickWand();
619 cr = call_ret(bytes, ci->str+5, ci->focus, 0, NULL, ci->str2);
621 DestroyMagickWand(wd);
624 status = MagickReadImageBlob(wd, cr.s, cr.i);
626 if (status == MagickFalse) {
627 DestroyMagickWand(wd);
633 MagickAutoOrientImage(wd);
635 int ih = MagickGetImageHeight(wd);
636 int iw = MagickGetImageWidth(wd);
638 if (iw <= 0 || iw <= 0) {
639 DestroyMagickWand(wd);
642 if (iw * h > ih * w) {
643 /* Image is wider than space, use less height */
645 switch(pos & (8+4)) {
647 y = (h - ih) / 2; break;
653 /* image is too tall, use less width */
655 switch (pos & (1+2)) {
657 x = (w - iw) / 2; break;
664 MagickAdaptiveResizeImage(wd, w, h);
665 stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, w);
666 buf = malloc(h * stride);
667 // Cairo expects 32bit values with A in the high byte, then RGB.
668 // Magick provides 8bit values in the order requests.
669 // So depending on byte order, a different string is needed
671 fmt[0] = ('A'<<24) | ('R' << 16) | ('G' << 8) | ('B' << 0);
673 MagickExportImagePixels(wd, 0, 0, w, h, (char*)fmt, CharPixel, buf);
674 surface = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32,
676 cairo_set_source_surface(ps->ctx, surface, x + xo, y + yo);
677 cairo_paint(ps->ctx);
678 cairo_surface_destroy(surface);
680 DestroyMagickWand(wd);
682 pane_damaged(ci->home, DAMAGED_POSTORDER);
687 static struct panes *sort_split(struct panes *p)
689 /* consider 'p' to be a list of panes with
690 * ordered subsets (ordered by p->abs_z).
691 * Remove every other such subset and return them
693 * If p is ordered, this means we return NULL.
695 struct panes *ret, **end = &ret;
698 for (; p && p->next; p = next) {
699 /* If these are not ordered, attach p->next at
700 * 'end', and make 'end' point to &p->next.
703 if (p->p->abs_z <= next->p->abs_z)
712 static struct panes *sort_merge(struct panes *p1, struct panes *p2)
714 /* merge p1 and p2 and return result */
715 struct panes *ret, **end = &ret;
719 /* if both arg large or smaller than lastz, choose
720 * least, else choose largest
722 struct panes *lo, *hi, *choice;
723 if (p1->p->abs_z <= p2->p->abs_z) {
728 if (lo->p->abs_z >= lastz || hi->p->abs_z <= lastz)
746 DEF_CMD(xcb_refresh_post)
748 struct xcb_data *xd = ci->home->data;
751 time_start(TIME_WINDOW);
752 /* First: ensure panes are sorted */
753 while ((ps = sort_split(xd->panes)) != NULL)
754 xd->panes = sort_merge(xd->panes, ps);
756 /* Now copy all panes onto the window */
757 for (ps = xd->panes; ps; ps = ps->next) {
758 double lox, hix, loy, hiy;
759 struct xy rel, lo, hi;
761 rel = pane_mapxy(ps->p, ci->home, 0, 0, False);
762 lo = pane_mapxy(ps->p, ci->home, 0, 0, True);
763 hi = pane_mapxy(ps->p, ci->home, ps->p->w, ps->p->h, True);
764 lox = lo.x; loy = lo.y;
765 hix = hi.x; hiy = hi.y;
766 cairo_save(xd->cairo);
767 cairo_set_source_surface(xd->cairo, ps->surface,
769 cairo_rectangle(xd->cairo, lox, loy, hix, hiy);
770 cairo_fill(xd->cairo);
771 cairo_restore(xd->cairo);
773 time_stop(TIME_WINDOW);
778 DEF_CMD(xcb_pane_close)
780 struct xcb_data *xd = ci->home->data;
781 struct panes **pp, *ps;
783 for (pp = &xd->panes; (ps = *pp) != NULL; pp = &(*pp)->next) {
784 if (ps->p != ci->focus)
788 cairo_destroy(ps->ctx);
789 cairo_surface_destroy(ps->surface);
790 xcb_free_pixmap(xd->conn, ps->draw);
792 pane_damaged(ci->home, DAMAGED_POSTORDER);
798 DEF_CMD(xcb_notify_display)
800 struct xcb_data *xd = ci->home->data;
801 comm_call(ci->comm2, "callback:display", ci->home, xd->last_event);
805 static void handle_button(struct pane *home safe,
806 xcb_button_press_event_t *be safe)
808 struct xcb_data *xd = home->data;
809 bool press = (be->response_type & 0x7f) == XCB_BUTTON_PRESS;
811 char key[2+2+2+9+1+1];
815 xd->motion_blocked = False;
816 if (be->state & XCB_KEY_BUT_MASK_MOD_1)
818 if (be->state & XCB_KEY_BUT_MASK_CONTROL)
820 if (be->state & XCB_KEY_BUT_MASK_SHIFT)
823 strcat(key, ":Press-X");
825 strcpy(key, ":Release-X");
826 key[strlen(key) - 1] = '0' + be->detail;
827 xd->last_event = time(NULL);
828 call("Mouse-event", home, be->detail, NULL, key,
829 press?1:2, NULL, mod,
830 be->event_x, be->event_y);
833 static void handle_motion(struct pane *home safe,
834 xcb_motion_notify_event_t *mne safe)
836 struct xcb_data *xd = home->data;
837 xcb_query_pointer_cookie_t c;
838 xcb_query_pointer_reply_t *qpr;
840 int x = mne->event_x, y = mne->event_y;
842 if (xd->motion_blocked)
844 ret = call("Mouse-event", home, 0, NULL, ":Motion",
845 3, NULL, NULL, x, y);
847 xd->motion_blocked = True;
848 c = xcb_query_pointer(xd->conn, xd->win);
849 qpr = xcb_query_pointer_reply(xd->conn, c, NULL);
853 static void handle_focus(struct pane *home safe, xcb_focus_in_event_t *fie safe)
855 struct xcb_data *xd = home->data;
856 bool in = (fie->response_type & 0x7f) == XCB_FOCUS_IN;
862 pt = call_ret(mark, "doc:point", p);
864 call("view:changed", p, 0, pt);
866 call("pane:refocus", home);
869 static void handle_key(struct pane *home safe, xcb_key_press_event_t *kpe safe)
871 //struct xcb_data *xd = home->data;
872 bool press = (kpe->response_type & 0x7f) == XCB_KEY_PRESS;
874 xcb_key_press_event_t *kpe;
877 xkb_state_update_key(xd->xkb_state, kpe->keycode,
879 keysym = xkb_state_key_get_one_sym(xd->xkb_state,
881 xkb_keysym_get_name(keysym, name, sizeof(name));
882 xkb_state_key_get_utf8(xd->xkb_state, kpe->keycode,
885 if (kpe->detail == 24)
886 call("event:deactivate", home);
887 else if (kpe->detail == 65 && press)
888 call("Keystroke", home, 0, NULL, "- ");
889 else LOG("key %d", kpe->detail);
893 //XLookupString(&xke, &buf, sizeof(buf), &keysym, NULL);
896 static void handle_configure(struct pane *home safe,
897 xcb_configure_notify_event_t *cne safe)
899 struct xcb_data *xd = home->data;
901 pane_resize(home, 0, 0, cne->width, cne->height);
902 cairo_xcb_surface_set_size(xd->surface, cne->width, cne->height);
907 struct xcb_data *xd = ci->home->data;
908 xcb_generic_event_t *ev;
910 while ((ev = xcb_poll_for_event(xd->conn)) != NULL) {
911 switch (ev->response_type & 0x7f) {
913 case XCB_KEY_RELEASE:
914 time_start(TIME_KEY);
915 handle_key(ci->home, safe_cast (void*)ev);
918 case XCB_BUTTON_PRESS:
919 case XCB_BUTTON_RELEASE:
920 time_start(TIME_KEY);
921 handle_button(ci->home, (void*)ev);
924 case XCB_MOTION_NOTIFY:
925 time_start(TIME_KEY);
926 handle_motion(ci->home, (void*)ev);
931 time_start(TIME_WINDOW);
932 handle_focus(ci->home, (void*)ev);
933 time_stop(TIME_WINDOW);
936 pane_damaged(ci->home, DAMAGED_POSTORDER);
938 case XCB_CONFIGURE_NOTIFY:
939 time_start(TIME_WINDOW);
940 handle_configure(ci->home, (void*)ev);
941 time_stop(TIME_WINDOW);
944 LOG("ignored %x", ev->response_type);
951 static struct pane *xcb_display_init(const char *d safe, struct pane *focus safe)
955 xcb_connection_t *conn;
956 xcb_intern_atom_cookie_t cookies[NR_ATOMS];
957 xcb_screen_iterator_t iter;
958 xcb_depth_iterator_t di;
966 cairo_surface_t *surface;
967 PangoFontDescription *fd;
968 // FIXME SCALE from environ?? or pango_cairo_context_set_resolution dpi
969 // 254 * width_in_pixels / width_in_millimeters / 10
971 conn = xcb_connect(d, &screen);
977 xd->motion_blocked = True;
981 xd->display = strdup(d);
982 xd->setup = safe_cast xcb_get_setup(conn);
983 iter = xcb_setup_roots_iterator(xd->setup);
984 for (i = 0; i < screen; i++)
985 xcb_screen_next(&iter);
986 xd->screen = safe_cast iter.data;
988 di = xcb_screen_allowed_depths_iterator(xd->screen);
989 while (di.data && di.data->depth < 24)
991 //?? look for class = TrueColor??
992 xd->visual = xcb_depth_visuals(di.data);
994 for (i = 0; i < NR_ATOMS; i++) {
995 char *n = atom_names[i];
998 cookies[i] = xcb_intern_atom(conn, 0, strlen(n), n);
1001 xd->win = xcb_generate_id(conn);
1002 valwin[0] = xd->screen->white_pixel;
1003 valwin[1] = (XCB_EVENT_MASK_KEY_PRESS |
1004 XCB_EVENT_MASK_KEY_RELEASE |
1005 XCB_EVENT_MASK_BUTTON_PRESS |
1006 XCB_EVENT_MASK_BUTTON_RELEASE |
1007 // XCB_EVENT_MASK_ENTER_WINDOW |
1008 // XCB_EVENT_MASK_LEAVE_WINDOW |
1009 XCB_EVENT_MASK_FOCUS_CHANGE |
1010 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
1011 XCB_EVENT_MASK_EXPOSURE |
1012 XCB_EVENT_MASK_POINTER_MOTION |
1013 // FIXME XCB_EVENT_MASK_POINTER_MOTION_HINT |
1016 xcb_create_window(conn, XCB_COPY_FROM_PARENT, xd->win,
1020 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
1021 xd->screen->root_visual,
1022 XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
1026 surface = cairo_xcb_surface_create(
1027 conn, xd->win, xd->visual, 100, 100);
1030 xd->surface = surface;
1031 cairo = cairo_create(xd->surface);
1035 fd = pango_font_description_new();
1039 pango_font_description_set_family(xd->fd, "mono");
1040 pango_font_description_set_size(xd->fd, 12 * PANGO_SCALE);
1042 layout = pango_cairo_create_layout(xd->cairo);
1043 pango_layout_set_font_description(layout, fd);
1044 pango_layout_set_text(layout, "M", 1);
1045 pango_layout_get_pixel_extents(layout, NULL, &log);
1046 g_object_unref(layout);
1047 xd->lineheight = log.height;
1048 xd->charwidth = log.width;
1050 valwin[0] = xd->charwidth * 80;
1051 valwin[1] = xd->lineheight * 26;
1052 xcb_configure_window(conn, xd->win,
1053 XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
1055 cairo_xcb_surface_set_size(xd->surface, valwin[0], valwin[1]);
1057 /* Now resolve all those cookies */
1058 for (i = 0; i < NR_ATOMS; i++) {
1059 xcb_intern_atom_reply_t *r;
1060 r = xcb_intern_atom_reply(conn, cookies[i], NULL);
1063 xd->atoms[i] = r->atom;
1068 xd->xkb = safe_cast xkb_context_new(XCB_CONTEXT_NO_FLAGS);
1069 xd->xkb_device_id = xkb_x11_get_core_keyboard_device_id(conn);
1070 xd->xkb_state = xkb_x11_state_new_from_device(xd->xkb_keymap, conn,
1075 * WM_PROTOCOLS WM_DELETE_WINDOW WM_TAKE_FOCUS _NET_WM_PING _NET_WM_SYN_REQUEST??
1076 * WM_NORMAL_HINTS WM_HINTS
1079 xcb_map_window(conn, xd->win);
1081 p = pane_register(pane_root(focus), 1, &xcb_handle.c, xd);
1084 pane_resize(p, 0, 0, xd->charwidth*80, xd->lineheight*26);
1085 call_comm("event:read", p, &xcb_input, xcb_get_file_descriptor(conn));
1086 attr_set_str(&p->attrs, "DISPLAY", d);
1087 snprintf(scale, sizeof(scale), "%dx%d", xd->charwidth, xd->lineheight);
1088 attr_set_str(&p->attrs, "scale:M", scale);
1089 //attr_set_int(&p->attrs, "scale", 2000);
1092 cairo_destroy(xd->cairo);
1093 cairo_surface_destroy(xd->surface);
1094 xcb_disconnect(conn);
1095 // FIXME free stuff;
1100 DEF_CMD(display_xcb)
1103 const char *d = ci->str;
1107 p = xcb_display_init(d, ci->focus);
1109 return comm_call(ci->comm2, "cb", p);
1113 DEF_CMD(xcb_new_display)
1116 char *d = pane_attr_get(ci->focus, "DISPLAY");
1120 p = xcb_display_init(d, ci->focus);
1122 p = call_ret(pane, "editor:activate-display", p);
1124 home_call(ci->focus, "doc:attach-view", p, 1);
1128 void edlib_init(struct pane *ed safe)
1130 call_comm("global-set-command", ed, &display_xcb, 0, NULL,
1131 "attach-display-x11");
1132 call_comm("global-set-command", ed, &xcb_new_display, 0, NULL,
1133 "interactive-cmd-x11window");
1135 xcb_map = key_alloc();
1137 key_add(xcb_map, "Display:close", &xcb_close_display);
1138 key_add(xcb_map, "Display:set-noclose", &xcb_set_noclose);
1139 key_add(xcb_map, "Display:external-viewer", &xcb_external_viewer);
1140 key_add(xcb_map, "Display:fullscreen", &xcb_fullscreen);
1141 key_add(xcb_map, "Display:new", &xcb_new_display);
1143 key_add(xcb_map, "Close", &xcb_close);
1144 key_add(xcb_map, "Free", &edlib_do_free);
1145 key_add(xcb_map, "Draw:clear", &xcb_clear);
1146 key_add(xcb_map, "Draw:text-size", &xcb_text_size);
1147 key_add(xcb_map, "Draw:text", &xcb_draw_text);
1148 key_add(xcb_map, "Draw:image", &xcb_draw_image);
1149 key_add(xcb_map, "Refresh:postorder", &xcb_refresh_post);
1150 key_add(xcb_map, "all-displays", &xcb_notify_display);
1151 key_add(xcb_map, "Notify:Close", &xcb_pane_close);