Add blur, shadow, and corner radius to layer-shell surfaces (#144)
Co-authored-by: Will McKinnon <contact@willmckinnon.com>
This commit is contained in:
parent
6707842942
commit
415e072a3a
|
@ -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>`
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ struct decoration_data {
|
|||
float *dim_color;
|
||||
bool has_titlebar;
|
||||
bool blur;
|
||||
bool shadow;
|
||||
};
|
||||
|
||||
struct blur_shader {
|
||||
|
|
20
include/sway/layer_criteria.h
Normal file
20
include/sway/layer_criteria.h
Normal 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);
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
33
sway/commands/layer_effects.c
Normal file
33
sway/commands/layer_effects.c
Normal 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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
88
sway/layer_criteria.c
Normal file
88
sway/layer_criteria.c
Normal 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);
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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": [
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue