Merge pull request #420 from dj95/feature/round_corners
Added: support for round corners
This commit is contained in:
commit
15c252afb0
1
config.h
1
config.h
@ -41,6 +41,7 @@ settings_t defaults = {
|
|||||||
.ignore_newline = false,
|
.ignore_newline = false,
|
||||||
.line_height = 0, /* if line height < font height, it will be raised to font height */
|
.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 */
|
.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 */
|
.separator_height = 2, /* height of the separator line between two notifications */
|
||||||
.padding = 0,
|
.padding = 0,
|
||||||
|
@ -28,6 +28,7 @@ pkg_config_packs := dbus-1 \
|
|||||||
pangocairo \
|
pangocairo \
|
||||||
x11 \
|
x11 \
|
||||||
xinerama \
|
xinerama \
|
||||||
|
xext \
|
||||||
"xrandr >= 1.5" \
|
"xrandr >= 1.5" \
|
||||||
xscrnsaver
|
xscrnsaver
|
||||||
|
|
||||||
|
@ -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
|
connected or disconnected which might break follow mode if the screen layout
|
||||||
changes.
|
changes.
|
||||||
|
|
||||||
|
=item B<corner_radius> (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
|
=back
|
||||||
|
|
||||||
=head2 Shortcut section
|
=head2 Shortcut section
|
||||||
|
7
dunstrc
7
dunstrc
@ -207,6 +207,13 @@
|
|||||||
# debug: all less than unimportant stuff
|
# debug: all less than unimportant stuff
|
||||||
verbosity = mesg
|
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
|
### Legacy
|
||||||
|
|
||||||
# Use the Xinerama extension instead of RandR for multi-monitor support.
|
# Use the Xinerama extension instead of RandR for multi-monitor support.
|
||||||
|
74
src/draw.c
74
src/draw.c
@ -172,6 +172,8 @@ static struct dimensions calculate_dimensions(GSList *layouts)
|
|||||||
dim.h += 2 * settings.frame_width;
|
dim.h += 2 * settings.frame_width;
|
||||||
dim.h += (g_slist_length(layouts) - 1) * settings.separator_height;
|
dim.h += (g_slist_length(layouts) - 1) * settings.separator_height;
|
||||||
|
|
||||||
|
dim.corner_radius = settings.corner_radius;
|
||||||
|
|
||||||
int text_width = 0, total_width = 0;
|
int text_width = 0, total_width = 0;
|
||||||
for (GSList *iter = layouts; iter; iter = iter->next) {
|
for (GSList *iter = layouts; iter; iter = iter->next) {
|
||||||
colored_layout *cl = iter->data;
|
colored_layout *cl = iter->data;
|
||||||
@ -217,6 +219,8 @@ static struct dimensions calculate_dimensions(GSList *layouts)
|
|||||||
dim.h += h;
|
dim.h += h;
|
||||||
text_width = MAX(w, text_width);
|
text_width = MAX(w, text_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dim.corner_radius = MIN(dim.corner_radius, h/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dim.w <= 0) {
|
if (dim.w <= 0) {
|
||||||
@ -389,12 +393,67 @@ static int layout_get_height(colored_layout *cl)
|
|||||||
return MAX(h, h_icon);
|
return MAX(h, h_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
const float degrees = M_PI / 180.0;
|
||||||
|
|
||||||
|
cairo_new_sub_path(c);
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
// bottom right
|
||||||
|
cairo_arc(c,
|
||||||
|
x + width - corner_radius,
|
||||||
|
y + height - corner_radius,
|
||||||
|
corner_radius,
|
||||||
|
degrees * 0,
|
||||||
|
degrees * 90);
|
||||||
|
// bottom left
|
||||||
|
cairo_arc(c,
|
||||||
|
x + corner_radius,
|
||||||
|
y + height - corner_radius,
|
||||||
|
corner_radius,
|
||||||
|
degrees * 90,
|
||||||
|
degrees * 180);
|
||||||
|
} else {
|
||||||
|
cairo_line_to(c, x + width, y + height);
|
||||||
|
cairo_line_to(c, x, y + height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
// top left
|
||||||
|
cairo_arc(c,
|
||||||
|
x + corner_radius,
|
||||||
|
y + corner_radius,
|
||||||
|
corner_radius,
|
||||||
|
degrees * 180,
|
||||||
|
degrees * 270);
|
||||||
|
// top right
|
||||||
|
cairo_arc(c,
|
||||||
|
x + width - corner_radius,
|
||||||
|
y + corner_radius,
|
||||||
|
corner_radius,
|
||||||
|
degrees * 270,
|
||||||
|
degrees * 360);
|
||||||
|
} else {
|
||||||
|
cairo_line_to(c, x, y);
|
||||||
|
cairo_line_to(c, x + width, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_close_path(c);
|
||||||
|
}
|
||||||
|
|
||||||
static cairo_surface_t *render_background(cairo_surface_t *srf,
|
static cairo_surface_t *render_background(cairo_surface_t *srf,
|
||||||
colored_layout *cl,
|
colored_layout *cl,
|
||||||
colored_layout *cl_next,
|
colored_layout *cl_next,
|
||||||
int y,
|
int y,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
|
int corner_radius,
|
||||||
bool first,
|
bool first,
|
||||||
bool last,
|
bool last,
|
||||||
int *ret_width)
|
int *ret_width)
|
||||||
@ -403,12 +462,15 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
|
|||||||
|
|
||||||
cairo_t *c = cairo_create(srf);
|
cairo_t *c = cairo_create(srf);
|
||||||
|
|
||||||
if (first) height += settings.frame_width;
|
if (first)
|
||||||
if (last) height += settings.frame_width;
|
height += settings.frame_width;
|
||||||
else height += settings.separator_height;
|
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_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);
|
cairo_fill(c);
|
||||||
|
|
||||||
/* adding frame */
|
/* adding frame */
|
||||||
@ -426,7 +488,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
|
|||||||
height -= settings.separator_height;
|
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);
|
||||||
cairo_rectangle(c, x, y, width, height);
|
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
|
||||||
cairo_fill(c);
|
cairo_fill(c);
|
||||||
|
|
||||||
if ( settings.sep_color != SEP_FRAME
|
if ( settings.sep_color != SEP_FRAME
|
||||||
@ -504,7 +566,7 @@ static struct dimensions layout_render(cairo_surface_t *srf,
|
|||||||
int bg_width = 0;
|
int bg_width = 0;
|
||||||
int bg_height = MAX(settings.notification_height, (2 * settings.padding) + cl_h);
|
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);
|
cairo_t *c = cairo_create(content);
|
||||||
|
|
||||||
render_content(c, cl, bg_width);
|
render_content(c, cl, bg_width);
|
||||||
|
@ -368,6 +368,12 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Transparency. range 0-100"
|
"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(
|
char *c = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
|
@ -84,6 +84,7 @@ typedef struct _settings {
|
|||||||
keyboard_shortcut history_ks;
|
keyboard_shortcut history_ks;
|
||||||
keyboard_shortcut context_ks;
|
keyboard_shortcut context_ks;
|
||||||
bool force_xinerama;
|
bool force_xinerama;
|
||||||
|
int corner_radius;
|
||||||
} settings_t;
|
} settings_t;
|
||||||
|
|
||||||
extern settings_t settings;
|
extern settings_t settings;
|
||||||
|
69
src/x11/x.c
69
src/x11/x.c
@ -6,6 +6,7 @@
|
|||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/extensions/shape.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <cairo-xlib.h>
|
#include <cairo-xlib.h>
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
@ -80,6 +81,72 @@ static void x_win_move(window_x11 *win, int x, int y, int width, int height)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void x_win_round_corners(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;
|
||||||
|
|
||||||
|
GC shape_gc = XCreateGC(xctx.dpy, mask, 0, &xgcv);
|
||||||
|
|
||||||
|
XSetForeground(xctx.dpy, shape_gc, 0);
|
||||||
|
XFillRectangle(xctx.dpy,
|
||||||
|
mask,
|
||||||
|
shape_gc,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
width,
|
||||||
|
height);
|
||||||
|
|
||||||
|
XSetForeground(xctx.dpy, shape_gc, 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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)
|
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);
|
x_win_move(win, dim->x, dim->y, dim->w, dim->h);
|
||||||
@ -89,6 +156,8 @@ void x_display_surface(cairo_surface_t *srf, window_x11 *win, const struct dimen
|
|||||||
cairo_paint(win->c_ctx);
|
cairo_paint(win->c_ctx);
|
||||||
cairo_show_page(win->c_ctx);
|
cairo_show_page(win->c_ctx);
|
||||||
|
|
||||||
|
x_win_round_corners(win, dim->corner_radius);
|
||||||
|
|
||||||
XFlush(xctx.dpy);
|
XFlush(xctx.dpy);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ struct dimensions {
|
|||||||
int y;
|
int y;
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
|
|
||||||
|
int corner_radius;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _xctx {
|
typedef struct _xctx {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user