Implement image backgrounds

This commit is contained in:
Drew DeVault 2018-03-27 15:36:56 -04:00
parent 632bb948b7
commit f018d30fe4

View file

@ -26,8 +26,14 @@ struct swaybg_args {
enum scaling_mode mode;
};
struct swaybg_context {
uint32_t color;
cairo_surface_t *image;
};
struct swaybg_state {
const struct swaybg_args *args;
struct swaybg_context context;
struct wl_display *display;
struct wl_compositor *compositor;
@ -62,6 +68,71 @@ bool is_valid_color(const char *color) {
return true;
}
static void render_image(struct swaybg_state *state) {
cairo_t *cairo = state->current_buffer->cairo;
cairo_surface_t *image = state->context.image;
double width = cairo_image_surface_get_width(image);
double height = cairo_image_surface_get_height(image);
int wwidth = state->width;
int wheight = state->height;
switch (state->args->mode) {
case SCALING_MODE_STRETCH:
cairo_scale(cairo, (double)wwidth / width, (double)wheight / height);
cairo_set_source_surface(cairo, image, 0, 0);
break;
case SCALING_MODE_FILL: {
double window_ratio = (double)wwidth / wheight;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double)wwidth / width;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
0, (double)wheight / 2 / scale - height / 2);
} else {
double scale = (double)wheight / height;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
(double)wwidth / 2 / scale - width / 2, 0);
}
break;
}
case SCALING_MODE_FIT: {
double window_ratio = (double)wwidth / wheight;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double)wheight / height;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
(double)wwidth / 2 / scale - width / 2, 0);
} else {
double scale = (double)wwidth / width;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
0, (double)wheight / 2 / scale - height / 2);
}
break;
}
case SCALING_MODE_CENTER:
cairo_set_source_surface(cairo, image,
(double)wwidth / 2 - width / 2,
(double)wheight / 2 - height / 2);
break;
case SCALING_MODE_TILE: {
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
cairo_set_source(cairo, pattern);
break;
}
case SCALING_MODE_SOLID_COLOR:
// Should never happen
break;
}
cairo_paint(cairo);
}
static void render_frame(struct swaybg_state *state) {
if (!state->run_display) {
return;
@ -73,11 +144,11 @@ static void render_frame(struct swaybg_state *state) {
switch (state->args->mode) {
case SCALING_MODE_SOLID_COLOR:
cairo_set_source_u32(cairo, parse_color(state->args->path));
cairo_set_source_u32(cairo, state->context.color);
cairo_paint(cairo);
break;
default:
exit(1);
render_image(state);
break;
}
@ -86,6 +157,41 @@ static void render_frame(struct swaybg_state *state) {
wl_surface_commit(state->surface);
}
static bool prepare_context(struct swaybg_state *state) {
if (state->args->mode == SCALING_MODE_SOLID_COLOR) {
state->context.color = parse_color(state->args->path);
return is_valid_color(state->args->path);
}
#ifdef WITH_GDK_PIXBUF
GError *err = NULL;
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err);
if (!pixbuf) {
wlr_log(L_ERROR, "Failed to load background image.");
return false;
}
state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
#else
state->context.image = cairo_image_surface_create_from_png(
state->args->path);
#endif //WITH_GDK_PIXBUF
if (!state->context.image) {
wlr_log(L_ERROR, "Failed to read background image.");
return false;
}
if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) {
wlr_log(L_ERROR, "Failed to read background image: %s."
#ifndef WITH_GDK_PIXBUF
"\nSway was compiled without gdk_pixbuf support, so only"
"\nPNG images can be loaded. This is the likely cause."
#endif //WITH_GDK_PIXBUF
, cairo_status_to_string(
cairo_surface_status(state->context.image)));
return false;
}
return true;
}
static void layer_surface_configure(void *data,
struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t width, uint32_t height) {
@ -217,6 +323,10 @@ int main(int argc, const char **argv) {
wl_surface_commit(state.surface);
wl_display_roundtrip(state.display);
if (!prepare_context(&state)) {
return 1;
}
state.run_display = true;
render_frame(&state);
while (wl_display_dispatch(state.display) != -1 && state.run_display) {