223 lines
5.1 KiB
C
223 lines
5.1 KiB
C
#include <xcb/xcb.h>
|
|
#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();
|
|
}
|