summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErik Reider <[email protected]>2023-05-19 21:14:06 +0200
committerGitHub <[email protected]>2023-05-19 21:14:06 +0200
commit415e072a3af292937f0b4c41acadafaee6958437 (patch)
tree354de329d9cbf66054260d50aebefd86a26d5055
parent67078429428f0a97333c107da8a3ad8fb678a602 (diff)
Add blur, shadow, and corner radius to layer-shell surfaces (#144)
Co-authored-by: Will McKinnon <[email protected]>
-rw-r--r--README.md8
-rw-r--r--include/sway/commands.h6
-rw-r--r--include/sway/config.h4
-rw-r--r--include/sway/desktop/fx_renderer/fx_renderer.h1
-rw-r--r--include/sway/layer_criteria.h20
-rw-r--r--include/sway/layers.h4
-rw-r--r--include/sway/output.h6
-rw-r--r--sway/commands.c1
-rw-r--r--sway/commands/blur.c2
-rw-r--r--sway/commands/corner_radius.c15
-rw-r--r--sway/commands/layer_effects.c33
-rw-r--r--sway/commands/shadows.c2
-rw-r--r--sway/config.c13
-rw-r--r--sway/desktop/layer_shell.c22
-rw-r--r--sway/desktop/output.c7
-rw-r--r--sway/desktop/render.c156
-rw-r--r--sway/ipc-json.c69
-rw-r--r--sway/layer_criteria.c88
-rw-r--r--sway/meson.build2
-rw-r--r--sway/sway-ipc.7.scd34
-rw-r--r--sway/sway.5.scd19
-rw-r--r--sway/tree/workspace.c22
22 files changed, 456 insertions, 78 deletions
diff --git a/README.md b/README.md
index 70f14f2a..948cf16c 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,14 @@ Sway is an incredible window manager, and certainly one of the most well establi
- `blur_xray enable|disable`
- `blur_passes <integer value 0 - 10>`
- `blur_radius <integer value 0 - 10>`
++ LayerShell effects: *ONLY ON SWAYFX-GIT, NOT YET RELEASED*
+ - `layer_effects <layer namespace> <effects>`
+ - Example: `layer_effects "waybar" blur enable; shadows enable; corner_radius 6`
+ - SwayIPC Example: `swaymsg "layer_effects 'waybar' 'blur enable; shadows enable; corner_radius 6'"`
+ - Available Effects:
+ - `blur <enable|disable>`
+ - `shadows <enable|disable>`
+ - `corner_radius <int>`
+ Dim unfocused windows:
- `default_dim_inactive <float value 0.0 - 1.0>`
- `for_window [CRITERIA_HERE] dim_inactive <float value 0.0 - 1.0>`
diff --git a/include/sway/commands.h b/include/sway/commands.h
index b895d5f2..920e8596 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -98,6 +98,11 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
uint32_t edge);
/**
+ * Effect handlers value parsers
+ */
+bool cmd_corner_radius_parse_value(char *arg, int* result);
+
+/**
* Handlers shared by exec and exec_always.
*/
sway_cmd cmd_exec_validate;
@@ -157,6 +162,7 @@ sway_cmd cmd_input;
sway_cmd cmd_seat;
sway_cmd cmd_ipc;
sway_cmd cmd_kill;
+sway_cmd cmd_layer_effects;
sway_cmd cmd_layout;
sway_cmd cmd_log_colors;
sway_cmd cmd_mark;
diff --git a/include/sway/config.h b/include/sway/config.h
index d84eef69..04e2969e 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -500,6 +500,8 @@ struct sway_config {
bool titlebar_separator;
bool scratchpad_minimize;
+ list_t *layer_criteria;
+
char *swaynag_command;
struct swaynag_instance swaynag_config_errors;
list_t *symbols;
@@ -765,6 +767,8 @@ int config_get_blur_size();
bool config_should_parameters_blur();
+bool config_should_parameters_shadow();
+
/* Global config singleton. */
extern struct sway_config *config;
diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h
index 90b68f1c..51f60557 100644
--- a/include/sway/desktop/fx_renderer/fx_renderer.h
+++ b/include/sway/desktop/fx_renderer/fx_renderer.h
@@ -33,6 +33,7 @@ struct decoration_data {
float *dim_color;
bool has_titlebar;
bool blur;
+ bool shadow;
};
struct blur_shader {
diff --git a/include/sway/layer_criteria.h b/include/sway/layer_criteria.h
new file mode 100644
index 00000000..f0906460
--- /dev/null
+++ b/include/sway/layer_criteria.h
@@ -0,0 +1,20 @@
+#include <stdbool.h>
+#include "sway/layers.h"
+#include "sway/config.h"
+
+struct layer_criteria {
+ char *namespace;
+ char *cmdlist;
+};
+
+void layer_criteria_destroy(struct layer_criteria *criteria);
+
+bool layer_criteria_is_equal(struct layer_criteria *a, struct layer_criteria *b);
+
+bool layer_criteria_already_exists(struct layer_criteria *criteria);
+
+// Gathers all of the matching criterias for a specified `sway_layer_surface`
+list_t *layer_criterias_for_sway_layer_surface(struct sway_layer_surface *sway_layer);
+
+// Parses the `layer_criteria` and applies the effects to the `sway_layer_surface`
+void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_criteria *criteria);
diff --git a/include/sway/layers.h b/include/sway/layers.h
index f8508493..b04990dc 100644
--- a/include/sway/layers.h
+++ b/include/sway/layers.h
@@ -27,6 +27,10 @@ struct sway_layer_surface {
enum zwlr_layer_shell_v1_layer layer;
struct wl_list subsurfaces;
+
+ bool has_shadow;
+ bool has_blur;
+ int corner_radius;
};
struct sway_layer_popup {
diff --git a/include/sway/output.h b/include/sway/output.h
index 65f7ca1a..3215c853 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -13,6 +13,12 @@
struct sway_server;
struct sway_container;
+struct render_data {
+ pixman_region32_t *damage;
+ struct wlr_box *clip_box;
+ struct decoration_data deco_data;
+};
+
struct sway_output_state {
list_t *workspaces;
struct sway_workspace *active_workspace;
diff --git a/sway/commands.c b/sway/commands.c
index 8e2d8f89..34bb08c3 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -84,6 +84,7 @@ static const struct cmd_handler handlers[] = {
{ "gaps", cmd_gaps },
{ "hide_edge_borders", cmd_hide_edge_borders },
{ "input", cmd_input },
+ { "layer_effects", cmd_layer_effects },
{ "mode", cmd_mode },
{ "mouse_warping", cmd_mouse_warping },
{ "new_float", cmd_new_float },
diff --git a/sway/commands/blur.c b/sway/commands/blur.c
index 15dd985d..5607d1e2 100644
--- a/sway/commands/blur.c
+++ b/sway/commands/blur.c
@@ -12,7 +12,7 @@ struct cmd_results *cmd_blur(int argc, char **argv) {
struct sway_container *con = config->handler_context.container;
- bool result = parse_boolean(argv[0], config->blur_enabled);
+ bool result = parse_boolean(argv[0], true);
if (con == NULL) {
config->blur_enabled = result;
} else {
diff --git a/sway/commands/corner_radius.c b/sway/commands/corner_radius.c
index ab216f6e..fe8b458f 100644
--- a/sway/commands/corner_radius.c
+++ b/sway/commands/corner_radius.c
@@ -4,15 +4,24 @@
#include "sway/tree/container.h"
#include "log.h"
+bool cmd_corner_radius_parse_value(char *arg, int* result) {
+ char *inv;
+ int value = strtol(arg, &inv, 10);
+ if (*inv != '\0' || value < 0 || value > 99) {
+ return false;
+ }
+ *result = value;
+ return true;
+}
+
struct cmd_results *cmd_corner_radius(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "corner_radius", EXPECTED_EQUAL_TO, 1))) {
return error;
}
- char *inv;
- int value = strtol(argv[0], &inv, 10);
- if (*inv != '\0' || value < 0 || value > 99) {
+ int value = 0;
+ if (!cmd_corner_radius_parse_value(argv[0], &value)) {
return cmd_results_new(CMD_FAILURE, "Invalid size specified");
}
diff --git a/sway/commands/layer_effects.c b/sway/commands/layer_effects.c
new file mode 100644
index 00000000..3d5cc8c0
--- /dev/null
+++ b/sway/commands/layer_effects.c
@@ -0,0 +1,33 @@
+#include <ctype.h>
+#include "log.h"
+#include "stringop.h"
+#include "sway/commands.h"
+#include "sway/config.h"
+#include "sway/layer_criteria.h"
+#include "sway/output.h"
+#include "util.h"
+
+struct cmd_results *cmd_layer_effects(int argc, char **argv) {
+ struct cmd_results *error = NULL;
+ if ((error = checkarg(argc, "layer_effects", EXPECTED_AT_LEAST, 2))) {
+ return error;
+ }
+
+ struct layer_criteria *criteria = malloc(sizeof(struct layer_criteria));
+ criteria->namespace = malloc(strlen(argv[0]) + 1);
+ strcpy(criteria->namespace, argv[0]);
+ criteria->cmdlist = join_args(argv + 1, argc - 1);
+
+ // Check if the rule already exists
+ if (layer_criteria_already_exists(criteria)) {
+ sway_log(SWAY_DEBUG, "layer_effect already exists: '%s' '%s'",
+ criteria->namespace, criteria->cmdlist);
+ layer_criteria_destroy(criteria);
+ return cmd_results_new(CMD_SUCCESS, NULL);
+ }
+
+ list_add(config->layer_criteria, criteria);
+ sway_log(SWAY_DEBUG, "layer_effect: '%s' '%s' added", criteria->namespace, criteria->cmdlist);
+
+ return cmd_results_new(CMD_SUCCESS, NULL);
+}
diff --git a/sway/commands/shadows.c b/sway/commands/shadows.c
index 2b57b2e6..ae7fc019 100644
--- a/sway/commands/shadows.c
+++ b/sway/commands/shadows.c
@@ -17,7 +17,7 @@ struct cmd_results *cmd_shadows(int argc, char **argv) {
struct sway_container *con = config->handler_context.container;
- bool result = parse_boolean(argv[0], config->shadow_enabled);
+ bool result = parse_boolean(argv[0], true);
if (con == NULL) {
config->shadow_enabled = result;
} else {
diff --git a/sway/config.c b/sway/config.c
index 04c75171..fbcf94a4 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -21,6 +21,7 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/criteria.h"
+#include "sway/layer_criteria.h"
#include "sway/desktop/transaction.h"
#include "sway/swaynag.h"
#include "sway/tree/arrange.h"
@@ -157,6 +158,12 @@ void free_config(struct sway_config *config) {
}
list_free(config->criteria);
}
+ if (config->layer_criteria) {
+ for (int i = 0; i < config->layer_criteria->length; ++i) {
+ layer_criteria_destroy(config->layer_criteria->items[i]);
+ }
+ list_free(config->layer_criteria);
+ }
list_free(config->no_focus);
list_free(config->active_bar_modifiers);
list_free_items_and_destroy(config->config_chain);
@@ -354,6 +361,8 @@ static void config_defaults(struct sway_config *config) {
config->titlebar_separator = true;
config->scratchpad_minimize = true;
+ if (!(config->layer_criteria = create_list())) goto cleanup;
+
// The keysym to keycode translation
struct xkb_rule_names rules = {0};
config->keysym_translation_state =
@@ -1092,3 +1101,7 @@ int config_get_blur_size() {
bool config_should_parameters_blur() {
return config->blur_params.radius > 0 && config->blur_params.num_passes > 0;
}
+
+bool config_should_parameters_shadow() {
+ return config->shadow_blur_sigma > 0 && config->shadow_color[3] > 0.0;
+}
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c
index a8356ad7..bbc2e6f3 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -6,6 +6,7 @@
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_subcompositor.h>
#include "log.h"
+#include "sway/layer_criteria.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/input/input-manager.h"
@@ -15,6 +16,21 @@
#include "sway/server.h"
#include "sway/tree/arrange.h"
#include "sway/tree/workspace.h"
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+
+static void layer_parse_criteria(struct sway_layer_surface *sway_layer) {
+ enum zwlr_layer_shell_v1_layer layer = sway_layer->layer;
+ if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+ return;
+ }
+
+ list_t *criterias = layer_criterias_for_sway_layer_surface(sway_layer);
+ for (int i = 0; i < criterias->length; i++) {
+ struct layer_criteria *criteria = criterias->items[i];
+ layer_criteria_parse(sway_layer, criteria);
+ }
+ list_free(criterias);
+}
static void apply_exclusive(struct wlr_box *usable_area,
uint32_t anchor, int32_t exclusive,
@@ -306,6 +322,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
wl_list_insert(&output->layers[layer_surface->current.layer],
&layer->link);
layer->layer = layer_surface->current.layer;
+ layer_parse_criteria(layer);
}
arrange_layers(output);
}
@@ -393,6 +410,7 @@ static void handle_map(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = sway_layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
+ layer_parse_criteria(sway_layer);
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true);
wlr_surface_send_enter(sway_layer->layer_surface->surface,
@@ -685,6 +703,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
sway_layer->layer_surface = layer_surface;
layer_surface->data = sway_layer;
+ sway_layer->has_blur = false;
+ sway_layer->has_shadow = false;
+ sway_layer->corner_radius = 0;
+
struct sway_output *output = layer_surface->output->data;
sway_layer->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->events.disable, &sway_layer->output_destroy);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index c3c240f8..7ea6a6e7 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -199,6 +199,13 @@ void output_layer_for_each_toplevel_surface(struct sway_output *output,
wl_list_for_each(layer_surface, layer_surfaces, link) {
struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
layer_surface->layer_surface;
+
+ struct render_data *data = user_data;
+ data->deco_data.blur = layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ?
+ layer_surface->has_blur : false;
+ data->deco_data.shadow = layer_surface->has_shadow;
+ data->deco_data.corner_radius = layer_surface->corner_radius;
+
output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
layer_surface->geo.x, layer_surface->geo.y, iterator,
user_data);
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index b01ca5e1..6efeaf5d 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -23,7 +23,6 @@
#include "sway/desktop/fx_renderer/fx_renderer.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
-#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/tree/arrange.h"
@@ -32,12 +31,6 @@
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
-struct render_data {
- pixman_region32_t *damage;
- struct wlr_box *clip_box;
- struct decoration_data deco_data;
-};
-
struct decoration_data get_undecorated_decoration_data() {
return (struct decoration_data) {
.alpha = 1.0f,
@@ -47,10 +40,10 @@ struct decoration_data get_undecorated_decoration_data() {
.saturation = 1.0f,
.has_titlebar = false,
.blur = false,
+ .shadow = false,
};
}
-
// TODO: contribute wlroots function to allow creating an fbox from a box?
struct wlr_fbox wlr_fbox_from_wlr_box(struct wlr_box *box) {
return (struct wlr_fbox) {
@@ -357,6 +350,61 @@ damage_finish:
pixman_region32_fini(&inverse_opaque);
}
+// _box.x and .y are expected to be layout-local
+// _box.width and .height are expected to be output-buffer-local
+void render_box_shadow(struct sway_output *output, pixman_region32_t *output_damage,
+ const struct wlr_box *_box, const float color[static 4],
+ float blur_sigma, float corner_radius) {
+ struct wlr_output *wlr_output = output->wlr_output;
+ struct fx_renderer *renderer = output->renderer;
+
+ struct wlr_box box;
+ memcpy(&box, _box, sizeof(struct wlr_box));
+ box.x -= blur_sigma;
+ box.y -= blur_sigma;
+ box.width += 2 * blur_sigma;
+ box.height += 2 * blur_sigma;
+
+ pixman_region32_t damage = create_damage(box, output_damage);
+
+ // don't damage area behind window since we dont render it anyway
+ struct wlr_box inner_box;
+ memcpy(&inner_box, _box, sizeof(struct wlr_box));
+ inner_box.x += corner_radius;
+ inner_box.y += corner_radius;
+ inner_box.width -= 2 * corner_radius;
+ inner_box.height -= 2 * corner_radius;
+ pixman_region32_t inner_damage = create_damage(inner_box, output_damage);
+ pixman_region32_subtract(&damage, &damage, &inner_damage);
+
+ bool damaged = pixman_region32_not_empty(&damage);
+ if (!damaged) {
+ goto damage_finish;
+ }
+
+ float matrix[9];
+ wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
+ wlr_output->transform_matrix);
+
+ // ensure the box is updated as per the output orientation
+ struct wlr_box transformed_box;
+ int width, height;
+ wlr_output_transformed_resolution(wlr_output, &width, &height);
+ wlr_box_transform(&transformed_box, &box,
+ wlr_output_transform_invert(wlr_output->transform), width, height);
+
+ int nrects;
+ pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
+ for (int i = 0; i < nrects; ++i) {
+ scissor_output(wlr_output, &rects[i]);
+
+ fx_render_box_shadow(renderer, &transformed_box, color, matrix,
+ corner_radius, blur_sigma);
+ }
+
+damage_finish:
+ pixman_region32_fini(&damage);
+}
static void render_surface_iterator(struct sway_output *output,
struct sway_view *view, struct wlr_surface *surface,
@@ -421,6 +469,7 @@ static void render_surface_iterator(struct sway_output *output,
pixman_region32_fini(&opaque_region);
}
+ // Render surface texture
struct wlr_fbox src_box;
wlr_surface_get_buffer_source_box(surface, &src_box);
struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture);
@@ -431,6 +480,30 @@ static void render_surface_iterator(struct sway_output *output,
wlr_output);
}
+// view will be NULL every time
+static void render_layer_iterator(struct sway_output *output,
+ struct sway_view *view, struct wlr_surface *surface,
+ struct wlr_box *_box, void *_data) {
+ struct render_data *data = _data;
+ struct decoration_data deco_data = data->deco_data;
+
+ // Ignore effects if this is a subsurface
+ if (wl_list_length(&surface->current.subsurfaces_above) > 0) {
+ deco_data = get_undecorated_decoration_data();
+ }
+
+ // render the layer's surface
+ render_surface_iterator(output, view, surface, _box, _data);
+
+ // render shadow
+ if (deco_data.shadow && config_should_parameters_shadow()) {
+ int corner_radius = deco_data.corner_radius *= output->wlr_output->scale;
+ scale_box(_box, output->wlr_output->scale);
+ render_box_shadow(output, data->damage, _box, config->shadow_color,
+ config->shadow_blur_sigma, corner_radius);
+ }
+}
+
static void render_layer_toplevel(struct sway_output *output,
pixman_region32_t *damage, struct wl_list *layer_surfaces) {
struct render_data data = {
@@ -438,7 +511,7 @@ static void render_layer_toplevel(struct sway_output *output,
.deco_data = get_undecorated_decoration_data(),
};
output_layer_for_each_toplevel_surface(output, layer_surfaces,
- render_surface_iterator, &data);
+ render_layer_iterator, &data);
}
static void render_layer_popups(struct sway_output *output,
@@ -448,7 +521,7 @@ static void render_layer_popups(struct sway_output *output,
.deco_data = get_undecorated_decoration_data(),
};
output_layer_for_each_popup_surface(output, layer_surfaces,
- render_surface_iterator, &data);
+ render_layer_iterator, &data);
}
#if HAVE_XWAYLAND
@@ -633,62 +706,6 @@ damage_finish:
pixman_region32_fini(&damage);
}
-// _box.x and .y are expected to be layout-local
-// _box.width and .height are expected to be output-buffer-local
-void render_box_shadow(struct sway_output *output, pixman_region32_t *output_damage,
- const struct wlr_box *_box, const float color[static 4],
- float blur_sigma, float corner_radius) {
- struct wlr_output *wlr_output = output->wlr_output;
- struct fx_renderer *renderer = output->renderer;
-
- struct wlr_box box;
- memcpy(&box, _box, sizeof(struct wlr_box));
- box.x -= blur_sigma;
- box.y -= blur_sigma;
- box.width += 2 * blur_sigma;
- box.height += 2 * blur_sigma;
-
- pixman_region32_t damage = create_damage(box, output_damage);
-
- // don't damage area behind window since we dont render it anyway
- struct wlr_box inner_box;
- memcpy(&inner_box, _box, sizeof(struct wlr_box));
- inner_box.x += corner_radius;
- inner_box.y += corner_radius;
- inner_box.width -= 2 * corner_radius;
- inner_box.height -= 2 * corner_radius;
- pixman_region32_t inner_damage = create_damage(inner_box, output_damage);
- pixman_region32_subtract(&damage, &damage, &inner_damage);
-
- bool damaged = pixman_region32_not_empty(&damage);
- if (!damaged) {
- goto damage_finish;
- }
-
- float matrix[9];
- wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
- wlr_output->transform_matrix);
-
- // ensure the box is updated as per the output orientation
- struct wlr_box transformed_box;
- int width, height;
- wlr_output_transformed_resolution(wlr_output, &width, &height);
- wlr_box_transform(&transformed_box, &box,
- wlr_output_transform_invert(wlr_output->transform), width, height);
-
- int nrects;
- pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
- for (int i = 0; i < nrects; ++i) {
- scissor_output(wlr_output, &rects[i]);
-
- fx_render_box_shadow(renderer, &transformed_box, color, matrix,
- corner_radius, blur_sigma);
- }
-
-damage_finish:
- pixman_region32_fini(&damage);
-}
-
void premultiply_alpha(float color[4], float opacity) {
color[3] *= opacity;
color[0] *= color[3];
@@ -807,6 +824,7 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output
}
}
+ // Render saved surface texture
struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture);
render_texture(wlr_output, damage, &fx_texture,
&saved_buf->source_box, &dst_box, matrix, deco_data);
@@ -833,7 +851,6 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
render_view_toplevels(view, output, damage, deco_data);
}
- // Only draw shadows on CSD windows if shadows_on_csd is enabled
if (state->border == B_CSD && !config->shadows_on_csd_enabled) {
return;
}
@@ -1404,6 +1421,7 @@ static void render_containers_linear(struct sway_output *output,
.saturation = child->saturation,
.has_titlebar = has_titlebar,
.blur = child->blur_enabled,
+ .shadow = child->shadow_enabled,
};
render_view(output, damage, child, colors, deco_data);
if (has_titlebar) {
@@ -1453,6 +1471,7 @@ static void render_containers_tabbed(struct sway_output *output,
.saturation = current->saturation,
.has_titlebar = true,
.blur = current->blur_enabled,
+ .shadow = current->shadow_enabled,
};
// Render tabs
@@ -1548,6 +1567,7 @@ static void render_containers_stacked(struct sway_output *output,
? 0 : current->corner_radius,
.has_titlebar = true,
.blur = current->blur_enabled,
+ .shadow = current->shadow_enabled,
};
// Render titles
@@ -1695,6 +1715,7 @@ static void render_floating_container(struct sway_output *soutput,
.corner_radius = con->corner_radius,
.has_titlebar = has_titlebar,
.blur = con->blur_enabled,
+ .shadow = con->shadow_enabled,
};
render_view(soutput, damage, con, colors, deco_data);
if (has_titlebar) {
@@ -1927,6 +1948,7 @@ void output_render(struct sway_output *output, struct timespec *when,
.saturation = focus->saturation,
.has_titlebar = false,
.blur = false,
+ .shadow = false,
};
render_view_popups(focus->view, output, damage, deco_data);
}
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index cd79e1c8..717ebf78 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -10,6 +10,7 @@
#include "log.h"
#include "sway/config.h"
#include "sway/ipc-json.h"
+#include "sway/layers.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
@@ -273,7 +274,8 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
return object;
}
-static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
+static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output,
+ struct sway_output *output, json_object *object) {
json_object_object_add(object, "primary", json_object_new_boolean(false));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
@@ -299,7 +301,7 @@ static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_obj
static void ipc_json_describe_output(struct sway_output *output,
json_object *object) {
- ipc_json_describe_wlr_output(output->wlr_output, object);
+ ipc_json_describe_wlr_output(output->wlr_output, output, object);
}
static void ipc_json_describe_enabled_output(struct sway_output *output,
@@ -331,6 +333,63 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
json_object_object_add(object, "adaptive_sync_status",
json_object_new_string(adaptive_sync_status));
+ struct json_object *layers = json_object_new_array();
+ size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
+ for (size_t i = 0; i < len; ++i) {
+ struct sway_layer_surface *lsurface;
+ wl_list_for_each(lsurface, &output->layers[i], link) {
+ json_object *layer = json_object_new_object();
+
+ json_object_object_add(layer, "namespace",
+ json_object_new_string(lsurface->layer_surface->namespace));
+
+ char *layer_name;
+ switch (lsurface->layer) {
+ case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
+ layer_name = "background";
+ break;
+ case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
+ layer_name = "bottom";
+ break;
+ case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
+ layer_name = "top";
+ break;
+ case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
+ layer_name = "overlay";
+ break;
+ }
+
+ json_object_object_add(layer, "layer",
+ json_object_new_string(layer_name));
+
+ json_object *extent = json_object_new_object();
+ json_object_object_add(extent, "width",
+ json_object_new_int(lsurface->extent.width));
+ json_object_object_add(extent, "height",
+ json_object_new_int(lsurface->extent.height));
+ json_object_object_add(extent, "x",
+ json_object_new_int(lsurface->extent.x));
+ json_object_object_add(extent, "y",
+ json_object_new_int(lsurface->extent.y));
+ json_object_object_add(layer, "extent", extent);
+
+ json_object *effects = json_object_new_array();
+ if (lsurface->has_blur) {
+ json_object_array_add(effects, json_object_new_string("blur"));
+ }
+ if (lsurface->has_shadow) {
+ json_object_array_add(effects, json_object_new_string("shadows"));
+ }
+ if (lsurface->corner_radius > 0) {
+ json_object_array_add(effects, json_object_new_string("corner_radius"));
+ }
+ json_object_object_add(layer, "effects", effects);
+
+ json_object_array_add(layers, layer);
+ }
+ }
+ json_object_object_add(object, "layer_shell_surfaces", layers);
+
struct sway_workspace *ws = output_get_active_workspace(output);
if (!sway_assert(ws, "Expected output to have a workspace")) {
return;
@@ -413,7 +472,7 @@ json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop
json_object *object = json_object_new_object();
- ipc_json_describe_wlr_output(wlr_output, object);
+ ipc_json_describe_wlr_output(wlr_output, NULL, object);
json_object_object_add(object, "non_desktop", json_object_new_boolean(true));
json_object_object_add(object, "type", json_object_new_string("output"));
@@ -1053,9 +1112,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
struct xkb_keymap *keymap = keyboard->keymap;
struct xkb_state *state = keyboard->xkb_state;
- json_object_object_add(object, "repeat_delay",
+ json_object_object_add(object, "repeat_delay",
json_object_new_int(keyboard->repeat_info.delay));
- json_object_object_add(object, "repeat_rate",
+ json_object_object_add(object, "repeat_rate",
json_object_new_int(keyboard->repeat_info.rate));
json_object *layouts_arr = json_object_new_array();
diff --git a/sway/layer_criteria.c b/sway/layer_criteria.c
new file mode 100644
index 00000000..694c5016
--- /dev/null
+++ b/sway/layer_criteria.c
@@ -0,0 +1,88 @@
+#include <ctype.h>
+#include "log.h"
+#include "stringop.h"
+#include "sway/commands.h"
+#include "sway/layer_criteria.h"
+#include "util.h"
+
+void layer_criteria_destroy(struct layer_criteria *criteria) {
+ free(criteria->namespace);
+ free(criteria->cmdlist);
+ free(criteria);
+}
+
+bool layer_criteria_is_equal(struct layer_criteria *a, struct layer_criteria *b) {
+ return strcmp(a->namespace, b->namespace) == 0
+ && strcmp(a->cmdlist, b->cmdlist) == 0;
+}
+
+bool layer_criteria_already_exists(struct layer_criteria *criteria) {
+ list_t *criterias = config->layer_criteria;
+ for (int i = 0; i < criterias->length; ++i) {
+ struct layer_criteria *existing = criterias->items[i];
+ if (layer_criteria_is_equal(criteria, existing)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+list_t *layer_criterias_for_sway_layer_surface(struct sway_layer_surface *sway_layer) {
+ list_t *criterias = config->layer_criteria;
+ list_t *matches = create_list();
+ for (int i = 0; i < criterias->length; ++i) {
+ struct layer_criteria *criteria = criterias->items[i];
+ if (strcmp(criteria->namespace, sway_layer->layer_surface->namespace) == 0) {
+ list_add(matches, criteria);
+ }
+ }
+ return matches;
+}
+
+void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_criteria *criteria) {
+ char matched_delim = ';';
+ char *head = malloc(strlen(criteria->cmdlist) + 1);
+ strcpy(head, criteria->cmdlist);
+ do {
+ // Trim leading whitespaces
+ for (; isspace(*head); ++head) {}
+ // Split command list
+ char *cmd = argsep(&head, ";,", &matched_delim);
+ for (; isspace(*cmd); ++cmd) {}
+
+ if (strcmp(cmd, "") == 0) {
+ sway_log(SWAY_INFO, "Ignoring empty layer effect.");
+ continue;
+ }
+ sway_log(SWAY_INFO, "Handling layer effect '%s'", cmd);
+
+ int argc;
+ char **argv = split_args(cmd, &argc);
+ // Strip all quotes from each token
+ for (int i = 1; i < argc; ++i) {
+ if (*argv[i] == '\"' || *argv[i] == '\'') {
+ strip_quotes(argv[i]);
+ }
+ }
+ if (strcmp(argv[0], "blur") == 0) {
+ sway_layer->has_blur = parse_boolean(argv[1], true);
+ continue;
+ } else if (strcmp(argv[0], "shadows") == 0) {
+ sway_layer->has_shadow = parse_boolean(argv[1], true);
+ continue;
+ } else if (strcmp(argv[0], "corner_radius") == 0) {
+ int value;
+ if (cmd_corner_radius_parse_value(argv[1], &value)) {
+ sway_layer->corner_radius = value;
+ continue;
+ }
+ sway_log(SWAY_ERROR,
+ "Invalid layer_effects corner_radius size! Got \"%s\"",
+ argv[1]);
+ return;
+ } else {
+ sway_log(SWAY_ERROR, "Invalid layer_effects effect! Got \"%s\"", cmd);
+ return;
+ }
+ } while(head);
+}
diff --git a/sway/meson.build b/sway/meson.build
index 54e41072..2e1c5d20 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -5,6 +5,7 @@ sway_sources = files(
'decoration.c',
'ipc-json.c',
'ipc-server.c',
+ 'layer_criteria.c',
'lock.c',
'main.c',
'realtime.c',
@@ -91,6 +92,7 @@ sway_sources = files(
'commands/include.c',
'commands/input.c',
'commands/layout.c',
+ 'commands/layer_effects.c',
'commands/mode.c',
'commands/mouse_warping.c',
'commands/move.c',
diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd
index f2ab30f7..2607c15c 100644
--- a/sway/sway-ipc.7.scd
+++ b/sway/sway-ipc.7.scd
@@ -230,6 +230,11 @@ following properties:
: string
: The transform currently in use for the output. This can be _normal_, _90_,
_180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_
+|- layer_shell_surfaces
+: array
+: An array of all layer-shell surfaces attached to the output. Each object
+ contains _namespace_, _layer_, _effects_, and _extent_ object that contains _x_, _y_
+ _width_, and _height_
|- current_workspace
: string
: The workspace currently visible on the output or _null_ for disabled outputs
@@ -259,6 +264,35 @@ following properties:
"scale": 1.0,
"subpixel_hinting": "rgb",
"transform": "normal",
+ "layer_shell_surfaces": [
+ {
+ "namespace": "wallpaper",
+ "layer": "background",
+ "extent": {
+ "width": 2560,
+ "height": 1440,
+ "x": 0,
+ "y": 0
+ },
+ "effects": [
+ ]
+ },
+ {
+ "namespace": "waybar",
+ "layer": "top",
+ "extent": {
+ "width": 2548,
+ "height": 31,
+ "x": 6,
+ "y": 6
+ },
+ "effects": [
+ "blur",
+ "shadow",
+ "corner_rounding"
+ ]
+ }
+ ],
"current_workspace": "1",
"modes": [
{
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 2288346b..06c27900 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -787,6 +787,25 @@ The default colors are:
Whenever a window that matches _criteria_ appears, run list of commands.
See *CRITERIA* for more details.
+*layer_effects* <layer-namespace> <effects>
+ Apply effects on specific layer shell surfaces, eg "waybar" or "rofi".
+ At least one effect needs to be provided. The <layer-namespace> can be
+ gotten through *sway-ipc*. Note: Surfaces in the _bottom_ layer cannot
+ use these effects.
+
+ Effects:
+ - *blur* <enable|disable>
+ - *shadows* <enable|disable>
+ - *corner_radius* <integer>
+
+ Example:
+
+ layer_effects "waybar" blur enable; shadows enable; corner_radius 6
+
+ SwayIPC Example:
+
+ swaymsg "layer_effects 'waybar' 'blur enable; shadows enable'"
+
*gaps* inner|outer|horizontal|vertical|top|right|bottom|left <amount>
Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner
affects spacing around each view and outer affects the spacing around each
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 161b1e9c..8bc62f3f 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -5,11 +5,13 @@
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
+#include <wayland-util.h>
#include "stringop.h"
#include "sway/input/input-manager.h"
#include "sway/input/cursor.h"
#include "sway/input/seat.h"
#include "sway/ipc-server.h"
+#include "sway/layers.h"
#include "sway/output.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
@@ -702,7 +704,25 @@ bool should_workspace_have_blur(struct sway_workspace *ws) {
if (!workspace_is_visible(ws)) {
return false;
}
- return (bool)workspace_find_container(ws, find_blurred_con_iterator, NULL);
+
+ if ((bool)workspace_find_container(ws, find_blurred_con_iterator, NULL)) {
+ return true;
+ }
+
+ // Check if any layer-shell surfaces will render effects
+ struct sway_output *sway_output = ws->output;
+ size_t len = sizeof(sway_output->layers) / sizeof(sway_output->layers[0]);
+ for (size_t i = 0; i < len; ++i) {
+ struct sway_layer_surface *lsurface;
+ wl_list_for_each(lsurface, &sway_output->layers[i], link) {
+ if (lsurface->has_blur && !lsurface->layer_surface->surface->opaque
+ && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
void workspace_for_each_container(struct sway_workspace *ws,