fix: make popup rendering deterministic
This commit is contained in:
153
win.c
153
win.c
@@ -1,6 +1,7 @@
|
||||
#include <xcb/xcb.h>
|
||||
#include "dat.h"
|
||||
#include "fn.h"
|
||||
#include "popup_layout.h"
|
||||
|
||||
enum {
|
||||
Asciitofull = 0xFEE0,
|
||||
@@ -12,9 +13,12 @@ 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)
|
||||
{
|
||||
@@ -26,7 +30,70 @@ getscr(xcb_connection_t *c, int n)
|
||||
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;
|
||||
@@ -35,11 +102,17 @@ wininit(void)
|
||||
xcb_intern_atom_reply_t *r1, *r2;
|
||||
|
||||
conn = xcb_connect(nil, &n);
|
||||
if(conn == nil || xcb_connection_has_error(conn))
|
||||
die("xcb_connect");
|
||||
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)
|
||||
die("no screen");
|
||||
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 |
|
||||
@@ -65,16 +138,29 @@ wininit(void)
|
||||
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){
|
||||
putfont(buf, maxw, maxh, x, y, *r++);
|
||||
x += Fontsz;
|
||||
if(*r != 0xfe0e && *r != 0xfe0f){
|
||||
putfont(buf, maxw, maxh, x, y, *r);
|
||||
x += Fontsz;
|
||||
}
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,19 +197,20 @@ drawkouho(Drawcmd *dc, int first, int n, int w, int h)
|
||||
static void
|
||||
putimage(int w, int h)
|
||||
{
|
||||
xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, win, gc,
|
||||
xcb_put_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc,
|
||||
w, h, 0, 0, 0, depth, w * h * 4, (u8int*)img);
|
||||
xcb_flush(conn);
|
||||
/* The retained background pixmap lets the server repaint exposures. */
|
||||
xcb_clear_area(conn, 0, win, 0, 0, w, h);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
winhide(void)
|
||||
{
|
||||
xcb_unmap_window(conn, win);
|
||||
xcb_flush(conn);
|
||||
return xcb_flush(conn) > 0;
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
winshow(Drawcmd *dc)
|
||||
{
|
||||
int npre, px, py, w, h, i, n, maxw;
|
||||
@@ -134,23 +221,27 @@ winshow(Drawcmd *dc)
|
||||
n = min(max(dc->nkouho, 0), Maxdisp);
|
||||
npre = dc->pre.n != 0;
|
||||
if(n == 0 && npre == 0){
|
||||
winhide();
|
||||
return;
|
||||
return winhide();
|
||||
}
|
||||
cookie = xcb_query_pointer(conn, scr->root);
|
||||
maxw = dc->pre.n;
|
||||
maxw = popupcells(dc->pre.r, dc->pre.n);
|
||||
for(i = 0; i < n; i++)
|
||||
maxw = max(maxw, dc->kouho[i].n);
|
||||
ptr = xcb_query_pointer_reply(conn, cookie, nil);
|
||||
if(ptr == nil)
|
||||
die("xcb_query_pointer");
|
||||
px = ptr->root_x + 10;
|
||||
py = ptr->root_y + 10;
|
||||
free(ptr);
|
||||
maxw = max(maxw, popupcells(dc->kouho[i].r, dc->kouho[i].n));
|
||||
vals[3] = h = (n + npre) * Fontsz;
|
||||
vals[2] = w = (maxw + 3) * Fontsz;
|
||||
vals[1] = py = max(0, min(py, scr->height_in_pixels - h));
|
||||
vals[0] = px = max(0, min(px, scr->width_in_pixels - w));
|
||||
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,
|
||||
@@ -158,6 +249,7 @@ winshow(Drawcmd *dc)
|
||||
xcb_map_window(conn, win);
|
||||
drawkouho(dc, 0, n, w, h);
|
||||
putimage(w, h);
|
||||
return xcb_flush(conn) > 0;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -166,13 +258,16 @@ drawthread(void*)
|
||||
Drawcmd dc;
|
||||
|
||||
threadsetname("draw");
|
||||
wininit();
|
||||
if(!wininit())
|
||||
return;
|
||||
while(chanrecv(drawc, &dc) > 0){
|
||||
while(channbrecv(drawc, &dc) > 0)
|
||||
;
|
||||
if(dc.nkouho == 0 && dc.pre.n == 0)
|
||||
winhide();
|
||||
else
|
||||
winshow(&dc);
|
||||
if(dc.nkouho == 0 && dc.pre.n == 0){
|
||||
if(!winhide())
|
||||
break;
|
||||
}else if(!winshow(&dc))
|
||||
break;
|
||||
}
|
||||
wincleanup();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user