Merge pull request #854 from fwSmit/wayland-hidpi

wayland hidpi support
This commit is contained in:
Nikos Tsipinakis 2021-05-26 20:31:50 +02:00 committed by GitHub
commit b77f76f02e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 222 additions and 89 deletions

View File

@ -141,10 +141,11 @@ static struct color layout_get_sepcolor(struct colored_layout *cl,
static void layout_setup_pango(PangoLayout *layout, int width) 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_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_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; PangoAlignment align;
switch (settings.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); 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) static struct dimensions calculate_dimensions(GSList *layouts)
{ {
struct dimensions dim = { 0 }; struct dimensions dim = { 0 };
int scale = output->get_scale();
const struct screen_info *scr = output->get_active_screen(); const struct screen_info *scr = output->get_active_screen();
if (have_dynamic_width()) { if (have_dynamic_width()) {
@ -221,10 +233,10 @@ static struct dimensions calculate_dimensions(GSList *layouts)
for (GSList *iter = layouts; iter; iter = iter->next) { for (GSList *iter = layouts; iter; iter = iter->next) {
struct colored_layout *cl = iter->data; struct colored_layout *cl = iter->data;
int w=0,h=0; 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) { if (cl->icon) {
h = MAX(cairo_image_surface_get_height(cl->icon), h); h = MAX(get_icon_height(cl->icon, scale), h);
w += cairo_image_surface_get_width(cl->icon) + settings.h_padding; w += get_icon_width(cl->icon, scale) + settings.h_padding;
} }
h = MAX(settings.notification_height, h + settings.padding * 2); h = MAX(settings.notification_height, h + settings.padding * 2);
dim.h += h; dim.h += h;
@ -250,15 +262,15 @@ static struct dimensions calculate_dimensions(GSList *layouts)
w -= 2 * settings.h_padding; w -= 2 * settings.h_padding;
w -= 2 * settings.frame_width; w -= 2 * settings.frame_width;
if (cl->icon) { 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); layout_setup_pango(cl->l, w);
/* re-read information */ /* re-read information */
pango_layout_get_pixel_size(cl->l, &w, &h); get_text_size(cl->l, &w, &h, scale);
if (cl->icon) { if (cl->icon) {
h = MAX(cairo_image_surface_get_height(cl->icon), h); h = MAX(get_icon_height(cl->icon, scale), h);
w += cairo_image_surface_get_width(cl->icon) + settings.h_padding; w += get_icon_width(cl->icon, scale) + settings.h_padding;
} }
h = MAX(settings.notification_height, h + settings.padding * 2); h = MAX(settings.notification_height, h + settings.padding * 2);
dim.h += h; 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)); struct colored_layout *cl = g_malloc(sizeof(struct colored_layout));
cl->l = layout_create(c); cl->l = layout_create(c);
int scale = output->get_scale();
if (!settings.word_wrap) { if (!settings.word_wrap) {
PangoEllipsizeMode ellipsize; 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.h_padding;
width -= 2 * settings.frame_width; width -= 2 * settings.frame_width;
if (cl->icon) { 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); 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); struct colored_layout *cl = layout_init_shared(c, n);
int scale = output->get_scale();
/* markup */ /* markup */
GError *err = NULL; 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)); get_text_size(cl->l, NULL, &(n->displayed_height), scale);
if (cl->icon) n->displayed_height = MAX(cairo_image_surface_get_height(cl->icon), n->displayed_height); 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; 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;
int h_icon = 0; int h_icon = 0;
int h_progress_bar = 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) 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)){ if (have_progress_bar(cl->n)){
h_progress_bar = settings.progress_bar_height + settings.padding; 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. * The top corners will get rounded by `corner_radius`, if `first` is set.
* Respectably the same for `last` with the bottom corners. * 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; const float degrees = M_PI / 180.0;
cairo_new_sub_path(c); 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); 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, static cairo_surface_t *render_background(cairo_surface_t *srf,
struct colored_layout *cl, struct colored_layout *cl,
struct colored_layout *cl_next, struct colored_layout *cl_next,
@ -540,7 +567,8 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
int corner_radius, int corner_radius,
bool first, bool first,
bool last, bool last,
int *ret_width) int *ret_width,
int scale)
{ {
int x = 0; int x = 0;
int radius_int = corner_radius; int radius_int = corner_radius;
@ -560,7 +588,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
else else
height += settings.separator_height; 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 */ /* adding frame */
x += settings.frame_width; 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); 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_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a);
cairo_fill(c); 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_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a);
cairo_fill(c); 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); 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_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); cairo_fill(c);
} }
@ -604,18 +632,18 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
if (ret_width) if (ret_width)
*ret_width = 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; int h_without_progress_bar = h;
if (have_progress_bar(cl->n)){ if (have_progress_bar(cl->n)){
h_without_progress_bar -= settings.progress_bar_height + settings.padding; h_without_progress_bar -= settings.progress_bar_height + settings.padding;
} }
int h_text; 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, int text_x = settings.h_padding,
text_y = settings.padding + h_without_progress_bar / 2 - h_text / 2; 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 // icon position
if (settings.icon_position == ICON_LEFT) { 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 } // 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); cairo_set_source_rgba(c, cl->fg.r, cl->fg.g, cl->fg.b, cl->fg.a);
pango_cairo_update_layout(c, cl->l); 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 // icon positioning
if (cl->icon) { if (cl->icon) {
unsigned int image_width = cairo_image_surface_get_width(cl->icon), unsigned int image_width = get_icon_width(cl->icon, scale),
image_height = cairo_image_surface_get_height(cl->icon), image_height = get_icon_height(cl->icon, scale),
image_x = width - settings.h_padding - image_width, image_x = width - settings.h_padding - image_width,
image_y = settings.padding + h_without_progress_bar/2 - image_height/2; 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; image_x = settings.h_padding;
} // else ICON_RIGHT } // else ICON_RIGHT
cairo_set_source_surface(c, cl->icon, image_x, image_y); cairo_set_source_surface(c, cl->icon, image_x * scale, image_y * scale);
cairo_rectangle(c, image_x, image_y, image_width, image_height); draw_rect(c, image_x, image_y, image_width, image_height, scale);
cairo_fill(c); 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 // Note: the bar could be drawn a bit smaller, because the frame is drawn on top
// left side // left side
cairo_set_source_rgba(c, cl->highlight.r, cl->highlight.g, cl->highlight.b, cl->highlight.a); 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); cairo_fill(c);
// right side // right side
cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a); 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); cairo_fill(c);
// border // border
cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a); 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); // TODO draw_rect instead of cairo_rectangle resulted in blurry lines. Why?
cairo_set_line_width(c, frame_width); 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); cairo_stroke(c);
} }
} }
@ -710,18 +739,19 @@ static struct dimensions layout_render(cairo_surface_t *srf,
bool first, bool first,
bool last) 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; 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_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, 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); cairo_t *c = cairo_create(content);
render_content(c, cl, bg_width); render_content(c, cl, bg_width, scale);
/* adding frame */ /* adding frame */
if (first) if (first)
@ -773,8 +803,9 @@ void draw(void)
GSList *layouts = create_layouts(output->win_get_context(win)); GSList *layouts = create_layouts(output->win_get_context(win));
struct dimensions dim = calculate_dimensions(layouts); 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; bool first = true;
for (GSList *iter = layouts; iter; iter = iter->next) { for (GSList *iter = layouts; iter; iter = iter->next) {
@ -799,4 +830,14 @@ void draw_deinit(void)
output->win_destroy(win); output->win_destroy(win);
output->deinit(); 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: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -12,9 +12,13 @@ void draw_setup(void);
void draw(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); void draw_deinit(void);
#endif #endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -24,6 +24,7 @@
GMainLoop *mainloop = NULL; GMainLoop *mainloop = NULL;
static struct dunst_status status; static struct dunst_status status;
static bool setup_done = false;
/* see dunst.h */ /* see dunst.h */
void dunst_status(const enum dunst_status_field field, void dunst_status(const enum dunst_status_field field,
@ -56,6 +57,14 @@ static gboolean run(void *data);
void wake_up(void) 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"); LOG_D("Waking up");
run(NULL); 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 // we do not call wakeup now, wake_up does not work here yet
} }
setup_done = true;
run(NULL); run(NULL);
g_main_loop_run(mainloop); g_main_loop_run(mainloop);
g_clear_pointer(&mainloop, g_main_loop_unref); g_clear_pointer(&mainloop, g_main_loop_unref);

View File

@ -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) cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf)
{ {
assert(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. * @param pixbuf (nullable) The pixbuf, which may be too big.
* Takes ownership of the reference. * 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 * @return the scaled version of the pixbuf. If scaling wasn't
* necessary, it returns the same pixbuf. Transfers full * necessary, it returns the same pixbuf. Transfers full
* ownership of the reference. * 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); ASSERT_OR_RET(pixbuf, NULL);
int w = gdk_pixbuf_get_width(pixbuf); int w = gdk_pixbuf_get_width(pixbuf);
int h = gdk_pixbuf_get_height(pixbuf); int h = gdk_pixbuf_get_height(pixbuf);
// TODO immediately rescale icon upon scale changes
if (icon_size_clamp(&w, &h)) { if (icon_size_clamp(&w, &h)) {
GdkPixbuf *scaled = gdk_pixbuf_scale_simple( GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
pixbuf, pixbuf,
w, w * dpi_scale,
h, h * dpi_scale,
GDK_INTERP_BILINEAR); GDK_INTERP_BILINEAR);
g_object_unref(pixbuf); g_object_unref(pixbuf);
pixbuf = scaled; pixbuf = scaled;
@ -168,7 +180,7 @@ static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
return 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)); char *path = string_to_path(g_strdup(filename));
GError *error = NULL; GError *error = NULL;
@ -179,10 +191,11 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename)
g_free(path); g_free(path);
return NULL; return NULL;
} }
// TODO immediately rescale icon upon scale changes
icon_size_clamp(&w, &h); icon_size_clamp(&w, &h);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path, GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
w, w * scale,
h, h * scale,
TRUE, TRUE,
&error); &error);
@ -250,7 +263,7 @@ char *get_path_from_icon_name(const char *iconname)
return new_name; 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); char *path = get_path_from_icon_name(iconname);
if (!path) { if (!path) {
@ -259,7 +272,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
GdkPixbuf *pixbuf = NULL; GdkPixbuf *pixbuf = NULL;
pixbuf = get_pixbuf_from_file(path); pixbuf = get_pixbuf_from_file(path, scale);
g_free(path); g_free(path);
if (!pixbuf) if (!pixbuf)
@ -268,18 +281,18 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
return pixbuf; 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(name, NULL);
ASSERT_OR_RET(id, NULL); ASSERT_OR_RET(id, NULL);
GdkPixbuf *pb = get_pixbuf_from_icon(name); GdkPixbuf *pb = get_pixbuf_from_icon(name, scale);
if (pb) if (pb)
*id = g_strdup(name); *id = g_strdup(name);
return pb; 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(data, NULL);
ASSERT_OR_RET(id, NULL); ASSERT_OR_RET(id, NULL);
@ -389,7 +402,7 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
g_free(data_chk); g_free(data_chk);
g_variant_unref(data_variant); g_variant_unref(data_variant);
pixbuf = icon_pixbuf_scale(pixbuf); pixbuf = icon_pixbuf_scale(pixbuf, dpi_scale);
return pixbuf; return pixbuf;
} }

