Merge pull request #713 from nick87720z/fix-ugly-frame

Fix ugly frame
This commit is contained in:
Nikos Tsipinakis 2020-05-22 19:08:04 +02:00 committed by GitHub
commit 813417915c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 74 deletions

View File

@ -388,12 +388,35 @@ static int layout_get_height(struct colored_layout *cl)
return MAX(h, h_icon); return MAX(h, h_icon);
} }
/* Attempt to make internal radius more organic.
* Simple r-w is not enough for too small r/w ratio.
* simplifications: r/2 == r - w + w*w / (r * 2) with (w == r)
* r, w - corner radius & frame width,
* h - box height
*/
static int frame_internal_radius (int r, int w, int h)
{
// Integer precision scaler, using 1/4 of int size
const int s = 2 << (8 * sizeof(int) / 4);
int r1, r2, ret;
h *= s;
r *= s;
w *= s;
r1 = r - w + w * w / (r * 2); // w < r
r2 = r * h / (h + (w - r) * 2); // w >= r
ret = (r > w) ? r1 : (r / 2 < r2) ? r / 2 : r2;
return ret / s;
}
/** /**
* Create a path on the given cairo context to draw the background of a notification. * Create a path on the given cairo context to draw the background of a notification.
* The top corners will get rounded by `corner_radius`, if `first` is set. * The top corners will get rounded by `corner_radius`, if `first` is set.
* Respectably the same for `last` with the bottom corners. * Respectably the same for `last` with the bottom corners.
*/ */
static void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last) void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last)
{ {
const float degrees = M_PI / 180.0; const float degrees = M_PI / 180.0;
@ -454,6 +477,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
int *ret_width) int *ret_width)
{ {
int x = 0; int x = 0;
int radius_int = corner_radius;
cairo_t *c = cairo_create(srf); cairo_t *c = cairo_create(srf);
@ -464,26 +488,31 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
else else
height += settings.separator_height; height += settings.separator_height;
cairo_set_source_rgb(c, cl->frame.r, cl->frame.g, cl->frame.b); if (settings.frame_width > 0)
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last); {
cairo_fill(c); cairo_set_source_rgb(c, cl->frame.r, cl->frame.g, cl->frame.b);
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
cairo_fill(c);
/* adding frame */ /* adding frame */
x += settings.frame_width; x += settings.frame_width;
if (first) { if (first) {
y += settings.frame_width; y += settings.frame_width;
height -= settings.frame_width; height -= settings.frame_width;
}
width -= 2 * settings.frame_width;
if (last)
height -= settings.frame_width;
else
height -= settings.separator_height;
radius_int = frame_internal_radius (corner_radius, settings.frame_width, height);
} }
width -= 2 * settings.frame_width;
if (last)
height -= settings.frame_width;
else
height -= settings.separator_height;
cairo_set_source_rgb(c, cl->bg.r, cl->bg.g, cl->bg.b); cairo_set_source_rgb(c, cl->bg.r, cl->bg.g, cl->bg.b);
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last); draw_rounded_rect(c, x, y, width, height, radius_int, first, last);
cairo_fill(c); cairo_fill(c);
if ( settings.sep_color.type != SEP_FRAME if ( settings.sep_color.type != SEP_FRAME

View File

@ -8,6 +8,8 @@ void draw_setup(void);
void draw(void); void draw(void);
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last);
void draw_deinit(void); void draw_deinit(void);
#endif #endif

View File

@ -85,84 +85,86 @@ static void x_win_move(struct window_x11 *win, int x, int y, int width, int heig
} }
} }
static void x_win_round_corners(struct window_x11 *win, const int rad) static void x_win_corners_shape(struct window_x11 *win, const int rad)
{ {
const int width = win->dim.w; const int width = win->dim.w;
const int height = win->dim.h; const int height = win->dim.h;
const int dia = 2 * rad;
const int degrees = 64; // the factor to convert degrees to XFillArc's angle param
Pixmap mask = XCreatePixmap(xctx.dpy, win->xwin, width, height, 1); Pixmap mask;
XGCValues xgcv; cairo_surface_t * cxbm;
cairo_t * cr;
Screen * scr;
GC shape_gc = XCreateGC(xctx.dpy, mask, 0, &xgcv); mask = XCreatePixmap(xctx.dpy, win->xwin, width, height, 1);
scr = ScreenOfDisplay(xctx.dpy, win->cur_screen);
cxbm = cairo_xlib_surface_create_for_bitmap(xctx.dpy, mask, scr, width, height);
cr = cairo_create(cxbm);
XSetForeground(xctx.dpy, shape_gc, 0); cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
XFillRectangle(xctx.dpy, cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
mask,
shape_gc,
0,
0,
width,
height);
XSetForeground(xctx.dpy, shape_gc, 1); cairo_set_source_rgba(cr, 0, 0, 0, 0);
cairo_paint(cr);
cairo_set_source_rgba(cr, 1, 1, 1, 1);
/* To mark all pixels, which should get exposed, we draw_rounded_rect(cr, 0, 0,
* use a circle for every corner and two overlapping rectangles */ width, height,
unsigned const int centercoords[] = { rad,
0, 0, true, true);
width - dia - 1, 0, cairo_fill(cr);
0, height - dia - 1,
width - dia - 1, height - dia - 1,
};
for (int i = 0; i < sizeof(centercoords)/sizeof(unsigned int); i = i+2) { cairo_show_page(cr);
XFillArc(xctx.dpy, cairo_destroy(cr);
mask, cairo_surface_flush(cxbm);
shape_gc, cairo_surface_destroy(cxbm);
centercoords[i],
centercoords[i+1],
dia,
dia,
degrees * 0,
degrees * 360);
}
XFillRectangle(xctx.dpy,
mask,
shape_gc,
rad,
0,
width-dia,
height);
XFillRectangle(xctx.dpy,
mask,
shape_gc,
0,
rad,
width,
height-dia);
XShapeCombineMask(xctx.dpy, win->xwin, ShapeBounding, 0, 0, mask, ShapeSet); XShapeCombineMask(xctx.dpy, win->xwin, ShapeBounding, 0, 0, mask, ShapeSet);
XFreeGC(xctx.dpy, shape_gc);
XFreePixmap(xctx.dpy, mask); XFreePixmap(xctx.dpy, mask);
XShapeSelectInput(xctx.dpy, XShapeSelectInput(xctx.dpy,
win->xwin, ShapeNotifyMask); win->xwin, ShapeNotifyMask);
} }
static void x_win_corners_unshape(struct window_x11 *win)
{
XRectangle rect = {
.x = 0,
.y = 0,
.width = win->dim.w,
.height = win->dim.h };
XShapeCombineRectangles(xctx.dpy, win->xwin, ShapeBounding, 0, 0, &rect, 1, ShapeSet, 1);
XShapeSelectInput(xctx.dpy,
win->xwin, ShapeNotifyMask);
}
static bool x_win_composited(struct window_x11 *win)
{
char astr[sizeof("_NET_WM_CM_S") / sizeof(char) + 8];
Atom cm_sel;
sprintf(astr, "_NET_WM_CM_S%i", win->cur_screen);
cm_sel = XInternAtom(xctx.dpy, astr, true);
return XGetSelectionOwner(xctx.dpy, cm_sel) != None;
}
void x_display_surface(cairo_surface_t *srf, struct window_x11 *win, const struct dimensions *dim) void x_display_surface(cairo_surface_t *srf, struct window_x11 *win, const struct dimensions *dim)
{ {
x_win_move(win, dim->x, dim->y, dim->w, dim->h); x_win_move(win, dim->x, dim->y, dim->w, dim->h);
cairo_xlib_surface_set_size(win->root_surface, dim->w, dim->h); cairo_xlib_surface_set_size(win->root_surface, dim->w, dim->h);
XClearWindow(xctx.dpy, win->xwin);
XFlush(xctx.dpy);
cairo_set_source_surface(win->c_ctx, srf, 0, 0); cairo_set_source_surface(win->c_ctx, srf, 0, 0);
cairo_paint(win->c_ctx); cairo_paint(win->c_ctx);
cairo_show_page(win->c_ctx); cairo_show_page(win->c_ctx);
if (settings.corner_radius != 0) if (settings.corner_radius != 0 && ! x_win_composited(win))
x_win_round_corners(win, dim->corner_radius); x_win_corners_shape(win, dim->corner_radius);
else
x_win_corners_unshape(win);
XFlush(xctx.dpy); XFlush(xctx.dpy);
@ -641,12 +643,27 @@ struct window_x11 *x_win_create(void)
struct window_x11 *win = g_malloc0(sizeof(struct window_x11)); struct window_x11 *win = g_malloc0(sizeof(struct window_x11));
Window root; Window root;
int scr_n;
int depth;
Visual * vis;
XVisualInfo vi;
XSetWindowAttributes wa; XSetWindowAttributes wa;
root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)); scr_n = DefaultScreen(xctx.dpy);
root = RootWindow(xctx.dpy, scr_n);
if (XMatchVisualInfo(xctx.dpy, scr_n, 32, TrueColor, &vi)) {
vis = vi.visual;
depth = vi.depth;
} else {
vis = DefaultVisual(xctx.dpy, scr_n);
depth = DefaultDepth(xctx.dpy, scr_n);
}
wa.override_redirect = true; wa.override_redirect = true;
wa.background_pixmap = ParentRelative; wa.background_pixmap = None;
wa.background_pixel = 0;
wa.border_pixel = 0;
wa.colormap = XCreateColormap(xctx.dpy, root, vis, AllocNone);
wa.event_mask = wa.event_mask =
ExposureMask | KeyPressMask | VisibilityChangeMask | ExposureMask | KeyPressMask | VisibilityChangeMask |
ButtonReleaseMask | FocusChangeMask| StructureNotifyMask; ButtonReleaseMask | FocusChangeMask| StructureNotifyMask;
@ -659,10 +676,10 @@ struct window_x11 *x_win_create(void)
scr->w, scr->w,
1, 1,
0, 0,
DefaultDepth(xctx.dpy, DefaultScreen(xctx.dpy)), depth,
CopyFromParent, CopyFromParent,
DefaultVisual(xctx.dpy, DefaultScreen(xctx.dpy)), vis,
CWOverrideRedirect | CWBackPixmap | CWEventMask, CWOverrideRedirect | CWBackPixmap | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask,
&wa); &wa);
x_set_wm(win->xwin); x_set_wm(win->xwin);
@ -673,7 +690,7 @@ struct window_x11 *x_win_create(void)
(0xffffffff / 100))); (0xffffffff / 100)));
win->root_surface = cairo_xlib_surface_create(xctx.dpy, win->xwin, win->root_surface = cairo_xlib_surface_create(xctx.dpy, win->xwin,
DefaultVisual(xctx.dpy, 0), vis,
WIDTH, HEIGHT); WIDTH, HEIGHT);
win->c_ctx = cairo_create(win->root_surface); win->c_ctx = cairo_create(win->root_surface);