fix(popup): fit the current X11 monitor
This commit is contained in:
222
win.c
222
win.c
@@ -1,4 +1,5 @@
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/randr.h>
|
||||
#include "dat.h"
|
||||
#include "fn.h"
|
||||
|
||||
@@ -8,7 +9,9 @@ static xcb_window_t win;
|
||||
static xcb_gcontext_t gc;
|
||||
static xcb_pixmap_t pix;
|
||||
static u32int *img;
|
||||
static int depth;
|
||||
static xcb_atom_t currentdesktop;
|
||||
static xcb_atom_t workarea;
|
||||
static int depth, hasrandr, imgh, imgw;
|
||||
|
||||
static xcb_screen_t*
|
||||
getscr(xcb_connection_t *c, int n)
|
||||
@@ -69,9 +72,137 @@ validformat(xcb_connection_t *c, xcb_screen_t *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xcb_atom_t
|
||||
getatom(char *name, int exists)
|
||||
{
|
||||
xcb_intern_atom_cookie_t cookie;
|
||||
xcb_intern_atom_reply_t *reply;
|
||||
xcb_atom_t atom;
|
||||
|
||||
cookie = xcb_intern_atom(conn, exists, strlen(name), name);
|
||||
reply = xcb_intern_atom_reply(conn, cookie, nil);
|
||||
atom = reply == nil ? XCB_ATOM_NONE : reply->atom;
|
||||
free(reply);
|
||||
return atom;
|
||||
}
|
||||
|
||||
static int
|
||||
getcardinals(xcb_atom_t atom, u32int off, int n, u32int *v)
|
||||
{
|
||||
xcb_get_property_cookie_t cookie;
|
||||
xcb_get_property_reply_t *reply;
|
||||
int ok;
|
||||
|
||||
if(atom == XCB_ATOM_NONE)
|
||||
return 0;
|
||||
cookie = xcb_get_property(conn, 0, scr->root, atom,
|
||||
XCB_ATOM_CARDINAL, off, n);
|
||||
reply = xcb_get_property_reply(conn, cookie, nil);
|
||||
ok = reply != nil && reply->type == XCB_ATOM_CARDINAL &&
|
||||
reply->format == 32 &&
|
||||
xcb_get_property_value_length(reply) == n*(int)sizeof v[0];
|
||||
if(ok)
|
||||
memmove(v, xcb_get_property_value(reply), n*sizeof v[0]);
|
||||
free(reply);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
getworkarea(Area *a)
|
||||
{
|
||||
u32int desktop, v[4];
|
||||
|
||||
if(!getcardinals(currentdesktop, 0, 1, &desktop) ||
|
||||
desktop > 0x3fffffff ||
|
||||
!getcardinals(workarea, desktop*4, 4, v) ||
|
||||
v[0] > 0x7fffffff || v[1] > 0x7fffffff ||
|
||||
v[2] == 0 || v[2] > 0x7fffffff ||
|
||||
v[3] == 0 || v[3] > 0x7fffffff)
|
||||
return 0;
|
||||
a->x = v[0];
|
||||
a->y = v[1];
|
||||
a->w = v[2];
|
||||
a->h = v[3];
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
getrootarea(Area *a)
|
||||
{
|
||||
xcb_get_geometry_cookie_t cookie;
|
||||
xcb_get_geometry_reply_t *reply;
|
||||
|
||||
a->x = 0;
|
||||
a->y = 0;
|
||||
a->w = scr->width_in_pixels;
|
||||
a->h = scr->height_in_pixels;
|
||||
cookie = xcb_get_geometry(conn, scr->root);
|
||||
reply = xcb_get_geometry_reply(conn, cookie, nil);
|
||||
if(reply != nil && reply->width > 0 && reply->height > 0){
|
||||
a->w = reply->width;
|
||||
a->h = reply->height;
|
||||
}
|
||||
free(reply);
|
||||
}
|
||||
|
||||
static void
|
||||
popupwork(int x, int y, Area *out)
|
||||
{
|
||||
xcb_generic_error_t *err;
|
||||
xcb_randr_get_monitors_cookie_t cookie;
|
||||
xcb_randr_get_monitors_reply_t *reply;
|
||||
xcb_randr_monitor_info_iterator_t it;
|
||||
Area root, net, *mon;
|
||||
int i, j, n;
|
||||
|
||||
mon = nil;
|
||||
n = 0;
|
||||
err = nil;
|
||||
reply = nil;
|
||||
if(hasrandr){
|
||||
cookie = xcb_randr_get_monitors(conn, scr->root, 1);
|
||||
reply = xcb_randr_get_monitors_reply(conn, cookie, &err);
|
||||
if(reply != nil)
|
||||
n = xcb_randr_get_monitors_monitors_length(reply);
|
||||
if(n > 0 && (uvlong)n <= (uvlong)(~(ulong)0)/sizeof mon[0])
|
||||
mon = malloc(n*sizeof mon[0]);
|
||||
if(mon != nil){
|
||||
it = xcb_randr_get_monitors_monitors_iterator(reply);
|
||||
for(i = j = 0; i < n && it.rem; i++, xcb_randr_monitor_info_next(&it)){
|
||||
if(it.data->width == 0 || it.data->height == 0)
|
||||
continue;
|
||||
mon[j].x = it.data->x;
|
||||
mon[j].y = it.data->y;
|
||||
mon[j].w = it.data->width;
|
||||
mon[j].h = it.data->height;
|
||||
j++;
|
||||
}
|
||||
n = j;
|
||||
if(n == 0){
|
||||
free(mon);
|
||||
mon = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mon == nil){
|
||||
getrootarea(&root);
|
||||
mon = &root;
|
||||
n = 1;
|
||||
}
|
||||
if(getworkarea(&net))
|
||||
popuparea(mon, n, &net, x, y, out);
|
||||
else
|
||||
popuparea(mon, n, nil, x, y, out);
|
||||
if(mon != &root)
|
||||
free(mon);
|
||||
free(reply);
|
||||
free(err);
|
||||
}
|
||||
|
||||
static void
|
||||
wincleanup(void)
|
||||
{
|
||||
textclose();
|
||||
free(img);
|
||||
img = nil;
|
||||
if(conn != nil){
|
||||
@@ -82,6 +213,11 @@ wincleanup(void)
|
||||
win = 0;
|
||||
gc = 0;
|
||||
pix = 0;
|
||||
currentdesktop = XCB_ATOM_NONE;
|
||||
workarea = XCB_ATOM_NONE;
|
||||
hasrandr = 0;
|
||||
imgh = 0;
|
||||
imgw = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -91,6 +227,9 @@ wininit(void)
|
||||
u32int mask, vals[4];
|
||||
xcb_intern_atom_cookie_t c1, c2;
|
||||
xcb_intern_atom_reply_t *r1, *r2;
|
||||
xcb_randr_query_version_cookie_t rc;
|
||||
xcb_randr_query_version_reply_t *rr;
|
||||
const xcb_query_extension_reply_t *rext;
|
||||
|
||||
conn = xcb_connect(nil, &n);
|
||||
if(conn == nil || xcb_connection_has_error(conn)){
|
||||
@@ -105,6 +244,16 @@ wininit(void)
|
||||
return 0;
|
||||
}
|
||||
depth = scr->root_depth;
|
||||
rext = xcb_get_extension_data(conn, &xcb_randr_id);
|
||||
if(rext != nil && rext->present){
|
||||
rc = xcb_randr_query_version(conn, 1, 5);
|
||||
rr = xcb_randr_query_version_reply(conn, rc, nil);
|
||||
hasrandr = rr != nil && (rr->major_version > 1 ||
|
||||
(rr->major_version == 1 && rr->minor_version >= 5));
|
||||
free(rr);
|
||||
}
|
||||
currentdesktop = getatom("_NET_CURRENT_DESKTOP", 0);
|
||||
workarea = getatom("_NET_WORKAREA", 0);
|
||||
win = xcb_generate_id(conn);
|
||||
mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL |
|
||||
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_SAVE_UNDER;
|
||||
@@ -129,11 +278,6 @@ 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]));
|
||||
if(!textinit()){
|
||||
fprint(2, "strans: popup disabled: no usable fonts\n");
|
||||
wincleanup();
|
||||
@@ -142,10 +286,53 @@ wininit(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
resizebacking(int w, int h)
|
||||
{
|
||||
xcb_generic_error_t *err;
|
||||
xcb_pixmap_t old, new;
|
||||
xcb_void_cookie_t cookie;
|
||||
uvlong pixels;
|
||||
int nh, nw;
|
||||
|
||||
if(w <= 0 || h <= 0)
|
||||
return 0;
|
||||
if(w <= imgw && h <= imgh)
|
||||
return 1;
|
||||
nw = max(w, imgw);
|
||||
nh = max(h, imgh);
|
||||
pixels = (uvlong)nw * nh;
|
||||
if(pixels > (uvlong)(~(ulong)0)/sizeof img[0])
|
||||
return 0;
|
||||
img = erealloc(img, pixels*sizeof img[0]);
|
||||
new = xcb_generate_id(conn);
|
||||
cookie = xcb_create_pixmap_checked(conn, depth, new, win, nw, nh);
|
||||
err = xcb_request_check(conn, cookie);
|
||||
if(err != nil){
|
||||
free(err);
|
||||
return 0;
|
||||
}
|
||||
cookie = xcb_change_window_attributes_checked(conn, win,
|
||||
XCB_CW_BACK_PIXMAP, &new);
|
||||
err = xcb_request_check(conn, cookie);
|
||||
if(err != nil){
|
||||
free(err);
|
||||
xcb_free_pixmap(conn, new);
|
||||
return 0;
|
||||
}
|
||||
old = pix;
|
||||
pix = new;
|
||||
imgw = nw;
|
||||
imgh = nh;
|
||||
if(old != 0)
|
||||
xcb_free_pixmap(conn, old);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
putimage(int w, int h)
|
||||
{
|
||||
if(w <= 0 || w > Imgw || h <= 0 || h > Imgh)
|
||||
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);
|
||||
@@ -163,8 +350,9 @@ winhide(void)
|
||||
static int
|
||||
winshow(Drawcmd *dc)
|
||||
{
|
||||
Area area;
|
||||
Popup p;
|
||||
int px, py;
|
||||
int ax, ay, px, py;
|
||||
u32int vals[4];
|
||||
xcb_query_pointer_reply_t *ptr;
|
||||
xcb_query_pointer_cookie_t cookie;
|
||||
@@ -172,11 +360,6 @@ winshow(Drawcmd *dc)
|
||||
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);
|
||||
@@ -187,17 +370,24 @@ winshow(Drawcmd *dc)
|
||||
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);
|
||||
ax = dc->caret.valid ? dc->caret.x : px;
|
||||
ay = dc->caret.valid ? dc->caret.y : py;
|
||||
popupwork(ax, ay, &area);
|
||||
popuplayout(dc, area.w, area.h, &p);
|
||||
if(p.w <= 0 || p.h <= 0 || !resizebacking(p.w, p.h))
|
||||
return 0;
|
||||
vals[2] = p.w;
|
||||
vals[3] = p.h;
|
||||
popupposition(&dc->caret, px, py, &area, 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);
|
||||
xcb_map_window(conn, win);
|
||||
return xcb_flush(conn) > 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user