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_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>`

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,
uint32_t edge);
/**
* Effect handlers value parsers
*/
bool cmd_corner_radius_parse_value(char *arg, int* result);
/**
* Handlers shared by exec and exec_always.
*/
@ -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;

View file

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

View file

@ -33,6 +33,7 @@ struct decoration_data {
float *dim_color;
bool has_titlebar;
bool blur;
bool shadow;
};
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;
struct wl_list subsurfaces;
bool has_shadow;
bool has_blur;
int corner_radius;
};
struct sway_layer_popup {

View file

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

View file

@ -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 },

View file

@ -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 {

View file

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

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;
bool result = parse_boolean(argv[0], config->shadow_enabled);
bool result = parse_boolean(argv[0], true);
if (con == NULL) {
config->shadow_enabled = result;
} else {

View file

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

View file

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

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

View file

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

View file

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

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',
'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',

View file

@ -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": [
{

View file

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

View file

@ -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,