diff options
-rw-r--r-- | examples/meson.build | 2 | ||||
-rw-r--r-- | examples/scene-graph.c | 15 | ||||
-rw-r--r-- | include/render/fx_renderer/shaders.h | 4 | ||||
-rw-r--r-- | include/scenefx/render/pass.h | 9 | ||||
-rw-r--r-- | include/scenefx/types/fx/shadow_data.h | 20 | ||||
-rw-r--r-- | include/scenefx/types/wlr_scene.h | 51 | ||||
-rw-r--r-- | render/fx_renderer/fx_pass.c | 50 | ||||
-rw-r--r-- | render/fx_renderer/gles2/shaders/box_shadow.frag | 10 | ||||
-rw-r--r-- | render/fx_renderer/shaders.c | 4 | ||||
-rw-r--r-- | tinywl/tinywl.c | 56 | ||||
-rw-r--r-- | types/fx/meson.build | 1 | ||||
-rw-r--r-- | types/fx/shadow_data.c | 15 | ||||
-rw-r--r-- | types/scene/wlr_scene.c | 260 |
13 files changed, 336 insertions, 161 deletions
diff --git a/examples/meson.build b/examples/meson.build index da32934..dc6f171 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -17,7 +17,7 @@ foreach name, info : compositors executable( name, [info.get('src'), extra_src], - dependencies: [wlroots, scenefx, libdrm_header, info.get('dep', [])], + dependencies: [scenefx, libdrm_header, info.get('dep', [])], build_by_default: get_option('examples'), ) endforeach diff --git a/examples/scene-graph.c b/examples/scene-graph.c index 562f72e..e2e7d19 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -2,6 +2,7 @@ #include <assert.h> #include <getopt.h> #include <scenefx/render/fx_renderer/fx_renderer.h> +#include <scenefx/types/wlr_scene.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -13,7 +14,6 @@ #include <wlr/render/wlr_renderer.h> #include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_output.h> -#include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_xdg_shell.h> #include <wlr/util/log.h> @@ -22,6 +22,7 @@ * New surfaces are stacked on top of the existing ones as they appear. */ static const int border_width = 3; +static const int corner_radius = 0; // TODO struct server { struct wl_display *display; @@ -40,6 +41,7 @@ struct surface { struct wlr_surface *wlr; struct wlr_scene_surface *scene_surface; struct wlr_scene_rect *border; + struct wlr_scene_shadow *shadow; struct wl_list link; struct wl_listener commit; @@ -99,12 +101,16 @@ static void surface_handle_commit(struct wl_listener *listener, void *data) { wlr_scene_rect_set_size(surface->border, surface->wlr->current.width + 2 * border_width, surface->wlr->current.height + 2 * border_width); + wlr_scene_shadow_set_size(surface->shadow, + surface->wlr->current.width + 2 * (surface->shadow->blur_sigma + border_width), + surface->wlr->current.height + 2 * (surface->shadow->blur_sigma + border_width)); } static void surface_handle_destroy(struct wl_listener *listener, void *data) { struct surface *surface = wl_container_of(listener, surface, destroy); wlr_scene_node_destroy(&surface->scene_surface->buffer->node); wlr_scene_node_destroy(&surface->border->node); + wlr_scene_node_destroy(&surface->shadow->node); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->link); free(surface); @@ -130,8 +136,15 @@ static void server_handle_new_surface(struct wl_listener *listener, 0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 }); wlr_scene_node_set_position(&surface->border->node, pos, pos); + /* Shadow dimensions will be set in surface.commit handler */ + float blur_sigma = 20.0f; + surface->shadow = wlr_scene_shadow_create(&server->scene->tree, + 0, 0, corner_radius, blur_sigma, (float[4]){ 1.0f, 0.f, 0.f, 1.0f }); + wlr_scene_node_set_position(&surface->shadow->node, + pos - blur_sigma, pos - blur_sigma); surface->scene_surface = wlr_scene_surface_create(&server->scene->tree, wlr_surface); + wlr_scene_buffer_set_corner_radius(surface->scene_surface->buffer, corner_radius); wlr_scene_node_set_position(&surface->scene_surface->buffer->node, pos + border_width, pos + border_width); diff --git a/include/render/fx_renderer/shaders.h b/include/render/fx_renderer/shaders.h index ad18167..11baa3d 100644 --- a/include/render/fx_renderer/shaders.h +++ b/include/render/fx_renderer/shaders.h @@ -158,9 +158,11 @@ struct box_shadow_shader { GLint pos_attrib; GLint position; GLint size; - GLint offset; GLint blur_sigma; GLint corner_radius; + GLint window_position; + GLint window_half_size; + GLint window_corner_radius; }; bool link_box_shadow_program(struct box_shadow_shader *shader); diff --git a/include/scenefx/render/pass.h b/include/scenefx/render/pass.h index 0f1d27c..0e87833 100644 --- a/include/scenefx/render/pass.h +++ b/include/scenefx/render/pass.h @@ -4,7 +4,6 @@ #include <stdbool.h> #include <wlr/render/pass.h> #include <wlr/render/interface.h> -#include "scenefx/types/fx/shadow_data.h" struct fx_gles_render_pass { struct wlr_render_pass base; @@ -62,13 +61,15 @@ struct fx_render_rect_grad_options { }; struct fx_render_box_shadow_options { - struct wlr_box shadow_box; - struct wlr_box clip_box; + struct wlr_box box; + struct wlr_box window_box; + int window_corner_radius; /* Clip region, leave NULL to disable clipping */ const pixman_region32_t *clip; - struct shadow_data *shadow_data; + float blur_sigma; int corner_radius; + struct wlr_render_color color; }; struct fx_render_rounded_rect_options { diff --git a/include/scenefx/types/fx/shadow_data.h b/include/scenefx/types/fx/shadow_data.h deleted file mode 100644 index c307871..0000000 --- a/include/scenefx/types/fx/shadow_data.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TYPES_DECORATION_DATA -#define TYPES_DECORATION_DATA - -#include <stdbool.h> -#include <wlr/util/addon.h> -#include <wlr/render/pass.h> - -struct shadow_data { - bool enabled; - struct wlr_render_color color; - float blur_sigma; - float offset_x; - float offset_y; -}; - -struct shadow_data shadow_data_get_default(void); - -bool scene_buffer_has_shadow(struct shadow_data *data); - -#endif diff --git a/include/scenefx/types/wlr_scene.h b/include/scenefx/types/wlr_scene.h index 5925931..9b68c4b 100644 --- a/include/scenefx/types/wlr_scene.h +++ b/include/scenefx/types/wlr_scene.h @@ -20,7 +20,6 @@ */ #include <pixman.h> -#include "scenefx/types/fx/shadow_data.h" #include <time.h> #include <wayland-server-core.h> #include <wlr/render/wlr_renderer.h> @@ -54,6 +53,7 @@ typedef void (*wlr_scene_buffer_iterator_func_t)( enum wlr_scene_node_type { WLR_SCENE_NODE_TREE, WLR_SCENE_NODE_RECT, + WLR_SCENE_NODE_SHADOW, WLR_SCENE_NODE_BUFFER, }; @@ -140,6 +140,15 @@ struct wlr_scene_rect { float color[4]; }; +/** A scene-graph node displaying a shadow */ +struct wlr_scene_shadow { + struct wlr_scene_node node; + int width, height; + int corner_radius; + float color[4]; + float blur_sigma; +}; + struct wlr_scene_outputs_update_event { struct wlr_scene_output **active; size_t size; @@ -178,7 +187,6 @@ struct wlr_scene_buffer { float opacity; int corner_radius; - struct shadow_data shadow_data; enum wlr_scale_filter_mode filter_mode; struct wlr_fbox src_box; @@ -359,6 +367,12 @@ struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node); struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node); /** + * If this node represents a wlr_scene_shadow, that shadow will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_shadow. + */ +struct wlr_scene_shadow *wlr_scene_shadow_from_node(struct wlr_scene_node *node); + +/** * If this buffer is backed by a surface, then the struct wlr_scene_surface is * returned. If not, NULL will be returned. */ @@ -382,6 +396,33 @@ void wlr_scene_rect_set_size(struct wlr_scene_rect *rect, int width, int height) void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[static 4]); /** + * Add a node displaying a shadow to the scene-graph. + */ +struct wlr_scene_shadow *wlr_scene_shadow_create(struct wlr_scene_tree *parent, + int width, int height, int corner_radius, float blur_sigma, + const float color[static 4]); + +/** + * Change the width and height of an existing shadow node. + */ +void wlr_scene_shadow_set_size(struct wlr_scene_shadow *shadow, int width, int height); + +/** + * Change the corner radius of an existing shadow node. + */ +void wlr_scene_shadow_set_corner_radius(struct wlr_scene_shadow *shadow, int corner_radius); + +/** + * Change the blur_sigma of an existing shadow node. + */ +void wlr_scene_shadow_set_blur_sigma(struct wlr_scene_shadow *shadow, float blur_sigma); + +/** + * Change the color of an existing shadow node. + */ +void wlr_scene_shadow_set_color(struct wlr_scene_shadow *shadow, const float color[static 4]); + +/** * Add a node displaying a buffer to the scene-graph. * * If the buffer is NULL, this node will not be displayed. @@ -457,12 +498,6 @@ void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer, int radii); /** -* Sets the shadow of this buffer -*/ -void wlr_scene_buffer_set_shadow_data(struct wlr_scene_buffer *scene_buffer, - struct shadow_data shadow_data); - -/** * Calls the buffer's frame_done signal. */ void wlr_scene_buffer_send_frame_done(struct wlr_scene_buffer *scene_buffer, diff --git a/render/fx_renderer/fx_pass.c b/render/fx_renderer/fx_pass.c index 808d9f4..8b83a2a 100644 --- a/render/fx_renderer/fx_pass.c +++ b/render/fx_renderer/fx_pass.c @@ -16,7 +16,6 @@ #include "scenefx/render/fx_renderer/fx_renderer.h" #include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "scenefx/types/fx/blur_data.h" -#include "scenefx/types/fx/shadow_data.h" #define MAX_QUADS 86 // 4kb @@ -609,28 +608,26 @@ void fx_render_pass_add_rounded_grad_border_corner(struct fx_gles_render_pass *p void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass, const struct fx_render_box_shadow_options *options) { struct fx_renderer *renderer = pass->buffer->renderer; - struct shadow_data *shadow_data = options->shadow_data; - const struct wlr_render_color *color = &shadow_data->color; - struct wlr_box shadow_box = options->shadow_box; - assert(shadow_box.width > 0 && shadow_box.height > 0); + struct wlr_box box = options->box; + assert(box.width > 0 && box.height > 0); - struct wlr_box surface_box = options->clip_box; + const struct wlr_box window_box = options->window_box; - pixman_region32_t render_region; - pixman_region32_init(&render_region); - - pixman_region32_t inner_region; - pixman_region32_init_rect(&inner_region, - surface_box.x + options->corner_radius * 0.3, - surface_box.y + options->corner_radius * 0.3, - fmax(surface_box.width - options->corner_radius * 0.6, 0), - fmax(surface_box.height - options->corner_radius * 0.6, 0)); - pixman_region32_subtract(&render_region, options->clip, &inner_region); - pixman_region32_fini(&inner_region); + pixman_region32_t clip_region; + if (options->clip) { + pixman_region32_init(&clip_region); + pixman_region32_copy(&clip_region, options->clip); + } else { + pixman_region32_init_rect(&clip_region, box.x, box.y, box.width, box.height); + } + pixman_region32_t window_region; + pixman_region32_init_rect(&window_region, window_box.x + options->window_corner_radius * 0.3, window_box.y + options->window_corner_radius * 0.3, + window_box.width - options->window_corner_radius * 0.6, window_box.height - options->window_corner_radius * 0.6); + pixman_region32_subtract(&clip_region, &clip_region, &window_region); + pixman_region32_fini(&window_region); push_fx_debug(renderer); - // blending will practically always be needed (unless we have a madman // who uses opaque shadows with zero sigma), so just enable it setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED); @@ -638,17 +635,18 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass, glUseProgram(renderer->shaders.box_shadow.program); - set_proj_matrix(renderer->shaders.box_shadow.proj, pass->projection_matrix, &shadow_box); + const struct wlr_render_color *color = &options->color; + set_proj_matrix(renderer->shaders.box_shadow.proj, pass->projection_matrix, &box); glUniform4f(renderer->shaders.box_shadow.color, color->r, color->g, color->b, color->a); - glUniform1f(renderer->shaders.box_shadow.blur_sigma, shadow_data->blur_sigma); + glUniform1f(renderer->shaders.box_shadow.blur_sigma, options->blur_sigma); glUniform1f(renderer->shaders.box_shadow.corner_radius, options->corner_radius); - glUniform2f(renderer->shaders.box_shadow.size, shadow_box.width, shadow_box.height); - glUniform2f(renderer->shaders.box_shadow.offset, options->shadow_data->offset_x, options->shadow_data->offset_y); - glUniform2f(renderer->shaders.box_shadow.position, shadow_box.x, shadow_box.y); + glUniform2f(renderer->shaders.box_shadow.size, box.width, box.height); + glUniform2f(renderer->shaders.box_shadow.position, box.x, box.y); + glUniform1f(renderer->shaders.box_shadow.window_corner_radius, options->window_corner_radius); + glUniform2f(renderer->shaders.box_shadow.window_half_size, window_box.width / 2.0, window_box.height / 2.0); + glUniform2f(renderer->shaders.box_shadow.window_position, window_box.x, window_box.y); - // TODO: also account for options->clip - render(&shadow_box, &render_region, renderer->shaders.box_shadow.pos_attrib); - pixman_region32_fini(&render_region); + render(&box, &clip_region, renderer->shaders.box_shadow.pos_attrib); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/render/fx_renderer/gles2/shaders/box_shadow.frag b/render/fx_renderer/gles2/shaders/box_shadow.frag index 1980434..ff8ac74 100644 --- a/render/fx_renderer/gles2/shaders/box_shadow.frag +++ b/render/fx_renderer/gles2/shaders/box_shadow.frag @@ -11,9 +11,11 @@ varying vec2 v_texcoord; uniform vec2 position; uniform vec2 size; -uniform vec2 offset; uniform float blur_sigma; uniform float corner_radius; +uniform vec2 window_position; +uniform vec2 window_half_size; +uniform float window_corner_radius; float gaussian(float x, float sigma) { const float pi = 3.141592653589793; @@ -80,8 +82,8 @@ void main() { // dither the alpha to break up color bands shadow_alpha += (random() - 0.5) / 128.0; - // get the window alpha so we can render around the window (fix pixel gap by adding 0.5 to radius) - float window_alpha = 1.0 - smoothstep(-1.0, 1.0, roundRectSDF((size * 0.5) - blur_sigma, position + blur_sigma, corner_radius + 0.5)); + // get the window alpha so we can render around the window (fix pixel gap by adding 1.0 to radius) + float window_alpha = smoothstep(-1.0, 1.0, roundRectSDF(window_half_size, window_position, window_corner_radius + 1.0)); - gl_FragColor = vec4(v_color.rgb, shadow_alpha) * (1.0 - window_alpha); + gl_FragColor = vec4(v_color.rgb, shadow_alpha) * window_alpha; } diff --git a/render/fx_renderer/shaders.c b/render/fx_renderer/shaders.c index 9015510..d898fdc 100644 --- a/render/fx_renderer/shaders.c +++ b/render/fx_renderer/shaders.c @@ -295,9 +295,11 @@ bool link_box_shadow_program(struct box_shadow_shader *shader) { shader->pos_attrib = glGetAttribLocation(prog, "pos"); shader->position = glGetUniformLocation(prog, "position"); shader->size = glGetUniformLocation(prog, "size"); - shader->offset = glGetUniformLocation(prog, "offset"); shader->blur_sigma = glGetUniformLocation(prog, "blur_sigma"); shader->corner_radius = glGetUniformLocation(prog, "corner_radius"); + shader->window_position = glGetUniformLocation(prog, "window_position"); + shader->window_half_size = glGetUniformLocation(prog, "window_half_size"); + shader->window_corner_radius = glGetUniformLocation(prog, "window_corner_radius"); return true; } diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index c7dfa3a..973780a 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -6,7 +6,6 @@ #include <stdio.h> #include <time.h> #include <scenefx/render/fx_renderer/fx_renderer.h> -#include <scenefx/types/fx/shadow_data.h> #include <scenefx/types/wlr_scene.h> #include <unistd.h> #include <wayland-server-core.h> @@ -85,9 +84,11 @@ struct tinywl_toplevel { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_toplevel *xdg_toplevel; + struct wlr_scene_tree *xdg_scene_tree; struct wlr_scene_tree *scene_tree; struct wl_listener map; struct wl_listener unmap; + struct wl_listener commit; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; @@ -96,7 +97,7 @@ struct tinywl_toplevel { float opacity; int corner_radius; - struct shadow_data shadow_data; + struct wlr_scene_shadow *shadow; }; struct tinywl_keyboard { @@ -583,13 +584,10 @@ static void output_configure_scene(struct wlr_scene_node *node, if (toplevel && xdg_surface && xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - // TODO: Be able to set whole decoration_data instead of calling - // each individually? wlr_scene_buffer_set_opacity(buffer, toplevel->opacity); if (!wlr_subsurface_try_from_wlr_surface(xdg_surface->surface)) { wlr_scene_buffer_set_corner_radius(buffer, toplevel->corner_radius); - wlr_scene_buffer_set_shadow_data(buffer, toplevel->shadow_data); } } } else if (node->type == WLR_SCENE_NODE_TREE) { @@ -707,15 +705,30 @@ static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, map); + wlr_scene_node_set_enabled(&toplevel->scene_tree->node, true); + wl_list_insert(&toplevel->server->toplevels, &toplevel->link); - focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); + struct wlr_surface *surface = toplevel->xdg_toplevel->base->surface; + + int blur_sigma = toplevel->shadow->blur_sigma; + struct wlr_box geometry; + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geometry); + wlr_scene_shadow_set_size(toplevel->shadow, + geometry.width + blur_sigma * 2, + geometry.height + blur_sigma * 2); + + wlr_scene_node_set_position(&toplevel->shadow->node, -blur_sigma, -blur_sigma); + + focus_toplevel(toplevel, surface); } static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); + wlr_scene_node_set_enabled(&toplevel->scene_tree->node, false); + /* Reset the cursor mode if the grabbed toplevel was unmapped. */ if (toplevel == toplevel->server->grabbed_toplevel) { reset_cursor_mode(toplevel->server); @@ -724,18 +737,33 @@ static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&toplevel->link); } +static void xdg_toplevel_commit(struct wl_listener *listener, void *data) { + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, commit); + + struct wlr_box geometry; + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geometry); + wlr_scene_subsurface_tree_set_clip(&toplevel->xdg_scene_tree->node, &geometry); + + int blur_sigma = toplevel->shadow->blur_sigma; + wlr_scene_shadow_set_size(toplevel->shadow, + geometry.width + blur_sigma * 2, geometry.height + blur_sigma * 2); +} + static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the xdg_toplevel is destroyed. */ struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, destroy); wl_list_remove(&toplevel->map.link); wl_list_remove(&toplevel->unmap.link); + wl_list_remove(&toplevel->commit.link); wl_list_remove(&toplevel->destroy.link); wl_list_remove(&toplevel->request_move.link); wl_list_remove(&toplevel->request_resize.link); wl_list_remove(&toplevel->request_maximize.link); wl_list_remove(&toplevel->request_fullscreen.link); + wlr_scene_node_destroy(&toplevel->scene_tree->node); + free(toplevel); } @@ -847,23 +875,29 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { struct tinywl_toplevel *toplevel = calloc(1, sizeof(*toplevel)); toplevel->server = server; toplevel->xdg_toplevel = xdg_surface->toplevel; - toplevel->scene_tree = wlr_scene_xdg_surface_create( - &toplevel->server->scene->tree, toplevel->xdg_toplevel->base); + toplevel->scene_tree = wlr_scene_tree_create(&toplevel->server->scene->tree); + toplevel->xdg_scene_tree = wlr_scene_xdg_surface_create( + toplevel->scene_tree, toplevel->xdg_toplevel->base); toplevel->scene_tree->node.data = toplevel; xdg_surface->data = toplevel->scene_tree; /* Set the scene_nodes decoration data */ toplevel->opacity = 1; toplevel->corner_radius = 20; - toplevel->shadow_data = shadow_data_get_default(); - toplevel->shadow_data.enabled = true; - toplevel->shadow_data.color = (struct wlr_render_color) {1.0f, 0.0f, 0.0f, 1.0f}; + + float blur_sigma = 20.0f; + toplevel->shadow = wlr_scene_shadow_create(toplevel->scene_tree, + 0, 0, toplevel->corner_radius, blur_sigma, (float[4]){ 1.0f, 0.f, 0.f, 1.0f }); + // Lower the shadow below the XDG scene tree + wlr_scene_node_lower_to_bottom(&toplevel->shadow->node); /* Listen to the various events it can emit */ toplevel->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->surface->events.map, &toplevel->map); toplevel->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_surface->surface->events.unmap, &toplevel->unmap); + toplevel->commit.notify = xdg_toplevel_commit; + wl_signal_add(&xdg_surface->surface->events.commit, &toplevel->commit); toplevel->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); diff --git a/types/fx/meson.build b/types/fx/meson.build index 3932965..d8bef8c 100644 --- a/types/fx/meson.build +++ b/types/fx/meson.build @@ -1,4 +1,3 @@ scenefx_files += files( - 'shadow_data.c', 'blur_data.c', ) diff --git a/types/fx/shadow_data.c b/types/fx/shadow_data.c deleted file mode 100644 index d74cadc..0000000 --- a/types/fx/shadow_data.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "scenefx/types/fx/shadow_data.h" - -struct shadow_data shadow_data_get_default(void) { - return (struct shadow_data) { - .blur_sigma = 20, - .color = {0.0f, 0.0f, 0.0f, 0.5f}, - .enabled = false, - .offset_x = 0.0f, - .offset_y = 0.0f, - }; -} - -bool scene_buffer_has_shadow(struct shadow_data *data) { - return data->enabled && data->blur_sigma > 0 && data->color.a > 0.0; -} diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 2f4dd45..e5d4f39 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -2,7 +2,6 @@ #include <assert.h> #include <pixman.h> #include <stdlib.h> -#include <scenefx/types/fx/shadow_data.h> #include <scenefx/types/wlr_scene.h> #include <string.h> #include <wlr/backend.h> @@ -18,7 +17,6 @@ #include <wlr/render/swapchain.h> #include "scenefx/render/pass.h" -#include "scenefx/types/fx/shadow_data.h" #include "types/wlr_buffer.h" #include "types/wlr_output.h" #include "types/wlr_scene.h" @@ -47,6 +45,12 @@ struct wlr_scene_buffer *wlr_scene_buffer_from_node( return buffer; } +struct wlr_scene_shadow *wlr_scene_shadow_from_node(struct wlr_scene_node *node) { + assert(node->type == WLR_SCENE_NODE_SHADOW); + struct wlr_scene_shadow *shadow = wl_container_of(node, shadow, node); + return shadow; +} + struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { struct wlr_scene_tree *tree; if (node->type == WLR_SCENE_NODE_TREE) { @@ -214,6 +218,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box } break; case WLR_SCENE_NODE_RECT: + case WLR_SCENE_NODE_SHADOW: case WLR_SCENE_NODE_BUFFER:; struct wlr_box node_box = { .x = lx, .y = ly }; scene_node_get_size(node, &node_box.width, &node_box.height); @@ -246,6 +251,9 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, if (scene_rect->color[3] != 1) { return; } + } else if (node->type == WLR_SCENE_NODE_SHADOW) { + // TODO: test & handle case of blur sigma = 0 and color[3] = 1? + return; } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); @@ -258,10 +266,7 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, } if (scene_buffer->corner_radius > 0) { - return; - } - - if (scene_buffer_has_shadow(&scene_buffer->shadow_data)) { + // TODO: this is incorrect return; } @@ -474,24 +479,6 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, pixman_region32_fini(&opaque); } - // Expand the nodes visible region by the shadow size - if (node->type == WLR_SCENE_NODE_BUFFER) { - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct shadow_data *shadow_data = &buffer->shadow_data; - if (scene_buffer_has_shadow(shadow_data)) { - // Expand towards the damage while also compensating for the x and y - // offsets - pixman_region32_t shadow; - pixman_region32_init(&shadow); - pixman_region32_copy(&shadow, &node->visible); - wlr_region_expand(&shadow, &shadow, shadow_data->blur_sigma); - pixman_region32_translate(&shadow, shadow_data->offset_x, shadow_data->offset_y); - pixman_region32_subtract(&shadow, &shadow, &node->visible); - pixman_region32_union(&node->visible, &node->visible, &shadow); - pixman_region32_fini(&shadow); - } - } - update_node_update_outputs(node, data->outputs, NULL, NULL); return false; @@ -634,6 +621,64 @@ void wlr_scene_rect_set_color(struct wlr_scene_rect *rect, const float color[sta scene_node_update(&rect->node, NULL); } +struct wlr_scene_shadow *wlr_scene_shadow_create(struct wlr_scene_tree *parent, + int width, int height, int corner_radius, float blur_sigma, + const float color [static 4]) { + struct wlr_scene_shadow *scene_shadow = calloc(1, sizeof(*scene_shadow)); + if (scene_shadow == NULL) { + return NULL; + } + assert(parent); + scene_node_init(&scene_shadow->node, WLR_SCENE_NODE_SHADOW, parent); + + scene_shadow->width = width; + scene_shadow->height = height; + scene_shadow->corner_radius = corner_radius; + scene_shadow->blur_sigma = blur_sigma; + memcpy(scene_shadow->color, color, sizeof(scene_shadow->color)); + + scene_node_update(&scene_shadow->node, NULL); + + return scene_shadow; +} + +void wlr_scene_shadow_set_size(struct wlr_scene_shadow *shadow, int width, int height) { + if (shadow->width == width && shadow->height == height) { + return; + } + + shadow->width = width; + shadow->height = height; + scene_node_update(&shadow->node, NULL); +} + +void wlr_scene_shadow_set_corner_radius(struct wlr_scene_shadow *shadow, int corner_radius) { + if (shadow->corner_radius == corner_radius) { + return; + } + + shadow->corner_radius = corner_radius; + scene_node_update(&shadow->node, NULL); +} + +void wlr_scene_shadow_set_blur_sigma(struct wlr_scene_shadow *shadow, float blur_sigma) { + if (shadow->blur_sigma == blur_sigma) { + return; + } + + shadow->blur_sigma = blur_sigma; + scene_node_update(&shadow->node, NULL); +} + +void wlr_scene_shadow_set_color(struct wlr_scene_shadow *shadow, const float color[static 4]) { + if (memcmp(shadow->color, color, sizeof(shadow->color)) == 0) { + return; + } + + memcpy(shadow->color, color, sizeof(shadow->color)); + scene_node_update(&shadow->node, NULL); +} + struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, struct wlr_buffer *buffer) { struct wlr_scene_buffer *scene_buffer = calloc(1, sizeof(*scene_buffer)); @@ -655,7 +700,6 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, pixman_region32_init(&scene_buffer->opaque_region); scene_buffer->opacity = 1; scene_buffer->corner_radius = 0; - scene_buffer->shadow_data = shadow_data_get_default(); scene_node_update(&scene_buffer->node, NULL); @@ -886,25 +930,6 @@ void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer, scene_node_update(&scene_buffer->node, NULL); } -void wlr_scene_buffer_set_shadow_data(struct wlr_scene_buffer *scene_buffer, - struct shadow_data shadow_data) { - struct shadow_data *buff_data = &scene_buffer->shadow_data; - if (buff_data->enabled == shadow_data.enabled && - buff_data->blur_sigma == shadow_data.blur_sigma && - buff_data->offset_x == shadow_data.offset_x && - buff_data->offset_y == shadow_data.offset_y && - buff_data->color.r && shadow_data.color.r && - buff_data->color.g && shadow_data.color.g && - buff_data->color.b && shadow_data.color.b && - buff_data->color.a && shadow_data.color.a) { - return; - } - - memcpy(&scene_buffer->shadow_data, &shadow_data, - sizeof(struct shadow_data)); - scene_node_update(&scene_buffer->node, NULL); -} - static struct wlr_texture *scene_buffer_get_texture( struct wlr_scene_buffer *scene_buffer, struct wlr_renderer *renderer) { struct wlr_client_buffer *client_buffer = @@ -935,6 +960,12 @@ static void scene_node_get_size(struct wlr_scene_node *node, *width = scene_rect->width; *height = scene_rect->height; break; + case WLR_SCENE_NODE_SHADOW:; + // TODO: compensate for blur_sigma? may not be needed + struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node); + *width = scene_shadow->width; + *height = scene_shadow->height; + break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->dst_width > 0 && scene_buffer->dst_height > 0) { @@ -953,6 +984,25 @@ static void scene_node_get_size(struct wlr_scene_node *node, } } +static void scene_node_get_corner_radius(struct wlr_scene_node *node, int *corner_radius) { + *corner_radius = 0; + + switch (node->type) { + case WLR_SCENE_NODE_TREE: + return; + case WLR_SCENE_NODE_RECT:; + return; + case WLR_SCENE_NODE_SHADOW:; + struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node); + *corner_radius = scene_shadow->corner_radius; + break; + case WLR_SCENE_NODE_BUFFER:; + struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); + *corner_radius = scene_buffer->corner_radius; + break; + } +} + static int scale_length(int length, int offset, float scale) { return round((offset + length) * scale) - round(offset * scale); } @@ -1133,6 +1183,9 @@ static bool scene_node_at_iterator(struct wlr_scene_node *node, !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) { return false; } + } else if (node->type == WLR_SCENE_NODE_SHADOW) { + // Disable interaction + return false; } at_data->rx = rx; @@ -1168,6 +1221,67 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, return NULL; } +static void get_tree_geometry(struct wlr_scene_node *node, + pixman_region32_t *region, int *max_corner_radius) { + if (!node->enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_BUFFER || node->type == WLR_SCENE_NODE_RECT) { + struct wlr_box child_box = {0}; + int radius = 0; + wlr_scene_node_coords(node, &child_box.x, &child_box.y); + scene_node_get_size(node, &child_box.width, &child_box.height); + scene_node_get_corner_radius(node, &radius); + + pixman_region32_union_rect(region, region, + child_box.x, child_box.y, child_box.width, child_box.height); + if (radius > *max_corner_radius) { + *max_corner_radius = radius; + } + } else if (node->type == WLR_SCENE_NODE_TREE) { + struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node); + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + // Could possibly be a tree, so look through it as well + get_tree_geometry(node, region, max_corner_radius); + } + } +} + +static void scene_get_next_sibling_geometry(struct wlr_scene_node *node, + struct wlr_box *box, int *corner_radius) { + assert(box && corner_radius); + + *box = (struct wlr_box) {0}; + *corner_radius = 0; + + // Don't check if it's the only child in the tree + if (node->link.next == node->link.prev) { + return; + } + + struct wlr_scene_node *sibling_node = + wl_container_of(node->link.next, sibling_node, link); + + pixman_region32_t region; + pixman_region32_init(®ion); + + get_tree_geometry(sibling_node, ®ion, corner_radius); + + if (pixman_region32_not_empty(®ion)) { + struct pixman_box32 *region_box = pixman_region32_extents(®ion); + *box = (struct wlr_box){ + .x = region_box->x1, + .y = region_box->y1, + .width = region_box->x2 - region_box->x1, + .height = region_box->y2 - region_box->y1, + }; + } + + pixman_region32_fini(®ion); +} + struct render_list_entry { struct wlr_scene_node *node; bool sent_dmabuf_feedback; @@ -1198,9 +1312,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren scene_node_get_size(node, &dst_box.width, &dst_box.height); scale_box(&dst_box, data->scale); - // Shadow box - struct wlr_box shadow_box = dst_box; - pixman_region32_t opaque; pixman_region32_init(&opaque); scene_node_opaque_region(node, x, y, &opaque); @@ -1231,6 +1342,36 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren }; fx_render_pass_add_rect(data->render_pass, &rect_options); break; + case WLR_SCENE_NODE_SHADOW:; + struct wlr_scene_shadow *scene_shadow = wlr_scene_shadow_from_node(node); + + // Look through the first direct sibling node and get the size of the + // node. If the sibling is a tree, get the total size of the whole tree. + // Cannot grab the first buffer in the tree due to it potentially being + // a CSD surface and not the actual toplevel surface. + struct wlr_box window_box; + int window_corner_radius; + scene_get_next_sibling_geometry(node, &window_box, &window_corner_radius); + + transform_output_box(&window_box, data); + + struct fx_render_box_shadow_options shadow_options = { + .box = dst_box, + // TODO: Use dst_box if larger? + .window_box = window_box, + .window_corner_radius = window_corner_radius, + .blur_sigma = scene_shadow->blur_sigma, + .corner_radius = scene_shadow->corner_radius, + .color = { + .r = scene_shadow->color[0], + .g = scene_shadow->color[1], + .b = scene_shadow->color[2], + .a = scene_shadow->color[3], + }, + .clip = &render_region, + }; + fx_render_pass_add_box_shadow(data->render_pass, &shadow_options); + break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); assert(scene_buffer->buffer); @@ -1245,27 +1386,6 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren wlr_output_transform_invert(scene_buffer->transform); transform = wlr_output_transform_compose(transform, data->transform); - // Shadow - if (scene_buffer_has_shadow(&scene_buffer->shadow_data)) { - struct shadow_data shadow_data = scene_buffer->shadow_data; - shadow_box.x -= shadow_data.blur_sigma - shadow_data.offset_x; - shadow_box.y -= shadow_data.blur_sigma - shadow_data.offset_y; - shadow_box.width += shadow_data.blur_sigma * 2; - shadow_box.height += shadow_data.blur_sigma * 2; - transform_output_box(&shadow_box, data); - - shadow_data.color.a *= scene_buffer->opacity; - - struct fx_render_box_shadow_options shadow_options = { - .shadow_box = shadow_box, - .clip_box = dst_box, - .clip = &render_region, - .shadow_data = &shadow_data, - .corner_radius = scene_buffer->corner_radius * data->scale, - }; - fx_render_pass_add_box_shadow(data->render_pass, &shadow_options); - } - struct fx_render_texture_options tex_options = { .base = (struct wlr_render_texture_options){ .texture = texture, @@ -1512,6 +1632,10 @@ static bool scene_node_invisible(struct wlr_scene_node *node) { struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); return rect->color[3] == 0.f; + } else if (node->type == WLR_SCENE_NODE_SHADOW) { + struct wlr_scene_shadow *shadow = wlr_scene_shadow_from_node(node); + + return shadow->color[3] == 0.f; } else if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); |