From 98a61f089605158bdb6c6c21dd26f797ca2515fa Mon Sep 17 00:00:00 2001 From: fwsmit Date: Mon, 19 Apr 2021 11:26:30 +0200 Subject: [PATCH 1/5] wayland: implement get_scale function for hidpi support --- src/output.c | 8 ++++++-- src/output.h | 2 ++ src/wayland/wl.c | 9 ++++++++- src/wayland/wl.h | 2 ++ src/x11/x.c | 4 ++++ src/x11/x.h | 1 + 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/output.c b/src/output.c index 06f1af5..751db0f 100644 --- a/src/output.c +++ b/src/output.c @@ -29,7 +29,9 @@ const struct output output_x11 = { get_active_screen, x_is_idle, - have_fullscreen_window + have_fullscreen_window, + + x_get_scale, }; #ifdef ENABLE_WAYLAND @@ -49,7 +51,9 @@ const struct output output_wl = { wl_get_active_screen, wl_is_idle, - wl_have_fullscreen_window + wl_have_fullscreen_window, + + wl_get_scale, }; #endif diff --git a/src/output.h b/src/output.h index a6cf5e7..11f307d 100644 --- a/src/output.h +++ b/src/output.h @@ -44,6 +44,8 @@ struct output { bool (*is_idle)(void); bool (*have_fullscreen_window)(void); + + int (*get_scale)(void); }; /** diff --git a/src/wayland/wl.c b/src/wayland/wl.c index 4fae2f4..a10bc34 100644 --- a/src/wayland/wl.c +++ b/src/wayland/wl.c @@ -135,7 +135,6 @@ static void create_output( struct wl_output *wl_output, uint32_t global_name) { LOG_I("New output found - id %i", number); output->global_name = global_name; output->wl_output = wl_output; - // TODO: Fix this output->scale = 1; output->fullscreen = false; wl_list_insert(&ctx.outputs, &output->link); @@ -885,4 +884,12 @@ bool wl_have_fullscreen_window(void) { LOG_D("Fullscreen queried: %i", have_fullscreen); return have_fullscreen; } + +int wl_get_scale(void) { + struct dunst_output *output = get_configured_output(); + if (output) + return output->scale; + else + return 1; +} /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/wayland/wl.h b/src/wayland/wl.h index fe54e12..f4b3af9 100644 --- a/src/wayland/wl.h +++ b/src/wayland/wl.h @@ -23,5 +23,7 @@ const struct screen_info* wl_get_active_screen(void); bool wl_is_idle(void); bool wl_have_fullscreen_window(void); + +int wl_get_scale(void); #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/x11/x.c b/src/x11/x.c index 8cdc0ab..971b57b 100644 --- a/src/x11/x.c +++ b/src/x11/x.c @@ -942,4 +942,8 @@ static void x_shortcut_init(struct keyboard_shortcut *ks) g_free(str_begin); } +int x_get_scale(void) { + return 1; +} + /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/x11/x.h b/src/x11/x.h index 28fb32c..4b0c3cf 100644 --- a/src/x11/x.h +++ b/src/x11/x.h @@ -51,5 +51,6 @@ void x_free(void); struct geometry x_parse_geometry(const char *geom_str); +int x_get_scale(void); #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ From aad4dbaf3d78918303003037e883e7b4b868521a Mon Sep 17 00:00:00 2001 From: fwsmit Date: Tue, 20 Apr 2021 21:42:55 +0200 Subject: [PATCH 2/5] wayland: Some minor cleanup --- src/wayland/wl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wayland/wl.c b/src/wayland/wl.c index a10bc34..6b61c7b 100644 --- a/src/wayland/wl.c +++ b/src/wayland/wl.c @@ -107,7 +107,7 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phy_width, int32_t phy_height, int32_t subpixel, const char *make, const char *model, int32_t transform) { - //TODO + //TODO do something with the subpixel data struct dunst_output *output = data; output->subpixel = subpixel; } @@ -226,7 +226,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, ctx.pointer.wl_pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(ctx.pointer.wl_pointer, &pointer_listener, ctx.seat); - LOG_I("Adding pointer"); } if (ctx.touch.wl_touch != NULL) { wl_touch_release(ctx.touch.wl_touch); From db5e6ce8f49385995f19546e95450472badde8e9 Mon Sep 17 00:00:00 2001 From: fwsmit Date: Wed, 21 Apr 2021 13:27:45 +0200 Subject: [PATCH 3/5] wayland: Add HiDPI support This commit implements support for HiDPI rendering for wayland. X11 should be unaffected by this. It is implemented by scaling everything that's rendered by a scale factor that's obtained from the wayland protocol. All sizes before rendering remain the same, so the same settings should provide the same output for different scaling factors, only scaled by that factor. --- src/draw.c | 121 ++++++++++++++++++++++++++++++--------------- src/draw.h | 6 ++- src/dunst.c | 10 ++++ src/icon.c | 37 +++++++++----- src/icon.h | 26 ++++++++-- src/notification.c | 5 +- src/wayland/wl.c | 14 ++++-- src/wayland/wl.h | 3 ++ src/x11/x.c | 2 +- test/icon.c | 20 ++++---- 10 files changed, 170 insertions(+), 74 deletions(-) diff --git a/src/draw.c b/src/draw.c index 3ee0668..f3e3a6a 100644 --- a/src/draw.c +++ b/src/draw.c @@ -141,10 +141,11 @@ static struct color layout_get_sepcolor(struct colored_layout *cl, static void layout_setup_pango(PangoLayout *layout, int width) { + int scale = output->get_scale(); pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); - pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_width(layout, width * scale * PANGO_SCALE); pango_layout_set_font_description(layout, pango_fdesc); - pango_layout_set_spacing(layout, settings.line_height * PANGO_SCALE); + pango_layout_set_spacing(layout, settings.line_height * scale * PANGO_SCALE); PangoAlignment align; switch (settings.align) { @@ -192,9 +193,20 @@ static bool have_progress_bar(const struct notification *n) return (n->progress >= 0 && settings.progress_bar == true); } +static void get_text_size(PangoLayout *l, int *w, int *h, int scale) { + pango_layout_get_pixel_size(l, w, h); + // scale the size down, because it may be rendered at higher DPI + + if (w) + *w /= scale; + if (h) + *h /= scale; +} + static struct dimensions calculate_dimensions(GSList *layouts) { struct dimensions dim = { 0 }; + int scale = output->get_scale(); const struct screen_info *scr = output->get_active_screen(); if (have_dynamic_width()) { @@ -221,10 +233,10 @@ static struct dimensions calculate_dimensions(GSList *layouts) for (GSList *iter = layouts; iter; iter = iter->next) { struct colored_layout *cl = iter->data; int w=0,h=0; - pango_layout_get_pixel_size(cl->l, &w, &h); + get_text_size(cl->l, &w, &h, scale); if (cl->icon) { - h = MAX(cairo_image_surface_get_height(cl->icon), h); - w += cairo_image_surface_get_width(cl->icon) + settings.h_padding; + h = MAX(get_icon_height(cl->icon, scale), h); + w += get_icon_width(cl->icon, scale) + settings.h_padding; } h = MAX(settings.notification_height, h + settings.padding * 2); dim.h += h; @@ -250,15 +262,15 @@ static struct dimensions calculate_dimensions(GSList *layouts) w -= 2 * settings.h_padding; w -= 2 * settings.frame_width; if (cl->icon) { - w -= cairo_image_surface_get_width(cl->icon) + get_text_icon_padding(); + w -= get_icon_width(cl->icon, scale) + get_text_icon_padding(); } layout_setup_pango(cl->l, w); /* re-read information */ - pango_layout_get_pixel_size(cl->l, &w, &h); + get_text_size(cl->l, &w, &h, scale); if (cl->icon) { - h = MAX(cairo_image_surface_get_height(cl->icon), h); - w += cairo_image_surface_get_width(cl->icon) + settings.h_padding; + h = MAX(get_icon_height(cl->icon, scale), h); + w += get_icon_width(cl->icon, scale) + settings.h_padding; } h = MAX(settings.notification_height, h + settings.padding * 2); dim.h += h; @@ -299,6 +311,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi { struct colored_layout *cl = g_malloc(sizeof(struct colored_layout)); cl->l = layout_create(c); + int scale = output->get_scale(); if (!settings.word_wrap) { PangoEllipsizeMode ellipsize; @@ -346,7 +359,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi width -= 2 * settings.h_padding; width -= 2 * settings.frame_width; if (cl->icon) { - width -= cairo_image_surface_get_width(cl->icon) + get_text_icon_padding(); + width -= get_icon_width(cl->icon, scale) + get_text_icon_padding(); } layout_setup_pango(cl->l, width); } @@ -367,6 +380,7 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi { struct colored_layout *cl = layout_init_shared(c, n); + int scale = output->get_scale(); /* markup */ GError *err = NULL; @@ -388,8 +402,8 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi } - pango_layout_get_pixel_size(cl->l, NULL, &(n->displayed_height)); - if (cl->icon) n->displayed_height = MAX(cairo_image_surface_get_height(cl->icon), n->displayed_height); + get_text_size(cl->l, NULL, &(n->displayed_height), scale); + if (cl->icon) n->displayed_height = MAX(get_icon_height(cl->icon, scale), n->displayed_height); n->displayed_height = n->displayed_height + settings.padding * 2; @@ -435,14 +449,14 @@ static GSList *create_layouts(cairo_t *c) } -static int layout_get_height(struct colored_layout *cl) +static int layout_get_height(struct colored_layout *cl, int scale) { int h; int h_icon = 0; int h_progress_bar = 0; - pango_layout_get_pixel_size(cl->l, NULL, &h); + get_text_size(cl->l, NULL, &h, scale); if (cl->icon) - h_icon = cairo_image_surface_get_height(cl->icon); + h_icon = get_icon_height(cl->icon, scale); if (have_progress_bar(cl->n)){ h_progress_bar = settings.progress_bar_height + settings.padding; } @@ -482,8 +496,14 @@ static int frame_internal_radius (int r, int w, int h) * The top corners will get rounded by `corner_radius`, if `first` is set. * Respectably the same for `last` with the bottom corners. */ -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, int scale, bool first, bool last) { + width *= scale; + height *= scale; + x *= scale; + y *= scale; + corner_radius *= scale; + const float degrees = M_PI / 180.0; cairo_new_sub_path(c); @@ -531,6 +551,13 @@ void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corn cairo_close_path(c); } +/** + * A small wrapper around cairo_rectange for drawing a scaled rectangle. + */ +void draw_rect(cairo_t *c, int x, int y, int width, int height, int scale) { + cairo_rectangle(c, x * scale, y * scale, width * scale, height * scale); +} + static cairo_surface_t *render_background(cairo_surface_t *srf, struct colored_layout *cl, struct colored_layout *cl_next, @@ -540,7 +567,8 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, int corner_radius, bool first, bool last, - int *ret_width) + int *ret_width, + int scale) { int x = 0; int radius_int = corner_radius; @@ -560,7 +588,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, else height += settings.separator_height; - draw_rounded_rect(c, x, y, width, height, corner_radius, first, last); + draw_rounded_rect(c, x, y, width, height, corner_radius, scale, first, last); /* adding frame */ x += settings.frame_width; @@ -578,11 +606,11 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, radius_int = frame_internal_radius(corner_radius, settings.frame_width, height); - draw_rounded_rect(c, x, y, width, height, radius_int, first, last); + draw_rounded_rect(c, x, y, width, height, radius_int, scale, first, last); cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a); cairo_fill(c); - draw_rounded_rect(c, x, y, width, height, radius_int, first, last); + draw_rounded_rect(c, x, y, width, height, radius_int, scale, first, last); cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a); cairo_fill(c); @@ -594,7 +622,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, struct color sep_color = layout_get_sepcolor(cl, cl_next); cairo_set_source_rgba(c, sep_color.r, sep_color.g, sep_color.b, sep_color.a); - cairo_rectangle(c, settings.frame_width, y + height, width, settings.separator_height); + draw_rect(c, settings.frame_width, y + height, width, settings.separator_height, scale); cairo_fill(c); } @@ -604,18 +632,18 @@ static cairo_surface_t *render_background(cairo_surface_t *srf, if (ret_width) *ret_width = width; - return cairo_surface_create_for_rectangle(srf, x, y, width, height); + return cairo_surface_create_for_rectangle(srf, x * scale, y * scale, width * scale, height * scale); } -static void render_content(cairo_t *c, struct colored_layout *cl, int width) +static void render_content(cairo_t *c, struct colored_layout *cl, int width, int scale) { - const int h = layout_get_height(cl); + const int h = layout_get_height(cl, scale); int h_without_progress_bar = h; if (have_progress_bar(cl->n)){ h_without_progress_bar -= settings.progress_bar_height + settings.padding; } int h_text; - pango_layout_get_pixel_size(cl->l, NULL, &h_text); + get_text_size(cl->l, NULL, &h_text, scale); int text_x = settings.h_padding, text_y = settings.padding + h_without_progress_bar / 2 - h_text / 2; @@ -633,10 +661,10 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width) // icon position if (settings.icon_position == ICON_LEFT) { - text_x = cairo_image_surface_get_width(cl->icon) + settings.h_padding + get_text_icon_padding(); + text_x = get_icon_width(cl->icon, scale) + settings.h_padding + get_text_icon_padding(); } // else ICON_RIGHT } - cairo_move_to(c, text_x, text_y); + cairo_move_to(c, text_x * scale, text_y * scale); cairo_set_source_rgba(c, cl->fg.r, cl->fg.g, cl->fg.b, cl->fg.a); pango_cairo_update_layout(c, cl->l); @@ -645,8 +673,8 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width) // icon positioning if (cl->icon) { - unsigned int image_width = cairo_image_surface_get_width(cl->icon), - image_height = cairo_image_surface_get_height(cl->icon), + unsigned int image_width = get_icon_width(cl->icon, scale), + image_height = get_icon_height(cl->icon, scale), image_x = width - settings.h_padding - image_width, image_y = settings.padding + h_without_progress_bar/2 - image_height/2; @@ -664,8 +692,8 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width) image_x = settings.h_padding; } // else ICON_RIGHT - cairo_set_source_surface(c, cl->icon, image_x, image_y); - cairo_rectangle(c, image_x, image_y, image_width, image_height); + cairo_set_source_surface(c, cl->icon, image_x * scale, image_y * scale); + draw_rect(c, image_x, image_y, image_width, image_height, scale); cairo_fill(c); } @@ -689,16 +717,17 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width) // Note: the bar could be drawn a bit smaller, because the frame is drawn on top // left side cairo_set_source_rgba(c, cl->highlight.r, cl->highlight.g, cl->highlight.b, cl->highlight.a); - cairo_rectangle(c, x_bar_1, frame_y, progress_width_1, progress_height); + draw_rect(c, x_bar_1, frame_y, progress_width_1, progress_height, scale); cairo_fill(c); // right side cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a); - cairo_rectangle(c, x_bar_2, frame_y, progress_width_2, progress_height); + draw_rect(c, x_bar_2, frame_y, progress_width_2, progress_height, scale); cairo_fill(c); // border cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a); - cairo_rectangle(c, frame_x + half_frame_width, frame_y + half_frame_width, progress_width - frame_width, progress_height); - cairo_set_line_width(c, frame_width); + // TODO draw_rect instead of cairo_rectangle resulted in blurry lines. Why? + cairo_rectangle(c, (frame_x + half_frame_width) * scale, (frame_y + half_frame_width) * scale, (progress_width - frame_width) * scale, progress_height * scale); + cairo_set_line_width(c, frame_width * scale); cairo_stroke(c); } } @@ -710,18 +739,19 @@ static struct dimensions layout_render(cairo_surface_t *srf, bool first, bool last) { - const int cl_h = layout_get_height(cl); + int scale = output->get_scale(); + const int cl_h = layout_get_height(cl, scale); int h_text = 0; - pango_layout_get_pixel_size(cl->l, NULL, &h_text); + get_text_size(cl->l, NULL, &h_text, scale); 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, dim.corner_radius, 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, scale); cairo_t *c = cairo_create(content); - render_content(c, cl, bg_width); + render_content(c, cl, bg_width, scale); /* adding frame */ if (first) @@ -773,8 +803,9 @@ void draw(void) GSList *layouts = create_layouts(output->win_get_context(win)); struct dimensions dim = calculate_dimensions(layouts); + int scale = output->get_scale(); - cairo_surface_t *image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dim.w, dim.h); + cairo_surface_t *image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dim.w * scale, dim.h * scale); bool first = true; for (GSList *iter = layouts; iter; iter = iter->next) { @@ -799,4 +830,14 @@ void draw_deinit(void) output->win_destroy(win); output->deinit(); } + +int draw_get_scale(void) +{ + if (output) { + return output->get_scale(); + } else { + LOG_W("Called draw_get_scale before output init"); + return 1; + } +} /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/draw.h b/src/draw.h index 43303a4..e968012 100644 --- a/src/draw.h +++ b/src/draw.h @@ -12,9 +12,13 @@ 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_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, int scale, bool first, bool last); + +// TODO get rid of this function by passing scale to everything that needs it. +int draw_get_scale(void); void draw_deinit(void); + #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/dunst.c b/src/dunst.c index dad4c7c..d201fad 100644 --- a/src/dunst.c +++ b/src/dunst.c @@ -24,6 +24,7 @@ GMainLoop *mainloop = NULL; static struct dunst_status status; +static bool setup_done = false; /* see dunst.h */ void dunst_status(const enum dunst_status_field field, @@ -56,6 +57,14 @@ static gboolean run(void *data); void wake_up(void) { + // If wake_up is being called before the output has been setup we should + // return. + if (!setup_done) + { + LOG_D("Ignoring wake up"); + return; + } + LOG_D("Waking up"); run(NULL); } @@ -197,6 +206,7 @@ int dunst_main(int argc, char *argv[]) // we do not call wakeup now, wake_up does not work here yet } + setup_done = true; run(NULL); g_main_loop_run(mainloop); g_clear_pointer(&mainloop, g_main_loop_unref); diff --git a/src/icon.c b/src/icon.c index fdca559..2437550 100644 --- a/src/icon.c +++ b/src/icon.c @@ -86,6 +86,14 @@ static void pixbuf_data_to_cairo_data( } } +int get_icon_width(cairo_surface_t *icon, int scale) { + return cairo_image_surface_get_width(icon) / scale; +} + +int get_icon_height(cairo_surface_t *icon, int scale) { + return cairo_image_surface_get_height(icon) / scale; +} + cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf) { assert(pixbuf); @@ -144,22 +152,26 @@ static bool icon_size_clamp(int *w, int *h) { * * @param pixbuf (nullable) The pixbuf, which may be too big. * Takes ownership of the reference. + * @param dpi_scale An integer for the dpi scaling. That doesn't mean the icon + * is always scaled by dpi_scale. * @return the scaled version of the pixbuf. If scaling wasn't * necessary, it returns the same pixbuf. Transfers full * ownership of the reference. */ -static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf) +static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf, int dpi_scale) { ASSERT_OR_RET(pixbuf, NULL); int w = gdk_pixbuf_get_width(pixbuf); int h = gdk_pixbuf_get_height(pixbuf); + + // TODO immediately rescale icon upon scale changes if (icon_size_clamp(&w, &h)) { GdkPixbuf *scaled = gdk_pixbuf_scale_simple( pixbuf, - w, - h, + w * dpi_scale, + h * dpi_scale, GDK_INTERP_BILINEAR); g_object_unref(pixbuf); pixbuf = scaled; @@ -168,7 +180,7 @@ static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf) return pixbuf; } -GdkPixbuf *get_pixbuf_from_file(const char *filename) +GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale) { char *path = string_to_path(g_strdup(filename)); GError *error = NULL; @@ -179,10 +191,11 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename) g_free(path); return NULL; } + // TODO immediately rescale icon upon scale changes icon_size_clamp(&w, &h); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path, - w, - h, + w * scale, + h * scale, TRUE, &error); @@ -250,7 +263,7 @@ char *get_path_from_icon_name(const char *iconname) return new_name; } -GdkPixbuf *get_pixbuf_from_icon(const char *iconname) +GdkPixbuf *get_pixbuf_from_icon(const char *iconname, int scale) { char *path = get_path_from_icon_name(iconname); if (!path) { @@ -259,7 +272,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname) GdkPixbuf *pixbuf = NULL; - pixbuf = get_pixbuf_from_file(path); + pixbuf = get_pixbuf_from_file(path, scale); g_free(path); if (!pixbuf) @@ -268,18 +281,18 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname) return pixbuf; } -GdkPixbuf *icon_get_for_name(const char *name, char **id) +GdkPixbuf *icon_get_for_name(const char *name, char **id, int scale) { ASSERT_OR_RET(name, NULL); ASSERT_OR_RET(id, NULL); - GdkPixbuf *pb = get_pixbuf_from_icon(name); + GdkPixbuf *pb = get_pixbuf_from_icon(name, scale); if (pb) *id = g_strdup(name); return pb; } -GdkPixbuf *icon_get_for_data(GVariant *data, char **id) +GdkPixbuf *icon_get_for_data(GVariant *data, char **id, int dpi_scale) { ASSERT_OR_RET(data, NULL); ASSERT_OR_RET(id, NULL); @@ -383,7 +396,7 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id) g_free(data_chk); g_variant_unref(data_variant); - pixbuf = icon_pixbuf_scale(pixbuf); + pixbuf = icon_pixbuf_scale(pixbuf, dpi_scale); return pixbuf; } diff --git a/src/icon.h b/src/icon.h index 242880b..ef781fb 100644 --- a/src/icon.h +++ b/src/icon.h @@ -11,11 +11,26 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf); /** Retrieve an icon by its full filepath, scaled according to settings. * * @param filename A string representing a readable file path + * @param scale An integer representing the output dpi scaling. * * @return an instance of `GdkPixbuf` * @retval NULL: file does not exist, not readable, etc.. */ -GdkPixbuf *get_pixbuf_from_file(const char *filename); +GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale); + + +/** + * Get the unscaled icon width. + * + * If scale is 2 for example, the icon will render in twice the size, but + * get_icon_width still returns the same size as when scale is 1. + */ +int get_icon_width(cairo_surface_t *icon, int scale); + +/** + * Get the unscaled icon height, see get_icon_width. + */ +int get_icon_height(cairo_surface_t *icon, int scale); /** Retrieve a path from an icon name. * @@ -33,11 +48,12 @@ char *get_path_from_icon_name(const char *iconname); * @param iconname A string describing a `file://` URL, an arbitary filename * or an icon name, which then gets searched for in the * settings.icon_path + * @param scale An integer representing the output dpi scaling. * * @return an instance of `GdkPixbuf` * @retval NULL: file does not exist, not readable, etc.. */ -GdkPixbuf *get_pixbuf_from_icon(const char *iconname); +GdkPixbuf *get_pixbuf_from_icon(const char *iconname, int scale); /** Read an icon from disk and convert it to a GdkPixbuf, scaled according to settings * @@ -49,10 +65,11 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname); * get searched in the folders of the icon_path setting. * @param id (necessary) A unique identifier of the returned pixbuf. Only filled, * if the return value is non-NULL. + * @param scale An integer representing the output dpi scaling. * @return an instance of `GdkPixbuf`, representing the name's image * @retval NULL: Invalid path given */ -GdkPixbuf *icon_get_for_name(const char *name, char **id); +GdkPixbuf *icon_get_for_name(const char *name, char **id, int dpi_scale); /** Convert a GVariant like described in GdkPixbuf, scaled according to settings * @@ -63,10 +80,11 @@ GdkPixbuf *icon_get_for_name(const char *name, char **id); * like described in the notification spec. * @param id (necessary) A unique identifier of the returned pixbuf. * Only filled, if the return value is non-NULL. + * @param scale An integer representing the output dpi scaling. * @return an instance of `GdkPixbuf` derived from the GVariant * @retval NULL: GVariant parameter nulled, invalid or in wrong format */ -GdkPixbuf *icon_get_for_data(GVariant *data, char **id); +GdkPixbuf *icon_get_for_data(GVariant *data, char **id, int scale); #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/notification.c b/src/notification.c index b16d671..015e4b6 100644 --- a/src/notification.c +++ b/src/notification.c @@ -23,6 +23,7 @@ #include "rules.h" #include "settings.h" #include "utils.h" +#include "draw.h" static void notification_extract_urls(struct notification *n); static void notification_format_message(struct notification *n); @@ -314,7 +315,7 @@ void notification_icon_replace_path(struct notification *n, const char *new_icon g_clear_object(&n->icon); g_clear_pointer(&n->icon_id, g_free); - n->icon = icon_get_for_name(new_icon, &n->icon_id); + n->icon = icon_get_for_name(new_icon, &n->icon_id, draw_get_scale()); } void notification_icon_replace_data(struct notification *n, GVariant *new_icon) @@ -325,7 +326,7 @@ void notification_icon_replace_data(struct notification *n, GVariant *new_icon) g_clear_object(&n->icon); g_clear_pointer(&n->icon_id, g_free); - n->icon = icon_get_for_data(new_icon, &n->icon_id); + n->icon = icon_get_for_data(new_icon, &n->icon_id, draw_get_scale()); } /* see notification.h */ diff --git a/src/wayland/wl.c b/src/wayland/wl.c index 6b61c7b..a623cf1 100644 --- a/src/wayland/wl.c +++ b/src/wayland/wl.c @@ -116,6 +116,8 @@ static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { struct dunst_output *output = data; output->scale = factor; + + wake_up(); } static const struct wl_output_listener output_listener = { @@ -622,7 +624,7 @@ static void schedule_frame_and_commit(); // Draw and commit a new frame. static void send_frame() { - int scale = 1; + int scale = wl_get_scale(); struct dunst_output *output = get_configured_output(); int height = ctx.cur_dim.h; @@ -689,8 +691,6 @@ static void send_frame() { if (ctx.height != height || ctx.width != width) { struct dimensions dim = ctx.cur_dim; // Set window size - LOG_D("Window dimensions %ix%i", dim.w, dim.h); - LOG_D("Window position %ix%i", dim.x, dim.y); zwlr_layer_surface_v1_set_size(ctx.layer_surface, dim.w, dim.h); @@ -815,12 +815,15 @@ void wl_win_hide(window win) { void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) { /* struct window_wl *win = (struct window_wl*)winptr; */ - ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, dim->w, dim->h); + int scale = wl_get_scale(); + LOG_D("Buffer size (scaled) %ix%i", dim->w * scale, dim->h * scale); + ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, + dim->w * scale, dim->h * scale); cairo_t *c = ctx.current_buffer->cairo; cairo_save(c); cairo_set_source_surface(c, srf, 0, 0); - cairo_rectangle(c, 0, 0, dim->w, dim->h); + cairo_rectangle(c, 0, 0, dim->w * scale, dim->h * scale); cairo_fill(c); cairo_restore(c); @@ -848,6 +851,7 @@ const struct screen_info* wl_get_active_screen(void) { .id = 0, .mmh = 500 }; + scr.dpi = wl_get_scale() * 96; return &scr; } diff --git a/src/wayland/wl.h b/src/wayland/wl.h index f4b3af9..5b5a914 100644 --- a/src/wayland/wl.h +++ b/src/wayland/wl.h @@ -24,6 +24,9 @@ const struct screen_info* wl_get_active_screen(void); bool wl_is_idle(void); bool wl_have_fullscreen_window(void); +// Return the dpi scaling of the current output. Everything that's rendered +// should be multiplied by this value, but don't use it to multiply other +// values. All sizes should be in unscaled units. int wl_get_scale(void); #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/x11/x.c b/src/x11/x.c index 971b57b..ab18c91 100644 --- a/src/x11/x.c +++ b/src/x11/x.c @@ -113,7 +113,7 @@ static void x_win_corners_shape(struct window_x11 *win, const int rad) draw_rounded_rect(cr, 0, 0, width, height, - rad, + rad, 1, true, true); cairo_fill(cr); diff --git a/test/icon.c b/test/icon.c index b9ebc7b..1afe1e0 100644 --- a/test/icon.c +++ b/test/icon.c @@ -12,6 +12,8 @@ extern const char *base; +int scale = 1; + TEST test_get_path_from_icon_null(void){ char *result = get_path_from_icon_name(NULL); ASSERT_EQ(result, NULL); @@ -86,7 +88,7 @@ TEST test_get_pixbuf_from_file_tilde(void) gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL); path = string_replace_at(path, 0, strlen(home), "~"); - GdkPixbuf *pixbuf = get_pixbuf_from_file(path); + GdkPixbuf *pixbuf = get_pixbuf_from_file(path, scale); g_clear_pointer(&path, g_free); ASSERT(pixbuf); @@ -101,7 +103,7 @@ TEST test_get_pixbuf_from_file_absolute(void) gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL); - GdkPixbuf *pixbuf = get_pixbuf_from_file(path); + GdkPixbuf *pixbuf = get_pixbuf_from_file(path, scale); g_clear_pointer(&path, g_free); ASSERT(pixbuf); @@ -113,7 +115,7 @@ TEST test_get_pixbuf_from_file_absolute(void) TEST test_get_pixbuf_from_icon_invalid(void) { - GdkPixbuf *pixbuf = get_pixbuf_from_icon("invalid"); + GdkPixbuf *pixbuf = get_pixbuf_from_icon("invalid", scale); ASSERT(pixbuf == NULL); g_clear_pointer(&pixbuf, g_object_unref); @@ -122,7 +124,7 @@ TEST test_get_pixbuf_from_icon_invalid(void) TEST test_get_pixbuf_from_icon_both(void) { - GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1"); + GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1", scale); // the first icon found is invalid, so the pixbuf is empty ASSERT(!pixbuf); g_clear_pointer(&pixbuf, g_object_unref); @@ -132,7 +134,7 @@ TEST test_get_pixbuf_from_icon_both(void) TEST test_get_pixbuf_from_icon_onlysvg(void) { - GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlysvg"); + GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlysvg", scale); ASSERT(pixbuf); ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf)); g_clear_pointer(&pixbuf, g_object_unref); @@ -142,7 +144,7 @@ TEST test_get_pixbuf_from_icon_onlysvg(void) TEST test_get_pixbuf_from_icon_onlypng(void) { - GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng"); + GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng", scale); ASSERT(pixbuf); ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf)); g_clear_pointer(&pixbuf, g_object_unref); @@ -153,7 +155,7 @@ TEST test_get_pixbuf_from_icon_onlypng(void) TEST test_get_pixbuf_from_icon_filename(void) { char *icon = g_strconcat(base, "/data/icons/valid.png", NULL); - GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon); + GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon, scale); ASSERT(pixbuf); ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf)); g_clear_pointer(&pixbuf, g_object_unref); @@ -165,7 +167,7 @@ TEST test_get_pixbuf_from_icon_filename(void) TEST test_get_pixbuf_from_icon_fileuri(void) { char *icon = g_strconcat("file://", base, "/data/icons/valid.svg", NULL); - GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon); + GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon, scale); ASSERT(pixbuf); ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf)); g_clear_pointer(&pixbuf, g_object_unref); @@ -220,7 +222,7 @@ TEST test_icon_size_clamp_too_small_then_too_big(void) TEST test_get_pixbuf_from_icon_both_is_scaled(void) { - GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng"); + GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng", scale); ASSERT(pixbuf); ASSERT_EQ(gdk_pixbuf_get_width(pixbuf), 16); ASSERT_EQ(gdk_pixbuf_get_height(pixbuf), 16); From e69adcefea87c0b1fff92d2359cc12c6ce56217a Mon Sep 17 00:00:00 2001 From: fwsmit Date: Fri, 30 Apr 2021 21:46:52 +0200 Subject: [PATCH 4/5] wayland: Add better detection for the current output When there's only one output, just return that output. --- src/wayland/wl.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/wayland/wl.c b/src/wayland/wl.c index a623cf1..8f744a9 100644 --- a/src/wayland/wl.c +++ b/src/wayland/wl.c @@ -341,20 +341,29 @@ static void add_seat_to_idle_handler(struct wl_seat *seat) { // Warning, can return NULL static struct dunst_output *get_configured_output() { + int n = 0; + int target_monitor = settings.monitor; + + struct dunst_output *first_output = NULL, *configured_output = NULL, + *tmp_output = NULL; + wl_list_for_each(tmp_output, &ctx.outputs, link) { + if (n == 0) + first_output = tmp_output; + if (n == target_monitor) + configured_output = tmp_output; + n++; + } + + // There's only 1 output, so return that + if (n == 1) + return first_output; switch (settings.f_mode){ case FOLLOW_NONE: ; // this semicolon is neccesary - int n = 0; - int target_monitor = settings.monitor; - - struct dunst_output *output; - wl_list_for_each(output, &ctx.outputs, link) { - if (n == target_monitor) - return output; - n++; + if (!configured_output) { + LOG_W("Monitor %i doesn't exist, using focused monitor", settings.monitor); } - LOG_W("Monitor %i doesn't exist, using focused monitor", settings.monitor); - return NULL; + return configured_output; case FOLLOW_MOUSE: // fallthrough case FOLLOW_KEYBOARD: From ea531ca9aea25de228a57bc23ea07277ea3d6e1f Mon Sep 17 00:00:00 2001 From: fwsmit Date: Fri, 30 Apr 2021 22:00:53 +0200 Subject: [PATCH 5/5] wayland: Return the largest scale when output cannot be found When follow mode != none dunst cannot detect what output is being used to display the notification (yet). With this commit dunst falls back to using the largest scale from any ouput. The compositor should scale the buffer down again if the scale of the output is smaller that the used scale. --- src/wayland/wl.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/wayland/wl.c b/src/wayland/wl.c index 8f744a9..b5df3de 100644 --- a/src/wayland/wl.c +++ b/src/wayland/wl.c @@ -898,10 +898,19 @@ bool wl_have_fullscreen_window(void) { } int wl_get_scale(void) { + int scale = 0; struct dunst_output *output = get_configured_output(); - if (output) - return output->scale; - else - return 1; + if (output) { + scale = output->scale; + } else { + // return the largest scale + struct dunst_output *output; + wl_list_for_each(output, &ctx.outputs, link) { + scale = MAX(output->scale, scale); + } + } + if (scale <= 0) + scale = 1; + return scale; } /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */