diff options
29 files changed, 1066 insertions, 107 deletions
@@ -19,6 +19,11 @@ Sway is an incredible window manager, and certainly one of the most well establi      - `shadows_on_csd enable|disable` (**Note**: The shadow might not fit some windows)      - `shadow_blur_radius <integer value 0 - 100>`      - `shadow_color <hex color with alpha> ex, #0000007F` ++ Window blur: *ONLY ON SWAYFX-GIT, NOT YET RELEASED* +    - `blur enable|disable` +    - `blur_xray enable|disable` +    - `blur_passes <integer value 0 - 10>` +    - `blur_radius <integer value 0 - 10>`  + Dim unfocused windows:      - `default_dim_inactive <float value 0.0 - 1.0>`      - `for_window [CRITERIA_HERE] dim_inactive <float value 0.0 - 1.0>` @@ -24,6 +24,12 @@ set $menu dmenu_path | dmenu | xargs swaymsg exec --  # window corner radius in px  corner_radius 10 +# Window background blur +blur off +blur_xray off +blur_passes 2 +blur_radius 5 +  shadows off  shadows_on_csd off  shadow_blur_radius 20 diff --git a/include/sway/commands.h b/include/sway/commands.h index 91b1fc58..b895d5f2 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -109,6 +109,10 @@ sway_cmd cmd_bindcode;  sway_cmd cmd_bindgesture;  sway_cmd cmd_bindswitch;  sway_cmd cmd_bindsym; +sway_cmd cmd_blur; +sway_cmd cmd_blur_passes; +sway_cmd cmd_blur_radius; +sway_cmd cmd_blur_xray;  sway_cmd cmd_border;  sway_cmd cmd_client_noop;  sway_cmd cmd_client_focused; diff --git a/include/sway/config.h b/include/sway/config.h index 2dc4b52d..cabc9cf5 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -470,23 +470,33 @@ enum xwayland_mode {  	XWAYLAND_MODE_IMMEDIATE,  }; +struct blur_parameters { +	int num_passes; +	int radius; +}; +  /**   * The configuration struct. The result of loading a config file.   */  struct sway_config { -	// SwayFX config options  	int corner_radius;  	bool smart_corner_radius; +  	float default_dim_inactive; -	// dim_inactive colors  	struct {  		float unfocused[4];  		float urgent[4];  	} dim_inactive_colors; +  	bool shadow_enabled;  	bool shadows_on_csd_enabled;  	int shadow_blur_sigma;  	float shadow_color[4]; + +	bool blur_enabled; +	bool blur_xray; +	struct blur_parameters blur_params; +  	bool titlebar_separator;  	bool scratchpad_minimize; diff --git a/include/sway/desktop/fx_renderer/fx_framebuffer.h b/include/sway/desktop/fx_renderer/fx_framebuffer.h new file mode 100644 index 00000000..965c1def --- /dev/null +++ b/include/sway/desktop/fx_renderer/fx_framebuffer.h @@ -0,0 +1,23 @@ +#ifndef FX_FRAMEBUFFER_H +#define FX_FRAMEBUFFER_H + +#include <GLES2/gl2.h> +#include <stdbool.h> +#include <wlr/types/wlr_output.h> + +#include "sway/desktop/fx_renderer/fx_texture.h" + +struct fx_framebuffer { +	struct fx_texture texture; +	GLuint fb; +}; + +void fx_framebuffer_bind(struct fx_framebuffer *buffer, GLsizei width, GLsizei height); + +void fx_framebuffer_create(struct wlr_output *output, struct fx_framebuffer *buffer, +		bool bind); + +void fx_framebuffer_release(struct fx_framebuffer *buffer); + + +#endif diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h index a48a00e1..37887d30 100644 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ b/include/sway/desktop/fx_renderer/fx_renderer.h @@ -5,6 +5,9 @@  #include <GLES2/gl2ext.h>  #include <stdbool.h> +#include "sway/desktop/fx_renderer/fx_framebuffer.h" +#include "sway/desktop/fx_renderer/fx_texture.h" +  enum corner_location { ALL, TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, NONE };  enum fx_tex_shader_source { @@ -24,8 +27,9 @@ struct decoration_data {  	float saturation;  	int corner_radius;  	float dim; -	float* dim_color; +	float *dim_color;  	bool has_titlebar; +	bool blur;  };  struct gles2_tex_shader { @@ -54,13 +58,34 @@ struct rounded_quad_shader {  	GLint radius;  }; +struct blur_shader { +	GLuint program; +	GLint proj; +	GLint tex; +	GLint pos_attrib; +	GLint tex_attrib; +	GLint radius; +	GLint halfpixel; +}; +  struct fx_renderer {  	struct wlr_egl *egl;  	float projection[9]; +	struct sway_output *sway_output; +  	GLuint stencil_buffer_id; +	struct fx_framebuffer wlr_buffer; // Just the framebuffer used by wlroots +	struct fx_framebuffer main_buffer; // The main FB used for rendering +	struct fx_framebuffer blur_buffer; // Contains the blurred background for tiled windows +	// Blur swaps between the two effects buffers everytime it scales the image +	struct fx_framebuffer effects_buffer; // Buffer used for effects +	struct fx_framebuffer effects_buffer_swapped; // Swap buffer used for effects + +	bool blur_buffer_dirty; +  	struct {  		bool OES_egl_image_external;  	} exts; @@ -83,6 +108,9 @@ struct fx_renderer {  		struct rounded_quad_shader rounded_tl_quad;  		struct rounded_quad_shader rounded_tr_quad; +		struct blur_shader blur1; +		struct blur_shader blur2; +  		struct {  			GLuint program;  			GLint proj; @@ -117,19 +145,21 @@ struct fx_renderer {  struct fx_renderer *fx_renderer_create(struct wlr_egl *egl); -void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t height); +void fx_renderer_fini(struct fx_renderer *renderer); + +void fx_renderer_begin(struct fx_renderer *renderer, struct sway_output *output); -void fx_renderer_end(); +void fx_renderer_end(struct fx_renderer *renderer);  void fx_renderer_clear(const float color[static 4]);  void fx_renderer_scissor(struct wlr_box *box); -bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, +bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,  		const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],  		struct decoration_data deco_data); -bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, +bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,  		const struct wlr_box *dst_box, const float matrix[static 9], struct decoration_data deco_data);  void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, @@ -146,4 +176,8 @@ void fx_render_border_corner(struct fx_renderer *renderer, const struct wlr_box  void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *box,  		const float color[static 4], const float projection[static 9], int radius, float blur_sigma); +void fx_render_blur(struct fx_renderer *renderer, struct sway_output *output, +		const float matrix[static 9], struct fx_framebuffer **buffer, +		struct blur_shader *shader, const struct wlr_box *box, int blur_radius); +  #endif diff --git a/include/sway/desktop/fx_renderer/fx_texture.h b/include/sway/desktop/fx_renderer/fx_texture.h new file mode 100644 index 00000000..0c375913 --- /dev/null +++ b/include/sway/desktop/fx_renderer/fx_texture.h @@ -0,0 +1,18 @@ +#ifndef FX_TEXTURE_H +#define FX_TEXTURE_H + +#include <GLES2/gl2.h> +#include <stdbool.h> +#include <wlr/render/wlr_texture.h> + +struct fx_texture { +	GLuint target; +	GLuint id; +	bool has_alpha; +	int width; +	int height; +}; + +struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture* tex); + +#endif diff --git a/include/sway/output.h b/include/sway/output.h index a6bec10a..65f7ca1a 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -24,6 +24,8 @@ struct sway_output {  	struct sway_server *server;  	struct wl_list link; +	struct fx_renderer *renderer; +  	struct wl_list layers[4]; // sway_layer_surface::link  	struct wlr_box usable_area; diff --git a/include/sway/server.h b/include/sway/server.h index 0c2eccf3..96c3623f 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -37,7 +37,6 @@ struct sway_server {  	// secondary headless backend used for creating virtual outputs on-the-fly  	struct wlr_backend *headless_backend;  	struct wlr_renderer *wlr_renderer; -	struct fx_renderer *renderer;  	struct wlr_allocator *allocator;  	struct wlr_compositor *compositor; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 1d8e5a36..3cd668f9 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -115,6 +115,8 @@ struct sway_container {  	bool shadow_enabled; +	bool blur_enabled; +  	float saturation;  	float alpha; diff --git a/sway/commands.c b/sway/commands.c index fbe17039..8e2d8f89 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -49,6 +49,10 @@ static const struct cmd_handler handlers[] = {  	{ "bindgesture", cmd_bindgesture },  	{ "bindswitch", cmd_bindswitch },  	{ "bindsym", cmd_bindsym }, +	{ "blur", cmd_blur }, +	{ "blur_passes", cmd_blur_passes }, +	{ "blur_radius", cmd_blur_radius }, +	{ "blur_xray", cmd_blur_xray },  	{ "client.background", cmd_client_noop },  	{ "client.focused", cmd_client_focused },  	{ "client.focused_inactive", cmd_client_focused_inactive }, diff --git a/sway/commands/blur.c b/sway/commands/blur.c new file mode 100644 index 00000000..15dd985d --- /dev/null +++ b/sway/commands/blur.c @@ -0,0 +1,31 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" +#include "util.h" + +struct cmd_results *cmd_blur(int argc, char **argv) { +	struct cmd_results *error = checkarg(argc, "blur", EXPECTED_AT_LEAST, 1); + +	if (error) { +		return error; +	} + +	struct sway_container *con = config->handler_context.container; + +	bool result = parse_boolean(argv[0], config->blur_enabled); +	if (con == NULL) { +		config->blur_enabled = result; +	} else { +		con->blur_enabled = result; +	} + +	struct sway_output *output; +	wl_list_for_each(output, &root->all_outputs, link) { +		if (output->renderer) { +			output->renderer->blur_buffer_dirty = true; +			output_damage_whole(output); +		} +	} + +	return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/blur_passes.c b/sway/commands/blur_passes.c new file mode 100644 index 00000000..0868a568 --- /dev/null +++ b/sway/commands/blur_passes.c @@ -0,0 +1,28 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *cmd_blur_passes(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "blur_passes", EXPECTED_EQUAL_TO, 1))) { +		return error; +	} + +	char *inv; +	int value = strtol(argv[0], &inv, 10); +	if (*inv != '\0' || value < 0 || value > 10) { +		return cmd_results_new(CMD_FAILURE, "Invalid size specified"); +	} + +	config->blur_params.num_passes = value; + +	struct sway_output *output; +	wl_list_for_each(output, &root->all_outputs, link) { +		if (output->renderer) { +			output->renderer->blur_buffer_dirty = true; +			output_damage_whole(output); +		} +	} + +	return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/blur_radius.c b/sway/commands/blur_radius.c new file mode 100644 index 00000000..f6e7d4ed --- /dev/null +++ b/sway/commands/blur_radius.c @@ -0,0 +1,28 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *cmd_blur_radius(int argc, char **argv) { +	struct cmd_results *error = NULL; +	if ((error = checkarg(argc, "blur_radius", EXPECTED_AT_LEAST, 1))) { +		return error; +	} + +	char *inv; +	int value = strtol(argv[0], &inv, 10); +	if (*inv != '\0' || value < 0 || value > 10) { +		return cmd_results_new(CMD_FAILURE, "Invalid size specified"); +	} + +	config->blur_params.radius = value; + +	struct sway_output *output; +	wl_list_for_each(output, &root->all_outputs, link) { +		if (output->renderer) { +			output->renderer->blur_buffer_dirty = true; +			output_damage_whole(output); +		} +	} + +	return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/blur_xray.c b/sway/commands/blur_xray.c new file mode 100644 index 00000000..045566d0 --- /dev/null +++ b/sway/commands/blur_xray.c @@ -0,0 +1,25 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" +#include "util.h" + +struct cmd_results *cmd_blur_xray(int argc, char **argv) { +	struct cmd_results *error = checkarg(argc, "blur_xray", EXPECTED_AT_LEAST, 1); + +	if (error) { +		return error; +	} + +	bool result = parse_boolean(argv[0], config->blur_xray); +	config->blur_xray = result; + +	struct sway_output *output; +	wl_list_for_each(output, &root->all_outputs, link) { +		if (output->renderer) { +			output->renderer->blur_buffer_dirty = true; +			output_damage_whole(output); +		} +	} + +	return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/config.c b/sway/config.c index 24022762..85e53679 100644 --- a/sway/config.c +++ b/sway/config.c @@ -336,13 +336,21 @@ static void config_defaults(struct sway_config *config) {  	// SwayFX defaults  	config->corner_radius = 0;  	config->smart_corner_radius = true; +  	config->default_dim_inactive = 0.0f;  	color_to_rgba(config->dim_inactive_colors.unfocused, 0x000000FF);  	color_to_rgba(config->dim_inactive_colors.urgent, 0x900000FF); +  	config->shadow_enabled = false;  	config->shadows_on_csd_enabled = false;  	config->shadow_blur_sigma = 20.0f;  	color_to_rgba(config->shadow_color, 0x0000007F); + +	config->blur_enabled = false; +	config->blur_xray = false; +	config->blur_params.num_passes = 2; +	config->blur_params.radius = 5; +  	config->titlebar_separator = true;  	config->scratchpad_minimize = true; diff --git a/sway/desktop/fx_renderer/fx_framebuffer.c b/sway/desktop/fx_renderer/fx_framebuffer.c new file mode 100644 index 00000000..db6f8928 --- /dev/null +++ b/sway/desktop/fx_renderer/fx_framebuffer.c @@ -0,0 +1,70 @@ +#include "log.h" +#include "sway/desktop/fx_renderer/fx_framebuffer.h" + +void fx_framebuffer_bind(struct fx_framebuffer *buffer, GLsizei width, GLsizei height) { +	glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb); +	glViewport(0, 0, width, height); +} + +void fx_framebuffer_create(struct wlr_output *output, struct fx_framebuffer *buffer, bool bind) { +	bool firstAlloc = false; + +	// Create a new framebuffer +	if (buffer->fb == (uint32_t) -1) { +		glGenFramebuffers(1, &buffer->fb); +		firstAlloc = true; +	} + +	if (buffer->texture.id == 0) { +		firstAlloc = true; +		glGenTextures(1, &buffer->texture.id); +		glBindTexture(GL_TEXTURE_2D, buffer->texture.id); +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +	} + +	int width, height; +	wlr_output_transformed_resolution(output, &width, &height); + +	if (firstAlloc || buffer->texture.width != width || buffer->texture.height != height) { +		glBindTexture(GL_TEXTURE_2D, buffer->texture.id); +		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + +		glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb); +		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, +				buffer->texture.id, 0); +		buffer->texture.target = GL_TEXTURE_2D; +		buffer->texture.has_alpha = false; +		buffer->texture.width = width; +		buffer->texture.height = height; + +		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); +		if (status != GL_FRAMEBUFFER_COMPLETE) { +			sway_log(SWAY_ERROR, "Framebuffer incomplete, couldn't create! (FB status: %i)", status); +			return; +		} +		sway_log(SWAY_DEBUG, "Framebuffer created, status %i", status); +	} + +	// Bind the default framebuffer +	glBindTexture(GL_TEXTURE_2D, 0); +	if (bind) { +		fx_framebuffer_bind(buffer, width, height); +	} +} + +void fx_framebuffer_release(struct fx_framebuffer *buffer) { +	if (buffer->fb != (uint32_t) -1 && buffer->fb) { +		glDeleteFramebuffers(1, &buffer->fb); +	} +	buffer->fb= -1; + +	if (buffer->texture.id) { +		glDeleteTextures(1, &buffer->texture.id); +	} +	buffer->texture.id = 0; +	buffer->texture.width = -1; +	buffer->texture.height = -1; +} diff --git a/sway/desktop/fx_renderer/fx_renderer.c b/sway/desktop/fx_renderer/fx_renderer.c index 36c5bb61..715c5b15 100644 --- a/sway/desktop/fx_renderer/fx_renderer.c +++ b/sway/desktop/fx_renderer/fx_renderer.c @@ -19,6 +19,8 @@  #include "sway/server.h"  // shaders +#include "blur1_frag_src.h" +#include "blur2_frag_src.h"  #include "box_shadow_frag_src.h"  #include "common_vert_src.h"  #include "corner_frag_src.h" @@ -33,6 +35,33 @@ static const GLfloat verts[] = {  	0, 1, // bottom left  }; +static void create_stencil_buffer(struct wlr_output* output, GLuint *buffer_id) { +	if (*buffer_id != (uint32_t) -1) { +		return; +	} + +	int width, height; +	wlr_output_transformed_resolution(output, &width, &height); + +	glGenRenderbuffers(1, buffer_id); +	glBindRenderbuffer(GL_RENDERBUFFER, *buffer_id); +	glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); +	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *buffer_id); +	GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); +	if (status != GL_FRAMEBUFFER_COMPLETE) { +		sway_log(SWAY_ERROR, "Stencilbuffer incomplete, couldn't create! (FB status: %i)", status); +		return; +	} +	sway_log(SWAY_DEBUG, "Stencilbuffer created, status %i", status); +} + +static void release_stencil_buffer(GLuint *buffer_id) { +	if (*buffer_id != (uint32_t)-1 && buffer_id) { +		glDeleteRenderbuffers(1, buffer_id); +	} +	*buffer_id = -1; +} +  static GLuint compile_shader(GLuint type, const GLchar *src) {  	GLuint shader = glCreateShader(type);  	glShaderSource(shader, 1, &src, NULL); @@ -179,6 +208,15 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {  	// TODO: needed?  	renderer->egl = egl; +	renderer->main_buffer.fb = -1; + +	renderer->blur_buffer.fb = -1; +	renderer->effects_buffer.fb = -1; +	renderer->effects_buffer_swapped.fb = -1; +	renderer->stencil_buffer_id = -1; + +	renderer->blur_buffer_dirty = true; +  	// get extensions  	const char *exts_str = (const char *)glGetString(GL_EXTENSIONS);  	if (exts_str == NULL) { @@ -258,6 +296,32 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {  	renderer->shaders.box_shadow.blur_sigma = glGetUniformLocation(prog, "blur_sigma");  	renderer->shaders.box_shadow.corner_radius = glGetUniformLocation(prog, "corner_radius"); +	// Blur 1 +	prog = link_program(blur1_frag_src); +	renderer->shaders.blur1.program = prog; +	if (!renderer->shaders.blur1.program) { +		goto error; +	} +	renderer->shaders.blur1.proj = glGetUniformLocation(prog, "proj"); +	renderer->shaders.blur1.tex = glGetUniformLocation(prog, "tex"); +	renderer->shaders.blur1.pos_attrib = glGetAttribLocation(prog, "pos"); +	renderer->shaders.blur1.tex_attrib = glGetAttribLocation(prog, "texcoord"); +	renderer->shaders.blur1.radius = glGetUniformLocation(prog, "radius"); +	renderer->shaders.blur1.halfpixel = glGetUniformLocation(prog, "halfpixel"); + +	// Blur 2 +	prog = link_program(blur2_frag_src); +	renderer->shaders.blur2.program = prog; +	if (!renderer->shaders.blur2.program) { +		goto error; +	} +	renderer->shaders.blur2.proj = glGetUniformLocation(prog, "proj"); +	renderer->shaders.blur2.tex = glGetUniformLocation(prog, "tex"); +	renderer->shaders.blur2.pos_attrib = glGetAttribLocation(prog, "pos"); +	renderer->shaders.blur2.tex_attrib = glGetAttribLocation(prog, "texcoord"); +	renderer->shaders.blur2.radius = glGetUniformLocation(prog, "radius"); +	renderer->shaders.blur2.halfpixel = glGetUniformLocation(prog, "halfpixel"); +  	// fragment shaders  	if (!link_tex_program(renderer, &renderer->shaders.tex_rgba,  			SHADER_SOURCE_TEXTURE_RGBA)) { @@ -289,6 +353,8 @@ error:  	glDeleteProgram(renderer->shaders.rounded_tr_quad.program);  	glDeleteProgram(renderer->shaders.corner.program);  	glDeleteProgram(renderer->shaders.box_shadow.program); +	glDeleteProgram(renderer->shaders.blur1.program); +	glDeleteProgram(renderer->shaders.blur2.program);  	glDeleteProgram(renderer->shaders.tex_rgba.program);  	glDeleteProgram(renderer->shaders.tex_rgbx.program);  	glDeleteProgram(renderer->shaders.tex_ext.program); @@ -305,27 +371,53 @@ error:  	return NULL;  } -void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t height) { -	// Create and render the stencil buffer -	if (renderer->stencil_buffer_id == 0) { -		glGenRenderbuffers(1, &renderer->stencil_buffer_id); +void fx_renderer_fini(struct fx_renderer *renderer) { +	fx_framebuffer_release(&renderer->main_buffer); +	fx_framebuffer_release(&renderer->blur_buffer); +	fx_framebuffer_release(&renderer->effects_buffer); +	fx_framebuffer_release(&renderer->effects_buffer_swapped); +	release_stencil_buffer(&renderer->stencil_buffer_id); +} + +void fx_renderer_begin(struct fx_renderer *renderer, struct sway_output *sway_output) { +	struct wlr_output *output = sway_output->wlr_output; + +	int width, height; +	wlr_output_transformed_resolution(output, &width, &height); + +	renderer->sway_output = sway_output; +	// Store the wlr framebuffer +	GLint wlr_fb = -1; +	glGetIntegerv(GL_FRAMEBUFFER_BINDING, &wlr_fb); +	if (wlr_fb < 0) { +		sway_log(SWAY_ERROR, "Failed to get wlr framebuffer!"); +		abort();  	} -	glBindRenderbuffer(GL_RENDERBUFFER, renderer->stencil_buffer_id); -	glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); -	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, -			GL_RENDERBUFFER, renderer->stencil_buffer_id); +	renderer->wlr_buffer.fb = wlr_fb; -	glViewport(0, 0, width, height); +	// Create the main framebuffer +	fx_framebuffer_create(output, &renderer->main_buffer, true); +	// Create the stencil buffer and attach it to our main_buffer +	create_stencil_buffer(output, &renderer->stencil_buffer_id); + +	// Create a new blur/effects framebuffers +	fx_framebuffer_create(output, &renderer->effects_buffer, false); +	fx_framebuffer_create(output, &renderer->effects_buffer_swapped, false);  	// refresh projection matrix  	matrix_projection(renderer->projection, width, height,  		WL_OUTPUT_TRANSFORM_FLIPPED_180);  	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + +	// Bind to our main framebuffer +	fx_framebuffer_bind(&renderer->main_buffer, width, height);  } -void fx_renderer_end() { -	// TODO +void fx_renderer_end(struct fx_renderer *renderer) { +	// Release the main buffer +	fx_framebuffer_release(&renderer->main_buffer); +	release_stencil_buffer(&renderer->stencil_buffer_id);  }  void fx_renderer_clear(const float color[static 4]) { @@ -335,26 +427,23 @@ void fx_renderer_clear(const float color[static 4]) {  }  void fx_renderer_scissor(struct wlr_box *box) { -		if (box) { -				glScissor(box->x, box->y, box->width, box->height); -				glEnable(GL_SCISSOR_TEST); -		} else { -				glDisable(GL_SCISSOR_TEST); -		} +	if (box) { +		glScissor(box->x, box->y, box->width, box->height); +		glEnable(GL_SCISSOR_TEST); +	} else { +		glDisable(GL_SCISSOR_TEST); +	}  } -bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, +bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture,  		const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9],  		struct decoration_data deco_data) { -	assert(wlr_texture_is_gles2(wlr_texture)); -	struct wlr_gles2_texture_attribs texture_attrs; -	wlr_gles2_texture_get_attribs(wlr_texture, &texture_attrs);  	struct gles2_tex_shader *shader = NULL; -	switch (texture_attrs.target) { +	switch (fx_texture->target) {  	case GL_TEXTURE_2D: -		if (texture_attrs.has_alpha) { +		if (fx_texture->has_alpha) {  			shader = &renderer->shaders.tex_rgba;  		} else {  			shader = &renderer->shaders.tex_rgbx; @@ -382,16 +471,18 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t  	wlr_matrix_transpose(gl_matrix, gl_matrix);  	// if there's no opacity or rounded corners we don't need to blend -	if (!texture_attrs.has_alpha && deco_data.alpha == 1.0 && !deco_data.corner_radius) { +	if (!fx_texture->has_alpha && deco_data.alpha == 1.0 && !deco_data.corner_radius) {  		glDisable(GL_BLEND);  	} else {  		glEnable(GL_BLEND);  	} +	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); +  	glActiveTexture(GL_TEXTURE0); -	glBindTexture(texture_attrs.target, texture_attrs.tex); +	glBindTexture(fx_texture->target, fx_texture->id); -	glTexParameteri(texture_attrs.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +	glTexParameteri(fx_texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  	glUseProgram(shader->program); @@ -408,10 +499,10 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t  	glUniform1f(shader->saturation, deco_data.saturation);  	glUniform1f(shader->radius, deco_data.corner_radius); -	const GLfloat x1 = src_box->x / wlr_texture->width; -	const GLfloat y1 = src_box->y / wlr_texture->height; -	const GLfloat x2 = (src_box->x + src_box->width) / wlr_texture->width; -	const GLfloat y2 = (src_box->y + src_box->height) / wlr_texture->height; +	const GLfloat x1 = src_box->x / fx_texture->width; +	const GLfloat y1 = src_box->y / fx_texture->height; +	const GLfloat x2 = (src_box->x + src_box->width) / fx_texture->width; +	const GLfloat y2 = (src_box->y + src_box->height) / fx_texture->height;  	const GLfloat texcoord[] = {  		x2, y1, // top right  		x1, y1, // top left @@ -430,21 +521,21 @@ bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct wlr_t  	glDisableVertexAttribArray(shader->pos_attrib);  	glDisableVertexAttribArray(shader->tex_attrib); -	glBindTexture(texture_attrs.target, 0); +	glBindTexture(fx_texture->target, 0);  	return true;  } -bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct wlr_texture *wlr_texture, +bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *texture,  		const struct wlr_box *dst_box, const float matrix[static 9],  		struct decoration_data deco_data) {  	struct wlr_fbox src_box = {  		.x = 0,  		.y = 0, -		.width = wlr_texture->width, -		.height = wlr_texture->height, +		.width = texture->width, +		.height = texture->height,  	}; -	return fx_render_subtexture_with_matrix(renderer, wlr_texture, &src_box, +	return fx_render_subtexture_with_matrix(renderer, texture, &src_box,  			dst_box, matrix, deco_data);  } @@ -669,3 +760,46 @@ void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *bo  	glClear(GL_STENCIL_BUFFER_BIT);  	glDisable(GL_STENCIL_TEST);  } + +void fx_render_blur(struct fx_renderer *renderer, struct sway_output *output, +		const float matrix[static 9], struct fx_framebuffer **buffer, +		struct blur_shader *shader, const struct wlr_box *box, int blur_radius) { +	glDisable(GL_BLEND); +	glDisable(GL_STENCIL_TEST); + +	glActiveTexture(GL_TEXTURE0); + +	glBindTexture((*buffer)->texture.target, (*buffer)->texture.id); + +	glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + +	glUseProgram(shader->program); + +	// OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set +	// to GL_FALSE +	float gl_matrix[9]; +	wlr_matrix_transpose(gl_matrix, matrix); +	glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); + +	glUniform1i(shader->tex, 0); +	glUniform1f(shader->radius, blur_radius); + +	int width, height; +	wlr_output_transformed_resolution(output->wlr_output, &width, &height); +	if (shader == &renderer->shaders.blur1) { +		glUniform2f(shader->halfpixel, 0.5f / (width / 2.0f), 0.5f / (height / 2.0f)); +	} else { +		glUniform2f(shader->halfpixel, 0.5f / (width * 2.0f), 0.5f / (height * 2.0f)); +	} + +	glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); +	glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); + +	glEnableVertexAttribArray(shader->pos_attrib); +	glEnableVertexAttribArray(shader->tex_attrib); + +	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + +	glDisableVertexAttribArray(shader->pos_attrib); +	glDisableVertexAttribArray(shader->tex_attrib); +} diff --git a/sway/desktop/fx_renderer/fx_texture.c b/sway/desktop/fx_renderer/fx_texture.c new file mode 100644 index 00000000..60aa9a26 --- /dev/null +++ b/sway/desktop/fx_renderer/fx_texture.c @@ -0,0 +1,19 @@ +#include <assert.h> +#include <wlr/render/gles2.h> + +#include "sway/desktop/fx_renderer/fx_texture.h" + +struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture* texture) { +	assert(wlr_texture_is_gles2(texture)); + +	struct wlr_gles2_texture_attribs texture_attrs; +	wlr_gles2_texture_get_attribs(texture, &texture_attrs); + +	return (struct fx_texture) { +		.target = texture_attrs.target, +		.id = texture_attrs.tex, +		.has_alpha = texture_attrs.has_alpha, +		.width = texture->width, +		.height = texture->height, +	}; +} diff --git a/sway/desktop/fx_renderer/shaders/blur1.frag b/sway/desktop/fx_renderer/shaders/blur1.frag new file mode 100644 index 00000000..e7cb1be8 --- /dev/null +++ b/sway/desktop/fx_renderer/shaders/blur1.frag @@ -0,0 +1,18 @@ +precision mediump float; +varying mediump vec2 v_texcoord; +uniform sampler2D tex; + +uniform float radius; +uniform vec2 halfpixel; + +void main() { +    vec2 uv = v_texcoord * 2.0; + +    vec4 sum = texture2D(tex, uv) * 4.0; +    sum += texture2D(tex, uv - halfpixel.xy * radius); +    sum += texture2D(tex, uv + halfpixel.xy * radius); +    sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); +    sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); + +    gl_FragColor = sum / 8.0; +} diff --git a/sway/desktop/fx_renderer/shaders/blur2.frag b/sway/desktop/fx_renderer/shaders/blur2.frag new file mode 100644 index 00000000..3755a294 --- /dev/null +++ b/sway/desktop/fx_renderer/shaders/blur2.frag @@ -0,0 +1,22 @@ +precision mediump float; +varying mediump vec2 v_texcoord; +uniform sampler2D tex; + +uniform float radius; +uniform vec2 halfpixel; + +void main() { +    vec2 uv = v_texcoord / 2.0; + +    vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); + +    sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; +    sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); +    sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; +    sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); +    sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; +    sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); +    sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; + +    gl_FragColor = sum / 12.0; +} diff --git a/sway/desktop/fx_renderer/shaders/meson.build b/sway/desktop/fx_renderer/shaders/meson.build index 6c0dd1d3..83119963 100644 --- a/sway/desktop/fx_renderer/shaders/meson.build +++ b/sway/desktop/fx_renderer/shaders/meson.build @@ -1,6 +1,8 @@  embed = find_program('./embed.sh', native: true)  shaders = [ +    'blur1.frag', +    'blur2.frag',      'box_shadow.frag',      'common.vert',      'corner.frag', diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 6e3cc0e2..a8356ad7 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -290,6 +290,12 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {  	struct sway_output *output = wlr_output->data;  	struct wlr_box old_extent = layer->extent; +	// Rerender the static blur on change +	if (layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND +			|| layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { +		output->renderer->blur_buffer_dirty = true; +	} +  	bool layer_changed = false;  	if (layer_surface->current.committed != 0  			|| layer->mapped != layer_surface->mapped) { @@ -366,6 +372,13 @@ static void handle_destroy(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; + +	// Rerender the static blur +	if (sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND +			|| sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { +		output->renderer->blur_buffer_dirty = true; +	} +  	arrange_layers(output);  	transaction_commit_dirty();  	wl_list_remove(&sway_layer->output_destroy.link); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a7269b7b..45c2f1c4 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -6,6 +6,7 @@  #include <wayland-server-core.h>  #include <wlr/backend/drm.h>  #include <wlr/backend/headless.h> +#include <wlr/render/gles2.h>  #include <wlr/render/wlr_renderer.h>  #include <wlr/types/wlr_buffer.h>  #include <wlr/types/wlr_drm_lease_v1.h> @@ -745,12 +746,11 @@ static void damage_child_views_iterator(struct sway_container *con,  void output_damage_whole_container(struct sway_output *output,  		struct sway_container *con) {  	// Pad the box by 1px, because the width is a double and might be a fraction -	int shadow_sigma = con->shadow_enabled ? config->shadow_blur_sigma : 0;  	struct wlr_box box = { -		.x = con->current.x - output->lx - 1 - shadow_sigma, -		.y = con->current.y - output->ly - 1 - shadow_sigma, -		.width = con->current.width + 2 + shadow_sigma * 2, -		.height = con->current.height + 2 + shadow_sigma * 2, +		.x = con->current.x - output->lx - 1, +		.y = con->current.y - output->ly - 1, +		.width = con->current.width + 2, +		.height = con->current.height + 2,  	};  	scale_box(&box, output->wlr_output->scale);  	if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { @@ -798,6 +798,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {  		output_disable(output);  	} +	fx_renderer_fini(output->renderer); +  	output_begin_destroy(output);  	wl_list_remove(&output->link); @@ -939,6 +941,14 @@ void handle_new_output(struct wl_listener *listener, void *data) {  	output->server = server;  	wlr_damage_ring_init(&output->damage_ring); +	// Init FX Renderer +	struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer); +	output->renderer = fx_renderer_create(egl); +	if (!output->renderer) { +		sway_log(SWAY_ERROR, "Failed to create fx_renderer"); +		abort(); +	} +  	wl_signal_add(&wlr_output->events.destroy, &output->destroy);  	output->destroy.notify = handle_destroy;  	wl_signal_add(&wlr_output->events.commit, &output->commit); @@ -963,6 +973,10 @@ void handle_new_output(struct wl_listener *listener, void *data) {  	transaction_commit_dirty(); +	// From sway upstream (fixes damage_ring bounds being INT_MAX) +	int width, height; +	wlr_output_transformed_resolution(output->wlr_output, &width, &height); +	wlr_damage_ring_set_bounds(&output->damage_ring, width, height);  	update_output_manager_config(server);  } diff --git a/sway/desktop/render.c b/sway/desktop/render.c index a89cbf6b..d2b366b5 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -19,6 +19,7 @@  #include "config.h"  #include "log.h"  #include "sway/config.h" +#include "sway/desktop/fx_renderer/fx_framebuffer.h"  #include "sway/desktop/fx_renderer/fx_renderer.h"  #include "sway/input/input-manager.h"  #include "sway/input/seat.h" @@ -37,6 +38,13 @@ struct render_data {  	struct decoration_data deco_data;  }; +struct workspace_effect_info { +	bool container_wants_blur; +	bool container_wants_shadow; +	bool should_render_optimized_blur; +	int expanded_size; +}; +  struct decoration_data get_undecorated_decoration_data() {  	return (struct decoration_data) {  		.alpha = 1.0f, @@ -45,9 +53,18 @@ struct decoration_data get_undecorated_decoration_data() {  		.corner_radius = 0,  		.saturation = 1.0f,  		.has_titlebar = false, +		.blur = false,  	};  } +int get_blur_size() { +	return pow(2, config->blur_params.num_passes) * config->blur_params.radius; +} + +bool should_parameters_blur() { +	return config->blur_params.radius > 0 && config->blur_params.num_passes > 0; +} +  /**   * Apply scale to a width or height.   * @@ -67,7 +84,7 @@ static int scale_length(int length, int offset, float scale) {  static void scissor_output(struct wlr_output *wlr_output,  		pixman_box32_t *rect) {  	struct sway_output *output = wlr_output->data; -	struct fx_renderer *renderer = output->server->renderer; +	struct fx_renderer *renderer = output->renderer;  	assert(renderer);  	struct wlr_box box = { @@ -80,30 +97,22 @@ static void scissor_output(struct wlr_output *wlr_output,  	int ow, oh;  	wlr_output_transformed_resolution(wlr_output, &ow, &oh); -	enum wl_output_transform transform = -		wlr_output_transform_invert(wlr_output->transform); +	enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform);  	wlr_box_transform(&box, &box, transform, ow, oh);  	fx_renderer_scissor(&box);  }  static void set_scale_filter(struct wlr_output *wlr_output, -		struct wlr_texture *texture, enum scale_filter_mode scale_filter) { -	if (!wlr_texture_is_gles2(texture)) { -		return; -	} - -	struct wlr_gles2_texture_attribs attribs; -	wlr_gles2_texture_get_attribs(texture, &attribs); - -	glBindTexture(attribs.target, attribs.tex); +		struct fx_texture *texture, enum scale_filter_mode scale_filter) { +	glBindTexture(texture->target, texture->id);  	switch (scale_filter) {  	case SCALE_FILTER_LINEAR: -		glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); +		glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  		break;  	case SCALE_FILTER_NEAREST: -		glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); +		glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  		break;  	case SCALE_FILTER_DEFAULT:  	case SCALE_FILTER_SMART: @@ -120,12 +129,19 @@ pixman_region32_t create_damage(const struct wlr_box damage_box, pixman_region32  	return damage;  } +struct wlr_box get_monitor_box(struct wlr_output *output) { +	int width, height; +	wlr_output_transformed_resolution(output, &width, &height); +	struct wlr_box monitor_box = { 0, 0, width, height }; +	return monitor_box; +} +  static void render_texture(struct wlr_output *wlr_output, -		pixman_region32_t *output_damage, struct wlr_texture *texture, +		pixman_region32_t *output_damage, struct fx_texture *texture,  		const struct wlr_fbox *src_box, const struct wlr_box *dst_box,  		const float matrix[static 9], struct decoration_data deco_data) {  	struct sway_output *output = wlr_output->data; -	struct fx_renderer *renderer = output->server->renderer; +	struct fx_renderer *renderer = output->renderer;  	pixman_region32_t damage = create_damage(*dst_box, output_damage);  	bool damaged = pixman_region32_not_empty(&damage); @@ -150,6 +166,170 @@ damage_finish:  	pixman_region32_fini(&damage);  } +/* Renders the blur for each damaged rect and swaps the buffer */ +void render_blur_segments(struct fx_renderer *renderer, struct sway_output *output, +		const float matrix[static 9], pixman_region32_t* damage, +		struct fx_framebuffer **buffer, struct blur_shader* shader, +		const struct wlr_box *box, int blur_radius) { +	int width, height; +	wlr_output_transformed_resolution(output->wlr_output, &width, &height); + +	if (*buffer == &renderer->effects_buffer) { +		fx_framebuffer_bind(&renderer->effects_buffer_swapped, width, height); +	} else { +		fx_framebuffer_bind(&renderer->effects_buffer, width, height); +	} + +	if (pixman_region32_not_empty(damage)) { +		int nrects; +		pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); +		for (int i = 0; i < nrects; ++i) { +			const pixman_box32_t box = rects[i]; +			struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 }; +			fx_renderer_scissor(&new_box); +			fx_render_blur(renderer, output, matrix, buffer, shader, &new_box, blur_radius); +		} +	} + +	if (*buffer != &renderer->effects_buffer) { +		*buffer = &renderer->effects_buffer; +	} else { +		*buffer = &renderer->effects_buffer_swapped; +	} +} + +/** Blurs the main_buffer content and returns the blurred framebuffer */ +struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct sway_output *output, +		pixman_region32_t *original_damage, const float box_matrix[static 9], const struct wlr_box *box) { +	struct wlr_box monitor_box = get_monitor_box(output->wlr_output); + +	const enum wl_output_transform transform = wlr_output_transform_invert(output->wlr_output->transform); +	float matrix[9]; +	wlr_matrix_project_box(matrix, &monitor_box, transform, 0, output->wlr_output->transform_matrix); + +	float gl_matrix[9]; +	wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); + +	pixman_region32_t damage; +	pixman_region32_init(&damage); +	pixman_region32_copy(&damage, original_damage); +	wlr_region_transform(&damage, &damage, wlr_output_transform_invert(output->wlr_output->transform), +			monitor_box.width, monitor_box.height); +	wlr_region_expand(&damage, &damage, get_blur_size()); + +	// Initially blur main_buffer content into the effects_buffers +	struct fx_framebuffer *current_buffer = &renderer->main_buffer; + +	// Bind to blur framebuffer +	fx_framebuffer_bind(&renderer->effects_buffer, monitor_box.width, monitor_box.height); +	glBindTexture(renderer->main_buffer.texture.target, renderer->main_buffer.texture.id); + +	// damage region will be scaled, make a temp +	pixman_region32_t tempDamage; +	pixman_region32_init(&tempDamage); +	// When DOWNscaling, we make the region twice as small because it's the TARGET +	wlr_region_scale(&tempDamage, &damage, 0.5f); + +	int blur_radius = config->blur_params.radius; +	int blur_passes = config->blur_params.num_passes; + +	// First pass +	render_blur_segments(renderer, output, gl_matrix, &tempDamage, ¤t_buffer, +			&renderer->shaders.blur1, box, blur_radius); + +	// Downscale +	for (int i = 1; i < blur_passes; ++i) { +		wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << (i + 1))); +		render_blur_segments(renderer, output, gl_matrix, &tempDamage, ¤t_buffer, +				&renderer->shaders.blur1, box, blur_radius); +	} + +	// Upscale +	for (int i = blur_passes - 1; i >= 0; --i) { +		// when upsampling we make the region twice as big +		wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << i)); +		render_blur_segments(renderer, output, gl_matrix, &tempDamage, ¤t_buffer, +				&renderer->shaders.blur2, box, blur_radius); +	} + +	pixman_region32_fini(&tempDamage); +	pixman_region32_fini(&damage); + +	// Bind back to the default buffer +	fx_framebuffer_bind(&renderer->main_buffer, monitor_box.width, monitor_box.height); + +	return current_buffer; +} + +void render_blur(bool optimized, struct sway_output *output, +		pixman_region32_t *output_damage, const struct wlr_fbox *src_box, +		const struct wlr_box *dst_box, pixman_region32_t *opaque_region, +		int surface_width, int surface_height, int32_t surface_scale, +		int corner_radius) { +	struct wlr_output *wlr_output = output->wlr_output; +	struct fx_renderer *renderer = output->renderer; + +	// Check if damage is inside of box rect +	pixman_region32_t damage = create_damage(*dst_box, output_damage); + +	pixman_region32_t inverse_opaque; +	pixman_region32_init(&inverse_opaque); + +	if (!pixman_region32_not_empty(&damage)) { +		goto damage_finish; +	} + +	/* +	 * Capture the back_buffer and blur it +	*/ + +	pixman_box32_t surface_box = { +		.x1 = 0, +		.y1 = 0, +		.x2 = dst_box->width / wlr_output->scale, +		.y2 = dst_box->height / wlr_output->scale, +	}; + +	wlr_region_scale(&inverse_opaque, &inverse_opaque, wlr_output->scale); + +	// Gets the non opaque region +	pixman_region32_copy(&inverse_opaque, opaque_region); +	pixman_region32_inverse(&inverse_opaque, &inverse_opaque, &surface_box); +	pixman_region32_intersect_rect(&inverse_opaque, &inverse_opaque, 0, 0, +			surface_width * surface_scale, +			surface_height * surface_scale); +	if (!pixman_region32_not_empty(&inverse_opaque)) { +		goto damage_finish; +	} + +	wlr_region_scale(&inverse_opaque, &inverse_opaque, wlr_output->scale); + +	struct fx_framebuffer *buffer = &renderer->blur_buffer; +	if (!buffer->texture.id || (!optimized && !config->blur_xray)) { +		pixman_region32_translate(&inverse_opaque, dst_box->x, dst_box->y); +		pixman_region32_intersect(&inverse_opaque, &inverse_opaque, &damage); + +		// Render the blur into its own buffer +		buffer = get_main_buffer_blur(renderer, output, &inverse_opaque, +				wlr_output->transform_matrix, dst_box); +	} + +	// Draw the blurred texture +	struct wlr_box monitor_box = get_monitor_box(wlr_output); +	enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); +	float matrix[9]; +	wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix); + +	struct decoration_data deco_data = get_undecorated_decoration_data(); +	deco_data.corner_radius = corner_radius; +	render_texture(wlr_output, &damage, &buffer->texture, src_box, dst_box, matrix, deco_data); + +damage_finish: +	pixman_region32_fini(&damage); +	pixman_region32_fini(&inverse_opaque); +} + +  static void render_surface_iterator(struct sway_output *output,  		struct sway_view *view, struct wlr_surface *surface,  		struct wlr_box *_box, void *_data) { @@ -162,17 +342,12 @@ static void render_surface_iterator(struct sway_output *output,  		return;  	} -	struct wlr_fbox src_box; -	wlr_surface_get_buffer_source_box(surface, &src_box); -  	struct wlr_box proj_box = *_box;  	scale_box(&proj_box, wlr_output->scale);  	float matrix[9]; -	enum wl_output_transform transform = -		wlr_output_transform_invert(surface->current.transform); -	wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, -		wlr_output->transform_matrix); +	enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); +	wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, wlr_output->transform_matrix);  	struct wlr_box dst_box = *_box;  	struct wlr_box *clip_box = data->clip_box; @@ -185,9 +360,40 @@ static void render_surface_iterator(struct sway_output *output,  	scale_box(&dst_box, wlr_output->scale); -	data->deco_data.corner_radius *= wlr_output->scale; -	render_texture(wlr_output, output_damage, texture, &src_box, &dst_box, -		matrix, data->deco_data); +	struct decoration_data deco_data = data->deco_data; +	deco_data.corner_radius *= wlr_output->scale; + +	// render blur (view->surface == surface excludes blurring subsurfaces) +	if (deco_data.blur && should_parameters_blur() && view->surface == surface) { +		pixman_region32_t opaque_region; +		pixman_region32_init(&opaque_region); + +		bool has_alpha = false; +		if (deco_data.alpha < 1.0) { +			has_alpha = true; +			pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0); +		} else { +			has_alpha = !surface->opaque; +			pixman_region32_copy(&opaque_region, &surface->opaque_region); +		} + +		if (has_alpha) { +			int width, height; +			wlr_output_transformed_resolution(wlr_output, &width, &height); +			struct wlr_fbox blur_src_box = { 0, 0, width, height }; +			bool is_floating = container_is_floating(view->container); +			render_blur(!is_floating, output, output_damage, &blur_src_box, &dst_box, &opaque_region, +					surface->current.width, surface->current.height, surface->current.scale, deco_data.corner_radius); +		} + +		pixman_region32_fini(&opaque_region); +	} + +	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); +	render_texture(wlr_output, output_damage, &fx_texture, &src_box, &dst_box, +		matrix, deco_data);  	wlr_presentation_surface_sampled_on_output(server.presentation, surface,  		wlr_output); @@ -235,13 +441,73 @@ static void render_drag_icons(struct sway_output *output,  		render_surface_iterator, &data);  } +void render_whole_output(struct fx_renderer *renderer, pixman_region32_t *original_damage, +		struct fx_texture *texture) { +	struct wlr_output *output = renderer->sway_output->wlr_output; +	struct wlr_box monitor_box = get_monitor_box(output); + +	enum wl_output_transform transform = wlr_output_transform_invert(output->transform); +	float matrix[9]; +	wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, output->transform_matrix); + +	pixman_region32_t damage; +	pixman_region32_init(&damage); +	pixman_region32_union_rect(&damage, &damage, monitor_box.x, monitor_box.y, +		monitor_box.width, monitor_box.height); +	pixman_region32_intersect(&damage, &damage, original_damage); +	bool damaged = pixman_region32_not_empty(&damage); +	if (!damaged) { +		goto damage_finish; +	} + +	int nrects; +	pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); +	for (int i = 0; i < nrects; ++i) { +		scissor_output(output, &rects[i]); +		fx_render_texture_with_matrix(renderer, texture, &monitor_box, matrix, get_undecorated_decoration_data()); +	} + +damage_finish: +	pixman_region32_fini(&damage); +} + +void render_monitor_blur(struct sway_output *output, pixman_region32_t *damage) { +	struct wlr_output *wlr_output = output->wlr_output; +	struct fx_renderer *renderer = output->renderer; + +	struct wlr_box monitor_box = get_monitor_box(wlr_output); +	pixman_region32_t fake_damage; +	pixman_region32_init_rect(&fake_damage, 0, 0, monitor_box.width, monitor_box.height); + +	// Render the blur +	struct fx_framebuffer *buffer = get_main_buffer_blur(renderer, output, &fake_damage, +			wlr_output->transform_matrix, &monitor_box); + +	// Render the newly blurred content into the blur_buffer +	fx_framebuffer_create(wlr_output, &renderer->blur_buffer, true); +	// Clear the damaged region of the blur_buffer +	float clear_color[] = { 0, 0, 0, 0 }; +	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_renderer_clear(clear_color); +	} +	render_whole_output(renderer, &fake_damage, &buffer->texture); +	fx_framebuffer_bind(&renderer->main_buffer, monitor_box.width, monitor_box.height); + +	pixman_region32_fini(&fake_damage); + +	renderer->blur_buffer_dirty = false; +} +  // _box.x and .y are expected to be layout-local  // _box.width and .height are expected to be output-buffer-local  void render_rect(struct sway_output *output,  		pixman_region32_t *output_damage, const struct wlr_box *_box,  		float color[static 4]) {  	struct wlr_output *wlr_output = output->wlr_output; -	struct fx_renderer *renderer = output->server->renderer; +	struct fx_renderer *renderer = output->renderer;  	struct wlr_box box;  	memcpy(&box, _box, sizeof(struct wlr_box)); @@ -269,7 +535,7 @@ void render_rounded_rect(struct sway_output *output, pixman_region32_t *output_d  		const struct wlr_box *_box, float color[static 4], int corner_radius,  		enum corner_location corner_location) {  	struct wlr_output *wlr_output = output->wlr_output; -	struct fx_renderer *renderer = output->server->renderer; +	struct fx_renderer *renderer = output->renderer;  	struct wlr_box box;  	memcpy(&box, _box, sizeof(struct wlr_box)); @@ -300,7 +566,7 @@ void render_border_corner(struct sway_output *output, pixman_region32_t *output_  		const struct wlr_box *_box, const float color[static 4], int corner_radius,  		int border_thickness, enum corner_location corner_location) {  	struct wlr_output *wlr_output = output->wlr_output; -	struct fx_renderer *renderer = output->server->renderer; +	struct fx_renderer *renderer = output->renderer;  	struct wlr_box box;  	memcpy(&box, _box, sizeof(struct wlr_box)); @@ -331,7 +597,7 @@ void render_box_shadow(struct sway_output *output, pixman_region32_t *output_dam  		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->server->renderer; +	struct fx_renderer *renderer = output->renderer;  	struct wlr_box box;  	memcpy(&box, _box, sizeof(struct wlr_box)); @@ -448,8 +714,7 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output  		float matrix[9];  		enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); -		wlr_matrix_project_box(matrix, &proj_box, transform, 0, -			wlr_output->transform_matrix); +		wlr_matrix_project_box(matrix, &proj_box, transform, 0, wlr_output->transform_matrix);  		struct sway_container_state state = view->container->current;  		dst_box.x = state.x - output->lx; @@ -465,7 +730,30 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output  		scale_box(&dst_box, wlr_output->scale);  		deco_data.corner_radius *= wlr_output->scale; -		render_texture(wlr_output, damage, saved_buf->buffer->texture, + +		// render blur +		if (deco_data.blur && should_parameters_blur()) { +			struct wlr_gles2_texture_attribs attribs; +			wlr_gles2_texture_get_attribs(saved_buf->buffer->texture, &attribs); + +			if (deco_data.alpha < 1.0 || attribs.has_alpha) { +				pixman_region32_t opaque_region; +				pixman_region32_init(&opaque_region); +				pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0); + +				struct wlr_box monitor_box = get_monitor_box(wlr_output); +				// TODO: contribute wlroots function to allow creating an fbox from a box? +				struct wlr_fbox src_box = { monitor_box.x, monitor_box.y, monitor_box.width, monitor_box.height }; +				bool is_floating = container_is_floating(view->container); +				render_blur(!is_floating, output, damage, &src_box, &dst_box, &opaque_region, +						saved_buf->width, saved_buf->height, 1, deco_data.corner_radius); + +				pixman_region32_fini(&opaque_region); +			} +		} + +		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);  	} @@ -481,6 +769,9 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,  		struct sway_container *con, struct border_colors *colors,  		struct decoration_data deco_data) {  	struct sway_view *view = con->view; +	struct sway_container_state *state = &con->current; + +	// render view  	if (!wl_list_empty(&view->saved_buffers)) {  		render_saved_view(view, output, damage, deco_data);  	} else if (view->surface) { @@ -768,7 +1059,8 @@ static void render_titlebar(struct sway_output *output,  		if (ob_inner_width < texture_box.width) {  			texture_box.width = ob_inner_width;  		} -		render_texture(output->wlr_output, output_damage, marks_texture, +		struct fx_texture fx_texture = fx_texture_from_wlr_texture(marks_texture); +		render_texture(output->wlr_output, output_damage, &fx_texture,  			NULL, &texture_box, matrix, deco_data);  		// Padding above @@ -844,7 +1136,8 @@ static void render_titlebar(struct sway_output *output,  			texture_box.width = ob_inner_width - ob_marks_width;  		} -		render_texture(output->wlr_output, output_damage, title_texture, +		struct fx_texture fx_texture = fx_texture_from_wlr_texture(title_texture); +		render_texture(output->wlr_output, output_damage, &fx_texture,  			NULL, &texture_box, matrix, deco_data);  		// Padding above @@ -1052,6 +1345,7 @@ static void render_containers_linear(struct sway_output *output,  				.corner_radius = corner_radius,  				.saturation = child->saturation,  				.has_titlebar = has_titlebar, +				.blur = child->blur_enabled,  			};  			render_view(output, damage, child, colors, deco_data);  			if (has_titlebar) { @@ -1159,6 +1453,7 @@ static void render_containers_tabbed(struct sway_output *output,  			.corner_radius = current->corner_radius,  			.saturation = current->saturation,  			.has_titlebar = true, +			.blur = current->blur_enabled,  		};  		render_view(output, damage, current, current_colors, deco_data);  	} else { @@ -1233,6 +1528,7 @@ static void render_containers_stacked(struct sway_output *output,  			.saturation = current->saturation,  			.corner_radius = current->corner_radius,  			.has_titlebar = true, +			.blur = current->blur_enabled,  		};  		render_view(output, damage, current, current_colors, deco_data);  	} else { @@ -1333,6 +1629,7 @@ static void render_floating_container(struct sway_output *soutput,  			.saturation = con->saturation,  			.corner_radius = con->corner_radius,  			.has_titlebar = has_titlebar, +			.blur = con->blur_enabled,  		};  		render_view(soutput, damage, con, colors, deco_data);  		if (has_titlebar) { @@ -1375,10 +1672,82 @@ static void render_seatops(struct sway_output *output,  	}  } +struct find_effect_iter_data { +	struct workspace_effect_info *effect_info; +	bool blur_buffer_dirty; +}; + +static bool find_con_effect_iterator(struct sway_container *con, void* _data) { +	struct sway_view *view = con->view; +	struct find_effect_iter_data *data = _data; +	struct workspace_effect_info *effect_info = data->effect_info; + +	if (!view) { +		return false; +	} + +	if (con->blur_enabled && !view->surface->opaque) { +		effect_info->container_wants_blur = true; + +		bool is_floating = container_is_floating(con); +		// Check if we should render optimized blur +		if (data->blur_buffer_dirty +				// Only test floating windows when xray is enabled +				&& (!is_floating || (is_floating && config->blur_xray))) { +			effect_info->should_render_optimized_blur = true; +		} +	} +	if (con->shadow_enabled) { +		effect_info->container_wants_shadow = true; +	} + +	// Stop the iteration if all of the effects have been found. +	// Ensures that no effect is skipped if returning early +	return effect_info->container_wants_blur +		&& effect_info->container_wants_shadow +		&& effect_info->should_render_optimized_blur; +} + +static struct workspace_effect_info get_workspace_effect_info(struct sway_output *sway_output) { +	struct fx_renderer *renderer = sway_output->renderer; +	struct sway_workspace *workspace = sway_output->current.active_workspace; + +	struct workspace_effect_info effect_info = { +		.container_wants_blur = false, +		.container_wants_shadow = false, +		.should_render_optimized_blur = false, +		.expanded_size = 0 +	}; + +	if (!workspace_is_visible(workspace)) { +		return effect_info; +	} + +	// Iterate through the workspace containers and check if any effects are requested +	struct find_effect_iter_data iter_data = { +		.effect_info = &effect_info, +		.blur_buffer_dirty = renderer->blur_buffer_dirty +	}; +	workspace_find_container(workspace, find_con_effect_iterator, &iter_data); + +	// Set the expanded damage region +	bool shadow_enabled = effect_info.container_wants_shadow || config->shadow_enabled; +	int shadow_sigma = shadow_enabled ? config->shadow_blur_sigma : 0; +	bool blur_enabled = effect_info.container_wants_blur || config->blur_enabled; +	int blur_size = blur_enabled ? get_blur_size() : 0; +	// +1 as a margin of error +	effect_info.expanded_size = MAX(shadow_sigma, blur_size) + 1; + +	return effect_info; +} +  void output_render(struct sway_output *output, struct timespec *when,  		pixman_region32_t *damage) {  	struct wlr_output *wlr_output = output->wlr_output; -	struct fx_renderer *renderer = output->server->renderer; +	struct fx_renderer *renderer = output->renderer; + +	int width, height; +	wlr_output_transformed_resolution(wlr_output, &width, &height);  	struct sway_workspace *workspace = output->current.active_workspace;  	if (workspace == NULL) { @@ -1390,23 +1759,56 @@ void output_render(struct sway_output *output, struct timespec *when,  		fullscreen_con = workspace->current.fullscreen;  	} -	fx_renderer_begin(renderer, wlr_output->width, wlr_output->height); -  	if (debug.damage == DAMAGE_RERENDER) { -		int width, height; -		wlr_output_transformed_resolution(wlr_output, &width, &height);  		pixman_region32_union_rect(damage, damage, 0, 0, width, height);  	} -	if (!pixman_region32_not_empty(damage)) { -		// Output isn't damaged but needs buffer swap -		goto renderer_end; +	bool has_blur = false; +	bool blur_optimize_should_render = false; +	bool damage_not_empty = pixman_region32_not_empty(damage); +	pixman_region32_t extended_damage; +	pixman_region32_init(&extended_damage); +	if (!fullscreen_con && !server.session_lock.locked && damage_not_empty) { +		// Check if there are any windows to blur +		struct workspace_effect_info effect_info = get_workspace_effect_info(output); +		has_blur = effect_info.container_wants_blur || config->blur_enabled; +		if (effect_info.should_render_optimized_blur) { +			blur_optimize_should_render = true; +			// Damage the whole output +			pixman_region32_union_rect(damage, damage, 0, 0, width, height); +		} + +		// Extend the damaged region +		int expanded_size = effect_info.expanded_size; +		if (expanded_size > 0) { +			int32_t damage_width = damage->extents.x2 - damage->extents.x1; +			int32_t damage_height = damage->extents.y2 - damage->extents.y1; +			// Limit the damage extent to the size of the monitor to prevent overflow +			if (damage_width > width || damage_height > height) { +				pixman_region32_intersect_rect(damage, damage, 0, 0, width, height); +			} + +			wlr_region_expand(damage, damage, expanded_size); +			pixman_region32_copy(&extended_damage, damage); +			wlr_region_expand(damage, damage, expanded_size); +		} else { +			pixman_region32_copy(&extended_damage, damage); +		} +	} else { +		pixman_region32_copy(&extended_damage, damage);  	} -	if (debug.damage == DAMAGE_HIGHLIGHT) { +	if (debug.damage == DAMAGE_HIGHLIGHT && damage_not_empty) {  		fx_renderer_clear((float[]){1, 1, 0, 1});  	} +	fx_renderer_begin(renderer, output); + +	if (!damage_not_empty) { +		// Output isn't damaged but needs buffer swap +		goto renderer_end; +	} +  	if (server.session_lock.locked) {  		float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};  		if (server.session_lock.lock == NULL) { @@ -1494,6 +1896,11 @@ void output_render(struct sway_output *output, struct timespec *when,  		render_layer_toplevel(output, damage,  			&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); +		bool blur_enabled = has_blur && should_parameters_blur(); +		if (blur_enabled && blur_optimize_should_render && renderer->blur_buffer_dirty) { +			render_monitor_blur(output, damage); +		} +  		render_workspace(output, damage, workspace, workspace->current.focused);  		render_floating(output, damage);  #if HAVE_XWAYLAND @@ -1524,6 +1931,7 @@ void output_render(struct sway_output *output, struct timespec *when,  			.corner_radius = 0,  			.saturation = focus->saturation,  			.has_titlebar = false, +			.blur = false,  		};  		render_view_popups(focus->view, output, damage, deco_data);  	} @@ -1536,29 +1944,45 @@ render_overlay:  	render_drag_icons(output, damage, &root->drag_icons);  renderer_end: +	// Draw the contents of our buffer into the wlr buffer +	fx_framebuffer_bind(&renderer->wlr_buffer, width, height); +	float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; +	if (pixman_region32_not_empty(&extended_damage)) { +		int nrects; +		pixman_box32_t *rects = pixman_region32_rectangles(&extended_damage, &nrects); +		for (int i = 0; i < nrects; ++i) { +			scissor_output(wlr_output, &rects[i]); +			fx_renderer_clear(clear_color); +		} +	} +	render_whole_output(renderer, &extended_damage, &renderer->main_buffer.texture);  	fx_renderer_scissor(NULL); +	fx_renderer_end(renderer); + +	// Draw the software cursors  	wlr_renderer_begin(output->server->wlr_renderer, wlr_output->width, wlr_output->height);  	wlr_output_render_software_cursors(wlr_output, damage);  	wlr_renderer_end(output->server->wlr_renderer); -	fx_renderer_end(); - -	int width, height; -	wlr_output_transformed_resolution(wlr_output, &width, &height); +	fx_renderer_scissor(NULL);  	pixman_region32_t frame_damage;  	pixman_region32_init(&frame_damage); -	enum wl_output_transform transform = -		wlr_output_transform_invert(wlr_output->transform); -	wlr_region_transform(&frame_damage, &output->damage_ring.current, -		transform, width, height); +	enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); +	/* +	 * Extend the frame damage by the blur size to properly calc damage for the +	 * next buffer swap. Thanks Emersion for your excellent damage tracking blog-post! +	 */ +	wlr_region_transform(&frame_damage, &extended_damage, transform, width, height); -	if (debug.damage != DAMAGE_DEFAULT) { +	if (debug.damage != DAMAGE_DEFAULT || blur_optimize_should_render) {  		pixman_region32_union_rect(&frame_damage, &frame_damage,  			0, 0, wlr_output->width, wlr_output->height);  	}  	wlr_output_set_damage(wlr_output, &frame_damage); + +	pixman_region32_fini(&extended_damage);  	pixman_region32_fini(&frame_damage);  	if (!wlr_output_commit(wlr_output)) { diff --git a/sway/meson.build b/sway/meson.build index 7eef011b..54e41072 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -14,7 +14,9 @@ sway_sources = files(  	'xdg_decoration.c',  	'desktop/desktop.c', +	'desktop/fx_renderer/fx_framebuffer.c',  	'desktop/fx_renderer/fx_renderer.c', +	'desktop/fx_renderer/fx_texture.c',  	'desktop/fx_renderer/matrix.c',  	'desktop/idle_inhibit_v1.c',  	'desktop/layer_shell.c', @@ -48,6 +50,10 @@ sway_sources = files(  	'commands/assign.c',  	'commands/bar.c',  	'commands/bind.c', +	'commands/blur.c', +	'commands/blur_passes.c', +	'commands/blur_radius.c', +	'commands/blur_xray.c',  	'commands/border.c',  	'commands/client.c',  	'commands/corner_radius.c', diff --git a/sway/server.c b/sway/server.c index c4d005b6..262b7788 100644 --- a/sway/server.c +++ b/sway/server.c @@ -86,12 +86,6 @@ bool server_init(struct sway_server *server) {  		sway_log(SWAY_ERROR, "Failed to create wlr_renderer");  		return false;  	} -	struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer); -	server->renderer = fx_renderer_create(egl); -	if (!server->renderer) { -		sway_log(SWAY_ERROR, "Failed to create fx_renderer"); -		return false; -	}  	wlr_renderer_init_wl_shm(server->wlr_renderer, server->wl_display); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 31edb038..2288346b 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -686,6 +686,21 @@ The default colors are:  *shadow_color* <hex color with alpha>  	The shadow color. Default color: #0000007F +*blur* enable|disable +	Sets whether blur should be drawn. Can also be set per window with +	*for_window*. + +*blur_xray* enable|disable +	Sets whether blur should only display the background. + +*blur_passes* <value> +	Adjusts the blur passes of windows between 0 (disabled) and 10 +	while 2 is the default value. + +*blur_radius* <value> +	Adjusts the blur radius of windows between 0 (disabled) and 10 +	while 5 is the default value. +  *default_border* normal|none|pixel [<n>]  	Set default border style for new tiled windows. diff --git a/sway/tree/container.c b/sway/tree/container.c index f256bafc..2e61ff5c 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -43,6 +43,7 @@ struct sway_container *container_create(struct sway_view *view) {  	c->saturation = 1.0f;  	c->dim = config->default_dim_inactive;  	c->shadow_enabled = config->shadow_enabled; +	c->blur_enabled = config->blur_enabled;  	c->corner_radius = config->corner_radius;  	if (!view) {  | 