View File

@ -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. /** Retrieve an icon by its full filepath, scaled according to settings.
* *
* @param filename A string representing a readable file path * @param filename A string representing a readable file path
* @param scale An integer representing the output dpi scaling.
* *
* @return an instance of `GdkPixbuf` * @return an instance of `GdkPixbuf`
* @retval NULL: file does not exist, not readable, etc.. * @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. /** 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 * @param iconname A string describing a `file://` URL, an arbitary filename
* or an icon name, which then gets searched for in the * or an icon name, which then gets searched for in the
* settings.icon_path * settings.icon_path
* @param scale An integer representing the output dpi scaling.
* *
* @return an instance of `GdkPixbuf` * @return an instance of `GdkPixbuf`
* @retval NULL: file does not exist, not readable, etc.. * @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 /** 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. * get searched in the folders of the icon_path setting.
* @param id (necessary) A unique identifier of the returned pixbuf. Only filled, * @param id (necessary) A unique identifier of the returned pixbuf. Only filled,
* if the return value is non-NULL. * 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 * @return an instance of `GdkPixbuf`, representing the name's image
* @retval NULL: Invalid path given * @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 /** 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. * like described in the notification spec.
* @param id (necessary) A unique identifier of the returned pixbuf. * @param id (necessary) A unique identifier of the returned pixbuf.
* Only filled, if the return value is non-NULL. * 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 * @return an instance of `GdkPixbuf` derived from the GVariant
* @retval NULL: GVariant parameter nulled, invalid or in wrong format * @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 #endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -23,6 +23,7 @@
#include "rules.h" #include "rules.h"
#include "settings.h" #include "settings.h"
#include "utils.h" #include "utils.h"
#include "draw.h"
static void notification_extract_urls(struct notification *n); static void notification_extract_urls(struct notification *n);
static void notification_format_message(struct notification *n); static void notification_format_message(struct notification *n);
@ -313,7 +314,7 @@ void notification_icon_replace_path(struct notification *n, const char *new_icon
g_clear_object(&n->icon); g_clear_object(&n->icon);
g_clear_pointer(&n->icon_id, g_free); 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) void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
@ -324,7 +325,7 @@ void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
g_clear_object(&n->icon); g_clear_object(&n->icon);
g_clear_pointer(&n->icon_id, g_free); 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 */ /* see notification.h */

