diff --git a/README.md b/README.md index 70f14f2a..948cf16c 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,14 @@ Sway is an incredible window manager, and certainly one of the most well establi - `blur_xray enable|disable` - `blur_passes ` - `blur_radius ` ++ 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 ` + - `corner_radius ` + Dim unfocused windows: - `default_dim_inactive ` - `for_window [CRITERIA_HERE] dim_inactive ` diff --git a/include/sway/commands.h b/include/sway/commands.h index b895d5f2..920e8596 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -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; diff --git a/include/sway/config.h b/include/sway/config.h index d84eef69..04e2969e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -500,6 +500,8 @@ struct sway_config { bool titlebar_separator; bool scratchpad_minimize; + list_t *layer_criteria; + char *swaynag_command; struct swaynag_instance swaynag_config_errors; list_t *symbols; @@ -765,6 +767,8 @@ int config_get_blur_size(); bool config_should_parameters_blur(); +bool config_should_parameters_shadow(); + /* Global config singleton. */ extern struct sway_config *config; diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h index 90b68f1c..51f60557 100644 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ b/include/sway/desktop/fx_renderer/fx_renderer.h @@ -33,6 +33,7 @@ struct decoration_data { float *dim_color; bool has_titlebar; bool blur; + bool shadow; }; struct blur_shader { diff --git a/include/sway/layer_criteria.h b/include/sway/layer_criteria.h new file mode 100644 index 00000000..f0906460 --- /dev/null +++ b/include/sway/layer_criteria.h @@ -0,0 +1,20 @@ +#include +#include "sway/layers.h" +#include "sway/config.h" + +struct layer_criteria { + char *namespace; + char *cmdlist; +}; + +void layer_criteria_destroy(struct layer_criteria *criteria); + +bool layer_criteria_is_equal(struct layer_criteria *a, struct layer_criteria *b); + +bool layer_criteria_already_exists(struct layer_criteria *criteria); + +// Gathers all of the matching criterias for a specified `sway_layer_surface` +list_t *layer_criterias_for_sway_layer_surface(struct sway_layer_surface *sway_layer); + +// Parses the `layer_criteria` and applies the effects to the `sway_layer_surface` +void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_criteria *criteria); diff --git a/include/sway/layers.h b/include/sway/layers.h index f8508493..b04990dc 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -27,6 +27,10 @@ struct sway_layer_surface { enum zwlr_layer_shell_v1_layer layer; struct wl_list subsurfaces; + + bool has_shadow; + bool has_blur; + int corner_radius; }; struct sway_layer_popup { diff --git a/include/sway/output.h b/include/sway/output.h index 65f7ca1a..3215c853 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -13,6 +13,12 @@ struct sway_server; struct sway_container; +struct render_data { + pixman_region32_t *damage; + struct wlr_box *clip_box; + struct decoration_data deco_data; +}; + struct sway_output_state { list_t *workspaces; struct sway_workspace *active_workspace; diff --git a/sway/commands.c b/sway/commands.c index 8e2d8f89..34bb08c3 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -84,6 +84,7 @@ static const struct cmd_handler handlers[] = { { "gaps", cmd_gaps }, { "hide_edge_borders", cmd_hide_edge_borders }, { "input", cmd_input }, + { "layer_effects", cmd_layer_effects }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, { "new_float", cmd_new_float }, diff --git a/sway/commands/blur.c b/sway/commands/blur.c index 15dd985d..5607d1e2 100644 --- a/sway/commands/blur.c +++ b/sway/commands/blur.c @@ -12,7 +12,7 @@ struct cmd_results *cmd_blur(int argc, char **argv) { struct sway_container *con = config->handler_context.container; - bool result = parse_boolean(argv[0], config->blur_enabled); + bool result = parse_boolean(argv[0], true); if (con == NULL) { config->blur_enabled = result; } else { diff --git a/sway/commands/corner_radius.c b/sway/commands/corner_radius.c index ab216f6e..fe8b458f 100644 --- a/sway/commands/corner_radius.c +++ b/sway/commands/corner_radius.c @@ -4,15 +4,24 @@ #include "sway/tree/container.h" #include "log.h" +bool cmd_corner_radius_parse_value(char *arg, int* result) { + char *inv; + int value = strtol(arg, &inv, 10); + if (*inv != '\0' || value < 0 || value > 99) { + return false; + } + *result = value; + return true; +} + struct cmd_results *cmd_corner_radius(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "corner_radius", EXPECTED_EQUAL_TO, 1))) { return error; } - char *inv; - int value = strtol(argv[0], &inv, 10); - if (*inv != '\0' || value < 0 || value > 99) { + int value = 0; + if (!cmd_corner_radius_parse_value(argv[0], &value)) { return cmd_results_new(CMD_FAILURE, "Invalid size specified"); } diff --git a/sway/commands/layer_effects.c b/sway/commands/layer_effects.c new file mode 100644 index 00000000..3d5cc8c0 --- /dev/null +++ b/sway/commands/layer_effects.c @@ -0,0 +1,33 @@ +#include +#include "log.h" +#include "stringop.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/layer_criteria.h" +#include "sway/output.h" +#include "util.h" + +struct cmd_results *cmd_layer_effects(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "layer_effects", EXPECTED_AT_LEAST, 2))) { + return error; + } + + struct layer_criteria *criteria = malloc(sizeof(struct layer_criteria)); + criteria->namespace = malloc(strlen(argv[0]) + 1); + strcpy(criteria->namespace, argv[0]); + criteria->cmdlist = join_args(argv + 1, argc - 1); + + // Check if the rule already exists + if (layer_criteria_already_exists(criteria)) { + sway_log(SWAY_DEBUG, "layer_effect already exists: '%s' '%s'", + criteria->namespace, criteria->cmdlist); + layer_criteria_destroy(criteria); + return cmd_results_new(CMD_SUCCESS, NULL); + } + + list_add(config->layer_criteria, criteria); + sway_log(SWAY_DEBUG, "layer_effect: '%s' '%s' added", criteria->namespace, criteria->cmdlist); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/shadows.c b/sway/commands/shadows.c index 2b57b2e6..ae7fc019 100644 --- a/sway/commands/shadows.c +++ b/sway/commands/shadows.c @@ -17,7 +17,7 @@ struct cmd_results *cmd_shadows(int argc, char **argv) { struct sway_container *con = config->handler_context.container; - bool result = parse_boolean(argv[0], config->shadow_enabled); + bool result = parse_boolean(argv[0], true); if (con == NULL) { config->shadow_enabled = result; } else { diff --git a/sway/config.c b/sway/config.c index 04c75171..fbcf94a4 100644 --- a/sway/config.c +++ b/sway/config.c @@ -21,6 +21,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/criteria.h" +#include "sway/layer_criteria.h" #include "sway/desktop/transaction.h" #include "sway/swaynag.h" #include "sway/tree/arrange.h" @@ -157,6 +158,12 @@ void free_config(struct sway_config *config) { } list_free(config->criteria); } + if (config->layer_criteria) { + for (int i = 0; i < config->layer_criteria->length; ++i) { + layer_criteria_destroy(config->layer_criteria->items[i]); + } + list_free(config->layer_criteria); + } list_free(config->no_focus); list_free(config->active_bar_modifiers); list_free_items_and_destroy(config->config_chain); @@ -354,6 +361,8 @@ static void config_defaults(struct sway_config *config) { config->titlebar_separator = true; config->scratchpad_minimize = true; + if (!(config->layer_criteria = create_list())) goto cleanup; + // The keysym to keycode translation struct xkb_rule_names rules = {0}; config->keysym_translation_state = @@ -1092,3 +1101,7 @@ int config_get_blur_size() { bool config_should_parameters_blur() { return config->blur_params.radius > 0 && config->blur_params.num_passes > 0; } + +bool config_should_parameters_shadow() { + return config->shadow_blur_sigma > 0 && config->shadow_color[3] > 0.0; +} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index a8356ad7..bbc2e6f3 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -6,6 +6,7 @@ #include #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" @@ -15,6 +16,21 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" + +static void layer_parse_criteria(struct sway_layer_surface *sway_layer) { + enum zwlr_layer_shell_v1_layer layer = sway_layer->layer; + if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { + return; + } + + list_t *criterias = layer_criterias_for_sway_layer_surface(sway_layer); + for (int i = 0; i < criterias->length; i++) { + struct layer_criteria *criteria = criterias->items[i]; + layer_criteria_parse(sway_layer, criteria); + } + list_free(criterias); +} static void apply_exclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, @@ -306,6 +322,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { wl_list_insert(&output->layers[layer_surface->current.layer], &layer->link); layer->layer = layer_surface->current.layer; + layer_parse_criteria(layer); } arrange_layers(output); } @@ -393,6 +410,7 @@ static void handle_map(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = sway_layer->layer_surface->output; sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; + layer_parse_criteria(sway_layer); output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, sway_layer->layer_surface->surface, true); wlr_surface_send_enter(sway_layer->layer_surface->surface, @@ -685,6 +703,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_layer->layer_surface = layer_surface; layer_surface->data = sway_layer; + sway_layer->has_blur = false; + sway_layer->has_shadow = false; + sway_layer->corner_radius = 0; + struct sway_output *output = layer_surface->output->data; sway_layer->output_destroy.notify = handle_output_destroy; wl_signal_add(&output->events.disable, &sway_layer->output_destroy); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c3c240f8..7ea6a6e7 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -199,6 +199,13 @@ void output_layer_for_each_toplevel_surface(struct sway_output *output, wl_list_for_each(layer_surface, layer_surfaces, link) { struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = layer_surface->layer_surface; + + struct render_data *data = user_data; + data->deco_data.blur = layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND ? + layer_surface->has_blur : false; + data->deco_data.shadow = layer_surface->has_shadow; + data->deco_data.corner_radius = layer_surface->corner_radius; + output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, layer_surface->geo.x, layer_surface->geo.y, iterator, user_data); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index b01ca5e1..6efeaf5d 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -23,7 +23,6 @@ #include "sway/desktop/fx_renderer/fx_renderer.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" -#include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -32,12 +31,6 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" -struct render_data { - pixman_region32_t *damage; - struct wlr_box *clip_box; - struct decoration_data deco_data; -}; - struct decoration_data get_undecorated_decoration_data() { return (struct decoration_data) { .alpha = 1.0f, @@ -47,10 +40,10 @@ struct decoration_data get_undecorated_decoration_data() { .saturation = 1.0f, .has_titlebar = false, .blur = false, + .shadow = false, }; } - // TODO: contribute wlroots function to allow creating an fbox from a box? struct wlr_fbox wlr_fbox_from_wlr_box(struct wlr_box *box) { return (struct wlr_fbox) { @@ -357,6 +350,61 @@ damage_finish: pixman_region32_fini(&inverse_opaque); } +// _box.x and .y are expected to be layout-local +// _box.width and .height are expected to be output-buffer-local +void render_box_shadow(struct sway_output *output, pixman_region32_t *output_damage, + const struct wlr_box *_box, const float color[static 4], + float blur_sigma, float corner_radius) { + struct wlr_output *wlr_output = output->wlr_output; + struct fx_renderer *renderer = output->renderer; + + struct wlr_box box; + memcpy(&box, _box, sizeof(struct wlr_box)); + box.x -= blur_sigma; + box.y -= blur_sigma; + box.width += 2 * blur_sigma; + box.height += 2 * blur_sigma; + + pixman_region32_t damage = create_damage(box, output_damage); + + // don't damage area behind window since we dont render it anyway + struct wlr_box inner_box; + memcpy(&inner_box, _box, sizeof(struct wlr_box)); + inner_box.x += corner_radius; + inner_box.y += corner_radius; + inner_box.width -= 2 * corner_radius; + inner_box.height -= 2 * corner_radius; + pixman_region32_t inner_damage = create_damage(inner_box, output_damage); + pixman_region32_subtract(&damage, &damage, &inner_damage); + + bool damaged = pixman_region32_not_empty(&damage); + if (!damaged) { + goto damage_finish; + } + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + wlr_output->transform_matrix); + + // ensure the box is updated as per the output orientation + struct wlr_box transformed_box; + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + wlr_box_transform(&transformed_box, &box, + wlr_output_transform_invert(wlr_output->transform), width, height); + + int nrects; + pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); + for (int i = 0; i < nrects; ++i) { + scissor_output(wlr_output, &rects[i]); + + fx_render_box_shadow(renderer, &transformed_box, color, matrix, + corner_radius, blur_sigma); + } + +damage_finish: + pixman_region32_fini(&damage); +} static void render_surface_iterator(struct sway_output *output, struct sway_view *view, struct wlr_surface *surface, @@ -421,6 +469,7 @@ static void render_surface_iterator(struct sway_output *output, pixman_region32_fini(&opaque_region); } + // Render surface texture struct wlr_fbox src_box; wlr_surface_get_buffer_source_box(surface, &src_box); struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture); @@ -431,6 +480,30 @@ static void render_surface_iterator(struct sway_output *output, wlr_output); } +// view will be NULL every time +static void render_layer_iterator(struct sway_output *output, + struct sway_view *view, struct wlr_surface *surface, + struct wlr_box *_box, void *_data) { + struct render_data *data = _data; + struct decoration_data deco_data = data->deco_data; + + // Ignore effects if this is a subsurface + if (wl_list_length(&surface->current.subsurfaces_above) > 0) { + deco_data = get_undecorated_decoration_data(); + } + + // render the layer's surface + render_surface_iterator(output, view, surface, _box, _data); + + // render shadow + if (deco_data.shadow && config_should_parameters_shadow()) { + int corner_radius = deco_data.corner_radius *= output->wlr_output->scale; + scale_box(_box, output->wlr_output->scale); + render_box_shadow(output, data->damage, _box, config->shadow_color, + config->shadow_blur_sigma, corner_radius); + } +} + static void render_layer_toplevel(struct sway_output *output, pixman_region32_t *damage, struct wl_list *layer_surfaces) { struct render_data data = { @@ -438,7 +511,7 @@ static void render_layer_toplevel(struct sway_output *output, .deco_data = get_undecorated_decoration_data(), }; output_layer_for_each_toplevel_surface(output, layer_surfaces, - render_surface_iterator, &data); + render_layer_iterator, &data); } static void render_layer_popups(struct sway_output *output, @@ -448,7 +521,7 @@ static void render_layer_popups(struct sway_output *output, .deco_data = get_undecorated_decoration_data(), }; output_layer_for_each_popup_surface(output, layer_surfaces, - render_surface_iterator, &data); + render_layer_iterator, &data); } #if HAVE_XWAYLAND @@ -633,62 +706,6 @@ damage_finish: pixman_region32_fini(&damage); } -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_box_shadow(struct sway_output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, const float color[static 4], - float blur_sigma, float corner_radius) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= blur_sigma; - box.y -= blur_sigma; - box.width += 2 * blur_sigma; - box.height += 2 * blur_sigma; - - pixman_region32_t damage = create_damage(box, output_damage); - - // don't damage area behind window since we dont render it anyway - struct wlr_box inner_box; - memcpy(&inner_box, _box, sizeof(struct wlr_box)); - inner_box.x += corner_radius; - inner_box.y += corner_radius; - inner_box.width -= 2 * corner_radius; - inner_box.height -= 2 * corner_radius; - pixman_region32_t inner_damage = create_damage(inner_box, output_damage); - pixman_region32_subtract(&damage, &damage, &inner_damage); - - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, &box, - wlr_output_transform_invert(wlr_output->transform), width, height); - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - - fx_render_box_shadow(renderer, &transformed_box, color, matrix, - corner_radius, blur_sigma); - } - -damage_finish: - pixman_region32_fini(&damage); -} - void premultiply_alpha(float color[4], float opacity) { color[3] *= opacity; color[0] *= color[3]; @@ -807,6 +824,7 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output } } + // Render saved surface texture struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture); render_texture(wlr_output, damage, &fx_texture, &saved_buf->source_box, &dst_box, matrix, deco_data); @@ -833,7 +851,6 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, render_view_toplevels(view, output, damage, deco_data); } - // Only draw shadows on CSD windows if shadows_on_csd is enabled if (state->border == B_CSD && !config->shadows_on_csd_enabled) { return; } @@ -1404,6 +1421,7 @@ static void render_containers_linear(struct sway_output *output, .saturation = child->saturation, .has_titlebar = has_titlebar, .blur = child->blur_enabled, + .shadow = child->shadow_enabled, }; render_view(output, damage, child, colors, deco_data); if (has_titlebar) { @@ -1453,6 +1471,7 @@ static void render_containers_tabbed(struct sway_output *output, .saturation = current->saturation, .has_titlebar = true, .blur = current->blur_enabled, + .shadow = current->shadow_enabled, }; // Render tabs @@ -1548,6 +1567,7 @@ static void render_containers_stacked(struct sway_output *output, ? 0 : current->corner_radius, .has_titlebar = true, .blur = current->blur_enabled, + .shadow = current->shadow_enabled, }; // Render titles @@ -1695,6 +1715,7 @@ static void render_floating_container(struct sway_output *soutput, .corner_radius = con->corner_radius, .has_titlebar = has_titlebar, .blur = con->blur_enabled, + .shadow = con->shadow_enabled, }; render_view(soutput, damage, con, colors, deco_data); if (has_titlebar) { @@ -1927,6 +1948,7 @@ void output_render(struct sway_output *output, struct timespec *when, .saturation = focus->saturation, .has_titlebar = false, .blur = false, + .shadow = false, }; render_view_popups(focus->view, output, damage, deco_data); } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index cd79e1c8..717ebf78 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -10,6 +10,7 @@ #include "log.h" #include "sway/config.h" #include "sway/ipc-json.h" +#include "sway/layers.h" #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -273,7 +274,8 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name, return object; } -static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) { +static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, + struct sway_output *output, json_object *object) { json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "make", json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown")); @@ -299,7 +301,7 @@ static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_obj static void ipc_json_describe_output(struct sway_output *output, json_object *object) { - ipc_json_describe_wlr_output(output->wlr_output, object); + ipc_json_describe_wlr_output(output->wlr_output, output, object); } static void ipc_json_describe_enabled_output(struct sway_output *output, @@ -331,6 +333,63 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, json_object_object_add(object, "adaptive_sync_status", json_object_new_string(adaptive_sync_status)); + struct json_object *layers = json_object_new_array(); + size_t len = sizeof(output->layers) / sizeof(output->layers[0]); + for (size_t i = 0; i < len; ++i) { + struct sway_layer_surface *lsurface; + wl_list_for_each(lsurface, &output->layers[i], link) { + json_object *layer = json_object_new_object(); + + json_object_object_add(layer, "namespace", + json_object_new_string(lsurface->layer_surface->namespace)); + + char *layer_name; + switch (lsurface->layer) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + layer_name = "background"; + break; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + layer_name = "bottom"; + break; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + layer_name = "top"; + break; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + layer_name = "overlay"; + break; + } + + json_object_object_add(layer, "layer", + json_object_new_string(layer_name)); + + json_object *extent = json_object_new_object(); + json_object_object_add(extent, "width", + json_object_new_int(lsurface->extent.width)); + json_object_object_add(extent, "height", + json_object_new_int(lsurface->extent.height)); + json_object_object_add(extent, "x", + json_object_new_int(lsurface->extent.x)); + json_object_object_add(extent, "y", + json_object_new_int(lsurface->extent.y)); + json_object_object_add(layer, "extent", extent); + + json_object *effects = json_object_new_array(); + if (lsurface->has_blur) { + json_object_array_add(effects, json_object_new_string("blur")); + } + if (lsurface->has_shadow) { + json_object_array_add(effects, json_object_new_string("shadows")); + } + if (lsurface->corner_radius > 0) { + json_object_array_add(effects, json_object_new_string("corner_radius")); + } + json_object_object_add(layer, "effects", effects); + + json_object_array_add(layers, layer); + } + } + json_object_object_add(object, "layer_shell_surfaces", layers); + struct sway_workspace *ws = output_get_active_workspace(output); if (!sway_assert(ws, "Expected output to have a workspace")) { return; @@ -413,7 +472,7 @@ json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop json_object *object = json_object_new_object(); - ipc_json_describe_wlr_output(wlr_output, object); + ipc_json_describe_wlr_output(wlr_output, NULL, object); json_object_object_add(object, "non_desktop", json_object_new_boolean(true)); json_object_object_add(object, "type", json_object_new_string("output")); @@ -1053,9 +1112,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { struct xkb_keymap *keymap = keyboard->keymap; struct xkb_state *state = keyboard->xkb_state; - json_object_object_add(object, "repeat_delay", + json_object_object_add(object, "repeat_delay", json_object_new_int(keyboard->repeat_info.delay)); - json_object_object_add(object, "repeat_rate", + json_object_object_add(object, "repeat_rate", json_object_new_int(keyboard->repeat_info.rate)); json_object *layouts_arr = json_object_new_array(); diff --git a/sway/layer_criteria.c b/sway/layer_criteria.c new file mode 100644 index 00000000..694c5016 --- /dev/null +++ b/sway/layer_criteria.c @@ -0,0 +1,88 @@ +#include +#include "log.h" +#include "stringop.h" +#include "sway/commands.h" +#include "sway/layer_criteria.h" +#include "util.h" + +void layer_criteria_destroy(struct layer_criteria *criteria) { + free(criteria->namespace); + free(criteria->cmdlist); + free(criteria); +} + +bool layer_criteria_is_equal(struct layer_criteria *a, struct layer_criteria *b) { + return strcmp(a->namespace, b->namespace) == 0 + && strcmp(a->cmdlist, b->cmdlist) == 0; +} + +bool layer_criteria_already_exists(struct layer_criteria *criteria) { + list_t *criterias = config->layer_criteria; + for (int i = 0; i < criterias->length; ++i) { + struct layer_criteria *existing = criterias->items[i]; + if (layer_criteria_is_equal(criteria, existing)) { + return true; + } + } + return false; +} + +list_t *layer_criterias_for_sway_layer_surface(struct sway_layer_surface *sway_layer) { + list_t *criterias = config->layer_criteria; + list_t *matches = create_list(); + for (int i = 0; i < criterias->length; ++i) { + struct layer_criteria *criteria = criterias->items[i]; + if (strcmp(criteria->namespace, sway_layer->layer_surface->namespace) == 0) { + list_add(matches, criteria); + } + } + return matches; +} + +void layer_criteria_parse(struct sway_layer_surface *sway_layer, struct layer_criteria *criteria) { + char matched_delim = ';'; + char *head = malloc(strlen(criteria->cmdlist) + 1); + strcpy(head, criteria->cmdlist); + do { + // Trim leading whitespaces + for (; isspace(*head); ++head) {} + // Split command list + char *cmd = argsep(&head, ";,", &matched_delim); + for (; isspace(*cmd); ++cmd) {} + + if (strcmp(cmd, "") == 0) { + sway_log(SWAY_INFO, "Ignoring empty layer effect."); + continue; + } + sway_log(SWAY_INFO, "Handling layer effect '%s'", cmd); + + int argc; + char **argv = split_args(cmd, &argc); + // Strip all quotes from each token + for (int i = 1; i < argc; ++i) { + if (*argv[i] == '\"' || *argv[i] == '\'') { + strip_quotes(argv[i]); + } + } + if (strcmp(argv[0], "blur") == 0) { + sway_layer->has_blur = parse_boolean(argv[1], true); + continue; + } else if (strcmp(argv[0], "shadows") == 0) { + sway_layer->has_shadow = parse_boolean(argv[1], true); + continue; + } else if (strcmp(argv[0], "corner_radius") == 0) { + int value; + if (cmd_corner_radius_parse_value(argv[1], &value)) { + sway_layer->corner_radius = value; + continue; + } + sway_log(SWAY_ERROR, + "Invalid layer_effects corner_radius size! Got \"%s\"", + argv[1]); + return; + } else { + sway_log(SWAY_ERROR, "Invalid layer_effects effect! Got \"%s\"", cmd); + return; + } + } while(head); +} diff --git a/sway/meson.build b/sway/meson.build index 54e41072..2e1c5d20 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -5,6 +5,7 @@ sway_sources = files( 'decoration.c', 'ipc-json.c', 'ipc-server.c', + 'layer_criteria.c', 'lock.c', 'main.c', 'realtime.c', @@ -91,6 +92,7 @@ sway_sources = files( 'commands/include.c', 'commands/input.c', 'commands/layout.c', + 'commands/layer_effects.c', 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index f2ab30f7..2607c15c 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -230,6 +230,11 @@ following properties: : string : The transform currently in use for the output. This can be _normal_, _90_, _180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_ +|- layer_shell_surfaces +: array +: An array of all layer-shell surfaces attached to the output. Each object + contains _namespace_, _layer_, _effects_, and _extent_ object that contains _x_, _y_ + _width_, and _height_ |- current_workspace : string : The workspace currently visible on the output or _null_ for disabled outputs @@ -259,6 +264,35 @@ following properties: "scale": 1.0, "subpixel_hinting": "rgb", "transform": "normal", + "layer_shell_surfaces": [ + { + "namespace": "wallpaper", + "layer": "background", + "extent": { + "width": 2560, + "height": 1440, + "x": 0, + "y": 0 + }, + "effects": [ + ] + }, + { + "namespace": "waybar", + "layer": "top", + "extent": { + "width": 2548, + "height": 31, + "x": 6, + "y": 6 + }, + "effects": [ + "blur", + "shadow", + "corner_rounding" + ] + } + ], "current_workspace": "1", "modes": [ { diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 2288346b..06c27900 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -787,6 +787,25 @@ The default colors are: Whenever a window that matches _criteria_ appears, run list of commands. See *CRITERIA* for more details. +*layer_effects* + 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 diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 161b1e9c..8bc62f3f 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -5,11 +5,13 @@ #include #include #include +#include #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,