aboutsummaryrefslogtreecommitdiffstats
path: root/swaybg/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'swaybg/main.c')
-rw-r--r--swaybg/main.c377
1 files changed, 203 insertions, 174 deletions
diff --git a/swaybg/main.c b/swaybg/main.c
index 2fdd4220..679b8c20 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -1,44 +1,55 @@
1#include "wayland-desktop-shell-client-protocol.h" 1#include <assert.h>
2#include <ctype.h>
2#include <stdbool.h> 3#include <stdbool.h>
3#include <stdio.h> 4#include <stdio.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <ctype.h>
6#include <wayland-client.h>
7#include <time.h>
8#include <string.h> 6#include <string.h>
9#include "client/window.h" 7#include <time.h>
10#include "client/registry.h" 8#include <wayland-client.h>
11#include "client/cairo.h" 9#include <wlr/util/log.h>
12#include "log.h" 10#include "background-image.h"
13#include "list.h" 11#include "pool-buffer.h"
12#include "cairo.h"
14#include "util.h" 13#include "util.h"
14#include "wlr-layer-shell-unstable-v1-client-protocol.h"
15 15
16list_t *surfaces; 16struct swaybg_args {
17struct registry *registry; 17 int output_idx;
18 const char *path;
19 enum background_mode mode;
20};
18 21
19enum scaling_mode { 22struct swaybg_context {
20 SCALING_MODE_STRETCH, 23 uint32_t color;
21 SCALING_MODE_FILL, 24 cairo_surface_t *image;
22 SCALING_MODE_FIT,
23 SCALING_MODE_CENTER,
24 SCALING_MODE_TILE,
25}; 25};
26 26
27void sway_terminate(int exit_code) { 27struct swaybg_state {
28 int i; 28 const struct swaybg_args *args;
29 for (i = 0; i < surfaces->length; ++i) { 29 struct swaybg_context context;
30 struct window *window = surfaces->items[i]; 30
31 window_teardown(window); 31 struct wl_display *display;
32 } 32 struct wl_compositor *compositor;
33 list_free(surfaces); 33 struct zwlr_layer_shell_v1 *layer_shell;
34 registry_teardown(registry); 34 struct wl_shm *shm;
35 exit(exit_code); 35
36} 36 struct wl_output *output;
37 struct wl_surface *surface;
38 struct wl_region *input_region;
39 struct zwlr_layer_surface_v1 *layer_surface;
40
41 bool run_display;
42 uint32_t width, height;
43 int32_t scale;
44 struct pool_buffer buffers[2];
45 struct pool_buffer *current_buffer;
46};
37 47
38bool is_valid_color(const char *color) { 48bool is_valid_color(const char *color) {
39 int len = strlen(color); 49 int len = strlen(color);
40 if (len != 7 || color[0] != '#') { 50 if (len != 7 || color[0] != '#') {
41 sway_log(L_ERROR, "%s is not a valid color for swaybg. Color should be specified as #rrggbb (no alpha).", color); 51 wlr_log(L_ERROR, "%s is not a valid color for swaybg. "
52 "Color should be specified as #rrggbb (no alpha).", color);
42 return false; 53 return false;
43 } 54 }
44 55
@@ -52,163 +63,181 @@ bool is_valid_color(const char *color) {
52 return true; 63 return true;
53} 64}
54 65
55int main(int argc, const char **argv) { 66static void render_frame(struct swaybg_state *state) {
56 init_log(L_INFO); 67 int buffer_width = state->width * state->scale,
57 surfaces = create_list(); 68 buffer_height = state->height * state->scale;
58 registry = registry_poll(); 69 state->current_buffer = get_next_buffer(state->shm,
59 70 state->buffers, buffer_width, buffer_height);
60 if (argc != 4) { 71 cairo_t *cairo = state->current_buffer->cairo;
61 sway_abort("Do not run this program manually. See man 5 sway and look for output options."); 72 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
73 cairo_set_source_u32(cairo, state->context.color);
74 cairo_paint(cairo);
75 } else {
76 render_background_image(cairo, state->context.image,
77 state->args->mode, buffer_width, buffer_height);
62 } 78 }
63 79
64 if (!registry->desktop_shell) { 80 wl_surface_set_buffer_scale(state->surface, state->scale);
65 sway_abort("swaybg requires the compositor to support the desktop-shell extension."); 81 wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0);
82 wl_surface_damage(state->surface, 0, 0, state->width, state->height);
83 wl_surface_commit(state->surface);
84}
85
86static bool prepare_context(struct swaybg_state *state) {
87 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
88 state->context.color = parse_color(state->args->path);
89 return is_valid_color(state->args->path);
66 } 90 }
91 if (!(state->context.image = load_background_image(state->args->path))) {
92 return false;
93 }
94 return true;
95}
67 96
68 int desired_output = atoi(argv[1]); 97static void layer_surface_configure(void *data,
69 sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); 98 struct zwlr_layer_surface_v1 *surface,
70 int i; 99 uint32_t serial, uint32_t width, uint32_t height) {
71 struct output_state *output = registry->outputs->items[desired_output]; 100 struct swaybg_state *state = data;
72 struct window *window = window_setup(registry, 101 state->width = width;
73 output->width, output->height, output->scale, false); 102 state->height = height;
74 if (!window) { 103 zwlr_layer_surface_v1_ack_configure(surface, serial);
75 sway_abort("Failed to create surfaces."); 104 render_frame(state);
105}
106
107static void layer_surface_closed(void *data,
108 struct zwlr_layer_surface_v1 *surface) {
109 struct swaybg_state *state = data;
110 zwlr_layer_surface_v1_destroy(state->layer_surface);
111 wl_surface_destroy(state->surface);
112 wl_region_destroy(state->input_region);
113 state->run_display = false;
114}
115
116struct zwlr_layer_surface_v1_listener layer_surface_listener = {
117 .configure = layer_surface_configure,
118 .closed = layer_surface_closed,
119};
120
121static void output_geometry(void *data, struct wl_output *output, int32_t x,
122 int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel,
123 const char *make, const char *model, int32_t transform) {
124 // Who cares
125}
126
127static void output_mode(void *data, struct wl_output *output, uint32_t flags,
128 int32_t width, int32_t height, int32_t refresh) {
129 // Who cares
130}
131
132static void output_done(void *data, struct wl_output *output) {
133 // Who cares
134}
135
136static void output_scale(void *data, struct wl_output *output, int32_t factor) {
137 struct swaybg_state *state = data;
138 state->scale = factor;
139 if (state->run_display) {
140 render_frame(state);
76 } 141 }
77 desktop_shell_set_background(registry->desktop_shell, output->output, window->surface); 142}
78 window_make_shell(window);
79 list_add(surfaces, window);
80
81 if (strcmp(argv[3], "solid_color") == 0 && is_valid_color(argv[2])) {
82 cairo_set_source_u32(window->cairo, parse_color(argv[2]));
83 cairo_paint(window->cairo);
84 window_render(window);
85 } else {
86#ifdef WITH_GDK_PIXBUF
87 GError *err = NULL;
88 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(argv[2], &err);
89 if (!pixbuf) {
90 sway_abort("Failed to load background image.");
91 }
92 cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
93 g_object_unref(pixbuf);
94#else
95 cairo_surface_t *image = cairo_image_surface_create_from_png(argv[2]);
96#endif //WITH_GDK_PIXBUF
97 if (!image) {
98 sway_abort("Failed to read background image.");
99 }
100 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
101 sway_abort("Failed to read background image: %s."
102#ifndef WITH_GDK_PIXBUF
103 "\nSway was compiled without gdk_pixbuf support, so only"
104 "\nPNG images can be loaded. This is the likely cause."
105#endif //WITH_GDK_PIXBUF
106 , cairo_status_to_string(cairo_surface_status(image)));
107 }
108 double width = cairo_image_surface_get_width(image);
109 double height = cairo_image_surface_get_height(image);
110
111 const char *scaling_mode_str = argv[3];
112 enum scaling_mode scaling_mode = SCALING_MODE_STRETCH;
113 if (strcmp(scaling_mode_str, "stretch") == 0) {
114 scaling_mode = SCALING_MODE_STRETCH;
115 } else if (strcmp(scaling_mode_str, "fill") == 0) {
116 scaling_mode = SCALING_MODE_FILL;
117 } else if (strcmp(scaling_mode_str, "fit") == 0) {
118 scaling_mode = SCALING_MODE_FIT;
119 } else if (strcmp(scaling_mode_str, "center") == 0) {
120 scaling_mode = SCALING_MODE_CENTER;
121 } else if (strcmp(scaling_mode_str, "tile") == 0) {
122 scaling_mode = SCALING_MODE_TILE;
123 } else {
124 sway_abort("Unsupported scaling mode: %s", scaling_mode_str);
125 }
126 143
127 int wwidth = window->width * window->scale; 144struct wl_output_listener output_listener = {
128 int wheight = window->height * window->scale; 145 .geometry = output_geometry,
129 146 .mode = output_mode,
130 for (i = 0; i < surfaces->length; ++i) { 147 .done = output_done,
131 struct window *window = surfaces->items[i]; 148 .scale = output_scale,
132 if (window_prerender(window) && window->cairo) { 149};
133 switch (scaling_mode) { 150
134 case SCALING_MODE_STRETCH: 151static void handle_global(void *data, struct wl_registry *registry,
135 cairo_scale(window->cairo, 152 uint32_t name, const char *interface, uint32_t version) {
136 (double) wwidth / width, 153 struct swaybg_state *state = data;
137 (double) wheight / height); 154 if (strcmp(interface, wl_compositor_interface.name) == 0) {
138 cairo_set_source_surface(window->cairo, image, 0, 0); 155 state->compositor = wl_registry_bind(registry, name,
139 break; 156 &wl_compositor_interface, 3);
140 case SCALING_MODE_FILL: 157 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
141 { 158 state->shm = wl_registry_bind(registry, name,
142 double window_ratio = (double) wwidth / wheight; 159 &wl_shm_interface, 1);
143 double bg_ratio = width / height; 160 } else if (strcmp(interface, wl_output_interface.name) == 0) {
144 161 static int output_idx = 0;
145 if (window_ratio > bg_ratio) { 162 if (output_idx == state->args->output_idx) {
146 double scale = (double) wwidth / width; 163 state->output = wl_registry_bind(registry, name,
147 cairo_scale(window->cairo, scale, scale); 164 &wl_output_interface, 3);
148 cairo_set_source_surface(window->cairo, image, 165 wl_output_add_listener(state->output, &output_listener, state);
149 0,
150 (double) wheight/2 / scale - height/2);
151 } else {
152 double scale = (double) wheight / height;
153 cairo_scale(window->cairo, scale, scale);
154 cairo_set_source_surface(window->cairo, image,
155 (double) wwidth/2 / scale - width/2,
156 0);
157 }
158 break;
159 }
160 case SCALING_MODE_FIT:
161 {
162 double window_ratio = (double) wwidth / wheight;
163 double bg_ratio = width / height;
164
165 if (window_ratio > bg_ratio) {
166 double scale = (double) wheight / height;
167 cairo_scale(window->cairo, scale, scale);
168 cairo_set_source_surface(window->cairo, image,
169 (double) wwidth/2 / scale - width/2,
170 0);
171 } else {
172 double scale = (double) wwidth / width;
173 cairo_scale(window->cairo, scale, scale);
174 cairo_set_source_surface(window->cairo, image,
175 0,
176 (double) wheight/2 / scale - height/2);
177 }
178 break;
179 }
180 case SCALING_MODE_CENTER:
181 cairo_set_source_surface(window->cairo, image,
182 (double) wwidth/2 - width/2,
183 (double) wheight/2 - height/2);
184 break;
185 case SCALING_MODE_TILE:
186 {
187 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
188 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
189 cairo_set_source(window->cairo, pattern);
190 break;
191 }
192 default:
193 sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str);
194 }
195
196 cairo_paint(window->cairo);
197
198 window_render(window);
199 }
200 } 166 }
167 output_idx++;
168 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
169 state->layer_shell = wl_registry_bind(
170 registry, name, &zwlr_layer_shell_v1_interface, 1);
171 }
172}
201 173
202 cairo_surface_destroy(image); 174static void handle_global_remove(void *data, struct wl_registry *registry,
175 uint32_t name) {
176 // who cares
177}
178
179static const struct wl_registry_listener registry_listener = {
180 .global = handle_global,
181 .global_remove = handle_global_remove,
182};
183
184int main(int argc, const char **argv) {
185 struct swaybg_args args = {0};
186 struct swaybg_state state = {0};
187 state.args = &args;
188 wlr_log_init(L_DEBUG, NULL);
189
190 if (argc != 4) {
191 wlr_log(L_ERROR, "Do not run this program manually. "
192 "See man 5 sway and look for output options.");
193 return 1;
203 } 194 }
195 args.output_idx = atoi(argv[1]);
196 args.path = argv[2];
204 197
205 while (wl_display_dispatch(registry->display) != -1); 198 args.mode = parse_background_mode(argv[3]);
199 if (args.mode == BACKGROUND_MODE_INVALID) {
200 return 1;
201 }
202 if (!prepare_context(&state)) {
203 return 1;
204 }
206 205
207 for (i = 0; i < surfaces->length; ++i) { 206 assert(state.display = wl_display_connect(NULL));
208 struct window *window = surfaces->items[i]; 207
209 window_teardown(window); 208 struct wl_registry *registry = wl_display_get_registry(state.display);
209 wl_registry_add_listener(registry, &registry_listener, &state);
210 wl_display_roundtrip(state.display);
211 assert(state.compositor && state.layer_shell && state.output && state.shm);
212
213 // Second roundtrip to get output properties
214 wl_display_roundtrip(state.display);
215
216 assert(state.surface = wl_compositor_create_surface(state.compositor));
217
218 assert(state.input_region = wl_compositor_create_region(state.compositor));
219 wl_surface_set_input_region(state.surface, state.input_region);
220
221 state.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
222 state.layer_shell, state.surface, state.output,
223 ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
224 assert(state.layer_surface);
225
226 zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0);
227 zwlr_layer_surface_v1_set_anchor(state.layer_surface,
228 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
229 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
230 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
231 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
232 zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1);
233 zwlr_layer_surface_v1_add_listener(state.layer_surface,
234 &layer_surface_listener, &state);
235 wl_surface_commit(state.surface);
236 wl_display_roundtrip(state.display);
237
238 state.run_display = true;
239 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
240 // This space intentionally left blank
210 } 241 }
211 list_free(surfaces);
212 registry_teardown(registry);
213 return 0; 242 return 0;
214} 243}