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, | ||||
| .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, | ||||
|  | ||||
| @ -28,6 +28,7 @@ pkg_config_packs := dbus-1 \ | ||||
|                     pangocairo \
 | ||||
|                     x11 \
 | ||||
|                     xinerama \
 | ||||
|                     xext \
 | ||||
|                     "xrandr >= 1.5" \
 | ||||
|                     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 | ||||
| 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 | ||||
| 
 | ||||
| =head2 Shortcut section | ||||
|  | ||||
							
								
								
									
										7
									
								
								dunstrc
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								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. | ||||
|  | ||||
							
								
								
									
										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 += (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,67 @@ static int layout_get_height(colored_layout *cl) | ||||
|         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, | ||||
|                                           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 +462,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 +488,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 +566,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); | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
							
								
								
									
										69
									
								
								src/x11/x.c
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								src/x11/x.c
									
									
									
									
									
								
							| @ -6,6 +6,7 @@ | ||||
| #include <X11/Xatom.h> | ||||
| #include <X11/Xlib.h> | ||||
| #include <X11/Xutil.h> | ||||
| #include <X11/extensions/shape.h> | ||||
| #include <assert.h> | ||||
| #include <cairo-xlib.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) | ||||
| { | ||||
|         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_show_page(win->c_ctx); | ||||
| 
 | ||||
|         x_win_round_corners(win, dim->corner_radius); | ||||
| 
 | ||||
|         XFlush(xctx.dpy); | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -31,6 +31,8 @@ struct dimensions { | ||||
|         int y; | ||||
|         int w; | ||||
|         int h; | ||||
| 
 | ||||
|         int corner_radius; | ||||
| }; | ||||
| 
 | ||||
| typedef struct _xctx { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nikos Tsipinakis
						Nikos Tsipinakis