diff --git a/config.h b/config.h index 8ffa7c9..3dc7b0a 100644 --- a/config.h +++ b/config.h @@ -41,6 +41,7 @@ settings_t defaults = { .ignore_newline = false, .line_height = 0, /* if line height < font height, it will be raised to font height */ .notification_height = 0, /* if notification height < font height and padding, it will be raised */ +.corner_radius = 0, .separator_height = 2, /* height of the separator line between two notifications */ .padding = 0, diff --git a/config.mk b/config.mk index 8a35a1a..3d57c92 100644 --- a/config.mk +++ b/config.mk @@ -28,6 +28,7 @@ pkg_config_packs := dbus-1 \ pangocairo \ x11 \ xinerama \ + xext \ "xrandr >= 1.5" \ xscrnsaver diff --git a/docs/dunst.pod b/docs/dunst.pod index a8954ed..f2af6ef 100644 --- a/docs/dunst.pod +++ b/docs/dunst.pod @@ -460,6 +460,18 @@ By enabling this setting dunst will not be able to detect when a monitor is connected or disconnected which might break follow mode if the screen layout changes. +=item B (default: 0) + +Define the corner radius in pixels. A corner radius of 0 will result in +rectangular shaped notifications. + +By enabling this setting the outer border and the frame will be shaped. +If you have multiple notifications, the whole window is shaped, not every +single notification. + +To avoid the corners clipping the icon or text the corner radius will be +automatically lowered to half of the notification height if it exceeds it. + =back =head2 Shortcut section diff --git a/dunstrc b/dunstrc index 095cd98..4fba54d 100644 --- a/dunstrc +++ b/dunstrc @@ -207,6 +207,13 @@ # debug: all less than unimportant stuff verbosity = mesg + # Define the corner radius of the notification window + # in pixel size. If the radius is 0, you have no rounded + # corners. + # The radius will be automatically lowered if it exceeds half of the + # notification height to avoid clipping text and/or icons. + corner_radius = 0 + ### Legacy # Use the Xinerama extension instead of RandR for multi-monitor support. diff --git a/src/draw.c b/src/draw.c index f6487ae..77d14a5 100644 --- a/src/draw.c +++ b/src/draw.c @@ -172,6 +172,8 @@ static struct dimensions calculate_dimensions(GSList *layouts) dim.h += 2 * settings.frame_width; dim.h += (g_slist_length(layouts) - 1) * settings.separator_height; + dim.corner_radius = settings.corner_radius; + int text_width = 0, total_width = 0; for (GSList *iter = layouts; iter; iter = iter->next) { colored_layout *cl = iter->data; @@ -217,6 +219,8 @@ static struct dimensions calculate_dimensions(GSList *layouts) dim.h += h; text_width = MAX(w, text_width); } + + dim.corner_radius = MIN(dim.corner_radius, h/2); } if (dim.w <= 0) { @@ -389,12 +393,103 @@ static int layout_get_height(colored_layout *cl) return MAX(h, h_icon); } +static 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; + + if (first && last) { + cairo_new_sub_path(c); + cairo_arc(c, + x + width - corner_radius, + y + corner_radius, + corner_radius, + -90 * degrees, + 0 * degrees); + cairo_arc(c, + x + width - corner_radius, + y + height - corner_radius, + corner_radius, + 0 * degrees, + 90 * degrees); + cairo_arc(c, + x + corner_radius, + y + height - corner_radius, + corner_radius, + 90 * degrees, + 180 * degrees); + cairo_arc(c, + x + corner_radius, + y + corner_radius, + corner_radius, + 180 * degrees, + 270 * degrees); + cairo_close_path(c); + } else if (first) { + cairo_new_sub_path(c); + cairo_arc(c, + x + width - corner_radius, + y + corner_radius, + corner_radius, + -90 * degrees, + 0 * degrees); + cairo_arc(c, + x + width, + y + height, + 0, + 0, + 0); + cairo_arc(c, + x, + y + height, + 0, + 0, + 0); + cairo_arc(c, + x + corner_radius, + y + corner_radius, + corner_radius, + 180 * degrees, + 270 * degrees); + cairo_close_path(c); + } else if (last) { + cairo_new_sub_path(c); + cairo_arc(c, + x + width, + y, + 0, + 0, + 0); + cairo_arc(c, + x + width - corner_radius, + y + height - corner_radius, + corner_radius, + 0 * degrees, + 90 * degrees); + cairo_arc(c, + x + corner_radius, + y + height - corner_radius, + corner_radius, + 90 * degrees, + 180 * degrees); + cairo_arc(c, + x, + y, + 0, + 180 * degrees, + 270 * degrees); + cairo_close_path(c); + } else { + cairo_rectangle(c, x, y, width, height); + } +} + static cairo_surface_t *render_background(cairo_surface_t *srf, colored_layout *cl, colored_layout *cl_next, int y, int width, int height, + int corner_radius, bool first, bool last, int *ret_width) @@ -403,12 +498,15 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, cairo_t *c = cairo_create(srf); - if (first) height += settings.frame_width; - if (last) height += settings.frame_width; - else height += settings.separator_height; + if (first) + height += settings.frame_width; + if (last) + height += settings.frame_width; + else + height += settings.separator_height; cairo_set_source_rgb(c, cl->frame.r, cl->frame.g, cl->frame.b); - cairo_rectangle(c, x, y, width, height); + draw_rounded_rect(c, x, y, width, height, corner_radius, first, last); cairo_fill(c); /* adding frame */ @@ -426,7 +524,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, height -= settings.separator_height; cairo_set_source_rgb(c, cl->bg.r, cl->bg.g, cl->bg.b); - cairo_rectangle(c, x, y, width, height); + draw_rounded_rect(c, x, y, width, height, corner_radius, first, last); cairo_fill(c); if ( settings.sep_color != SEP_FRAME @@ -504,7 +602,7 @@ static struct dimensions layout_render(cairo_surface_t *srf, int bg_width = 0; int bg_height = MAX(settings.notification_height, (2 * settings.padding) + cl_h); - cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, first, last, &bg_width); + cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, first, last, &bg_width); cairo_t *c = cairo_create(content); render_content(c, cl, bg_width); diff --git a/src/settings.c b/src/settings.c index 397d45b..173c910 100644 --- a/src/settings.c +++ b/src/settings.c @@ -368,6 +368,12 @@ void load_settings(char *cmdline_config_path) "Transparency. range 0-100" ); + settings.corner_radius = option_get_int( + "global", + "corner_radius", "-corner_radius", defaults.corner_radius, + "Window corner radius" + ); + { char *c = option_get_string( "global", diff --git a/src/settings.h b/src/settings.h index ee7e4a1..ece154e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -84,6 +84,7 @@ typedef struct _settings { keyboard_shortcut history_ks; keyboard_shortcut context_ks; bool force_xinerama; + int corner_radius; } settings_t; extern settings_t settings; diff --git a/src/x11/x.c b/src/x11/x.c index a559fbf..f3eb336 100644 --- a/src/x11/x.c +++ b/src/x11/x.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,89 @@ static void x_win_move(window_x11 *win, int x, int y, int width, int height) } } +void x_win_round_corners(window_x11 *win, const int rad) +{ + const int width = win->dim.w; + const int height = win->dim.h; + + Pixmap mask = XCreatePixmap(xctx.dpy, win->xwin, width, height, 1); + XGCValues xgcv; + + GC shape_gc = XCreateGC(xctx.dpy, mask, 0, &xgcv); + + const int dia = 2 * rad; + + XSetForeground(xctx.dpy, shape_gc, 0); + XFillRectangle(xctx.dpy, + mask, + shape_gc, + 0, + 0, + width, + height); + + XSetForeground(xctx.dpy, shape_gc, 1); + + XFillArc(xctx.dpy, + mask, + shape_gc, + 0, + 0, + dia, + dia, + 0, + 360 * 64); + XFillArc(xctx.dpy, + mask, + shape_gc, + width - dia - 1, + 0, + dia, + dia, + 0, + 360 * 64); + XFillArc(xctx.dpy, + mask, + shape_gc, + 0, + height - dia - 1, + dia, + dia, + 0, + 360 * 64); + XFillArc(xctx.dpy, + mask, + shape_gc, + width - dia - 1, + height - dia - 1, + dia, + dia, + 0, + 360 * 64); + + 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); + + XFreePixmap(xctx.dpy, mask); + + XShapeSelectInput(xctx.dpy, + win->xwin, ShapeNotifyMask); +} + void x_display_surface(cairo_surface_t *srf, window_x11 *win, const struct dimensions *dim) { x_win_move(win, dim->x, dim->y, dim->w, dim->h); @@ -89,6 +173,8 @@ void x_display_surface(cairo_surface_t *srf, window_x11 *win, const struct dimen cairo_paint(win->c_ctx); cairo_show_page(win->c_ctx); + x_win_round_corners(win, dim->corner_radius); + XFlush(xctx.dpy); } diff --git a/src/x11/x.h b/src/x11/x.h index f32fefa..6974ca1 100644 --- a/src/x11/x.h +++ b/src/x11/x.h @@ -31,6 +31,8 @@ struct dimensions { int y; int w; int h; + + int corner_radius; }; typedef struct _xctx {