Refactored layer_effects command to parse multiline configs

Now supports commands like these:

```
layer_effects "waybar" {
  blur enable;
  shadows disable;
  corner_radius 6;
}
```

Also now able to override previous criterias and add criterias during runtime
This commit is contained in:
Erik Reider 2023-05-10 19:15:10 +02:00
parent cdf106c43c
commit fba74b3617
14 changed files with 190 additions and 151 deletions

View file

@ -34,6 +34,7 @@ Sway is an incredible window manager, and certainly one of the most well establi
+ LayerShell effects: *ONLY ON SWAYFX-GIT, NOT YET RELEASED* + LayerShell effects: *ONLY ON SWAYFX-GIT, NOT YET RELEASED*
- `layer_effects <layer namespace> <effects>` - `layer_effects <layer namespace> <effects>`
- Example: `layer_effects "waybar" blur enable; shadows enable; corner_radius 6` - 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: - Available Effects:
- `blur <enable|disable>` - `blur <enable|disable>`
- `shadows <enable|disable>` - `shadows <enable|disable>`

View file

@ -476,11 +476,6 @@ struct blur_parameters {
int radius; int radius;
}; };
struct layer_effects {
char *namespace;
struct decoration_data deco_data;
};
/** /**
* The configuration struct. The result of loading a config file. * The configuration struct. The result of loading a config file.
*/ */
@ -506,7 +501,7 @@ struct sway_config {
bool titlebar_separator; bool titlebar_separator;
bool scratchpad_minimize; bool scratchpad_minimize;
list_t *layer_effects; list_t *layer_criteria;
char *swaynag_command; char *swaynag_command;
struct swaynag_instance swaynag_config_errors; struct swaynag_instance swaynag_config_errors;

View file

@ -0,0 +1,19 @@
#include <stdbool.h>
#include "sway/layers.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

@ -29,7 +29,7 @@ struct sway_layer_surface {
struct wl_list subsurfaces; struct wl_list subsurfaces;
struct layer_effects *effects; struct decoration_data deco_data;
}; };
struct sway_layer_popup { struct sway_layer_popup {

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 },
@ -119,7 +120,6 @@ static const struct cmd_handler handlers[] = {
static const struct cmd_handler config_handlers[] = { static const struct cmd_handler config_handlers[] = {
{ "default_orientation", cmd_default_orientation }, { "default_orientation", cmd_default_orientation },
{ "include", cmd_include }, { "include", cmd_include },
{ "layer_effects", cmd_layer_effects },
{ "scratchpad_minimize", cmd_scratchpad_minimize }, { "scratchpad_minimize", cmd_scratchpad_minimize },
{ "swaybg_command", cmd_swaybg_command }, { "swaybg_command", cmd_swaybg_command },
{ "swaynag_command", cmd_swaynag_command }, { "swaynag_command", cmd_swaynag_command },

View file

@ -3,101 +3,31 @@
#include "stringop.h" #include "stringop.h"
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/layer_criteria.h"
#include "sway/output.h" #include "sway/output.h"
#include "util.h" #include "util.h"
struct cmd_results *parse_effects(int argc, char **argv, struct layer_effects *effect) {
char matched_delim = ';';
char *head = join_args(argv + 1, argc - 1);
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) {
effect->deco_data.blur = cmd_blur_parse_value(argv[1]);
continue;
} else if (strcmp(argv[0], "shadows") == 0) {
effect->deco_data.shadow = cmd_shadows_parse_value(argv[1]);
continue;
} else if (strcmp(argv[0], "corner_radius") == 0) {
int value;
if (cmd_corner_radius_parse_value(argv[1], &value)) {
effect->deco_data.corner_radius = value;
continue;
}
return cmd_results_new(CMD_INVALID,
"Invalid layer_effects corner_radius size! Got \"%s\"",
argv[1]);
} else {
return cmd_results_new(CMD_INVALID,
"Invalid layer_effects effect! Got \"%s\"",
cmd);
}
} while(head);
return NULL;
}
struct cmd_results *cmd_layer_effects(int argc, char **argv) { struct cmd_results *cmd_layer_effects(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layer_effects", EXPECTED_AT_LEAST, 2))) { if ((error = checkarg(argc, "layer_effects", EXPECTED_AT_LEAST, 2))) {
return error; return error;
} }
struct layer_effects *effect = malloc(sizeof(struct layer_effects)); struct layer_criteria *criteria = malloc(sizeof(struct layer_criteria));
size_t len = sizeof(argv[0]); criteria->namespace = malloc(strlen(argv[0]) + 1);
effect->namespace = malloc(len + 1); strcpy(criteria->namespace, argv[0]);
memcpy(effect->namespace, argv[0], len); criteria->cmdlist = join_args(argv + 1, argc - 1);
effect->deco_data = get_undecorated_decoration_data();
// Parse the commands // Check if the rule already exists
if ((error = parse_effects(argc, argv, effect))) { if (layer_criteria_already_exists(criteria)) {
return error; sway_log(SWAY_DEBUG, "layer_effect already exists: '%s' '%s'",
} criteria->namespace, criteria->cmdlist);
layer_criteria_destroy(criteria);
// Ignore if nothing has changed
// TODO: Add deco_data cmp function?
if (!effect->deco_data.blur
&& !effect->deco_data.shadow
&& effect->deco_data.corner_radius < 1) {
sway_log(SWAY_ERROR,
"Ignoring layer effect \"%s\". Nothing changed\n",
join_args(argv + 1, argc - 1));
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
} }
// Check if the rule already exists list_add(config->layer_criteria, criteria);
list_t *effects = config->layer_effects; sway_log(SWAY_DEBUG, "layer_effect: '%s' '%s' added", criteria->namespace, criteria->cmdlist);
for (int i = 0; i < effects->length; ++i) {
struct layer_effects *existing = effects->items[i];
// Replace the duplicate entry
if (strcmp(existing->namespace, effect->namespace) == 0) {
memcpy(existing, effect, sizeof(struct layer_effects));
free(effect);
effect = NULL;
break;
}
}
if (effect) {
list_add(effects, effect);
}
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
} }

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,13 +158,11 @@ void free_config(struct sway_config *config) {
} }
list_free(config->criteria); list_free(config->criteria);
} }
if (config->layer_effects) { if (config->layer_criteria) {
for (int i = 0; i < config->layer_effects->length; ++i) { for (int i = 0; i < config->layer_criteria->length; ++i) {
struct layer_effects *effect = config->layer_effects->items[i]; layer_criteria_destroy(config->layer_criteria->items[i]);
free(effect->namespace);
free(effect);
} }
list_free(config->layer_effects); 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);
@ -362,7 +361,7 @@ 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_effects = create_list())) goto cleanup; 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};

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"
@ -17,6 +18,22 @@
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "wlr-layer-shell-unstable-v1-protocol.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;
}
sway_layer->deco_data.can_blur_xray = layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
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,
int32_t margin_top, int32_t margin_right, int32_t margin_top, int32_t margin_right,
@ -307,6 +324,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);
} }
@ -394,6 +412,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,
@ -686,18 +705,7 @@ 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;
enum zwlr_layer_shell_v1_layer layer = layer_surface->current.layer; sway_layer->deco_data = get_undecorated_decoration_data();
if (layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
for (int i = 0; i < config->layer_effects->length; ++i) {
struct layer_effects *effect = config->layer_effects->items[i];
if (strcmp(effect->namespace, layer_surface->namespace) == 0) {
sway_layer->effects = effect;
// Blur optimization won't work for BOTTOM layered surfaces
sway_layer->effects->deco_data.can_blur_xray = layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
break;
}
}
}
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;

