aboutsummaryrefslogtreecommitdiffstats
path: root/swaybg
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2018-03-28 15:25:33 -0400
committerLibravatar GitHub <noreply@github.com>2018-03-28 15:25:33 -0400
commit9070950eecded7bfa64e7bca3bb76b150ccc8b72 (patch)
tree509a9c669bf2679085e27a1ff1b0c95526abf14c /swaybg
parentMerge pull request #1641 from swaywm/transformed-events (diff)
parentAddress review comments (diff)
downloadsway-9070950eecded7bfa64e7bca3bb76b150ccc8b72.tar.gz
sway-9070950eecded7bfa64e7bca3bb76b150ccc8b72.tar.zst
sway-9070950eecded7bfa64e7bca3bb76b150ccc8b72.zip
Merge pull request #1638 from swaywm/swaybg-layers
Reimplement swaybg using surface layers
Diffstat (limited to 'swaybg')
-rw-r--r--swaybg/main.c445
-rw-r--r--swaybg/meson.build18
2 files changed, 290 insertions, 173 deletions
diff --git a/swaybg/main.c b/swaybg/main.c
index 2fdd4220..f431526c 100644
--- a/swaybg/main.c
+++ b/swaybg/main.c
@@ -1,44 +1,61 @@
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 "pool-buffer.h"
13#include "list.h" 11#include "cairo.h"
14#include "util.h" 12#include "util.h"
13#include "wlr-layer-shell-unstable-v1-client-protocol.h"
15 14
16list_t *surfaces; 15enum background_mode {
17struct registry *registry; 16 BACKGROUND_MODE_STRETCH,
17 BACKGROUND_MODE_FILL,
18 BACKGROUND_MODE_FIT,
19 BACKGROUND_MODE_CENTER,
20 BACKGROUND_MODE_TILE,
21 BACKGROUND_MODE_SOLID_COLOR,
22};
18 23
19enum scaling_mode { 24struct swaybg_args {
20 SCALING_MODE_STRETCH, 25 int output_idx;
21 SCALING_MODE_FILL, 26 const char *path;
22 SCALING_MODE_FIT, 27 enum background_mode mode;
23 SCALING_MODE_CENTER,
24 SCALING_MODE_TILE,
25}; 28};
26 29
27void sway_terminate(int exit_code) { 30struct swaybg_context {
28 int i; 31 uint32_t color;
29 for (i = 0; i < surfaces->length; ++i) { 32 cairo_surface_t *image;
30 struct window *window = surfaces->items[i]; 33};
31 window_teardown(window); 34
32 } 35struct swaybg_state {
33 list_free(surfaces); 36 const struct swaybg_args *args;
34 registry_teardown(registry); 37 struct swaybg_context context;
35 exit(exit_code); 38
36} 39 struct wl_display *display;
40 struct wl_compositor *compositor;
41 struct zwlr_layer_shell_v1 *layer_shell;
42 struct wl_shm *shm;
43
44 struct wl_output *output;
45 struct wl_surface *surface;
46 struct zwlr_layer_surface_v1 *layer_surface;
47
48 bool run_display;
49 uint32_t width, height;
50 struct pool_buffer buffers[2];
51 struct pool_buffer *current_buffer;
52};
37 53
38bool is_valid_color(const char *color) { 54bool is_valid_color(const char *color) {
39 int len = strlen(color); 55 int len = strlen(color);
40 if (len != 7 || color[0] != '#') { 56 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); 57 wlr_log(L_ERROR, "%s is not a valid color for swaybg. "
58 "Color should be specified as #rrggbb (no alpha).", color);
42 return false; 59 return false;
43 } 60 }
44 61
@@ -52,163 +69,245 @@ bool is_valid_color(const char *color) {
52 return true; 69 return true;
53} 70}
54 71
55int main(int argc, const char **argv) { 72static void render_image(struct swaybg_state *state) {
56 init_log(L_INFO); 73 cairo_t *cairo = state->current_buffer->cairo;
57 surfaces = create_list(); 74 cairo_surface_t *image = state->context.image;
58 registry = registry_poll(); 75 double width = cairo_image_surface_get_width(image);
76 double height = cairo_image_surface_get_height(image);
77 int wwidth = state->width;
78 int wheight = state->height;
59 79
60 if (argc != 4) { 80 switch (state->args->mode) {
61 sway_abort("Do not run this program manually. See man 5 sway and look for output options."); 81 case BACKGROUND_MODE_STRETCH:
62 } 82 cairo_scale(cairo, (double)wwidth / width, (double)wheight / height);
83 cairo_set_source_surface(cairo, image, 0, 0);
84 break;
85 case BACKGROUND_MODE_FILL: {
86 double window_ratio = (double)wwidth / wheight;
87 double bg_ratio = width / height;
63 88
64 if (!registry->desktop_shell) { 89 if (window_ratio > bg_ratio) {
65 sway_abort("swaybg requires the compositor to support the desktop-shell extension."); 90 double scale = (double)wwidth / width;
91 cairo_scale(cairo, scale, scale);
92 cairo_set_source_surface(cairo, image,
93 0, (double)wheight / 2 / scale - height / 2);
94 } else {
95 double scale = (double)wheight / height;
96 cairo_scale(cairo, scale, scale);
97 cairo_set_source_surface(cairo, image,
98 (double)wwidth / 2 / scale - width / 2, 0);
99 }
100 break;
66 } 101 }
102 case BACKGROUND_MODE_FIT: {
103 double window_ratio = (double)wwidth / wheight;
104 double bg_ratio = width / height;
67 105
68 int desired_output = atoi(argv[1]); 106 if (window_ratio > bg_ratio) {
69 sway_log(L_INFO, "Using output %d of %d", desired_output, registry->outputs->length); 107 double scale = (double)wheight / height;
70 int i; 108 cairo_scale(cairo, scale, scale);
71 struct output_state *output = registry->outputs->items[desired_output]; 109 cairo_set_source_surface(cairo, image,
72 struct window *window = window_setup(registry, 110 (double)wwidth / 2 / scale - width / 2, 0);
73 output->width, output->height, output->scale, false);
74 if (!window) {
75 sway_abort("Failed to create surfaces.");
76 }
77 desktop_shell_set_background(registry->desktop_shell, output->output, window->surface);
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 { 111 } else {
124 sway_abort("Unsupported scaling mode: %s", scaling_mode_str); 112 double scale = (double)wwidth / width;
113 cairo_scale(cairo, scale, scale);
114 cairo_set_source_surface(cairo, image,
115 0, (double)wheight / 2 / scale - height / 2);
125 } 116 }
117 break;
118 }
119 case BACKGROUND_MODE_CENTER:
120 cairo_set_source_surface(cairo, image,
121 (double)wwidth / 2 - width / 2,
122 (double)wheight / 2 - height / 2);
123 break;
124 case BACKGROUND_MODE_TILE: {
125 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
126 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
127 cairo_set_source(cairo, pattern);
128 break;
129 }
130 case BACKGROUND_MODE_SOLID_COLOR:
131 assert(0);
132 break;
133 }
134 cairo_paint(cairo);
135}
136
137static void render_frame(struct swaybg_state *state) {
138 state->current_buffer = get_next_buffer(state->shm,
139 state->buffers, state->width, state->height);
140 cairo_t *cairo = state->current_buffer->cairo;
141
142 switch (state->args->mode) {
143 case BACKGROUND_MODE_SOLID_COLOR:
144 cairo_set_source_u32(cairo, state->context.color);
145 cairo_paint(cairo);
146 break;
147 default:
148 render_image(state);
149 break;
150 }
151
152 wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0);
153 wl_surface_damage(state->surface, 0, 0, state->width, state->height);
154 wl_surface_commit(state->surface);
155}
126 156
127 int wwidth = window->width * window->scale; 157static bool prepare_context(struct swaybg_state *state) {
128 int wheight = window->height * window->scale; 158 if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) {
129 159 state->context.color = parse_color(state->args->path);
130 for (i = 0; i < surfaces->length; ++i) { 160 return is_valid_color(state->args->path);
131 struct window *window = surfaces->items[i]; 161 }
132 if (window_prerender(window) && window->cairo) { 162#ifdef HAVE_GDK_PIXBUF
133 switch (scaling_mode) { 163 GError *err = NULL;
134 case SCALING_MODE_STRETCH: 164 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(state->args->path, &err);
135 cairo_scale(window->cairo, 165 if (!pixbuf) {
136 (double) wwidth / width, 166 wlr_log(L_ERROR, "Failed to load background image.");
137 (double) wheight / height); 167 return false;
138 cairo_set_source_surface(window->cairo, image, 0, 0); 168 }
139 break; 169 state->context.image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
140 case SCALING_MODE_FILL: 170 g_object_unref(pixbuf);
141 { 171#else
142 double window_ratio = (double) wwidth / wheight; 172 state->context.image = cairo_image_surface_create_from_png(
143 double bg_ratio = width / height; 173 state->args->path);
144 174#endif //HAVE_GDK_PIXBUF
145 if (window_ratio > bg_ratio) { 175 if (!state->context.image) {
146 double scale = (double) wwidth / width; 176 wlr_log(L_ERROR, "Failed to read background image.");
147 cairo_scale(window->cairo, scale, scale); 177 return false;
148 cairo_set_source_surface(window->cairo, image, 178 }
149 0, 179 if (cairo_surface_status(state->context.image) != CAIRO_STATUS_SUCCESS) {
150 (double) wheight/2 / scale - height/2); 180 wlr_log(L_ERROR, "Failed to read background image: %s."
151 } else { 181#ifndef HAVE_GDK_PIXBUF
152 double scale = (double) wheight / height; 182 "\nSway was compiled without gdk_pixbuf support, so only"
153 cairo_scale(window->cairo, scale, scale); 183 "\nPNG images can be loaded. This is the likely cause."
154 cairo_set_source_surface(window->cairo, image, 184#endif //HAVE_GDK_PIXBUF
155 (double) wwidth/2 / scale - width/2, 185 , cairo_status_to_string(
156 0); 186 cairo_surface_status(state->context.image)));
157 } 187 return false;
158 break; 188 }
159 } 189 return true;
160 case SCALING_MODE_FIT: 190}
161 { 191
162 double window_ratio = (double) wwidth / wheight; 192static void layer_surface_configure(void *data,
163 double bg_ratio = width / height; 193 struct zwlr_layer_surface_v1 *surface,
164 194 uint32_t serial, uint32_t width, uint32_t height) {
165 if (window_ratio > bg_ratio) { 195 struct swaybg_state *state = data;
166 double scale = (double) wheight / height; 196 state->width = width;
167 cairo_scale(window->cairo, scale, scale); 197 state->height = height;
168 cairo_set_source_surface(window->cairo, image, 198 zwlr_layer_surface_v1_ack_configure(surface, serial);
169 (double) wwidth/2 / scale - width/2, 199 render_frame(state);
170 0); 200}
171 } else { 201
172 double scale = (double) wwidth / width; 202static void layer_surface_closed(void *data,
173 cairo_scale(window->cairo, scale, scale); 203 struct zwlr_layer_surface_v1 *surface) {
174 cairo_set_source_surface(window->cairo, image, 204 struct swaybg_state *state = data;
175 0, 205 zwlr_layer_surface_v1_destroy(state->layer_surface);
176 (double) wheight/2 / scale - height/2); 206 wl_surface_destroy(state->surface);
177 } 207 state->run_display = false;
178 break; 208}
179 } 209
180 case SCALING_MODE_CENTER: 210struct zwlr_layer_surface_v1_listener layer_surface_listener = {
181 cairo_set_source_surface(window->cairo, image, 211 .configure = layer_surface_configure,
182 (double) wwidth/2 - width/2, 212 .closed = layer_surface_closed,
183 (double) wheight/2 - height/2); 213};
184 break; 214
185 case SCALING_MODE_TILE: 215static void handle_global(void *data, struct wl_registry *registry,
186 { 216 uint32_t name, const char *interface, uint32_t version) {
187 cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); 217 struct swaybg_state *state = data;
188 cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); 218 if (strcmp(interface, wl_compositor_interface.name) == 0) {
189 cairo_set_source(window->cairo, pattern); 219 state->compositor = wl_registry_bind(registry, name,
190 break; 220 &wl_compositor_interface, 1);
191 } 221 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
192 default: 222 state->shm = wl_registry_bind(registry, name,
193 sway_abort("Scaling mode '%s' not implemented yet!", scaling_mode_str); 223 &wl_shm_interface, 1);
194 } 224 } else if (strcmp(interface, wl_output_interface.name) == 0) {
195 225 static int output_idx = 0;
196 cairo_paint(window->cairo); 226 if (output_idx == state->args->output_idx) {
197 227 state->output = wl_registry_bind(registry, name,
198 window_render(window); 228 &wl_output_interface, 1);
199 }
200 } 229 }
230 output_idx++;
231 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
232 state->layer_shell = wl_registry_bind(
233 registry, name, &zwlr_layer_shell_v1_interface, 1);
234 }
235}
236
237static void handle_global_remove(void *data, struct wl_registry *registry,
238 uint32_t name) {
239 // who cares
240}
241
242static const struct wl_registry_listener registry_listener = {
243 .global = handle_global,
244 .global_remove = handle_global_remove,
245};
201 246
202 cairo_surface_destroy(image); 247int main(int argc, const char **argv) {
248 struct swaybg_args args = {0};
249 struct swaybg_state state = {0};
250 state.args = &args;
251 wlr_log_init(L_DEBUG, NULL);
252
253 if (argc != 4) {
254 wlr_log(L_ERROR, "Do not run this program manually. "
255 "See man 5 sway and look for output options.");
256 return 1;
203 } 257 }
258 args.output_idx = atoi(argv[1]);
259 args.path = argv[2];
260
261 args.mode = BACKGROUND_MODE_STRETCH;
262 if (strcmp(argv[3], "stretch") == 0) {
263 args.mode = BACKGROUND_MODE_STRETCH;
264 } else if (strcmp(argv[3], "fill") == 0) {
265 args.mode = BACKGROUND_MODE_FILL;
266 } else if (strcmp(argv[3], "fit") == 0) {
267 args.mode = BACKGROUND_MODE_FIT;
268 } else if (strcmp(argv[3], "center") == 0) {
269 args.mode = BACKGROUND_MODE_CENTER;
270 } else if (strcmp(argv[3], "tile") == 0) {
271 args.mode = BACKGROUND_MODE_TILE;
272 } else if (strcmp(argv[3], "solid_color") == 0) {
273 args.mode = BACKGROUND_MODE_SOLID_COLOR;
274 } else {
275 wlr_log(L_ERROR, "Unsupported background mode: %s", argv[3]);
276 return 1;
277 }
278
279 if (!prepare_context(&state)) {
280 return 1;
281 }
282
283 assert(state.display = wl_display_connect(NULL));
284
285 struct wl_registry *registry = wl_display_get_registry(state.display);
286 wl_registry_add_listener(registry, &registry_listener, &state);
287 wl_display_roundtrip(state.display);
288 assert(state.compositor && state.layer_shell && state.output && state.shm);
289
290 assert(state.surface = wl_compositor_create_surface(state.compositor));
291
292 state.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
293 state.layer_shell, state.surface, state.output,
294 ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");
295 assert(state.layer_surface);
204 296
205 while (wl_display_dispatch(registry->display) != -1); 297 zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0);
298 zwlr_layer_surface_v1_set_anchor(state.layer_surface,
299 ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
300 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
301 ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
302 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
303 zwlr_layer_surface_v1_add_listener(state.layer_surface,
304 &layer_surface_listener, &state);
305 state.run_display = true;
306 wl_surface_commit(state.surface);
307 wl_display_roundtrip(state.display);
206 308
207 for (i = 0; i < surfaces->length; ++i) { 309 while (wl_display_dispatch(state.display) != -1 && state.run_display) {
208 struct window *window = surfaces->items[i]; 310 // This space intentionally left blank
209 window_teardown(window);
210 } 311 }
211 list_free(surfaces);
212 registry_teardown(registry);
213 return 0; 312 return 0;
214} 313}
diff --git a/swaybg/meson.build b/swaybg/meson.build
new file mode 100644
index 00000000..5e10f3c7
--- /dev/null
+++ b/swaybg/meson.build
@@ -0,0 +1,18 @@
1executable(
2 'swaybg',
3 'main.c',
4 include_directories: [sway_inc],
5 dependencies: [
6 cairo,
7 gdk_pixbuf,
8 jsonc,
9 math,
10 pango,
11 pangocairo,
12 sway_protos,
13 wayland_client,
14 wlroots,
15 ],
16 link_with: [lib_sway_common, lib_sway_client],
17 install: true
18)