View File

@ -29,7 +29,9 @@ const struct output output_x11 = {
get_active_screen, get_active_screen,
x_is_idle, x_is_idle,
have_fullscreen_window have_fullscreen_window,
x_get_scale,
}; };
#ifdef ENABLE_WAYLAND #ifdef ENABLE_WAYLAND
@ -49,7 +51,9 @@ const struct output output_wl = {
wl_get_active_screen, wl_get_active_screen,
wl_is_idle, wl_is_idle,
wl_have_fullscreen_window wl_have_fullscreen_window,
wl_get_scale,
}; };
#endif #endif

View File

@ -44,6 +44,8 @@ struct output {
bool (*is_idle)(void); bool (*is_idle)(void);
bool (*have_fullscreen_window)(void); bool (*have_fullscreen_window)(void);
int (*get_scale)(void);
}; };
/** /**

View File

@ -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 x, int32_t y, int32_t phy_width, int32_t phy_height,
int32_t subpixel, const char *make, const char *model, int32_t subpixel, const char *make, const char *model,
int32_t transform) { int32_t transform) {
//TODO //TODO do something with the subpixel data
struct dunst_output *output = data; struct dunst_output *output = data;
output->subpixel = subpixel; output->subpixel = subpixel;
} }
@ -116,6 +116,8 @@ static void output_handle_scale(void *data, struct wl_output *wl_output,
int32_t factor) { int32_t factor) {
struct dunst_output *output = data; struct dunst_output *output = data;
output->scale = factor; output->scale = factor;
wake_up();
} }
static const struct wl_output_listener output_listener = { static const struct wl_output_listener output_listener = {
@ -135,7 +137,6 @@ static void create_output( struct wl_output *wl_output, uint32_t global_name) {
LOG_I("New output found - id %i", number); LOG_I("New output found - id %i", number);
output->global_name = global_name; output->global_name = global_name;
output->wl_output = wl_output; output->wl_output = wl_output;
// TODO: Fix this
output->scale = 1; output->scale = 1;
output->fullscreen = false; output->fullscreen = false;
wl_list_insert(&ctx.outputs, &output->link); wl_list_insert(&ctx.outputs, &output->link);
@ -227,7 +228,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
ctx.pointer.wl_pointer = wl_seat_get_pointer(wl_seat); ctx.pointer.wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(ctx.pointer.wl_pointer, wl_pointer_add_listener(ctx.pointer.wl_pointer,
&pointer_listener, ctx.seat); &pointer_listener, ctx.seat);
LOG_I("Adding pointer");
} }
if (ctx.touch.wl_touch != NULL) { if (ctx.touch.wl_touch != NULL) {
wl_touch_release(ctx.touch.wl_touch); wl_touch_release(ctx.touch.wl_touch);
@ -343,20 +343,29 @@ static void add_seat_to_idle_handler(struct wl_seat *seat) {
// Warning, can return NULL // Warning, can return NULL
static struct dunst_output *get_configured_output() { static struct dunst_output *get_configured_output() {
switch (settings.f_mode){
case FOLLOW_NONE: ; // this semicolon is neccesary
int n = 0; int n = 0;
int target_monitor = settings.monitor; int target_monitor = settings.monitor;
struct dunst_output *output; struct dunst_output *first_output = NULL, *configured_output = NULL,
wl_list_for_each(output, &ctx.outputs, link) { *tmp_output = NULL;
wl_list_for_each(tmp_output, &ctx.outputs, link) {
if (n == 0)
first_output = tmp_output;
if (n == target_monitor) if (n == target_monitor)
return output; configured_output = tmp_output;
n++; 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
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: case FOLLOW_MOUSE:
// fallthrough // fallthrough
case FOLLOW_KEYBOARD: case FOLLOW_KEYBOARD:
@ -626,7 +635,7 @@ static void schedule_frame_and_commit();
// Draw and commit a new frame. // Draw and commit a new frame.
static void send_frame() { static void send_frame() {
int scale = 1; int scale = wl_get_scale();
struct dunst_output *output = get_configured_output(); struct dunst_output *output = get_configured_output();
int height = ctx.cur_dim.h; int height = ctx.cur_dim.h;
@ -692,8 +701,6 @@ static void send_frame() {
if (ctx.height != height || ctx.width != width) { if (ctx.height != height || ctx.width != width) {
struct dimensions dim = ctx.cur_dim; struct dimensions dim = ctx.cur_dim;
// Set window size // 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, zwlr_layer_surface_v1_set_size(ctx.layer_surface,
dim.w, dim.h); dim.w, dim.h);
@ -818,12 +825,15 @@ void wl_win_hide(window win) {
void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) { void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) {
/* struct window_wl *win = (struct window_wl*)winptr; */ /* 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_t *c = ctx.current_buffer->cairo;
cairo_save(c); cairo_save(c);
cairo_set_source_surface(c, srf, 0, 0); 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_fill(c);
cairo_restore(c); cairo_restore(c);
@ -851,6 +861,7 @@ const struct screen_info* wl_get_active_screen(void) {
.id = 0, .id = 0,
.mmh = 500 .mmh = 500
}; };
scr.dpi = wl_get_scale() * 96;
return &scr; return &scr;
} }
@ -886,4 +897,21 @@ bool wl_have_fullscreen_window(void) {
LOG_D("Fullscreen queried: %i", have_fullscreen); LOG_D("Fullscreen queried: %i", have_fullscreen);
return have_fullscreen; return have_fullscreen;
} }
int wl_get_scale(void) {
int scale = 0;
struct dunst_output *output = get_configured_output();
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: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -23,5 +23,10 @@ const struct screen_info* wl_get_active_screen(void);
bool wl_is_idle(void); bool wl_is_idle(void);
bool wl_have_fullscreen_window(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 #endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -113,7 +113,7 @@ static void x_win_corners_shape(struct window_x11 *win, const int rad)
draw_rounded_rect(cr, 0, 0, draw_rounded_rect(cr, 0, 0,
width, height, width, height,
rad, rad, 1,
true, true); true, true);
cairo_fill(cr); cairo_fill(cr);
@ -942,4 +942,8 @@ static void x_shortcut_init(struct keyboard_shortcut *ks)
g_free(str_begin); g_free(str_begin);
} }
int x_get_scale(void) {
return 1;
}
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -51,5 +51,6 @@ void x_free(void);
struct geometry x_parse_geometry(const char *geom_str); struct geometry x_parse_geometry(const char *geom_str);
int x_get_scale(void);
#endif #endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -12,6 +12,8 @@
extern const char *base; extern const char *base;
int scale = 1;
TEST test_get_path_from_icon_null(void){ TEST test_get_path_from_icon_null(void){
char *result = get_path_from_icon_name(NULL); char *result = get_path_from_icon_name(NULL);
ASSERT_EQ(result, 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); gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
path = string_replace_at(path, 0, strlen(home), "~"); 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); g_clear_pointer(&path, g_free);
ASSERT(pixbuf); 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); 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); g_clear_pointer(&path, g_free);
ASSERT(pixbuf); ASSERT(pixbuf);
@ -113,7 +115,7 @@ TEST test_get_pixbuf_from_file_absolute(void)
TEST test_get_pixbuf_from_icon_invalid(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); ASSERT(pixbuf == NULL);
g_clear_pointer(&pixbuf, g_object_unref); 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) 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 // the first icon found is invalid, so the pixbuf is empty
ASSERT(!pixbuf); ASSERT(!pixbuf);
g_clear_pointer(&pixbuf, g_object_unref); 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) 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); ASSERT(pixbuf);
ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf)); ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref); 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) 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); ASSERT(pixbuf);
ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf)); ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref); 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) TEST test_get_pixbuf_from_icon_filename(void)
{ {
char *icon = g_strconcat(base, "/data/icons/valid.png", NULL); 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); ASSERT(pixbuf);
ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf)); ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref); 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) TEST test_get_pixbuf_from_icon_fileuri(void)
{ {
char *icon = g_strconcat("file://", base, "/data/icons/valid.svg", NULL); 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); ASSERT(pixbuf);
ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf)); ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref); 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) 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(pixbuf);
ASSERT_EQ(gdk_pixbuf_get_width(pixbuf), 16); ASSERT_EQ(gdk_pixbuf_get_width(pixbuf), 16);
ASSERT_EQ(gdk_pixbuf_get_height(pixbuf), 16); ASSERT_EQ(gdk_pixbuf_get_height(pixbuf), 16);