Add blur, shadow, and corner radius to layer-shell surfaces (#144)

Co-authored-by: Will McKinnon <contact@willmckinnon.com>
This commit is contained in:
Erik Reider 2023-05-19 21:14:06 +02:00 committed by GitHub
parent 6707842942
commit 415e072a3a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 456 additions and 78 deletions

View file

@ -31,6 +31,14 @@ Sway is an incredible window manager, and certainly one of the most well establi
- `blur_xray enable|disable` - `blur_xray enable|disable`
- `blur_passes <integer value 0 - 10>` - `blur_passes <integer value 0 - 10>`
- `blur_radius <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: + Dim unfocused windows:
- `default_dim_inactive <float value 0.0 - 1.0>` - `default_dim_inactive <float value 0.0 - 1.0>`
- `for_window [CRITERIA_HERE] dim_inactive <float value 0.0 - 1.0>` - `for_window [CRITERIA_HERE] dim_inactive <float value 0.0 - 1.0>`

View file

@ -97,6 +97,11 @@ void container_resize_tiled(struct sway_container *parent, uint32_t axis,
struct sway_container *container_find_resize_parent(struct sway_container *con, struct sway_container *container_find_resize_parent(struct sway_container *con,
uint32_t edge); uint32_t edge);
/**
* Effect handlers value parsers
*/
bool cmd_corner_radius_parse_value(char *arg, int* result);
/** /**
* Handlers shared by exec and exec_always. * Handlers shared by exec and exec_always.
*/ */
@ -157,6 +162,7 @@ sway_cmd cmd_input;
sway_cmd cmd_seat; sway_cmd cmd_seat;
sway_cmd cmd_ipc; sway_cmd cmd_ipc;
sway_cmd cmd_kill; sway_cmd cmd_kill;
sway_cmd cmd_layer_effects;
sway_cmd cmd_layout; sway_cmd cmd_layout;
sway_cmd cmd_log_colors; sway_cmd cmd_log_colors;
sway_cmd cmd_mark; sway_cmd cmd_mark;

View file

@ -500,6 +500,8 @@ struct sway_config {
bool titlebar_separator; bool titlebar_separator;
bool scratchpad_minimize; bool scratchpad_minimize;
list_t *layer_criteria;
char *swaynag_command; char *swaynag_command;
struct swaynag_instance swaynag_config_errors; struct swaynag_instance swaynag_config_errors;
list_t *symbols; list_t *symbols;
@ -765,6 +767,8 @@ int config_get_blur_size();
bool config_should_parameters_blur(); bool config_should_parameters_blur();
bool config_should_parameters_shadow();
/* Global config singleton. */ /* Global config singleton. */
extern struct sway_config *config; extern struct sway_config *config;

View file

@ -33,6 +33,7 @@ struct decoration_data {
float *dim_color; float *dim_color;
bool has_titlebar; bool has_titlebar;
bool blur; bool blur;
bool shadow;
}; };
struct blur_shader { struct blur_shader {

View file

@ -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);

View file

@ -27,6 +27,10 @@ struct sway_layer_surface {
enum zwlr_layer_shell_v1_layer layer; enum zwlr_layer_shell_v1_layer layer;
struct wl_list subsurfaces; struct wl_list subsurfaces;
bool has_shadow;
bool has_blur;
int corner_radius;
}; };
struct sway_layer_popup { struct sway_layer_popup {

View file

@ -13,6 +13,12 @@
struct sway_server; struct sway_server;
struct sway_container; struct sway_container;
struct render_data {
pixman_region32_t *damage;
struct wlr_box *clip_box;
struct decoration_data deco_data;
};
struct sway_output_state { struct sway_output_state {
list_t *workspaces; list_t *workspaces;
struct sway_workspace *active_workspace; struct sway_workspace *active_workspace;

View file

@ -84,6 +84,7 @@ static const struct cmd_handler handlers[] = {
{ "gaps", cmd_gaps }, { "gaps", cmd_gaps },
{ "hide_edge_borders", cmd_hide_edge_borders }, { "hide_edge_borders", cmd_hide_edge_borders },
{ "input", cmd_input }, { "input", cmd_input },
{ "layer_effects", cmd_layer_effects },
{ "mode", cmd_mode }, { "mode", cmd_mode },
{ "mouse_warping", cmd_mouse_warping }, { "mouse_warping", cmd_mouse_warping },
{ "new_float", cmd_new_float }, { "new_float", cmd_new_float },

View file

@ -12,7 +12,7 @@ struct cmd_results *cmd_blur(int argc, char **argv) {
struct sway_container *con = config->handler_context.container; 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) { if (con == NULL) {
config->blur_enabled = result; config->blur_enabled = result;
} else { } else {

View file

@ -4,15 +4,24 @@
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "log.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 *cmd_corner_radius(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "corner_radius", EXPECTED_EQUAL_TO, 1))) { if ((error = checkarg(argc, "corner_radius", EXPECTED_EQUAL_TO, 1))) {
return error; return error;
} }
char *inv; int value = 0;
int value = strtol(argv[0], &inv, 10); if (!cmd_corner_radius_parse_value(argv[0], &value)) {
if (*inv != '\0' || value < 0 || value > 99) {
return cmd_results_new(CMD_FAILURE, "Invalid size specified"); return cmd_results_new(CMD_FAILURE, "Invalid size specified");
} }

View file

@ -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);
}

View file

@ -17,7 +17,7 @@ struct cmd_results *cmd_shadows(int argc, char **argv) {
struct sway_container *con = config->handler_context.container; 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) { if (con == NULL) {
config->shadow_enabled = result; config->shadow_enabled = result;
} else { } else {

View file

@ -21,6 +21,7 @@
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/criteria.h" #include "sway/criteria.h"
#include "sway/layer_criteria.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/swaynag.h" #include "sway/swaynag.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
@ -157,6 +158,12 @@ void free_config(struct sway_config *config) {
} }
list_free(config->criteria); 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->no_focus);
list_free(config->active_bar_modifiers); list_free(config->active_bar_modifiers);
list_free_items_and_destroy(config->config_chain); 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->titlebar_separator = true;
config->scratchpad_minimize = true; config->scratchpad_minimize = true;
if (!(config->layer_criteria = create_list())) goto cleanup;
// The keysym to keycode translation // The keysym to keycode translation
struct xkb_rule_names rules = {0}; struct xkb_rule_names rules = {0};
config->keysym_translation_state = config->keysym_translation_state =
@ -1092,3 +1101,7 @@ int config_get_blur_size() {
bool config_should_parameters_blur() { bool config_should_parameters_blur() {
return config->blur_params.radius > 0 && config->blur_params.num_passes > 0; 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;
}

View file

@ -6,6 +6,7 @@
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_subcompositor.h> #include <wlr/types/wlr_subcompositor.h>
#include "log.h" #include "log.h"
#include "sway/layer_criteria.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/input/cursor.h" #include "sway/input/cursor.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
@ -15,6 +16,21 @@
#include "sway/server.h" #include "sway/server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/workspace.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, static void apply_exclusive(struct wlr_box *usable_area,
uint32_t anchor, int32_t exclusive, 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], wl_list_insert(&output->layers[layer_surface->current.layer],
&layer->link); &layer->link);
layer->layer = layer_surface->current.layer; layer->layer = layer_surface->current.layer;
layer_parse_criteria(layer);
} }
arrange_layers(output); 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; struct wlr_output *wlr_output = sway_layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
layer_parse_criteria(sway_layer);
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true); sway_layer->layer_surface->surface, true);
wlr_surface_send_enter(sway_layer->layer_surface->surface, 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; sway_layer->layer_surface = layer_surface;
layer_surface->data = sway_layer; 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; struct sway_output *output = layer_surface->output->data;
sway_layer->output_destroy.notify = handle_output_destroy; sway_layer->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->events.disable, &sway_layer->output_destroy); wl_signal_add(&output->events.disable, &sway_layer->output_destroy);

View file

@ -199,6 +199,13 @@ void output_layer_for_each_toplevel_surface(struct sway_output *output,
wl_list_for_each(layer_surface, layer_surfaces, link) { wl_list_for_each(layer_surface, layer_surfaces, link) {
struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
layer_surface->layer_surface; 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, output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
layer_surface->geo.x, layer_surface->geo.y, iterator, layer_surface->geo.x, layer_surface->geo.y, iterator,
user_data); user_data);

View file

@ -23,7 +23,6 @@
#include "sway/desktop/fx_renderer/fx_renderer.h" #include "sway/desktop/fx_renderer/fx_renderer.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/layers.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
@ -32,12 +31,6 @@
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.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() { struct decoration_data get_undecorated_decoration_data() {
return (struct decoration_data) { return (struct decoration_data) {
.alpha = 1.0f, .alpha = 1.0f,
@ -47,10 +40,10 @@ struct decoration_data get_undecorated_decoration_data() {
.saturation = 1.0f, .saturation = 1.0f,
.has_titlebar = false, .has_titlebar = false,
.blur = false, .blur = false,
.shadow = false,
}; };
} }
// TODO: contribute wlroots function to allow creating an fbox from a box? // TODO: contribute wlroots function to allow creating an fbox from a box?
struct wlr_fbox wlr_fbox_from_wlr_box(struct wlr_box *box) { struct wlr_fbox wlr_fbox_from_wlr_box(struct wlr_box *box) {
return (struct wlr_fbox) { return (struct wlr_fbox) {
@ -357,6 +350,61 @@ damage_finish:
pixman_region32_fini(&inverse_opaque); 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, static void render_surface_iterator(struct sway_output *output,
struct sway_view *view, struct wlr_surface *surface, 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); pixman_region32_fini(&opaque_region);
} }
// Render surface texture
struct wlr_fbox src_box; struct wlr_fbox src_box;
wlr_surface_get_buffer_source_box(surface, &src_box); wlr_surface_get_buffer_source_box(surface, &src_box);
struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture); 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); 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, static void render_layer_toplevel(struct sway_output *output,
pixman_region32_t *damage, struct wl_list *layer_surfaces) { pixman_region32_t *damage, struct wl_list *layer_surfaces) {
struct render_data data = { struct render_data data = {
@ -438,7 +511,7 @@ static void render_layer_toplevel(struct sway_output *output,
.deco_data = get_undecorated_decoration_data(), .deco_data = get_undecorated_decoration_data(),
}; };
output_layer_for_each_toplevel_surface(output, layer_surfaces, 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, 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(), .deco_data = get_undecorated_decoration_data(),
}; };
output_layer_for_each_popup_surface(output, layer_surfaces, output_layer_for_each_popup_surface(output, layer_surfaces,
render_surface_iterator, &data); render_layer_iterator, &data);
} }
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
@ -633,62 +706,6 @@ damage_finish:
pixman_region32_fini(&damage); 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) { void premultiply_alpha(float color[4], float opacity) {
color[3] *= opacity; color[3] *= opacity;
color[0] *= color[3]; 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); struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture);
render_texture(wlr_output, damage, &fx_texture, render_texture(wlr_output, damage, &fx_texture,
&saved_buf->source_box, &dst_box, matrix, deco_data); &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); 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) { if (state->border == B_CSD && !config->shadows_on_csd_enabled) {
return; return;
} }
@ -1404,6 +1421,7 @@ static void render_containers_linear(struct sway_output *output,
.saturation = child->saturation, .saturation = child->saturation,
.has_titlebar = has_titlebar, .has_titlebar = has_titlebar,
.blur = child->blur_enabled, .blur = child->blur_enabled,
.shadow = child->shadow_enabled,
}; };
render_view(output, damage, child, colors, deco_data); render_view(output, damage, child, colors, deco_data);
if (has_titlebar) { if (has_titlebar) {
@ -1453,6 +1471,7 @@ static void render_containers_tabbed(struct sway_output *output,
.saturation = current->saturation, .saturation = current->saturation,
.has_titlebar = true, .has_titlebar = true,
.blur = current->blur_enabled, .blur = current->blur_enabled,
.shadow = current->shadow_enabled,
}; };
// Render tabs // Render tabs
@ -1548,6 +1567,7 @@ static void render_containers_stacked(struct sway_output *output,
? 0 : current->corner_radius, ? 0 : current->corner_radius,
.has_titlebar = true, .has_titlebar = true,
.blur = current->blur_enabled, .blur = current->blur_enabled,
.shadow = current->shadow_enabled,
}; };
// Render titles // Render titles
@ -1695,6 +1715,7 @@ static void render_floating_container(struct sway_output *soutput,
.corner_radius = con->corner_radius, .corner_radius = con->corner_radius,
.has_titlebar = has_titlebar, .has_titlebar = has_titlebar,
.blur = con->blur_enabled, .blur = con->blur_enabled,
.shadow = con->shadow_enabled,
}; };
render_view(soutput, damage, con, colors, deco_data); render_view(soutput, damage, con, colors, deco_data);
if (has_titlebar) { if (has_titlebar) {
@ -1927,6 +1948,7 @@ void output_render(struct sway_output *output, struct timespec *when,
.saturation = focus->saturation, .saturation = focus->saturation,
.has_titlebar = false, .has_titlebar = false,
.blur = false, .blur = false,
.shadow = false,
}; };
render_view_popups(focus->view, output, damage, deco_data); render_view_popups(focus->view, output, damage, deco_data);
} }

View file

@ -10,6 +10,7 @@
#include "log.h" #include "log.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/ipc-json.h" #include "sway/ipc-json.h"
#include "sway/layers.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.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; 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, "primary", json_object_new_boolean(false));
json_object_object_add(object, "make", json_object_object_add(object, "make",
json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); 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, static void ipc_json_describe_output(struct sway_output *output,
json_object *object) { 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, 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_object_add(object, "adaptive_sync_status",
json_object_new_string(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); struct sway_workspace *ws = output_get_active_workspace(output);
if (!sway_assert(ws, "Expected output to have a workspace")) { if (!sway_assert(ws, "Expected output to have a workspace")) {
return; 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(); 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, "non_desktop", json_object_new_boolean(true));
json_object_object_add(object, "type", json_object_new_string("output")); 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_keymap *keymap = keyboard->keymap;
struct xkb_state *state = keyboard->xkb_state; 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_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_new_int(keyboard->repeat_info.rate));
json_object *layouts_arr = json_object_new_array(); json_object *layouts_arr = json_object_new_array();

88
sway/layer_criteria.c Normal file
View file

@ -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);
}

View file

@ -5,6 +5,7 @@ sway_sources = files(
'decoration.c', 'decoration.c',
'ipc-json.c', 'ipc-json.c',
'ipc-server.c', 'ipc-server.c',
'layer_criteria.c',
'lock.c', 'lock.c',
'main.c', 'main.c',
'realtime.c', 'realtime.c',
@ -91,6 +92,7 @@ sway_sources = files(
'commands/include.c', 'commands/include.c',
'commands/input.c', 'commands/input.c',
'commands/layout.c', 'commands/layout.c',
'commands/layer_effects.c',
'commands/mode.c', 'commands/mode.c',
'commands/mouse_warping.c', 'commands/mouse_warping.c',
'commands/move.c', 'commands/move.c',

View file

@ -230,6 +230,11 @@ following properties:
: string : string
: The transform currently in use for the output. This can be _normal_, _90_, : The transform currently in use for the output. This can be _normal_, _90_,
_180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_ _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 |- current_workspace
: string : string
: The workspace currently visible on the output or _null_ for disabled outputs : The workspace currently visible on the output or _null_ for disabled outputs
@ -259,6 +264,35 @@ following properties:
"scale": 1.0, "scale": 1.0,
"subpixel_hinting": "rgb", "subpixel_hinting": "rgb",
"transform": "normal", "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", "current_workspace": "1",
"modes": [ "modes": [
{ {

View file

@ -787,6 +787,25 @@ The default colors are:
Whenever a window that matches _criteria_ appears, run list of commands. Whenever a window that matches _criteria_ appears, run list of commands.
See *CRITERIA* for more details. 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> *gaps* inner|outer|horizontal|vertical|top|right|bottom|left <amount>
Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner
affects spacing around each view and outer affects the spacing around each affects spacing around each view and outer affects the spacing around each

View file

@ -5,11 +5,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <strings.h> #include <strings.h>
#include <wayland-util.h>
#include "stringop.h" #include "stringop.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/input/cursor.h" #include "sway/input/cursor.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/ipc-server.h" #include "sway/ipc-server.h"
#include "sway/layers.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
@ -702,7 +704,25 @@ bool should_workspace_have_blur(struct sway_workspace *ws) {
if (!workspace_is_visible(ws)) { if (!workspace_is_visible(ws)) {
return false; 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, void workspace_for_each_container(struct sway_workspace *ws,