#include #include "dat.h" #include "fn.h" 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; 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])); if(!textinit()){ fprint(2, "strans: popup disabled: no usable fonts\n"); wincleanup(); return 0; } return 1; } static void putimage(int w, int h) { if(w <= 0 || w > Imgw || h <= 0 || h > Imgh) return; 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) { Popup p; int px, py; u32int vals[4]; xcb_query_pointer_reply_t *ptr; xcb_query_pointer_cookie_t cookie; if(dc->nkouho <= 0 && dc->pre.n == 0){ return winhide(); } popuplayout(dc, scr->width_in_pixels, &p); if(p.w <= 0 || p.w > Imgw || p.h <= 0 || p.h > Imgh) return 0; vals[2] = p.w; vals[3] = p.h; 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, p.w, p.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); popupdraw(img, dc, &p); putimage(p.w, p.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(); }