diff options
| author | Erik Reider <[email protected]> | 2023-05-19 21:14:06 +0200 | 
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-19 21:14:06 +0200 | 
| commit | 415e072a3af292937f0b4c41acadafaee6958437 (patch) | |
| tree | 354de329d9cbf66054260d50aebefd86a26d5055 | |
| parent | 67078429428f0a97333c107da8a3ad8fb678a602 (diff) | |
Add blur, shadow, and corner radius to layer-shell surfaces (#144)
Co-authored-by: Will McKinnon <[email protected]>
| -rw-r--r-- | README.md | 8 | ||||
| -rw-r--r-- | include/sway/commands.h | 6 | ||||
| -rw-r--r-- | include/sway/config.h | 4 | ||||
| -rw-r--r-- | include/sway/desktop/fx_renderer/fx_renderer.h | 1 | ||||
| -rw-r--r-- | include/sway/layer_criteria.h | 20 | ||||
| -rw-r--r-- | include/sway/layers.h | 4 | ||||
| -rw-r--r-- | include/sway/output.h | 6 | ||||
| -rw-r--r-- | sway/commands.c | 1 | ||||
| -rw-r--r-- | sway/commands/blur.c | 2 | ||||
| -rw-r--r-- | sway/commands/corner_radius.c | 15 | ||||
| -rw-r--r-- | sway/commands/layer_effects.c | 33 | ||||
| -rw-r--r-- | sway/commands/shadows.c | 2 | ||||
| -rw-r--r-- | sway/config.c | 13 | ||||
| -rw-r--r-- | sway/desktop/layer_shell.c | 22 | ||||
| -rw-r--r-- | sway/desktop/output.c | 7 | ||||
| -rw-r--r-- | sway/desktop/render.c | 156 | ||||
| -rw-r--r-- | sway/ipc-json.c | 69 | ||||
| -rw-r--r-- | sway/layer_criteria.c | 88 | ||||
| -rw-r--r-- | sway/meson.build | 2 | ||||
| -rw-r--r-- | sway/sway-ipc.7.scd | 34 | ||||
| -rw-r--r-- | sway/sway.5.scd | 19 | ||||
| -rw-r--r-- | sway/tree/workspace.c | 22 | 
22 files changed, 456 insertions, 78 deletions
@@ -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,  | 
