#include #include "dat.h" #include "fn.h" #include "popup_layout.h" enum { Asciitofull = 0xFEE0, }; extern char *fontdir; static xcb_connection_t *conn; static xcb_screen_t *scr; static xcb_window_t win; static xcb_gcontext_t gc; static xcb_pixmap_t pix; static u32int *img; static int depth; extern int fontready(void); static xcb_screen_t* getscr(xcb_connection_t *c, int n) { xcb_screen_iterator_t i; for(i = xcb_setup_roots_iterator(xcb_get_setup(c)); i.rem; xcb_screen_next(&i)) if(n-- == 0) return i.data; return nil; } static xcb_visualtype_t* getvisual(xcb_screen_t *s) { xcb_depth_iterator_t di; xcb_visualtype_iterator_t vi; for(di = xcb_screen_allowed_depths_iterator(s); di.rem; xcb_depth_next(&di)){ if(di.data->depth != s->root_depth) continue; for(vi = xcb_depth_visuals_iterator(di.data); vi.rem; xcb_visualtype_next(&vi)) if(vi.data->visual_id == s->root_visual) return vi.data; } return nil; } static int validformat(xcb_connection_t *c, xcb_screen_t *s) { const xcb_setup_t *setup; xcb_format_iterator_t fi; xcb_visualtype_t *v; u32int one; int lsb; setup = xcb_get_setup(c); v = getvisual(s); if(setup == nil || v == nil || s->root_depth != 24 || v->_class != XCB_VISUAL_CLASS_TRUE_COLOR || v->bits_per_rgb_value != 8 || v->red_mask != 0xff0000 || v->green_mask != 0x00ff00 || v->blue_mask != 0x0000ff) return 0; one = 1; lsb = *(uchar*)&one != 0; if((lsb && setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST) || (!lsb && setup->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST)) return 0; for(fi = xcb_setup_pixmap_formats_iterator(setup); fi.rem; xcb_format_next(&fi)) if(fi.data->depth == 24 && fi.data->bits_per_pixel == 32 && fi.data->scanline_pad == 32) return 1; return 0; } static void wincleanup(void) { free(img); img = nil; if(conn != nil){ xcb_disconnect(conn); conn = nil; } scr = nil; win = 0; gc = 0; pix = 0; } static int wininit(void) { int n; u32int mask, vals[4]; xcb_intern_atom_cookie_t c1, c2; xcb_intern_atom_reply_t *r1, *r2; conn = xcb_connect(nil, &n); if(conn == nil || xcb_connection_has_error(conn)){ fprint(2, "strans: popup disabled: cannot connect to X display\n"); wincleanup(); return 0; } scr = getscr(conn, n); if(scr == nil || !validformat(conn, scr)){ fprint(2, "strans: popup disabled: unsupported X root format\n"); wincleanup(); return 0; } depth = scr->root_depth; win = xcb_generate_id(conn); mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER; vals[0] = Colbg; vals[1] = 0; vals[2] = 1; vals[3] = 1; xcb_create_window(conn, XCB_COPY_FROM_PARENT, win, scr->root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, scr->root_visual, mask, vals); c1 = xcb_intern_atom(conn, 0, strlen("_NET_WM_WINDOW_TYPE"), "_NET_WM_WINDOW_TYPE"); c2 = xcb_intern_atom(conn, 0, strlen("_NET_WM_WINDOW_TYPE_TOOLTIP"), "_NET_WM_WINDOW_TYPE_TOOLTIP"); r1 = xcb_intern_atom_reply(conn, c1, nil); r2 = xcb_intern_atom_reply(conn, c2, nil); if(r1 != nil && r2 != nil) xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, r1->atom, XCB_ATOM_ATOM, 32, 1, &r2->atom); free(r1); free(r2); gc = xcb_generate_id(conn); xcb_create_gc(conn, gc, win, 0, nil); pix = xcb_generate_id(conn); xcb_create_pixmap(conn, depth, pix, win, Imgw, Imgh); mask = XCB_CW_BACK_PIXMAP; xcb_change_window_attributes(conn, win, mask, &pix); img = emalloc(Imgw * Imgh * sizeof(img[0])); fontinit(fontdir); if(!fontready()){ fprint(2, "strans: popup disabled: no usable fonts\n"); wincleanup(); return 0; } return 1; } static void drawstr(u32int *buf, int x, int y, Rune *r, int n, int maxw, int maxh) { while(n-- > 0){ if(*r != 0xfe0e && *r != 0xfe0f){ putfont(buf, maxw, maxh, x, y, *r); x += Fontsz; } r++; } } static void fill(u32int *buf, int n, u32int color) { int i; for(i = 0; i < n; i++) buf[i] = color; } static void drawkouho(Drawcmd *dc, int first, int n, int w, int h) { int npre, sely, y, i; Str *s; fill(img, w * h, Colbg); npre = dc->pre.n != 0; if(npre) drawstr(img, 0, 0, dc->pre.r, dc->pre.n, w, h); if(dc->sel >= 0 && dc->sel < n){ sely = (npre + dc->sel) * Fontsz; fill(img + sely * w, Fontsz * w, Colsel); } for(i = 0, y = npre * Fontsz; i < n; i++, y += Fontsz){ s = &dc->kouho[first+i]; putfont(img, w, h, 0, y, '1' + i + Asciitofull); drawstr(img, 2*Fontsz, y, s->r, s->n, w, h); } } static void putimage(int w, int h) { xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, w, h, 0, 0, 0, depth, w * h * 4, (u8int*)img); /* The retained background pixmap lets the server repaint exposures. */ xcb_clear_area(conn, 0, win, 0, 0, w, h); } static int winhide(void) { xcb_unmap_window(conn, win); return xcb_flush(conn) > 0; } static int winshow(Drawcmd *dc) { int npre, px, py, w, h, i, n, maxw; u32int vals[4]; xcb_query_pointer_reply_t *ptr; xcb_query_pointer_cookie_t cookie; n = min(max(dc->nkouho, 0), Maxdisp); npre = dc->pre.n != 0; if(n == 0 && npre == 0){ return winhide(); } maxw = popupcells(dc->pre.r, dc->pre.n); for(i = 0; i < n; i++) maxw = max(maxw, popupcells(dc->kouho[i].r, dc->kouho[i].n)); vals[3] = h = (n + npre) * Fontsz; vals[2] = w = (maxw + 3) * Fontsz; px = py = 0; if(!dc->caret.valid){ cookie = xcb_query_pointer(conn, scr->root); ptr = xcb_query_pointer_reply(conn, cookie, nil); if(ptr == nil) return 0; px = ptr->root_x; py = ptr->root_y; free(ptr); } popupposition(&dc->caret, px, py, scr->width_in_pixels, scr->height_in_pixels, w, h, &px, &py); vals[1] = py; vals[0] = px; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, vals); xcb_map_window(conn, win); drawkouho(dc, 0, n, w, h); putimage(w, h); return xcb_flush(conn) > 0; } void drawthread(void*) { Drawcmd dc; threadsetname("draw"); if(!wininit()) return; while(chanrecv(drawc, &dc) > 0){ while(channbrecv(drawc, &dc) > 0) ; if(dc.nkouho == 0 && dc.pre.n == 0){ if(!winhide()) break; }else if(!winshow(&dc)) break; } wincleanup(); }