diff --git a/src/draw.c b/src/draw.c index bdda7f3..14dfd8b 100644 --- a/src/draw.c +++ b/src/draw.c @@ -388,12 +388,35 @@ static int layout_get_height(struct colored_layout *cl) 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. * The top corners will get rounded by `corner_radius`, if `first` is set. * 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; @@ -454,6 +477,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, int *ret_width) { int x = 0; + int radius_int = corner_radius; cairo_t *c = cairo_create(srf); @@ -464,26 +488,31 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, else height += settings.separator_height; - 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); + if (settings.frame_width > 0) + { + 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 */ - x += settings.frame_width; - if (first) { - y += settings.frame_width; - height -= settings.frame_width; + /* adding frame */ + x += settings.frame_width; + if (first) { + y += 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); - 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); if ( settings.sep_color.type != SEP_FRAME diff --git a/src/draw.h b/src/draw.h index d646dc2..be7a467 100644 --- a/src/draw.h +++ b/src/draw.h @@ -8,6 +8,8 @@ void draw_setup(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); #endif diff --git a/src/x11/x.c b/src/x11/x.c index 6595d28..772ae74 100644 --- a/src/x11/x.c +++ b/src/x11/x.c @@ -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 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); - XGCValues xgcv; + Pixmap mask; + 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); - XFillRectangle(xctx.dpy, - mask, - shape_gc, - 0, - 0, - width, - height); + cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - 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 - * use a circle for every corner and two overlapping rectangles */ - unsigned const int centercoords[] = { - 0, 0, - width - dia - 1, 0, - 0, height - dia - 1, - width - dia - 1, height - dia - 1, - }; + draw_rounded_rect(cr, 0, 0, + width, height, + rad, + true, true); + cairo_fill(cr); - for (int i = 0; i < sizeof(centercoords)/sizeof(unsigned int); i = i+2) { - XFillArc(xctx.dpy, - mask, - shape_gc, - 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); + cairo_show_page(cr); + cairo_destroy(cr); + cairo_surface_flush(cxbm); + cairo_surface_destroy(cxbm); XShapeCombineMask(xctx.dpy, win->xwin, ShapeBounding, 0, 0, mask, ShapeSet); - XFreeGC(xctx.dpy, shape_gc); XFreePixmap(xctx.dpy, mask); XShapeSelectInput(xctx.dpy, 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) { x_win_move(win, dim->x, dim->y, 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_paint(win->c_ctx); cairo_show_page(win->c_ctx); - if (settings.corner_radius != 0) - x_win_round_corners(win, dim->corner_radius); + if (settings.corner_radius != 0 && ! x_win_composited(win)) + x_win_corners_shape(win, dim->corner_radius); + else + x_win_corners_unshape(win); XFlush(xctx.dpy); @@ -641,12 +643,27 @@ struct window_x11 *x_win_create(void) struct window_x11 *win = g_malloc0(sizeof(struct window_x11)); Window root; + int scr_n; + int depth; + Visual * vis; + XVisualInfo vi; 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.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 = ExposureMask | KeyPressMask | VisibilityChangeMask | ButtonReleaseMask | FocusChangeMask| StructureNotifyMask; @@ -659,10 +676,10 @@ struct window_x11 *x_win_create(void) scr->w, 1, 0, - DefaultDepth(xctx.dpy, DefaultScreen(xctx.dpy)), + depth, CopyFromParent, - DefaultVisual(xctx.dpy, DefaultScreen(xctx.dpy)), - CWOverrideRedirect | CWBackPixmap | CWEventMask, + vis, + CWOverrideRedirect | CWBackPixmap | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &wa); x_set_wm(win->xwin); @@ -673,7 +690,7 @@ struct window_x11 *x_win_create(void) (0xffffffff / 100))); win->root_surface = cairo_xlib_surface_create(xctx.dpy, win->xwin, - DefaultVisual(xctx.dpy, 0), + vis, WIDTH, HEIGHT); win->c_ctx = cairo_create(win->root_surface);