diff --git a/README.md b/README.md index 9c9551d6..06726bb1 100644 --- a/README.md +++ b/README.md @@ -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* - `layer_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 ` - `shadows ` diff --git a/include/sway/config.h b/include/sway/config.h index ef23c233..bda1e73f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -476,11 +476,6 @@ struct blur_parameters { int radius; }; -struct layer_effects { - char *namespace; - struct decoration_data deco_data; -}; - /** * The configuration struct. The result of loading a config file. */ @@ -506,7 +501,7 @@ struct sway_config { bool titlebar_separator; bool scratchpad_minimize; - list_t *layer_effects; + list_t *layer_criteria; char *swaynag_command; struct swaynag_instance swaynag_config_errors; diff --git a/include/sway/layer_criteria.h b/include/sway/layer_criteria.h new file mode 100644 index 00000000..f86576ea --- /dev/null +++ b/include/sway/layer_criteria.h @@ -0,0 +1,19 @@ +#include +#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); diff --git a/include/sway/layers.h b/include/sway/layers.h index 6ebdbfab..6037ed09 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -29,7 +29,7 @@ struct sway_layer_surface { struct wl_list subsurfaces; - struct layer_effects *effects; + struct decoration_data deco_data; }; struct sway_layer_popup { diff --git a/sway/commands.c b/sway/commands.c index 2b515228..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 }, @@ -119,7 +120,6 @@ static const struct cmd_handler handlers[] = { static const struct cmd_handler config_handlers[] = { { "default_orientation", cmd_default_orientation }, { "include", cmd_include }, - { "layer_effects", cmd_layer_effects }, { "scratchpad_minimize", cmd_scratchpad_minimize }, { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, diff --git a/sway/commands/layer_effects.c b/sway/commands/layer_effects.c index 0aadd684..3d5cc8c0 100644 --- a/sway/commands/layer_effects.c +++ b/sway/commands/layer_effects.c @@ -3,101 +3,31 @@ #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 *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 *error = NULL; if ((error = checkarg(argc, "layer_effects", EXPECTED_AT_LEAST, 2))) { return error; } - struct layer_effects *effect = malloc(sizeof(struct layer_effects)); - size_t len = sizeof(argv[0]); - effect->namespace = malloc(len + 1); - memcpy(effect->namespace, argv[0], len); - effect->deco_data = get_undecorated_decoration_data(); + 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); - // Parse the commands - if ((error = parse_effects(argc, argv, effect))) { - return error; - } - - // 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)); + // 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); } - // Check if the rule already exists - list_t *effects = config->layer_effects; - 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); - } + 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/config.c b/sway/config.c index d847c335..539c716c 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,13 +158,11 @@ void free_config(struct sway_config *config) { } list_free(config->criteria); } - if (config->layer_effects) { - for (int i = 0; i < config->layer_effects->length; ++i) { - struct layer_effects *effect = config->layer_effects->items[i]; - free(effect->namespace); - free(effect); + 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_effects); + list_free(config->layer_criteria); } list_free(config->no_focus); list_free(config->active_bar_modifiers); @@ -362,7 +361,7 @@ static void config_defaults(struct sway_config *config) { config->titlebar_separator = 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 struct xkb_rule_names rules = {0}; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 256ad98c..4b6e90f6 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -6,6 +6,7 @@ #include #include #include "log.h" +#include "sway/layer_criteria.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -17,6 +18,22 @@ #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; + } + + 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, uint32_t anchor, int32_t exclusive, 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], &layer->link); layer->layer = layer_surface->current.layer; + layer_parse_criteria(layer); } 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; 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, @@ -686,18 +705,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_layer->layer_surface = layer_surface; layer_surface->data = sway_layer; - enum zwlr_layer_shell_v1_layer layer = layer_surface->current.layer; - 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; - } - } - } + sway_layer->deco_data = get_undecorated_decoration_data(); struct sway_output *output = layer_surface->output->data; sway_layer->output_destroy.notify = handle_output_destroy; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f309eb1b..a67b95fc 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -201,9 +201,7 @@ void output_layer_for_each_toplevel_surface(struct sway_output *output, layer_surface->layer_surface; struct render_data *data = user_data; data->sway_layer = layer_surface; - if (layer_surface->effects) { - data->deco_data = layer_surface->effects->deco_data; - } + data->deco_data = layer_surface->deco_data; output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, layer_surface->geo.x, layer_surface->geo.y, iterator, diff --git a/sway/desktop/render.c b/sway/desktop/render.c index bb8c113f..7d9de1bf 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1848,21 +1848,20 @@ static struct workspace_effect_info get_workspace_effect_info(struct sway_output for (size_t i = 0; i < len; ++i) { struct sway_layer_surface *lsurface; wl_list_for_each(lsurface, &sway_output->layers[i], link) { - struct layer_effects *layer_effects = lsurface->effects; - if (layer_effects) { - if (layer_effects->deco_data.blur && !lsurface->layer_surface->surface->opaque) { - effect_info.container_wants_blur = true; - // Check if we should render optimized blur - if (renderer->blur_buffer_dirty && config->blur_xray - && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND - && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - effect_info.should_render_optimized_blur = true; - } - } - if (layer_effects->deco_data.shadow) { - effect_info.container_wants_shadow = true; + struct decoration_data deco_data = lsurface->deco_data; + if (deco_data.blur && !lsurface->layer_surface->surface->opaque) { + effect_info.container_wants_blur = true; + // Check if we should render optimized blur + if (renderer->blur_buffer_dirty && config->blur_xray + && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND + && lsurface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { + effect_info.should_render_optimized_blur = true; } } + if (deco_data.shadow) { + effect_info.container_wants_shadow = true; + } + if (effect_info.container_wants_blur && effect_info.container_wants_shadow && effect_info.should_render_optimized_blur) { diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 7c9ed82c..c71c0a0d 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -374,17 +374,15 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, json_object_object_add(layer, "extent", extent); json_object *effects = json_object_new_array(); - struct layer_effects *layer_effects = lsurface->effects; - if (layer_effects) { - if (layer_effects->deco_data.blur) { - json_object_array_add(effects, json_object_new_string("blur")); - } - if (layer_effects->deco_data.shadow) { - json_object_array_add(effects, json_object_new_string("shadows")); - } - if (layer_effects->deco_data.corner_radius > 0) { - json_object_array_add(effects, json_object_new_string("corner_radius")); - } + struct decoration_data deco_data = lsurface->deco_data; + if (deco_data.blur) { + json_object_array_add(effects, json_object_new_string("blur")); + } + if (deco_data.shadow) { + json_object_array_add(effects, json_object_new_string("shadows")); + } + if (deco_data.corner_radius > 0) { + json_object_array_add(effects, json_object_new_string("corner_radius")); } json_object_object_add(layer, "effects", effects); diff --git a/sway/layer_criteria.c b/sway/layer_criteria.c new file mode 100644 index 00000000..0905a735 --- /dev/null +++ b/sway/layer_criteria.c @@ -0,0 +1,87 @@ +#include +#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); +} diff --git a/sway/meson.build b/sway/meson.build index 5d3f8b09..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', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 9ad3626e..06c27900 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -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; subsequent attempts will be ignored. -*layer_effects* - Apply effects on specific layer shell surfaces, eg waybar or rofi. - At least one effect needs to be provided. The can be - gotten through *sway-ipc*. Note: Surfaces in the _bottom_ layer cannot - use these effects. - - Effects: - - *blur* - - *shadows* - - *corner_radius* - - Example: - - layer_effects "waybar" blur enable; shadows enable; corner_radius 6 - *scratchpad_minimize* enable|disable Adjusts if minimized windows should be moved into the scratchpad. 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. See *CRITERIA* for more details. +*layer_effects* + Apply effects on specific layer shell surfaces, eg "waybar" or "rofi". + At least one effect needs to be provided. The can be + gotten through *sway-ipc*. Note: Surfaces in the _bottom_ layer cannot + use these effects. + + Effects: + - *blur* + - *shadows* + - *corner_radius* + + 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 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner affects spacing around each view and outer affects the spacing around each