View file

@ -201,9 +201,7 @@ void output_layer_for_each_toplevel_surface(struct sway_output *output,
layer_surface->layer_surface; layer_surface->layer_surface;
struct render_data *data = user_data; struct render_data *data = user_data;
data->sway_layer = layer_surface; data->sway_layer = layer_surface;
if (layer_surface->effects) { data->deco_data = layer_surface->deco_data;
data->deco_data = layer_surface->effects->deco_data;
}
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,

View file

@ -1848,9 +1848,8 @@ static struct workspace_effect_info get_workspace_effect_info(struct sway_output
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
struct sway_layer_surface *lsurface; struct sway_layer_surface *lsurface;
wl_list_for_each(lsurface, &sway_output->layers[i], link) { wl_list_for_each(lsurface, &sway_output->layers[i], link) {
struct layer_effects *layer_effects = lsurface->effects; struct decoration_data deco_data = lsurface->deco_data;
if (layer_effects) { if (deco_data.blur && !lsurface->layer_surface->surface->opaque) {
if (layer_effects->deco_data.blur && !lsurface->layer_surface->surface->opaque) {
effect_info.container_wants_blur = true; effect_info.container_wants_blur = true;
// Check if we should render optimized blur // Check if we should render optimized blur
if (renderer->blur_buffer_dirty && config->blur_xray if (renderer->blur_buffer_dirty && config->blur_xray
@ -1859,10 +1858,10 @@ static struct workspace_effect_info get_workspace_effect_info(struct sway_output
effect_info.should_render_optimized_blur = true; effect_info.should_render_optimized_blur = true;
} }
} }
if (layer_effects->deco_data.shadow) { if (deco_data.shadow) {
effect_info.container_wants_shadow = true; effect_info.container_wants_shadow = true;
} }
}
if (effect_info.container_wants_blur if (effect_info.container_wants_blur
&& effect_info.container_wants_shadow && effect_info.container_wants_shadow
&& effect_info.should_render_optimized_blur) { && effect_info.should_render_optimized_blur) {

View file

@ -374,18 +374,16 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
json_object_object_add(layer, "extent", extent); json_object_object_add(layer, "extent", extent);
json_object *effects = json_object_new_array(); json_object *effects = json_object_new_array();
struct layer_effects *layer_effects = lsurface->effects; struct decoration_data deco_data = lsurface->deco_data;
if (layer_effects) { if (deco_data.blur) {
if (layer_effects->deco_data.blur) {
json_object_array_add(effects, json_object_new_string("blur")); json_object_array_add(effects, json_object_new_string("blur"));
} }
if (layer_effects->deco_data.shadow) { if (deco_data.shadow) {
json_object_array_add(effects, json_object_new_string("shadows")); json_object_array_add(effects, json_object_new_string("shadows"));
} }
if (layer_effects->deco_data.corner_radius > 0) { if (deco_data.corner_radius > 0) {
json_object_array_add(effects, json_object_new_string("corner_radius")); json_object_array_add(effects, json_object_new_string("corner_radius"));
} }
}
json_object_object_add(layer, "effects", effects); json_object_object_add(layer, "effects", effects);
json_object_array_add(layers, layer); json_object_array_add(layers, layer);

87
sway/layer_criteria.c Normal file
View file

@ -0,0 +1,87 @@
#include <ctype.h>
#include "log.h"
#include "stringop.h"
#include "sway/commands.h"
#include "sway/layer_criteria.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->deco_data.blur = cmd_blur_parse_value(argv[1]);
continue;
} else if (strcmp(argv[0], "shadows") == 0) {
sway_layer->deco_data.shadow = cmd_shadows_parse_value(argv[1]);
continue;
} else if (strcmp(argv[0], "corner_radius") == 0) {
int value;
if (cmd_corner_radius_parse_value(argv[1], &value)) {
sway_layer->deco_data.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',

View file

@ -71,21 +71,6 @@ The following commands may only be used in the configuration file.
*wordexp*(3) for details). The same include file can only be included once; *wordexp*(3) for details). The same include file can only be included once;
subsequent attempts will be ignored. subsequent attempts will be ignored.
*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
*scratchpad_minimize* enable|disable *scratchpad_minimize* enable|disable
Adjusts if minimized windows should be moved into the scratchpad. Adjusts if minimized windows should be moved into the scratchpad.
Must be set at config-time (when starting sway). Must be set at config-time (when starting sway).
@ -802,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