diff options
author | Erik Reider <[email protected]> | 2023-12-30 11:25:16 +0100 |
---|---|---|
committer | Erik Reider <[email protected]> | 2024-01-02 11:21:58 +0100 |
commit | 0b52aa9d137b03017313e028accc92dc5d536440 (patch) | |
tree | 06649993526e0a339fff34f0ae8b4c8ec2fa4d13 | |
parent | b929a2bbadf467864796ad4ec90882ce86cfebff (diff) |
Initial rebase without effects
-rw-r--r-- | include/render/fx_renderer/matrix.h | 8 | ||||
-rw-r--r-- | include/render/pixel_format.h | 56 | ||||
-rw-r--r-- | include/types/wlr_buffer.h | 69 | ||||
-rw-r--r-- | include/types/wlr_output.h | 10 | ||||
-rw-r--r-- | include/types/wlr_scene.h | 2 | ||||
-rw-r--r-- | include/util/env.h | 14 | ||||
-rw-r--r-- | include/util/time.h | 8 | ||||
-rw-r--r-- | include/wlr/types/wlr_scene.h | 152 | ||||
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | render/pixel_format.c | 398 | ||||
-rw-r--r-- | tinywl/tinywl.c | 407 | ||||
-rw-r--r-- | types/buffer/buffer.c | 43 | ||||
-rw-r--r-- | types/meson.build | 6 | ||||
-rw-r--r-- | types/output/wlr_output.c | 24 | ||||
-rw-r--r-- | types/scene/layer_shell_v1.c | 186 | ||||
-rw-r--r-- | types/scene/output_layout.c | 157 | ||||
-rw-r--r-- | types/scene/subsurface_tree.c | 259 | ||||
-rw-r--r-- | types/scene/surface.c | 211 | ||||
-rw-r--r-- | types/scene/wlr_scene.c | 1299 | ||||
-rw-r--r-- | types/scene/xdg_shell.c | 124 | ||||
-rw-r--r-- | util/env.c | 2 | ||||
-rw-r--r-- | util/time.c | 6 |
22 files changed, 1647 insertions, 1796 deletions
diff --git a/include/render/fx_renderer/matrix.h b/include/render/fx_renderer/matrix.h index 6931e8d..c3bae42 100644 --- a/include/render/fx_renderer/matrix.h +++ b/include/render/fx_renderer/matrix.h @@ -3,7 +3,13 @@ #include <wlr/types/wlr_output.h> +/** + * Writes a 2D orthographic projection matrix to mat of (width, height) with a + * specified wl_output_transform. + * + * Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied. + */ void matrix_projection(float mat[static 9], int width, int height, - enum wl_output_transform transform); + enum wl_output_transform transform); #endif diff --git a/include/render/pixel_format.h b/include/render/pixel_format.h index d045b6a..a024ff9 100644 --- a/include/render/pixel_format.h +++ b/include/render/pixel_format.h @@ -3,24 +3,62 @@ #include <wayland-server-protocol.h> +/** + * Information about a pixel format. + * + * A pixel format is identified via its DRM four character code (see <drm_fourcc.h>). + * + * Simple formats have a block size of 1×1 pixels and bytes_per_block contains + * the number of bytes per pixel (including padding). + * + * Tiled formats (e.g. sub-sampled YCbCr) are described with a block size + * greater than 1×1 pixels. A block is a rectangle of pixels which are stored + * next to each other in a byte-aligned memory region. + */ struct wlr_pixel_format_info { - uint32_t drm_format; + uint32_t drm_format; - /* Equivalent of the format if it has an alpha channel, - * DRM_FORMAT_INVALID (0) if NA - */ - uint32_t opaque_substitute; + /* Equivalent of the format if it has an alpha channel, + * DRM_FORMAT_INVALID (0) if NA + */ + uint32_t opaque_substitute; - /* Bits per pixels */ - uint32_t bpp; + /* Bytes per block (including padding) */ + uint32_t bytes_per_block; + /* Size of a block in pixels (zero for 1×1) */ + uint32_t block_width, block_height; - /* True if the format has an alpha channel */ - bool has_alpha; + /* True if the format has an alpha channel */ + bool has_alpha; }; +/** + * Get pixel format information from a DRM FourCC. + * + * NULL is returned if the pixel format is unknown. + */ const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt); +/** + * Get the number of pixels per block for a pixel format. + */ +uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info *info); +/** + * Get the minimum stride for a given pixel format and width. + */ +int32_t pixel_format_info_min_stride(const struct wlr_pixel_format_info *info, int32_t width); +/** + * Check whether a stride is large enough for a given pixel format and width. + */ +bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *info, + int32_t stride, int32_t width); +/** + * Convert an enum wl_shm_format to a DRM FourCC. + */ uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt); +/** + * Convert a DRM FourCC to an enum wl_shm_format. + */ enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt); #endif diff --git a/include/types/wlr_buffer.h b/include/types/wlr_buffer.h index 59d78e9..016cae8 100644 --- a/include/types/wlr_buffer.h +++ b/include/types/wlr_buffer.h @@ -3,75 +3,6 @@ #include <wlr/types/wlr_buffer.h> -struct wlr_shm_client_buffer { - struct wlr_buffer base; - - uint32_t format; - size_t stride; - - // The following fields are NULL if the client has destroyed the wl_buffer - struct wl_resource *resource; - struct wl_shm_buffer *shm_buffer; - - // This is used to keep the backing storage alive after the client has - // destroyed the wl_buffer - struct wl_shm_pool *saved_shm_pool; - void *saved_data; - - struct wl_listener resource_destroy; - struct wl_listener release; -}; - -struct wlr_shm_client_buffer *shm_client_buffer_get_or_create( - struct wl_resource *resource); - -/** - * A read-only buffer that holds a data pointer. - * - * This is suitable for passing raw pixel data to a function that accepts a - * wlr_buffer. - */ -struct wlr_readonly_data_buffer { - struct wlr_buffer base; - - const void *data; - uint32_t format; - size_t stride; - - void *saved_data; -}; - -/** - * Wraps a read-only data pointer into a wlr_buffer. The data pointer may be - * accessed until readonly_data_buffer_drop() is called. - */ -struct wlr_readonly_data_buffer *readonly_data_buffer_create(uint32_t format, - size_t stride, uint32_t width, uint32_t height, const void *data); -/** - * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and - * perform a copy of the data pointer if a consumer still has the buffer locked. - */ -bool readonly_data_buffer_drop(struct wlr_readonly_data_buffer *buffer); - -struct wlr_dmabuf_buffer { - struct wlr_buffer base; - struct wlr_dmabuf_attributes dmabuf; - bool saved; -}; - -/** - * Wraps a DMA-BUF into a wlr_buffer. The DMA-BUF may be accessed until - * dmabuf_buffer_drop() is called. - */ -struct wlr_dmabuf_buffer *dmabuf_buffer_create( - struct wlr_dmabuf_attributes *dmabuf); -/** - * Drops ownership of the buffer (see wlr_buffer_drop() for more details) and - * takes a reference to the DMA-BUF (by dup'ing its file descriptors) if a - * consumer still has the buffer locked. - */ -bool dmabuf_buffer_drop(struct wlr_dmabuf_buffer *buffer); - /** * Check whether a buffer is fully opaque. * diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h new file mode 100644 index 0000000..b239b6a --- /dev/null +++ b/include/types/wlr_output.h @@ -0,0 +1,10 @@ +#ifndef TYPES_WLR_OUTPUT_H +#define TYPES_WLR_OUTPUT_H + +#include <wlr/render/drm_format_set.h> +#include <wlr/types/wlr_output.h> + +void output_pending_resolution(struct wlr_output *output, + const struct wlr_output_state *state, int *width, int *height); + +#endif diff --git a/include/types/wlr_scene.h b/include/types/wlr_scene.h index 64c11bc..80dcfd1 100644 --- a/include/types/wlr_scene.h +++ b/include/types/wlr_scene.h @@ -5,4 +5,6 @@ struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node); +void scene_surface_set_clip(struct wlr_scene_surface *surface, struct wlr_box *clip); + #endif diff --git a/include/util/env.h b/include/util/env.h index 6720fa8..e271f4b 100644 --- a/include/util/env.h +++ b/include/util/env.h @@ -4,8 +4,20 @@ #include <stdbool.h> #include <unistd.h> +/** + * Parse a bool from an environment variable. + * + * On success, the parsed value is returned. On error, false is returned. + */ bool env_parse_bool(const char *option); -ssize_t env_parse_switch(const char *option, const char **switches); +/** + * Pick a choice from an environment variable. + * + * On success, the choice index is returned. On error, zero is returned. + * + * switches is a NULL-terminated array. + */ +size_t env_parse_switch(const char *option, const char **switches); #endif diff --git a/include/util/time.h b/include/util/time.h index 287698d..3f76aa4 100644 --- a/include/util/time.h +++ b/include/util/time.h @@ -1,12 +1,13 @@ #ifndef UTIL_TIME_H #define UTIL_TIME_H +#include <stdint.h> #include <time.h> /** * Get the current time, in milliseconds. */ -uint32_t get_current_time_msec(void); +int64_t get_current_time_msec(void); /** * Convert a timespec to milliseconds. @@ -14,6 +15,11 @@ uint32_t get_current_time_msec(void); int64_t timespec_to_msec(const struct timespec *a); /** + * Convert a timespec to nanoseconds. + */ +int64_t timespec_to_nsec(const struct timespec *a); + +/** * Convert nanoseconds to a timespec. */ void timespec_from_nsec(struct timespec *r, int64_t nsec); diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 7b4c002..f52e167 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -20,21 +20,33 @@ */ #include <pixman.h> +#include <time.h> #include <wayland-server-core.h> -#include <wlr/types/wlr_compositor.h> +#include <wlr/render/wlr_renderer.h> #include <wlr/types/wlr_damage_ring.h> #include "types/fx/shadow_data.h" +#include <wlr/types/wlr_linux_dmabuf_v1.h> +#include <wlr/util/addon.h> +#include <wlr/util/box.h> struct wlr_output; struct wlr_output_layout; +struct wlr_output_layout_output; struct wlr_xdg_surface; struct wlr_layer_surface_v1; +struct wlr_drag_icon; +struct wlr_surface; struct wlr_scene_node; struct wlr_scene_buffer; +struct wlr_scene_output_layout; + +struct wlr_presentation; +struct wlr_linux_dmabuf_v1; +struct wlr_output_state; typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( - struct wlr_scene_buffer *buffer, int sx, int sy); + struct wlr_scene_buffer *buffer, double *sx, double *sy); typedef void (*wlr_scene_buffer_iterator_func_t)( struct wlr_scene_buffer *buffer, int sx, int sy, void *user_data); @@ -89,10 +101,12 @@ struct wlr_scene { // May be NULL struct wlr_presentation *presentation; + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; // private state struct wl_listener presentation_destroy; + struct wl_listener linux_dmabuf_v1_destroy; enum wlr_scene_debug_damage_option debug_damage_option; bool direct_scanout; @@ -106,11 +120,14 @@ struct wlr_scene_surface { // private state + struct wlr_box clip; + struct wlr_addon addon; + struct wl_listener outputs_update; struct wl_listener output_enter; struct wl_listener output_leave; - struct wl_listener output_present; + struct wl_listener output_sample; struct wl_listener frame_done; struct wl_listener surface_destroy; struct wl_listener surface_commit; @@ -123,6 +140,16 @@ struct wlr_scene_rect { float color[4]; }; +struct wlr_scene_outputs_update_event { + struct wlr_scene_output **active; + size_t size; +}; + +struct wlr_scene_output_sample_event { + struct wlr_scene_output *output; + bool direct_scanout; +}; + /** A scene-graph node displaying a buffer */ struct wlr_scene_buffer { struct wlr_scene_node node; @@ -131,9 +158,10 @@ struct wlr_scene_buffer { struct wlr_buffer *buffer; struct { + struct wl_signal outputs_update; // struct wlr_scene_outputs_update_event struct wl_signal output_enter; // struct wlr_scene_output struct wl_signal output_leave; // struct wlr_scene_output - struct wl_signal output_present; // struct wlr_scene_output + struct wl_signal output_sample; // struct wlr_scene_output_sample_event struct wl_signal frame_done; // struct timespec } events; @@ -148,18 +176,20 @@ struct wlr_scene_buffer { */ struct wlr_scene_output *primary_output; - // private state - float opacity; int corner_radius; struct shadow_data shadow_data; - - uint64_t active_outputs; - struct wlr_texture *texture; + enum wlr_scale_filter_mode filter_mode; struct wlr_fbox src_box; int dst_width, dst_height; enum wl_output_transform transform; pixman_region32_t opaque_region; + + // private state + + uint64_t active_outputs; + struct wlr_texture *texture; + struct wlr_linux_dmabuf_feedback_v1_init_options prev_feedback_options; }; /** A viewport for an output in the scene-graph */ @@ -183,7 +213,6 @@ struct wlr_scene_output { bool prev_scanout; struct wl_listener output_commit; - struct wl_listener output_mode; struct wl_listener output_damage; struct wl_listener output_needs_frame; @@ -192,6 +221,11 @@ struct wlr_scene_output { struct wl_array render_list; }; +struct wlr_scene_timer { + int64_t pre_render_duration; + struct wlr_render_timer *render_timer; +}; + /** A layer shell scene helper */ struct wlr_scene_layer_surface_v1 { struct wlr_scene_tree *tree; @@ -269,6 +303,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, * Create a new scene-graph. */ struct wlr_scene *wlr_scene_create(void); + /** * Handle presentation feedback for all surfaces in the scene, assuming that * scene outputs and the scene rendering functions are used. @@ -279,6 +314,15 @@ void wlr_scene_set_presentation(struct wlr_scene *scene, struct wlr_presentation *presentation); /** + * Handles linux_dmabuf_v1 feedback for all surfaces in the scene. + * + * Asserts that a struct wlr_linux_dmabuf_v1 hasn't already been set for the scene. + */ +void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1); + + +/** * Add a node displaying nothing but its children. */ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); @@ -295,13 +339,29 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent); struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, struct wlr_surface *surface); +/** + * If this node represents a wlr_scene_buffer, that buffer will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_buffer. + */ struct wlr_scene_buffer *wlr_scene_buffer_from_node(struct wlr_scene_node *node); /** + * If this node represents a wlr_scene_tree, that tree will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_tree. + */ +struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node); + +/** + * If this node represents a wlr_scene_rect, that rect will be returned. It + * is not legal to feed a node that does not represent a wlr_scene_rect. + */ +struct wlr_scene_rect *wlr_scene_rect_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. */ -struct wlr_scene_surface *wlr_scene_surface_from_buffer( +struct wlr_scene_surface *wlr_scene_surface_try_from_buffer( struct wlr_scene_buffer *scene_buffer); /** @@ -343,14 +403,14 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, * the whole buffer node will be damaged. */ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, pixman_region32_t *region); + struct wlr_buffer *buffer, const pixman_region32_t *region); /** * Sets the buffer's opaque region. This is an optimization hint used to * determine if buffers which reside under this one need to be rendered or not. */ void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, - pixman_region32_t *region); + const pixman_region32_t *region); /** * Set the source rectangle describing the region of the buffer which will be @@ -384,6 +444,12 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, float opacity); /** +* Sets the filter mode to use when scaling the buffer +*/ +void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, + enum wlr_scale_filter_mode filter_mode); + +/** * Sets the corner radius of this buffer */ void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer, @@ -417,10 +483,32 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output); */ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly); + +struct wlr_scene_output_state_options { + struct wlr_scene_timer *timer; +}; + /** * Render and commit an output. */ -bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); +bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, + const struct wlr_scene_output_state_options *options); + +/** + * Render and populate given output state. + */ +bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, + struct wlr_output_state *state, const struct wlr_scene_output_state_options *options); + +/** + * Retrieve the duration in nanoseconds between the last wlr_scene_output_commit() call and the end + * of its operations, including those on the GPU that may have finished after the call returned. + * + * Returns -1 if the duration is unavailable. + */ +int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer); +void wlr_scene_timer_finish(struct wlr_scene_timer *timer); + /** * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by * wlr_scene_output_commit() for which wlr_scene_surface.primary_output @@ -446,15 +534,24 @@ struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, /** * Attach an output layout to a scene. * - * Adding, removing, or repositioning an output in the output layout - * will respectively add, remove or reposition a corresponding - * scene-graph output. When the output layout is destroyed, scene-graph - * outputs which were created by this helper will be destroyed. + * The resulting scene output layout allows to synchronize the positions of scene + * outputs with the positions of corresponding layout outputs. + * + * It is automatically destroyed when the scene or the output layout is destroyed. */ -bool wlr_scene_attach_output_layout(struct wlr_scene *scene, +struct wlr_scene_output_layout *wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_output_layout *output_layout); /** + * Add an output to the scene output layout. + * + * When the layout output is repositioned, the scene output will be repositioned + * accordingly. + */ +void wlr_scene_output_layout_add_output(struct wlr_scene_output_layout *sol, + struct wlr_output_layout_output *lo, struct wlr_scene_output *so); + +/** * Add a node displaying a surface and all of its sub-surfaces to the * scene-graph. */ @@ -462,6 +559,16 @@ struct wlr_scene_tree *wlr_scene_subsurface_tree_create( struct wlr_scene_tree *parent, struct wlr_surface *surface); /** + * Sets a cropping region for any subsurface trees that are children of this + * scene node. The clip coordinate space will be that of the root surface of + * the subsurface tree. + * + * A NULL or empty clip will disable clipping + */ +void wlr_scene_subsurface_tree_set_clip(struct wlr_scene_node *node, + struct wlr_box *clip); + +/** * Add a node displaying an xdg_surface and all of its sub-surfaces to the * scene-graph. * @@ -495,4 +602,11 @@ void wlr_scene_layer_surface_v1_configure( struct wlr_scene_layer_surface_v1 *scene_layer_surface, const struct wlr_box *full_area, struct wlr_box *usable_area); +/** + * Add a node displaying a drag icon and all its sub-surfaces to the + * scene-graph. + */ +struct wlr_scene_tree *wlr_scene_drag_icon_create( + struct wlr_scene_tree *parent, struct wlr_drag_icon *drag_icon); + #endif diff --git a/meson.build b/meson.build index e96f2e6..a69a9c6 100644 --- a/meson.build +++ b/meson.build @@ -107,7 +107,7 @@ wayland_server = dependency('wayland-server', ) wlroots_options = [ 'examples=false' ] -wlroots_version = ['>=0.16.0', '<0.17.0'] +wlroots_version = ['>=0.17.0', '<0.18.0'] subproject( 'wlroots', default_options: wlroots_options, diff --git a/render/pixel_format.c b/render/pixel_format.c index 46bcecf..e303799 100644 --- a/render/pixel_format.c +++ b/render/pixel_format.c @@ -1,178 +1,252 @@ -#include "render/pixel_format.h" #include <drm_fourcc.h> +#include <wlr/util/log.h> + +#include "render/pixel_format.h" static const struct wlr_pixel_format_info pixel_format_info[] = { - { - .drm_format = DRM_FORMAT_XRGB8888, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 32, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_ARGB8888, - .opaque_substitute = DRM_FORMAT_XRGB8888, - .bpp = 32, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR8888, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 32, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_ABGR8888, - .opaque_substitute = DRM_FORMAT_XBGR8888, - .bpp = 32, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_RGBX8888, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 32, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_RGBA8888, - .opaque_substitute = DRM_FORMAT_RGBX8888, - .bpp = 32, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_BGRX8888, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 32, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_BGRA8888, - .opaque_substitute = DRM_FORMAT_BGRX8888, - .bpp = 32, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_BGR888, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 24, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_RGBX4444, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 16, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_RGBA4444, - .opaque_substitute = DRM_FORMAT_RGBX4444, - .bpp = 16, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_RGBX5551, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 16, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_RGBA5551, - .opaque_substitute = DRM_FORMAT_RGBX5551, - .bpp = 16, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_RGB565, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 16, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_BGR565, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 16, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_XRGB2101010, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 32, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_ARGB2101010, - .opaque_substitute = DRM_FORMAT_XRGB2101010, - .bpp = 32, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR2101010, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 32, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_ABGR2101010, - .opaque_substitute = DRM_FORMAT_XBGR2101010, - .bpp = 32, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR16161616F, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 64, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_ABGR16161616F, - .opaque_substitute = DRM_FORMAT_XBGR16161616F, - .bpp = 64, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR16161616, - .opaque_substitute = DRM_FORMAT_INVALID, - .bpp = 64, - .has_alpha = false, - }, - { - .drm_format = DRM_FORMAT_ABGR16161616, - .opaque_substitute = DRM_FORMAT_XBGR16161616, - .bpp = 64, - .has_alpha = true, - }, + { + .drm_format = DRM_FORMAT_XRGB8888, + .bytes_per_block = 4, + }, + { + .drm_format = DRM_FORMAT_ARGB8888, + .opaque_substitute = DRM_FORMAT_XRGB8888, + .bytes_per_block = 4, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_XBGR8888, + .bytes_per_block = 4, + }, + { + .drm_format = DRM_FORMAT_ABGR8888, + .opaque_substitute = DRM_FORMAT_XBGR8888, + .bytes_per_block = 4, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_RGBX8888, + .bytes_per_block = 4, + }, + { + .drm_format = DRM_FORMAT_RGBA8888, + .opaque_substitute = DRM_FORMAT_RGBX8888, + .bytes_per_block = 4, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_BGRX8888, + .bytes_per_block = 4, + }, + { + .drm_format = DRM_FORMAT_BGRA8888, + .opaque_substitute = DRM_FORMAT_BGRX8888, + .bytes_per_block = 4, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_R8, + .bytes_per_block = 1, + }, + { + .drm_format = DRM_FORMAT_GR88, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_RGB888, + .bytes_per_block = 3, + }, + { + .drm_format = DRM_FORMAT_BGR888, + .bytes_per_block = 3, + }, + { + .drm_format = DRM_FORMAT_RGBX4444, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_RGBA4444, + .opaque_substitute = DRM_FORMAT_RGBX4444, + .bytes_per_block = 2, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_BGRX4444, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_BGRA4444, + .opaque_substitute = DRM_FORMAT_BGRX4444, + .bytes_per_block = 2, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_RGBX5551, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_RGBA5551, + .opaque_substitute = DRM_FORMAT_RGBX5551, + .bytes_per_block = 2, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_BGRX5551, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_BGRA5551, + .opaque_substitute = DRM_FORMAT_BGRX5551, + .bytes_per_block = 2, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_XRGB1555, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_ARGB1555, + .opaque_substitute = DRM_FORMAT_XRGB1555, + .bytes_per_block = 2, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_RGB565, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_BGR565, + .bytes_per_block = 2, + }, + { + .drm_format = DRM_FORMAT_XRGB2101010, + .bytes_per_block = 4, + }, + { + .drm_format = DRM_FORMAT_ARGB2101010, + .opaque_substitute = DRM_FORMAT_XRGB2101010, + .bytes_per_block = 4, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_XBGR2101010, + .bytes_per_block = 4, + }, + { + .drm_format = DRM_FORMAT_ABGR2101010, + .opaque_substitute = DRM_FORMAT_XBGR2101010, + .bytes_per_block = 4, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_XBGR16161616F, + .bytes_per_block = 8, + }, + { + .drm_format = DRM_FORMAT_ABGR16161616F, + .opaque_substitute = DRM_FORMAT_XBGR16161616F, + .bytes_per_block = 8, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_XBGR16161616, + .bytes_per_block = 8, + }, + { + .drm_format = DRM_FORMAT_ABGR16161616, + .opaque_substitute = DRM_FORMAT_XBGR16161616, + .bytes_per_block = 8, + .has_alpha = true, + }, + { + .drm_format = DRM_FORMAT_YVYU, + .bytes_per_block = 4, + .block_width = 2, + .block_height = 1, + }, + { + .drm_format = DRM_FORMAT_VYUY, + .bytes_per_block = 4, + .block_width = 2, + .block_height = 1, + }, }; static const size_t pixel_format_info_size = - sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); + sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt) { - for (size_t i = 0; i < pixel_format_info_size; ++i) { - if (pixel_format_info[i].drm_format == fmt) { - return &pixel_format_info[i]; - } - } + for (size_t i = 0; i < pixel_format_info_size; ++i) { + if (pixel_format_info[i].drm_format == fmt) { + return &pixel_format_info[i]; + } + } - return NULL; + return NULL; } uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) { - switch (fmt) { - case WL_SHM_FORMAT_XRGB8888: - return DRM_FORMAT_XRGB8888; - case WL_SHM_FORMAT_ARGB8888: - return DRM_FORMAT_ARGB8888; - default: - return (uint32_t)fmt; - } + switch (fmt) { + case WL_SHM_FORMAT_XRGB8888: + return DRM_FORMAT_XRGB8888; + case WL_SHM_FORMAT_ARGB8888: + return DRM_FORMAT_ARGB8888; + default: + return (uint32_t)fmt; + } } enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) { - switch (fmt) { - case DRM_FORMAT_XRGB8888: - return WL_SHM_FORMAT_XRGB8888; - case DRM_FORMAT_ARGB8888: - return WL_SHM_FORMAT_ARGB8888; - default: - return (enum wl_shm_format)fmt; - } + switch (fmt) { + case DRM_FORMAT_XRGB8888: + return WL_SHM_FORMAT_XRGB8888; + case DRM_FORMAT_ARGB8888: + return WL_SHM_FORMAT_ARGB8888; + default: + return (enum wl_shm_format)fmt; + } +} + +uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info *info) { + uint32_t pixels = info->block_width * info->block_height; + return pixels > 0 ? pixels : 1; +} + +static int32_t div_round_up(int32_t dividend, int32_t divisor) { + int32_t quotient = dividend / divisor; + if (dividend % divisor != 0) { + quotient++; + } + return quotient; +} + +int32_t pixel_format_info_min_stride(const struct wlr_pixel_format_info *fmt, int32_t width) { + int32_t pixels_per_block = (int32_t)pixel_format_info_pixels_per_block(fmt); + int32_t bytes_per_block = (int32_t)fmt->bytes_per_block; + if (width > INT32_MAX / bytes_per_block) { + wlr_log(WLR_DEBUG, "Invalid width %d (overflow)", width); + return 0; + } + return div_round_up(width * bytes_per_block, pixels_per_block); +} + +bool pixel_format_info_check_stride(const struct wlr_pixel_format_info *fmt, + int32_t stride, int32_t width) { + int32_t bytes_per_block = (int32_t)fmt->bytes_per_block; + if (stride % bytes_per_block != 0) { + wlr_log(WLR_DEBUG, "Invalid stride %d (incompatible with %d " + "bytes-per-block)", stride, bytes_per_block); + return false; + } + + int32_t min_stride = pixel_format_info_min_stride(fmt, width); + if (min_stride <= 0) { + return false; + } else if (stride < min_stride) { + wlr_log(WLR_DEBUG, "Invalid stride %d (too small for %d " + "bytes-per-block and width %d)", stride, bytes_per_block, width); + return false; + } + + return true; } diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 56091e3..adc7b38 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -42,10 +42,11 @@ struct tinywl_server { struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_scene *scene; + struct wlr_scene_output_layout *scene_layout; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; - struct wl_list views; + struct wl_list toplevels; struct wlr_cursor *cursor; struct wlr_xcursor_manager *cursor_mgr; @@ -61,7 +62,7 @@ struct tinywl_server { struct wl_listener request_set_selection; struct wl_list keyboards; enum tinywl_cursor_mode cursor_mode; - struct tinywl_view *grabbed_view; + struct tinywl_toplevel *grabbed_toplevel; double grab_x, grab_y; struct wlr_box grab_geobox; uint32_t resize_edges; @@ -76,10 +77,11 @@ struct tinywl_output { struct tinywl_server *server; struct wlr_output *wlr_output; struct wl_listener frame; + struct wl_listener request_state; struct wl_listener destroy; }; -struct tinywl_view { +struct tinywl_toplevel { struct wl_list link; struct tinywl_server *server; struct wlr_xdg_toplevel *xdg_toplevel; @@ -91,9 +93,6 @@ struct tinywl_view { struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; - int x, y; - - struct wlr_addon addon; float opacity; int corner_radius; @@ -110,50 +109,12 @@ struct tinywl_keyboard { struct wl_listener destroy; }; -static void tinywl_view_destroy(struct tinywl_view *view) { - wl_list_remove(&view->map.link); - wl_list_remove(&view->unmap.link); - wl_list_remove(&view->destroy.link); - wl_list_remove(&view->request_move.link); - wl_list_remove(&view->request_resize.link); - wl_list_remove(&view->request_maximize.link); - wl_list_remove(&view->request_fullscreen.link); - - free(view); -} - -static void tinywl_view_addon_handle_destroy(struct wlr_addon *addon) { - struct tinywl_view *view = wl_container_of(addon, view, addon); - - tinywl_view_destroy(view); -} -static const struct wlr_addon_interface tinywl_view_addon_impl = { - .name = "tinywl_view", - .destroy = tinywl_view_addon_handle_destroy, -}; - -static void tinywl_view_addon_assign(struct tinywl_view *view, struct wlr_addon_set *addons, - const void * owner) { - wlr_addon_init(&view->addon, addons, owner, &tinywl_view_addon_impl); -} - -static struct tinywl_view *tinywl_view_addon_get(struct wlr_addon_set *addons, - const void * owner) { - struct wlr_addon *addon = - wlr_addon_find(addons, owner, &tinywl_view_addon_impl); - if (addon == NULL) { - return NULL; - } - struct tinywl_view *view = wl_container_of(addon, view, addon); - return view; -} - -static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { +static void focus_toplevel(struct tinywl_toplevel *toplevel, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ - if (view == NULL) { + if (toplevel == NULL) { return; } - struct tinywl_server *server = view->server; + struct tinywl_server *server = toplevel->server; struct wlr_seat *seat = server->seat; struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; if (prev_surface == surface) { @@ -166,25 +127,26 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { * it no longer has focus and the client will repaint accordingly, e.g. * stop displaying a caret. */ - struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( - seat->keyboard_state.focused_surface); - assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - wlr_xdg_toplevel_set_activated(previous->toplevel, false); + struct wlr_xdg_toplevel *prev_toplevel = + wlr_xdg_toplevel_try_from_wlr_surface(prev_surface); + if (prev_toplevel != NULL) { + wlr_xdg_toplevel_set_activated(prev_toplevel, false); + } } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); - /* Move the view to the front */ - wlr_scene_node_raise_to_top(&view->scene_tree->node); - wl_list_remove(&view->link); - wl_list_insert(&server->views, &view->link); + /* Move the toplevel to the front */ + wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); + wl_list_remove(&toplevel->link); + wl_list_insert(&server->toplevels, &toplevel->link); /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true); + wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); /* * Tell the seat to have the keyboard enter this surface. wlroots will keep * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ if (keyboard != NULL) { - wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface, + wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } } @@ -220,13 +182,13 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { wl_display_terminate(server->wl_display); break; case XKB_KEY_F1: - /* Cycle to the next view */ - if (wl_list_length(&server->views) < 2) { + /* Cycle to the next toplevel */ + if (wl_list_length(&server->toplevels) < 2) { break; } - struct tinywl_view *next_view = wl_container_of( - server->views.prev, next_view, link); - focus_view(next_view, next_view->xdg_toplevel->base->surface); + struct tinywl_toplevel *next_toplevel = + wl_container_of(server->toplevels.prev, next_toplevel, link); + focus_toplevel(next_toplevel, next_toplevel->xdg_toplevel->base->surface); break; default: return false; @@ -287,8 +249,7 @@ static void server_new_keyboard(struct tinywl_server *server, struct wlr_input_device *device) { struct wlr_keyboard *wlr_keyboard = wlr_keyboard_from_input_device(device); - struct tinywl_keyboard *keyboard = - calloc(1, sizeof(struct tinywl_keyboard)); + struct tinywl_keyboard *keyboard = calloc(1, sizeof(*keyboard)); keyboard->server = server; keyboard->wlr_keyboard = wlr_keyboard; @@ -382,12 +343,12 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(server->seat, event->source, event->serial); } -static struct tinywl_view *desktop_view_at( +static struct tinywl_toplevel *desktop_toplevel_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { /* This returns the topmost node in the scene at the given layout coords. - * we only care about surface nodes as we are specifically looking for a - * surface in the surface tree of a tinywl_view. */ + * We only care about surface nodes as we are specifically looking for a + * surface in the surface tree of a tinywl_toplevel. */ struct wlr_scene_node *node = wlr_scene_node_at( &server->scene->tree.node, lx, ly, sx, sy); if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) { @@ -395,13 +356,13 @@ static struct tinywl_view *desktop_view_at( } struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_buffer(scene_buffer); + wlr_scene_surface_try_from_buffer(scene_buffer); if (!scene_surface) { return NULL; } *surface = scene_surface->surface; - /* Find the node corresponding to the tinywl_view at the root of this + /* Find the node corresponding to the tinywl_toplevel at the root of this * surface tree, it is the only one for which we set the data field. */ struct wlr_scene_tree *tree = node->parent; while (tree != NULL && tree->node.data == NULL) { @@ -413,29 +374,29 @@ static struct tinywl_view *desktop_view_at( static void reset_cursor_mode(struct tinywl_server *server) { /* Reset the cursor mode to passthrough. */ server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; - server->grabbed_view = NULL; + server->grabbed_toplevel = NULL; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { - /* Move the grabbed view to the new position. */ - struct tinywl_view *view = server->grabbed_view; - view->x = server->cursor->x - server->grab_x; - view->y = server->cursor->y - server->grab_y; - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); + /* Move the grabbed toplevel to the new position. */ + struct tinywl_toplevel *toplevel = server->grabbed_toplevel; + wlr_scene_node_set_position(&toplevel->scene_tree->node, + server->cursor->x - server->grab_x, + server->cursor->y - server->grab_y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { /* - * Resizing the grabbed view can be a little bit complicated, because we - * could be resizing from any corner or edge. This not only resizes the view - * on one or two axes, but can also move the view if you resize from the top - * or left edges (or top-left corner). + * Resizing the grabbed toplevel can be a little bit complicated, because we + * could be resizing from any corner or edge. This not only resizes the + * toplevel on one or two axes, but can also move the toplevel if you resize + * from the top or left edges (or top-left corner). * - * Note that I took some shortcuts here. In a more fleshed-out compositor, - * you'd wait for the client to prepare a buffer at the new size, then - * commit any movement that was prepared. + * Note that some shortcuts are taken here. In a more fleshed-out + * compositor, you'd wait for the client to prepare a buffer at the new + * size, then commit any movement that was prepared. */ - struct tinywl_view *view = server->grabbed_view; + struct tinywl_toplevel *toplevel = server->grabbed_toplevel; double border_x = server->cursor->x - server->grab_x; double border_y = server->cursor->y - server->grab_y; int new_left = server->grab_geobox.x; @@ -467,14 +428,13 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { } struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); - view->x = new_left - geo_box.x; - view->y = new_top - geo_box.y; - wlr_scene_node_set_position(&view->scene_tree->node, view->x, view->y); + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); + wlr_scene_node_set_position(&toplevel->scene_tree->node, + new_left - geo_box.x, new_top - geo_box.y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; - wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height); + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, new_width, new_height); } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { @@ -487,18 +447,17 @@ static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { return; } - /* Otherwise, find the view under the pointer and send the event along. */ + /* Otherwise, find the toplevel under the pointer and send the event along. */ double sx, sy; struct wlr_seat *seat = server->seat; struct wlr_surface *surface = NULL; - struct tinywl_view *view = desktop_view_at(server, + struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); - if (!view) { - /* If there's no view under the cursor, set the cursor image to a + if (!toplevel) { + /* If there's no toplevel under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it - * around the screen, not over any views. */ - wlr_xcursor_manager_set_cursor_image( - server->cursor_mgr, "left_ptr", server->cursor); + * around the screen, not over any toplevels. */ + wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); } if (surface) { /* @@ -564,14 +523,14 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { event->time_msec, event->button, event->state); double sx, sy; struct wlr_surface *surface = NULL; - struct tinywl_view *view = desktop_view_at(server, + struct tinywl_toplevel *toplevel = desktop_toplevel_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { /* If you released any buttons, we exit interactive move/resize mode. */ reset_cursor_mode(server); } else { /* Focus that client if the button was _pressed_ */ - focus_view(view, surface); + focus_toplevel(toplevel, surface); } } @@ -599,47 +558,45 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { } static void output_configure_scene(struct wlr_scene_node *node, - struct tinywl_view *view) { + struct tinywl_toplevel *toplevel) { if (!node->enabled) { return; } - struct tinywl_view *_view = tinywl_view_addon_get(&node->addons, node); - if (_view) { - view = _view; + struct tinywl_toplevel *_toplevel = node->data; + if (_toplevel) { + toplevel = _toplevel; } if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct wlr_scene_surface * scene_surface - = wlr_scene_surface_from_buffer(buffer); + struct wlr_scene_surface * scene_surface = + wlr_scene_surface_try_from_buffer(buffer); if (!scene_surface) { return; } - struct wlr_xdg_surface *xdg_surface = NULL; - if (wlr_surface_is_xdg_surface(scene_surface->surface)) { - xdg_surface = wlr_xdg_surface_from_wlr_surface(scene_surface->surface); - } + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_try_from_wlr_surface(scene_surface->surface); - if (view && + 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, view->opacity); + wlr_scene_buffer_set_opacity(buffer, toplevel->opacity); - if (!wlr_surface_is_subsurface(xdg_surface->surface)) { - wlr_scene_buffer_set_corner_radius(buffer, view->corner_radius); - wlr_scene_buffer_set_shadow_data(buffer, view->shadow_data); + 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) { struct wlr_scene_tree *tree = wl_container_of(node, tree, node); struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { - output_configure_scene(node, view); + output_configure_scene(node, toplevel); } } } @@ -656,17 +613,27 @@ static void output_frame(struct wl_listener *listener, void *data) { output_configure_scene(&scene_output->scene->tree.node, NULL); /* Render the scene if needed and commit the output */ - wlr_scene_output_commit(scene_output); + wlr_scene_output_commit(scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(scene_output, &now); } +static void output_request_state(struct wl_listener *listener, void *data) { + /* This function is called when the backend requests a new state for + * the output. For example, Wayland and X11 backends request a new mode + * when the output window is resized. */ + struct tinywl_output *output = wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(output->wlr_output, event->state); +} + static void output_destroy(struct wl_listener *listener, void *data) { struct tinywl_output *output = wl_container_of(listener, output, destroy); wl_list_remove(&output->frame.link); + wl_list_remove(&output->request_state.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->link); free(output); @@ -683,30 +650,39 @@ static void server_new_output(struct wl_listener *listener, void *data) { * and our renderer. Must be done once, before commiting the output */ wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* The output may be disabled, switch it on. */ + struct wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We * just pick the monitor's preferred mode, a more sophisticated compositor * would let the user configure it. */ - if (!wl_list_empty(&wlr_output->modes)) { - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - wlr_output_set_mode(wlr_output, mode); - wlr_output_enable(wlr_output, true); - if (!wlr_output_commit(wlr_output)) { - return; - } + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + if (mode != NULL) { + wlr_output_state_set_mode(&state, mode); } + /* Atomically applies the new output state. */ + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); + /* Allocates and configures our state for this output */ - struct tinywl_output *output = - calloc(1, sizeof(struct tinywl_output)); + struct tinywl_output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; - /* Sets up a listener for the frame notify event. */ + + /* Sets up a listener for the frame event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); - /* Sets up a listener for the destroy notify event. */ + /* Sets up a listener for the state request event. */ + output->request_state.notify = output_request_state; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + + /* Sets up a listener for the destroy event. */ output->destroy.notify = output_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); @@ -721,70 +697,81 @@ static void server_new_output(struct wl_listener *listener, void *data) { * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ - wlr_output_layout_add_auto(server->output_layout, wlr_output); + struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout, + wlr_output); + struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); + wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); } 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_view *view = wl_container_of(listener, view, map); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, map); - wl_list_insert(&view->server->views, &view->link); + wl_list_insert(&toplevel->server->toplevels, &toplevel->link); - focus_view(view, view->xdg_toplevel->base->surface); + focus_toplevel(toplevel, toplevel->xdg_toplevel->base->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_view *view = wl_container_of(listener, view, unmap); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, unmap); - /* Reset the cursor mode if the grabbed view was unmapped. */ - if (view == view->server->grabbed_view) { - reset_cursor_mode(view->server); + /* Reset the cursor mode if the grabbed toplevel was unmapped. */ + if (toplevel == toplevel->server->grabbed_toplevel) { + reset_cursor_mode(toplevel->server); } - wl_list_remove(&view->link); + wl_list_remove(&toplevel->link); } static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { - /* Called when the surface is destroyed and should never be shown again. */ - struct tinywl_view *view = wl_container_of(listener, view, destroy); - - tinywl_view_destroy(view); + /* 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->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); + + free(toplevel); } -static void begin_interactive(struct tinywl_view *view, +static void begin_interactive(struct tinywl_toplevel *toplevel, enum tinywl_cursor_mode mode, uint32_t edges) { /* This function sets up an interactive move or resize operation, where the * compositor stops propegating pointer events to clients and instead * consumes them itself, to move or resize windows. */ - struct tinywl_server *server = view->server; + struct tinywl_server *server = toplevel->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; - if (view->xdg_toplevel->base->surface != + if (toplevel->xdg_toplevel->base->surface != wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } - server->grabbed_view = view; + server->grabbed_toplevel = toplevel; server->cursor_mode = mode; if (mode == TINYWL_CURSOR_MOVE) { - server->grab_x = server->cursor->x - view->x; - server->grab_y = server->cursor->y - view->y; + server->grab_x = server->cursor->x - toplevel->scene_tree->node.x; + server->grab_y = server->cursor->y - toplevel->scene_tree->node.y; } else { struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); + wlr_xdg_surface_get_geometry(toplevel->xdg_toplevel->base, &geo_box); - double border_x = (view->x + geo_box.x) + + double border_x = (toplevel->scene_tree->node.x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (view->y + geo_box.y) + + double border_y = (toplevel->scene_tree->node.y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; server->grab_geobox = geo_box; - server->grab_geobox.x += view->x; - server->grab_geobox.y += view->y; + server->grab_geobox.x += toplevel->scene_tree->node.x; + server->grab_geobox.y += toplevel->scene_tree->node.y; server->resize_edges = edges; } @@ -797,8 +784,8 @@ static void xdg_toplevel_request_move( * decorations. Note that a more sophisticated compositor should check the * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ - struct tinywl_view *view = wl_container_of(listener, view, request_move); - begin_interactive(view, TINYWL_CURSOR_MOVE, 0); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_move); + begin_interactive(toplevel, TINYWL_CURSOR_MOVE, 0); } static void xdg_toplevel_request_resize( @@ -809,8 +796,8 @@ static void xdg_toplevel_request_resize( * provided serial against a list of button press serials sent to this * client, to prevent the client from requesting this whenever they want. */ struct wlr_xdg_toplevel_resize_event *event = data; - struct tinywl_view *view = wl_container_of(listener, view, request_resize); - begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); + struct tinywl_toplevel *toplevel = wl_container_of(listener, toplevel, request_resize); + begin_interactive(toplevel, TINYWL_CURSOR_RESIZE, event->edges); } static void xdg_toplevel_request_maximize( @@ -820,17 +807,17 @@ static void xdg_toplevel_request_maximize( * client-side decorations. tinywl doesn't support maximization, but * to conform to xdg-shell protocol we still must send a configure. * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ - struct tinywl_view *view = - wl_container_of(listener, view, request_maximize); - wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct tinywl_toplevel *toplevel = + wl_container_of(listener, toplevel, request_maximize); + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void xdg_toplevel_request_fullscreen( struct wl_listener *listener, void *data) { /* Just as with request_maximize, we must send a configure here. */ - struct tinywl_view *view = - wl_container_of(listener, view, request_fullscreen); - wlr_xdg_surface_schedule_configure(view->xdg_toplevel->base); + struct tinywl_toplevel *toplevel = + wl_container_of(listener, toplevel, request_fullscreen); + wlr_xdg_surface_schedule_configure(toplevel->xdg_toplevel->base); } static void server_new_xdg_surface(struct wl_listener *listener, void *data) { @@ -846,8 +833,9 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { * we always set the user data field of xdg_surfaces to the corresponding * scene node. */ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface( - xdg_surface->popup->parent); + struct wlr_xdg_surface *parent = + wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); + assert(parent != NULL); struct wlr_scene_tree *parent_tree = parent->data; xdg_surface->data = wlr_scene_xdg_surface_create( parent_tree, xdg_surface); @@ -855,46 +843,42 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { } assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - /* Allocate a tinywl_view for this surface */ - struct tinywl_view *view = - calloc(1, sizeof(struct tinywl_view)); - view->server = server; - view->xdg_toplevel = xdg_surface->toplevel; - view->scene_tree = wlr_scene_xdg_surface_create( - &view->server->scene->tree, view->xdg_toplevel->base); - view->scene_tree->node.data = view; - xdg_surface->data = view->scene_tree; - - /* Add the view to the trees addon */ - tinywl_view_addon_assign(view, &view->scene_tree->node.addons, &view->scene_tree->node); + /* Allocate a tinywl_toplevel for this surface */ + 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->node.data = toplevel; + xdg_surface->data = toplevel->scene_tree; /* Set the scene_nodes decoration data */ - view->opacity = 1; - view->corner_radius = 20; - view->shadow_data = shadow_data_get_default(); - view->shadow_data.enabled = true; - memcpy(view->shadow_data.color, (float[]) {1.0f, 0.0f, 0.0f, 1.0f}, sizeof(float[4])); + toplevel->opacity = 1; + toplevel->corner_radius = 20; + toplevel->shadow_data = shadow_data_get_default(); + toplevel->shadow_data.enabled = true; + memcpy(toplevel->shadow_data.color, (float[]) {1.0f, 0.0f, 0.0f, 1.0f}, sizeof(float[4])); /* Listen to the various events it can emit */ - view->map.notify = xdg_toplevel_map; - wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = xdg_toplevel_unmap; - wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = xdg_toplevel_destroy; - wl_signal_add(&xdg_surface->events.destroy, &view->destroy); + 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->destroy.notify = xdg_toplevel_destroy; + wl_signal_add(&xdg_surface->events.destroy, &toplevel->destroy); /* cotd */ - struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; - view->request_move.notify = xdg_toplevel_request_move; - wl_signal_add(&toplevel->events.request_move, &view->request_move); - view->request_resize.notify = xdg_toplevel_request_resize; - wl_signal_add(&toplevel->events.request_resize, &view->request_resize); - view->request_maximize.notify = xdg_toplevel_request_maximize; - wl_signal_add(&toplevel->events.request_maximize, - &view->request_maximize); - view->request_fullscreen.notify = xdg_toplevel_request_fullscreen; - wl_signal_add(&toplevel->events.request_fullscreen, - &view->request_fullscreen); + struct wlr_xdg_toplevel *xdg_toplevel = xdg_surface->toplevel; + toplevel->request_move.notify = xdg_toplevel_request_move; + wl_signal_add(&xdg_toplevel->events.request_move, &toplevel->request_move); + toplevel->request_resize.notify = xdg_toplevel_request_resize; + wl_signal_add(&xdg_toplevel->events.request_resize, &toplevel->request_resize); + toplevel->request_maximize.notify = xdg_toplevel_request_maximize; + wl_signal_add(&xdg_toplevel->events.request_maximize, + &toplevel->request_maximize); + toplevel->request_fullscreen.notify = xdg_toplevel_request_fullscreen; + wl_signal_add(&xdg_toplevel->events.request_fullscreen, + &toplevel->request_fullscreen); } int main(int argc, char *argv[]) { @@ -917,7 +901,7 @@ int main(int argc, char *argv[]) { return 0; } - struct tinywl_server server; + struct tinywl_server server = {0}; /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); @@ -925,7 +909,7 @@ int main(int argc, char *argv[]) { * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ - server.backend = wlr_backend_autocreate(server.wl_display); + server.backend = wlr_backend_autocreate(server.wl_display, NULL); if (server.backend == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_backend"); return 1; @@ -947,7 +931,7 @@ int main(int argc, char *argv[]) { * The allocator is the bridge between the renderer and the backend. It * handles the buffer creation, allowing wlroots to render onto the * screen */ - server.allocator = wlr_allocator_autocreate(server.backend, + server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); if (server.allocator == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_allocator"); @@ -961,7 +945,7 @@ int main(int argc, char *argv[]) { * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ - wlr_compositor_create(server.wl_display, server.renderer); + wlr_compositor_create(server.wl_display, 5, server.renderer); wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); @@ -982,15 +966,13 @@ int main(int argc, char *argv[]) { * necessary. */ server.scene = wlr_scene_create(); - wlr_scene_attach_output_layout(server.scene, server.output_layout); + server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); /* Set up xdg-shell version 3. The xdg-shell is a Wayland protocol which is - * used for application windows. For more detail on shells, refer to my - * article: - * - * https://drewdevault.com/2018/07/29/Wayland-shells.html + * used for application windows. For more detail on shells, refer to + * https://drewdevault.com/2018/07/29/Wayland-shells.html. */ - wl_list_init(&server.views); + wl_list_init(&server.toplevels); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); server.new_xdg_surface.notify = server_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, @@ -1006,19 +988,16 @@ int main(int argc, char *argv[]) { /* Creates an xcursor manager, another wlroots utility which loads up * Xcursor themes to source cursor images from and makes sure that cursor * images are available at all scale factors on the screen (necessary for - * HiDPI support). We add a cursor theme at scale factor 1 to begin with. */ + * HiDPI support). */ server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); - wlr_xcursor_manager_load(server.cursor_mgr, 1); /* * wlr_cursor *only* displays an image on screen. It does not move around * when the pointer moves. However, we can attach input devices to it, and * it will generate aggregate events for all of them. In these events, we * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: - * - * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html + * moving the cursor around. More detail on this process is described in + * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html. * * And more comments are sprinkled throughout the notify functions above. */ @@ -1083,8 +1062,12 @@ int main(int argc, char *argv[]) { socket); wl_display_run(server.wl_display); - /* Once wl_display_run returns, we shut down the server. */ + /* Once wl_display_run returns, we destroy all clients then shut down the + * server. */ wl_display_destroy_clients(server.wl_display); + wlr_scene_node_destroy(&server.scene->tree.node); + wlr_xcursor_manager_destroy(server.cursor_mgr); + wlr_output_layout_destroy(server.output_layout); wl_display_destroy(server.wl_display); return 0; } diff --git a/types/buffer/buffer.c b/types/buffer/buffer.c index d4c47bc..5303927 100644 --- a/types/buffer/buffer.c +++ b/types/buffer/buffer.c @@ -2,28 +2,27 @@ #include "types/wlr_buffer.h" bool buffer_is_opaque(struct wlr_buffer *buffer) { - void *data; - uint32_t format; - size_t stride; - struct wlr_dmabuf_attributes dmabuf; - struct wlr_shm_attributes shm; - if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { - format = dmabuf.format; - } else if (wlr_buffer_get_shm(buffer, &shm)) { - format = shm.format; - } else if (wlr_buffer_begin_data_ptr_access(buffer, - WLR_BUFFER_DATA_PTR_ACCESS_READ, - &data, &format, &stride)) { - wlr_buffer_end_data_ptr_access(buffer); - } else { - return false; - } + void *data; + uint32_t format; + size_t stride; + struct wlr_dmabuf_attributes dmabuf; + struct wlr_shm_attributes shm; + if (wlr_buffer_get_dmabuf(buffer, &dmabuf)) { + format = dmabuf.format; + } else if (wlr_buffer_get_shm(buffer, &shm)) { + format = shm.format; + } else if (wlr_buffer_begin_data_ptr_access(buffer, + WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) { + wlr_buffer_end_data_ptr_access(buffer); + } else { + return false; + } - const struct wlr_pixel_format_info *format_info = - drm_get_pixel_format_info(format); - if (format_info == NULL) { - return false; - } + const struct wlr_pixel_format_info *format_info = + drm_get_pixel_format_info(format); + if (format_info == NULL) { + return false; + } - return !format_info->has_alpha; + return !format_info->has_alpha; } diff --git a/types/meson.build b/types/meson.build index b5857d0..0527b7c 100644 --- a/types/meson.build +++ b/types/meson.build @@ -1,10 +1,6 @@ wlr_files += files( - 'scene/subsurface_tree.c', - 'scene/surface.c', + 'output/wlr_output.c', 'scene/wlr_scene.c', - 'scene/output_layout.c', - 'scene/xdg_shell.c', - 'scene/layer_shell_v1.c', 'buffer/buffer.c', ) diff --git a/types/output/wlr_output.c b/types/output/wlr_output.c new file mode 100644 index 0000000..43325cf --- /dev/null +++ b/types/output/wlr_output.c @@ -0,0 +1,24 @@ +#define _POSIX_C_SOURCE 200809L +#include <stdlib.h> + +#include "types/wlr_output.h" + +void output_pending_resolution(struct wlr_output *output, + const struct wlr_output_state *state, int *width, int *height) { + if (state->committed & WLR_OUTPUT_STATE_MODE) { + switch (state->mode_type) { + case WLR_OUTPUT_STATE_MODE_FIXED: + *width = state->mode->width; + *height = state->mode->height; + return; + case WLR_OUTPUT_STATE_MODE_CUSTOM: + *width = state->custom_mode.width; + *height = state->custom_mode.height; + return; + } + abort(); + } else { + *width = output->width; + *height = output->height; + } +} diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c deleted file mode 100644 index 3ed616a..0000000 --- a/types/scene/layer_shell_v1.c +++ /dev/null @@ -1,186 +0,0 @@ -#include <stdlib.h> -#include <wlr/types/wlr_scene.h> -#include <wlr/types/wlr_layer_shell_v1.h> - -static void scene_layer_surface_handle_tree_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, tree_destroy); - // tree and surface_node will be cleaned up by scene_node_finish - wl_list_remove(&scene_layer_surface->tree_destroy.link); - wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); - wl_list_remove(&scene_layer_surface->layer_surface_map.link); - wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); - free(scene_layer_surface); -} - -static void scene_layer_surface_handle_layer_surface_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_destroy); - wlr_scene_node_destroy(&scene_layer_surface->tree->node); -} - -static void scene_layer_surface_handle_layer_surface_map( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_map); - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, true); -} - -static void scene_layer_surface_handle_layer_surface_unmap( - struct wl_listener *listener, void *data) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - wl_container_of(listener, scene_layer_surface, layer_surface_unmap); - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, false); -} - -static void layer_surface_exclusive_zone( - struct wlr_layer_surface_v1_state *state, - struct wlr_box *usable_area) { - switch (state->anchor) { - case ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor top - usable_area->y += state->exclusive_zone + state->margin.top; - usable_area->height -= state->exclusive_zone + state->margin.top; - break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor bottom - usable_area->height -= state->exclusive_zone + state->margin.bottom; - break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): - // Anchor left - usable_area->x += state->exclusive_zone + state->margin.left; - usable_area->width -= state->exclusive_zone + state->margin.left; - break; - case ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT: - case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): - // Anchor right - usable_area->width -= state->exclusive_zone + state->margin.right; - break; - } -} - -void wlr_scene_layer_surface_v1_configure( - struct wlr_scene_layer_surface_v1 *scene_layer_surface, - const struct wlr_box *full_area, struct wlr_box *usable_area) { - struct wlr_layer_surface_v1 *layer_surface = - scene_layer_surface->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer_surface->current; - - // If the exclusive zone is set to -1, the layer surface will use the - // full area of the output, otherwise it is constrained to the - // remaining usable area. - struct wlr_box bounds; - if (state->exclusive_zone == -1) { - bounds = *full_area; - } else { - bounds = *usable_area; - } - - struct wlr_box box = { - .width = state->desired_width, - .height = state->desired_height, - }; - - // Horizontal positioning - if (box.width == 0) { - box.x = bounds.x + state->margin.left; - box.width = bounds.width - - (state->margin.left + state->margin.right); - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT && - state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { - box.x = bounds.x + bounds.width/2 -box.width/2; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { - box.x = bounds.x + state->margin.left; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { - box.x = bounds.x + bounds.width - box.width - state->margin.right; - } else { - box.x = bounds.x + bounds.width/2 - box.width/2; - } - - // Vertical positioning - if (box.height == 0) { - box.y = bounds.y + state->margin.top; - box.height = bounds.height - - (state->margin.top + state->margin.bottom); - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP && - state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { - box.y = bounds.y + bounds.height/2 - box.height/2; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { - box.y = bounds.y + state->margin.top; - } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { - box.y = bounds.y + bounds.height - box.height - state->margin.bottom; - } else { - box.y = bounds.y + bounds.height/2 - box.height/2; - } - - wlr_scene_node_set_position(&scene_layer_surface->tree->node, box.x, box.y); - wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); - - if (layer_surface->mapped && state->exclusive_zone > 0) { - layer_surface_exclusive_zone(state, usable_area); - } -} - -struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( - struct wlr_scene_tree *parent, - struct wlr_layer_surface_v1 *layer_surface) { - struct wlr_scene_layer_surface_v1 *scene_layer_surface = - calloc(1, sizeof(*scene_layer_surface)); - if (scene_layer_surface == NULL) { - return NULL; - } - - scene_layer_surface->layer_surface = layer_surface; - - scene_layer_surface->tree = wlr_scene_tree_create(parent); - if (scene_layer_surface->tree == NULL) { - free(scene_layer_surface); - return NULL; - } - - struct wlr_scene_tree *surface_tree = wlr_scene_subsurface_tree_create( - scene_layer_surface->tree, layer_surface->surface); - if (surface_tree == NULL) { - wlr_scene_node_destroy(&scene_layer_surface->tree->node); - free(scene_layer_surface); - return NULL; - } - - scene_layer_surface->tree_destroy.notify = - scene_layer_surface_handle_tree_destroy; - wl_signal_add(&scene_layer_surface->tree->node.events.destroy, - &scene_layer_surface->tree_destroy); - - scene_layer_surface->layer_surface_destroy.notify = - scene_layer_surface_handle_layer_surface_destroy; - wl_signal_add(&layer_surface->events.destroy, - &scene_layer_surface->layer_surface_destroy); - - scene_layer_surface->layer_surface_map.notify = - scene_layer_surface_handle_layer_surface_map; - wl_signal_add(&layer_surface->events.map, - &scene_layer_surface->layer_surface_map); - - scene_layer_surface->layer_surface_unmap.notify = - scene_layer_surface_handle_layer_surface_unmap; - wl_signal_add(&layer_surface->events.unmap, - &scene_layer_surface->layer_surface_unmap); - - wlr_scene_node_set_enabled(&scene_layer_surface->tree->node, - layer_surface->mapped); - - return scene_layer_surface; -} diff --git a/types/scene/output_layout.c b/types/scene/output_layout.c deleted file mode 100644 index 1d1484a..0000000 --- a/types/scene/output_layout.c +++ /dev/null @@ -1,157 +0,0 @@ -#include <stdlib.h> -#include <wlr/types/wlr_output_layout.h> -#include <wlr/types/wlr_scene.h> - -struct wlr_scene_output_layout { - struct wlr_output_layout *layout; - struct wlr_scene *scene; - - struct wl_list outputs; // wlr_scene_output_layout_output.link - - struct wl_listener layout_add; - struct wl_listener layout_change; - struct wl_listener layout_destroy; - struct wl_listener scene_destroy; -}; - -struct wlr_scene_output_layout_output { - struct wlr_output_layout_output *layout_output; - struct wlr_scene_output *scene_output; - - struct wl_list link; // wlr_scene_output_layout.outputs - - struct wl_listener layout_output_destroy; - struct wl_listener scene_output_destroy; -}; - -static void scene_output_layout_output_destroy( - struct wlr_scene_output_layout_output *solo) { - wl_list_remove(&solo->layout_output_destroy.link); - wl_list_remove(&solo->scene_output_destroy.link); - wl_list_remove(&solo->link); - wlr_scene_output_destroy(solo->scene_output); - free(solo); -} - -static void scene_output_layout_output_handle_layout_output_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout_output *solo = - wl_container_of(listener, solo, layout_output_destroy); - scene_output_layout_output_destroy(solo); -} - -static void scene_output_layout_output_handle_scene_output_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout_output *solo = - wl_container_of(listener, solo, scene_output_destroy); - solo->scene_output = NULL; - scene_output_layout_output_destroy(solo); -} - -static void scene_output_layout_destroy(struct wlr_scene_output_layout *sol) { - struct wlr_scene_output_layout_output *solo, *tmp; - wl_list_for_each_safe(solo, tmp, &sol->outputs, link) { - scene_output_layout_output_destroy(solo); - } - wl_list_remove(&sol->layout_add.link); - wl_list_remove(&sol->layout_change.link); - wl_list_remove(&sol->layout_destroy.link); - wl_list_remove(&sol->scene_destroy.link); - free(sol); -} - -static void scene_output_layout_handle_layout_change( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_change); - - struct wlr_scene_output_layout_output *solo; - wl_list_for_each(solo, &sol->outputs, link) { - wlr_scene_output_set_position(solo->scene_output, - solo->layout_output->x, solo->layout_output->y); - } -} - -static void scene_output_layout_add(struct wlr_scene_output_layout *sol, - struct wlr_output_layout_output *lo) { - struct wlr_scene_output_layout_output *solo = calloc(1, sizeof(*solo)); - if (solo == NULL) { - return; - } - - solo->scene_output = wlr_scene_output_create(sol->scene, lo->output); - if (solo->scene_output == NULL) { - free(solo); - return; - } - - solo->layout_output = lo; - - solo->layout_output_destroy.notify = - scene_output_layout_output_handle_layout_output_destroy; - wl_signal_add(&lo->events.destroy, &solo->layout_output_destroy); - - solo->scene_output_destroy.notify = - scene_output_layout_output_handle_scene_output_destroy; - wl_signal_add(&solo->scene_output->events.destroy, - &solo->scene_output_destroy); - - wl_list_insert(&sol->outputs, &solo->link); - - wlr_scene_output_set_position(solo->scene_output, lo->x, lo->y); -} - -static void scene_output_layout_handle_layout_add( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_add); - struct wlr_output_layout_output *lo = data; - - scene_output_layout_add(sol, lo); -} - -static void scene_output_layout_handle_layout_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, layout_destroy); - scene_output_layout_destroy(sol); -} - -static void scene_output_layout_handle_scene_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_output_layout *sol = - wl_container_of(listener, sol, scene_destroy); - scene_output_layout_destroy(sol); -} - -bool wlr_scene_attach_output_layout(struct wlr_scene *scene, - struct wlr_output_layout *output_layout) { - struct wlr_scene_output_layout *sol = calloc(1, sizeof(*sol)); - if (sol == NULL) { - return false; - } - - sol->scene = scene; - sol->layout = output_layout; - - wl_list_init(&sol->outputs); - - sol->layout_destroy.notify = scene_output_layout_handle_layout_destroy; - wl_signal_add(&output_layout->events.destroy, &sol->layout_destroy); - - sol->layout_change.notify = scene_output_layout_handle_layout_change; - wl_signal_add(&output_layout->events.change, &sol->layout_change); - - sol->layout_add.notify = scene_output_layout_handle_layout_add; - wl_signal_add(&output_layout->events.add, &sol->layout_add); - - sol->scene_destroy.notify = scene_output_layout_handle_scene_destroy; - wl_signal_add(&scene->tree.node.events.destroy, &sol->scene_destroy); - - struct wlr_output_layout_output *lo; - wl_list_for_each(lo, &output_layout->outputs, link) { - scene_output_layout_add(sol, lo); - } - - return true; -} diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c deleted file mode 100644 index 35420ab..0000000 --- a/types/scene/subsurface_tree.c +++ /dev/null @@ -1,259 +0,0 @@ -#include <assert.h> -#include <stdlib.h> -#include <wlr/types/wlr_scene.h> -#include <wlr/types/wlr_subcompositor.h> -#include <wlr/util/addon.h> - -/** - * A tree for a surface and all of its child sub-surfaces. - * - * `tree` contains `scene_surface` and one node per sub-surface. - */ -struct wlr_scene_subsurface_tree { - struct wlr_scene_tree *tree; - struct wlr_surface *surface; - struct wlr_scene_surface *scene_surface; - - struct wl_listener tree_destroy; - struct wl_listener surface_destroy; - struct wl_listener surface_commit; - struct wl_listener surface_new_subsurface; - - struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface - - // Only valid if the surface is a sub-surface - - struct wlr_addon surface_addon; - - struct wl_listener subsurface_destroy; - struct wl_listener subsurface_map; - struct wl_listener subsurface_unmap; -}; - -static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, tree_destroy); - // tree and scene_surface will be cleaned up by scene_node_finish - if (subsurface_tree->parent) { - wlr_addon_finish(&subsurface_tree->surface_addon); - wl_list_remove(&subsurface_tree->subsurface_destroy.link); - wl_list_remove(&subsurface_tree->subsurface_map.link); - wl_list_remove(&subsurface_tree->subsurface_unmap.link); - } - wl_list_remove(&subsurface_tree->tree_destroy.link); - wl_list_remove(&subsurface_tree->surface_destroy.link); - wl_list_remove(&subsurface_tree->surface_commit.link); - wl_list_remove(&subsurface_tree->surface_new_subsurface.link); - free(subsurface_tree); -} - -static const struct wlr_addon_interface subsurface_tree_addon_impl; - -static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( - struct wlr_scene_subsurface_tree *parent, - struct wlr_subsurface *subsurface) { - struct wlr_addon *addon = wlr_addon_find(&subsurface->surface->addons, - parent, &subsurface_tree_addon_impl); - assert(addon != NULL); - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(addon, subsurface_tree, surface_addon); - return subsurface_tree; -} - -static void subsurface_tree_reconfigure( - struct wlr_scene_subsurface_tree *subsurface_tree) { - struct wlr_surface *surface = subsurface_tree->surface; - - struct wlr_scene_node *prev = NULL; - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - struct wlr_scene_subsurface_tree *child = - subsurface_tree_from_subsurface(subsurface_tree, subsurface); - if (prev != NULL) { - wlr_scene_node_place_above(&child->tree->node, prev); - } - prev = &child->tree->node; - - wlr_scene_node_set_position(&child->tree->node, - subsurface->current.x, subsurface->current.y); - } - - if (prev != NULL) { - wlr_scene_node_place_above(&subsurface_tree->scene_surface->buffer->node, prev); - } - prev = &subsurface_tree->scene_surface->buffer->node; - - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - struct wlr_scene_subsurface_tree *child = - subsurface_tree_from_subsurface(subsurface_tree, subsurface); - wlr_scene_node_place_above(&child->tree->node, prev); - prev = &child->tree->node; - - wlr_scene_node_set_position(&child->tree->node, - subsurface->current.x, subsurface->current.y); - } -} - -static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_destroy); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - -static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_commit); - - // TODO: only do this on subsurface order or position change - subsurface_tree_reconfigure(subsurface_tree); -} - -static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, subsurface_destroy); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - -static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, subsurface_map); - - wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true); -} - -static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, subsurface_unmap); - - wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false); -} - -static void subsurface_tree_addon_destroy(struct wlr_addon *addon) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(addon, subsurface_tree, surface_addon); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - -static const struct wlr_addon_interface subsurface_tree_addon_impl = { - .name = "wlr_scene_subsurface_tree", - .destroy = subsurface_tree_addon_destroy, -}; - -static struct wlr_scene_subsurface_tree *scene_surface_tree_create( - struct wlr_scene_tree *parent, struct wlr_surface *surface); - -static bool subsurface_tree_create_subsurface( - struct wlr_scene_subsurface_tree *parent, - struct wlr_subsurface *subsurface) { - struct wlr_scene_subsurface_tree *child = scene_surface_tree_create( - parent->tree, subsurface->surface); - if (child == NULL) { - return false; - } - - child->parent = parent; - wlr_scene_node_set_enabled(&child->tree->node, subsurface->mapped); - - wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, - parent, &subsurface_tree_addon_impl); - - child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy; - wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy); - - child->subsurface_map.notify = subsurface_tree_handle_subsurface_map; - wl_signal_add(&subsurface->events.map, &child->subsurface_map); - - child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap; - wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap); - - return true; -} - -static void subsurface_tree_handle_surface_new_subsurface( - struct wl_listener *listener, void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { - wl_resource_post_no_memory(subsurface->resource); - } -} - -static struct wlr_scene_subsurface_tree *scene_surface_tree_create( - struct wlr_scene_tree *parent, struct wlr_surface *surface) { - struct wlr_scene_subsurface_tree *subsurface_tree = - calloc(1, sizeof(*subsurface_tree)); - if (subsurface_tree == NULL) { - return NULL; - } - - subsurface_tree->tree = wlr_scene_tree_create(parent); - if (subsurface_tree->tree == NULL) { - goto error_surface_tree; - } - - subsurface_tree->scene_surface = - wlr_scene_surface_create(subsurface_tree->tree, surface); - if (subsurface_tree->scene_surface == NULL) { - goto error_scene_surface; - } - - subsurface_tree->surface = surface; - - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { - goto error_scene_surface; - } - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - if (!subsurface_tree_create_subsurface(subsurface_tree, subsurface)) { - goto error_scene_surface; - } - } - - subsurface_tree_reconfigure(subsurface_tree); - - subsurface_tree->tree_destroy.notify = subsurface_tree_handle_tree_destroy; - wl_signal_add(&subsurface_tree->tree->node.events.destroy, - &subsurface_tree->tree_destroy); - - subsurface_tree->surface_destroy.notify = subsurface_tree_handle_surface_destroy; - wl_signal_add(&surface->events.destroy, &subsurface_tree->surface_destroy); - - subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; - wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); - - subsurface_tree->surface_new_subsurface.notify = - subsurface_tree_handle_surface_new_subsurface; - wl_signal_add(&surface->events.new_subsurface, - &subsurface_tree->surface_new_subsurface); - - return subsurface_tree; - -error_scene_surface: - wlr_scene_node_destroy(&subsurface_tree->tree->node); -error_surface_tree: - free(subsurface_tree); - return NULL; -} - -struct wlr_scene_tree *wlr_scene_subsurface_tree_create( - struct wlr_scene_tree *parent, struct wlr_surface *surface) { - struct wlr_scene_subsurface_tree *subsurface_tree = - scene_surface_tree_create(parent, surface); - if (subsurface_tree == NULL) { - return NULL; - } - return subsurface_tree->tree; -} diff --git a/types/scene/surface.c b/types/scene/surface.c deleted file mode 100644 index 553cc42..0000000 --- a/types/scene/surface.c +++ /dev/null @@ -1,211 +0,0 @@ -#include <assert.h> -#include <stdlib.h> -#include <wlr/types/wlr_scene.h> -#include <wlr/types/wlr_presentation_time.h> -#include "types/wlr_scene.h" - -static void handle_scene_buffer_output_enter( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_enter); - struct wlr_scene_output *output = data; - - wlr_surface_send_enter(surface->surface, output->output); -} - -static void handle_scene_buffer_output_leave( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_leave); - struct wlr_scene_output *output = data; - - wlr_surface_send_leave(surface->surface, output->output); -} - -static void handle_scene_buffer_output_present( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, output_present); - struct wlr_scene_output *scene_output = data; - - if (surface->buffer->primary_output == scene_output) { - struct wlr_scene *root = scene_node_get_root(&surface->buffer->node); - struct wlr_presentation *presentation = root->presentation; - - if (presentation) { - wlr_presentation_surface_sampled_on_output( - presentation, surface->surface, scene_output->output); - } - } -} - -static void handle_scene_buffer_frame_done( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, frame_done); - struct timespec *now = data; - - wlr_surface_send_frame_done(surface->surface, now); -} - -static void scene_surface_handle_surface_destroy( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, surface_destroy); - - wlr_scene_node_destroy(&surface->buffer->node); -} - -// This is used for wlr_scene where it unconditionally locks buffers preventing -// reuse of the existing texture for shm clients. With the usage pattern of -// wlr_scene surface handling, we can mark its locked buffer as safe -// for mutation. -static void client_buffer_mark_next_can_damage(struct wlr_client_buffer *buffer) { - buffer->n_ignore_locks++; -} - -static void scene_buffer_unmark_client_buffer(struct wlr_scene_buffer *scene_buffer) { - if (!scene_buffer->buffer) { - return; - } - - struct wlr_client_buffer *buffer = wlr_client_buffer_get(scene_buffer->buffer); - if (!buffer) { - return; - } - - assert(buffer->n_ignore_locks > 0); - buffer->n_ignore_locks--; -} - -static void set_buffer_with_surface_state(struct wlr_scene_buffer *scene_buffer, - struct wlr_surface *surface) { - struct wlr_surface_state *state = &surface->current; - - wlr_scene_buffer_set_opaque_region(scene_buffer, &surface->opaque_region); - - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - wlr_scene_buffer_set_source_box(scene_buffer, &src_box); - - wlr_scene_buffer_set_dest_size(scene_buffer, state->width, state->height); - wlr_scene_buffer_set_transform(scene_buffer, state->transform); - - scene_buffer_unmark_client_buffer(scene_buffer); - - if (surface->buffer) { - client_buffer_mark_next_can_damage(surface->buffer); - - wlr_scene_buffer_set_buffer_with_damage(scene_buffer, - &surface->buffer->base, &surface->buffer_damage); - } else { - wlr_scene_buffer_set_buffer(scene_buffer, NULL); - } -} - -static void handle_scene_surface_surface_commit( - struct wl_listener *listener, void *data) { - struct wlr_scene_surface *surface = - wl_container_of(listener, surface, surface_commit); - struct wlr_scene_buffer *scene_buffer = surface->buffer; - - set_buffer_with_surface_state(scene_buffer, surface->surface); - - // If the surface has requested a frame done event, honour that. The - // frame_callback_list will be populated in this case. We should only - // schedule the frame however if the node is enabled and there is an - // output intersecting, otherwise the frame done events would never reach - // the surface anyway. - int lx, ly; - bool enabled = wlr_scene_node_coords(&scene_buffer->node, &lx, &ly); - - if (!wl_list_empty(&surface->surface->current.frame_callback_list) && - surface->buffer->primary_output != NULL && enabled) { - wlr_output_schedule_frame(surface->buffer->primary_output->output); - } -} - -static bool scene_buffer_point_accepts_input(struct wlr_scene_buffer *scene_buffer, - int sx, int sy) { - struct wlr_scene_surface *scene_surface = - wlr_scene_surface_from_buffer(scene_buffer); - - return wlr_surface_point_accepts_input(scene_surface->surface, sx, sy); -} - -static void surface_addon_destroy(struct wlr_addon *addon) { - struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); - - scene_buffer_unmark_client_buffer(surface->buffer); - - wlr_addon_finish(&surface->addon); - - wl_list_remove(&surface->output_enter.link); - wl_list_remove(&surface->output_leave.link); - wl_list_remove(&surface->output_present.link); - wl_list_remove(&surface->frame_done.link); - wl_list_remove(&surface->surface_destroy.link); - wl_list_remove(&surface->surface_commit.link); - - free(surface); -} - -static const struct wlr_addon_interface surface_addon_impl = { - .name = "wlr_scene_surface", - .destroy = surface_addon_destroy, -}; - -struct wlr_scene_surface *wlr_scene_surface_from_buffer( - struct wlr_scene_buffer *scene_buffer) { - struct wlr_addon *addon = wlr_addon_find(&scene_buffer->node.addons, - scene_buffer, &surface_addon_impl); - if (!addon) { - return NULL; - } - - struct wlr_scene_surface *surface = wl_container_of(addon, surface, addon); - return surface; -} - -struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_tree *parent, - struct wlr_surface *wlr_surface) { - struct wlr_scene_surface *surface = calloc(1, sizeof(*surface)); - if (surface == NULL) { - return NULL; - } - - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(parent, NULL); - if (!scene_buffer) { - free(surface); - return NULL; - } - - surface->buffer = scene_buffer; - surface->surface = wlr_surface; - scene_buffer->point_accepts_input = scene_buffer_point_accepts_input; - - surface->output_enter.notify = handle_scene_buffer_output_enter; - wl_signal_add(&scene_buffer->events.output_enter, &surface->output_enter); - - surface->output_leave.notify = handle_scene_buffer_output_leave; - wl_signal_add(&scene_buffer->events.output_leave, &surface->output_leave); - - surface->output_present.notify = handle_scene_buffer_output_present; - wl_signal_add(&scene_buffer->events.output_present, &surface->output_present); - - surface->frame_done.notify = handle_scene_buffer_frame_done; - wl_signal_add(&scene_buffer->events.frame_done, &surface->frame_done); - - surface->surface_destroy.notify = scene_surface_handle_surface_destroy; - wl_signal_add(&wlr_surface->events.destroy, &surface->surface_destroy); - - surface->surface_commit.notify = handle_scene_surface_surface_commit; - wl_signal_add(&wlr_surface->events.commit, &surface->surface_commit); - - wlr_addon_init(&surface->addon, &scene_buffer->node.addons, - scene_buffer, &surface_addon_impl); - - set_buffer_with_surface_state(scene_buffer, wlr_surface); - - return surface; -} diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 4a32a6b..fab40a5 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -4,18 +4,21 @@ #include <string.h> #include <wlr/backend.h> #include <wlr/render/gles2.h> -#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_damage_ring.h> #include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_scene.h> #include <wlr/types/wlr_subcompositor.h> +#include <wlr/types/wlr_xdg_shell.h> #include <wlr/util/log.h> #include <wlr/util/region.h> +#include <wlr/render/swapchain.h> + #include "render/fx_renderer/fx_renderer.h" #include "types/fx/shadow_data.h" #include "types/wlr_buffer.h" +#include "types/wlr_output.h" #include "types/wlr_scene.h" #include "util/array.h" #include "util/env.h" @@ -23,14 +26,13 @@ #define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250 -static struct wlr_scene_tree *scene_tree_from_node(struct wlr_scene_node *node) { +struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_TREE); struct wlr_scene_tree *tree = wl_container_of(node, tree, node); return tree; } -static struct wlr_scene_rect *scene_rect_from_node( - struct wlr_scene_node *node) { +struct wlr_scene_rect *wlr_scene_rect_from_node(struct wlr_scene_node *node) { assert(node->type == WLR_SCENE_NODE_RECT); struct wlr_scene_rect *rect = wl_container_of(node, rect, node); return rect; @@ -46,7 +48,7 @@ struct wlr_scene_buffer *wlr_scene_buffer_from_node( struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { struct wlr_scene_tree *tree; if (node->type == WLR_SCENE_NODE_TREE) { - tree = scene_tree_from_node(node); + tree = wlr_scene_tree_from_node(node); } else { tree = node->parent; } @@ -54,15 +56,17 @@ struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { while (tree->node.parent != NULL) { tree = tree->node.parent; } - return (struct wlr_scene *)tree; + struct wlr_scene *scene = wl_container_of(tree, scene, tree); + return scene; } static void scene_node_init(struct wlr_scene_node *node, enum wlr_scene_node_type type, struct wlr_scene_tree *parent) { - memset(node, 0, sizeof(*node)); - node->type = type; - node->parent = parent; - node->enabled = true; + *node = (struct wlr_scene_node){ + .type = type, + .parent = parent, + .enabled = true, + }; wl_list_init(&node->link); @@ -114,7 +118,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { wlr_buffer_unlock(scene_buffer->buffer); pixman_region32_fini(&scene_buffer->opaque_region); } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); if (scene_tree == &scene->tree) { assert(!node->parent); @@ -124,6 +128,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { } wl_list_remove(&scene->presentation_destroy.link); + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); } else { assert(node->parent); } @@ -142,13 +147,13 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { static void scene_tree_init(struct wlr_scene_tree *tree, struct wlr_scene_tree *parent) { - memset(tree, 0, sizeof(*tree)); + *tree = (struct wlr_scene_tree){0}; scene_node_init(&tree->node, WLR_SCENE_NODE_TREE, parent); wl_list_init(&tree->children); } struct wlr_scene *wlr_scene_create(void) { - struct wlr_scene *scene = calloc(1, sizeof(struct wlr_scene)); + struct wlr_scene *scene = calloc(1, sizeof(*scene)); if (scene == NULL) { return NULL; } @@ -157,6 +162,7 @@ struct wlr_scene *wlr_scene_create(void) { wl_list_init(&scene->outputs); wl_list_init(&scene->presentation_destroy.link); + wl_list_init(&scene->linux_dmabuf_v1_destroy.link); const char *debug_damage_options[] = { "none", @@ -175,7 +181,7 @@ struct wlr_scene *wlr_scene_create(void) { struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_tree *parent) { assert(parent); - struct wlr_scene_tree *tree = calloc(1, sizeof(struct wlr_scene_tree)); + struct wlr_scene_tree *tree = calloc(1, sizeof(*tree)); if (tree == NULL) { return NULL; } @@ -197,7 +203,7 @@ static bool _scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box switch (node->type) { case WLR_SCENE_NODE_TREE:; - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each_reverse(child, &scene_tree->children, link) { if (_scene_nodes_in_box(child, box, iterator, user_data, lx + child->x, ly + child->y)) { @@ -230,8 +236,11 @@ static bool scene_nodes_in_box(struct wlr_scene_node *node, struct wlr_box *box, static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, pixman_region32_t *opaque) { + int width, height; + scene_node_get_size(node, &width, &height); + if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); if (scene_rect->color[3] != 1) { return; } @@ -242,20 +251,22 @@ static void scene_node_opaque_region(struct wlr_scene_node *node, int x, int y, return; } - // Buffer is translucent - if (scene_buffer->opacity != 1 || scene_buffer->corner_radius > 0) { + if (scene_buffer->opacity != 1) { + return; + } + + if (scene_buffer->corner_radius > 0) { return; } if (!buffer_is_opaque(scene_buffer->buffer)) { pixman_region32_copy(opaque, &scene_buffer->opaque_region); + pixman_region32_intersect_rect(opaque, opaque, 0, 0, width, height); pixman_region32_translate(opaque, x, y); return; } } - int width, height; - scene_node_get_size(node, &width, &height); pixman_region32_fini(opaque); pixman_region32_init_rect(opaque, x, y, width, height); } @@ -287,6 +298,28 @@ static void scale_output_damage(pixman_region32_t *damage, float scale) { } } +struct render_data { + enum wl_output_transform transform; + float scale; + struct wlr_box logical; + int trans_width, trans_height; + + struct wlr_scene_output *output; + + struct wlr_render_pass *render_pass; + pixman_region32_t damage; +}; + +static void transform_output_damage(pixman_region32_t *damage, const struct render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + wlr_region_transform(damage, damage, transform, data->trans_width, data->trans_height); +} + +static void transform_output_box(struct wlr_box *box, const struct render_data *data) { + enum wl_output_transform transform = wlr_output_transform_invert(data->transform); + wlr_box_transform(box, box, transform, data->trans_width, data->trans_height); +} + static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *damage) { if (!pixman_region32_not_empty(damage)) { return; @@ -308,7 +341,8 @@ static void scene_damage_outputs(struct wlr_scene *scene, pixman_region32_t *dam } static void update_node_update_outputs(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore) { + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force) { if (node->type != WLR_SCENE_NODE_BUFFER) { return; } @@ -316,8 +350,10 @@ static void update_node_update_outputs(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); uint32_t largest_overlap = 0; + struct wlr_scene_output *old_primary_output = scene_buffer->primary_output; scene_buffer->primary_output = NULL; + size_t count = 0; uint64_t active_outputs = 0; // let's update the outputs in two steps: @@ -356,11 +392,17 @@ static void update_node_update_outputs(struct wlr_scene_node *node, } active_outputs |= 1ull << scene_output->index; + count++; } pixman_region32_fini(&intersection); } + if (old_primary_output != scene_buffer->primary_output) { + scene_buffer->prev_feedback_options = + (struct wlr_linux_dmabuf_feedback_v1_init_options){0}; + } + uint64_t old_active = scene_buffer->active_outputs; scene_buffer->active_outputs = active_outputs; @@ -379,6 +421,31 @@ static void update_node_update_outputs(struct wlr_scene_node *node, // if there are active outputs on this node, we should always have a primary // output assert(!scene_buffer->active_outputs || scene_buffer->primary_output); + + // Skip output update event if nothing was updated + if (old_active == active_outputs && + (!force || ((1ull << force->index) & ~active_outputs)) && + old_primary_output == scene_buffer->primary_output) { + return; + } + + struct wlr_scene_output *outputs_array[64]; + struct wlr_scene_outputs_update_event event = { + .active = outputs_array, + .size = count, + }; + + size_t i = 0; + wl_list_for_each(scene_output, outputs, link) { + if (~active_outputs & (1ull << scene_output->index)) { + continue; + } + + assert(i < count); + outputs_array[i++] = scene_output; + } + + wl_signal_emit_mutable(&scene_buffer->events.outputs_update, &event); } static bool scene_node_update_iterator(struct wlr_scene_node *node, @@ -410,7 +477,7 @@ static bool scene_node_update_iterator(struct wlr_scene_node *node, } } - update_node_update_outputs(node, data->outputs, NULL); + update_node_update_outputs(node, data->outputs, NULL, NULL); return false; } @@ -422,7 +489,7 @@ static void scene_node_visibility(struct wlr_scene_node *node, } if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_visibility(child, visible); @@ -440,7 +507,7 @@ static void scene_node_bounds(struct wlr_scene_node *node, } if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_bounds(child, x + child->x, y + child->y, visible); @@ -517,8 +584,7 @@ static void scene_node_update(struct wlr_scene_node *node, struct wlr_scene_rect *wlr_scene_rect_create(struct wlr_scene_tree *parent, int width, int height, const float color[static 4]) { - struct wlr_scene_rect *scene_rect = - calloc(1, sizeof(struct wlr_scene_rect)); + struct wlr_scene_rect *scene_rect = calloc(1, sizeof(*scene_rect)); if (scene_rect == NULL) { return NULL; } @@ -566,12 +632,12 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, scene_buffer->buffer = wlr_buffer_lock(buffer); } + wl_signal_init(&scene_buffer->events.outputs_update); wl_signal_init(&scene_buffer->events.output_enter); wl_signal_init(&scene_buffer->events.output_leave); - wl_signal_init(&scene_buffer->events.output_present); + wl_signal_init(&scene_buffer->events.output_sample); wl_signal_init(&scene_buffer->events.frame_done); pixman_region32_init(&scene_buffer->opaque_region); - scene_buffer->opacity = 1; scene_buffer->corner_radius = 0; scene_buffer->shadow_data = shadow_data_get_default(); @@ -582,14 +648,13 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_tree *parent, } void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buffer, - struct wlr_buffer *buffer, pixman_region32_t *damage) { + struct wlr_buffer *buffer, const pixman_region32_t *damage) { // specifying a region for a NULL buffer doesn't make sense. We need to know // about the buffer to scale the buffer local coordinates down to scene // coordinates. assert(buffer || !damage); bool update = false; - wlr_buffer_unlock(scene_buffer->buffer); wlr_texture_destroy(scene_buffer->texture); scene_buffer->texture = NULL; @@ -603,8 +668,10 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff (scene_buffer->buffer->width != buffer->width || scene_buffer->buffer->height != buffer->height)); + wlr_buffer_unlock(scene_buffer->buffer); scene_buffer->buffer = wlr_buffer_lock(buffer); } else { + wlr_buffer_unlock(scene_buffer->buffer); update = true; scene_buffer->buffer = NULL; } @@ -694,8 +761,8 @@ void wlr_scene_buffer_set_buffer_with_damage(struct wlr_scene_buffer *scene_buff pixman_region32_fini(&cull_region); pixman_region32_translate(&output_damage, - (lx - scene_output->x) * output_scale, - (ly - scene_output->y) * output_scale); + (int)round((lx - scene_output->x) * output_scale), + (int)round((ly - scene_output->y) * output_scale)); if (wlr_damage_ring_add(&scene_output->damage_ring, &output_damage)) { wlr_output_schedule_frame(scene_output->output); } @@ -712,27 +779,35 @@ void wlr_scene_buffer_set_buffer(struct wlr_scene_buffer *scene_buffer, } void wlr_scene_buffer_set_opaque_region(struct wlr_scene_buffer *scene_buffer, - pixman_region32_t *region) { + const pixman_region32_t *region) { if (pixman_region32_equal(&scene_buffer->opaque_region, region)) { return; } pixman_region32_copy(&scene_buffer->opaque_region, region); - scene_node_update(&scene_buffer->node, NULL); + + int x, y; + if (!wlr_scene_node_coords(&scene_buffer->node, &x, &y)) { + return; + } + + pixman_region32_t update_region; + pixman_region32_init(&update_region); + scene_node_bounds(&scene_buffer->node, x, y, &update_region); + scene_update_region(scene_node_get_root(&scene_buffer->node), &update_region); + pixman_region32_fini(&update_region); } void wlr_scene_buffer_set_source_box(struct wlr_scene_buffer *scene_buffer, const struct wlr_fbox *box) { - struct wlr_fbox *cur = &scene_buffer->src_box; - if ((wlr_fbox_empty(box) && wlr_fbox_empty(cur)) || - (box != NULL && wlr_fbox_equal(cur, box))) { + if (wlr_fbox_equal(&scene_buffer->src_box, box)) { return; } if (box != NULL) { - memcpy(cur, box, sizeof(*box)); + scene_buffer->src_box = *box; } else { - memset(cur, 0, sizeof(*cur)); + scene_buffer->src_box = (struct wlr_fbox){0}; } scene_node_update(&scene_buffer->node, NULL); @@ -776,6 +851,16 @@ void wlr_scene_buffer_set_opacity(struct wlr_scene_buffer *scene_buffer, scene_node_update(&scene_buffer->node, NULL); } +void wlr_scene_buffer_set_filter_mode(struct wlr_scene_buffer *scene_buffer, + enum wlr_scale_filter_mode filter_mode) { + if (scene_buffer->filter_mode == filter_mode) { + return; + } + + scene_buffer->filter_mode = filter_mode; + scene_node_update(&scene_buffer->node, NULL); +} + void wlr_scene_buffer_set_corner_radius(struct wlr_scene_buffer *scene_buffer, int radii) { if (scene_buffer->corner_radius == radii) { @@ -826,7 +911,7 @@ static void scene_node_get_size(struct wlr_scene_node *node, case WLR_SCENE_NODE_TREE: return; case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); *width = scene_rect->width; *height = scene_rect->height; break; @@ -995,7 +1080,7 @@ static void scene_node_for_each_scene_buffer(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); user_iterator(scene_buffer, lx, ly, user_data); } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_for_each_scene_buffer(child, lx, ly, user_iterator, user_data); @@ -1025,7 +1110,7 @@ static bool scene_node_at_iterator(struct wlr_scene_node *node, struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); if (scene_buffer->point_accepts_input && - !scene_buffer->point_accepts_input(scene_buffer, rx, ry)) { + !scene_buffer->point_accepts_input(scene_buffer, &rx, &ry)) { return false; } } @@ -1063,208 +1148,300 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, return NULL; } -static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - fx_renderer_scissor(&box); -} - -static void render_rect(struct fx_renderer *fx_renderer, struct wlr_output *output, - pixman_region32_t *damage, const float color[static 4], - const struct wlr_box *box, const float matrix[static 9]) { - assert(fx_renderer); - - 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_rect(fx_renderer, box, color, matrix); - } -} - -static void render_texture(struct fx_renderer *fx_renderer, struct wlr_output *output, - pixman_region32_t *damage, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9], float opacity, int corner_radius) { - assert(fx_renderer); - - struct wlr_fbox default_src_box = {0}; - if (wlr_fbox_empty(src_box)) { - default_src_box.width = texture->width; - default_src_box.height = texture->height; - src_box = &default_src_box; - } - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - wlr_box_transform(&transformed_box, dst_box, - wlr_output_transform_invert(output->transform), width, height); - - 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_subtexture_with_matrix(fx_renderer, texture, src_box, - &transformed_box, matrix, opacity, corner_radius); - } -} - -static void render_box_shadow(struct fx_renderer *fx_renderer, - struct wlr_output *output, pixman_region32_t *surface_damage, - const struct wlr_box *surface_box, int corner_radius, - struct shadow_data *shadow_data) { - // don't damage area behind window since we dont render it anyway - pixman_region32_t inner_region; - pixman_region32_init(&inner_region); - pixman_region32_union_rect(&inner_region, &inner_region, - surface_box->x + corner_radius * 0.5, - surface_box->y + corner_radius * 0.5, - surface_box->width - corner_radius, - surface_box->height - corner_radius); - pixman_region32_intersect(&inner_region, &inner_region, surface_damage); - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_subtract(&damage, surface_damage, &inner_region); - if (!pixman_region32_not_empty(&damage)) { - goto damage_finish; - } - - struct wlr_box shadow_box = { - .x = surface_box->x - shadow_data->blur_sigma, - .y = surface_box->y - shadow_data->blur_sigma, - .width = surface_box->width + 2 * shadow_data->blur_sigma, - .height = surface_box->height + 2 * shadow_data->blur_sigma, - }; - float matrix[9]; - wlr_matrix_project_box(matrix, &shadow_box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - output->transform_matrix); - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - wlr_box_transform(&transformed_box, &shadow_box, - wlr_output_transform_invert(output->transform), width, height); - - 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_box_shadow(fx_renderer, &transformed_box, surface_box, matrix, - corner_radius, shadow_data); - } - -damage_finish: - pixman_region32_fini(&damage); - pixman_region32_fini(&inner_region); -} - -static void scene_node_render(struct fx_renderer *fx_renderer, struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, pixman_region32_t *damage) { +// static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { +// struct wlr_box box = { +// .x = rect->x1, +// .y = rect->y1, +// .width = rect->x2 - rect->x1, +// .height = rect->y2 - rect->y1, +// }; +// +// int ow, oh; +// wlr_output_transformed_resolution(output, &ow, &oh); +// +// enum wl_output_transform transform = +// wlr_output_transform_invert(output->transform); +// wlr_box_transform(&box, &box, transform, ow, oh); +// +// fx_renderer_scissor(&box); +// } +// +// static void render_rect(struct fx_renderer *fx_renderer, struct wlr_output *output, +// pixman_region32_t *damage, const float color[static 4], +// const struct wlr_box *box, const float matrix[static 9]) { +// assert(fx_renderer); +// +// 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_rect(fx_renderer, box, color, matrix); +// } +// } +// +// static void render_texture(struct fx_renderer *fx_renderer, struct wlr_output *output, +// pixman_region32_t *damage, struct wlr_texture *texture, +// const struct wlr_fbox *src_box, const struct wlr_box *dst_box, +// const float matrix[static 9], float opacity, int corner_radius) { +// assert(fx_renderer); +// +// struct wlr_fbox default_src_box = {0}; +// if (wlr_fbox_empty(src_box)) { +// default_src_box.width = texture->width; +// default_src_box.height = texture->height; +// src_box = &default_src_box; +// } +// +// // ensure the box is updated as per the output orientation +// struct wlr_box transformed_box; +// int width, height; +// wlr_output_transformed_resolution(output, &width, &height); +// wlr_box_transform(&transformed_box, dst_box, +// wlr_output_transform_invert(output->transform), width, height); +// +// 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_subtexture_with_matrix(fx_renderer, texture, src_box, +// &transformed_box, matrix, opacity, corner_radius); +// } +// } +// +// static void render_box_shadow(struct fx_renderer *fx_renderer, +// struct wlr_output *output, pixman_region32_t *surface_damage, +// const struct wlr_box *surface_box, int corner_radius, +// struct shadow_data *shadow_data) { +// // don't damage area behind window since we dont render it anyway +// pixman_region32_t inner_region; +// pixman_region32_init(&inner_region); +// pixman_region32_union_rect(&inner_region, &inner_region, +// surface_box->x + corner_radius * 0.5, +// surface_box->y + corner_radius * 0.5, +// surface_box->width - corner_radius, +// surface_box->height - corner_radius); +// pixman_region32_intersect(&inner_region, &inner_region, surface_damage); +// +// pixman_region32_t damage; +// pixman_region32_init(&damage); +// pixman_region32_subtract(&damage, surface_damage, &inner_region); +// if (!pixman_region32_not_empty(&damage)) { +// goto damage_finish; +// } +// +// struct wlr_box shadow_box = { +// .x = surface_box->x - shadow_data->blur_sigma, +// .y = surface_box->y - shadow_data->blur_sigma, +// .width = surface_box->width + 2 * shadow_data->blur_sigma, +// .height = surface_box->height + 2 * shadow_data->blur_sigma, +// }; +// float matrix[9]; +// wlr_matrix_project_box(matrix, &shadow_box, WL_OUTPUT_TRANSFORM_NORMAL, 0, +// output->transform_matrix); +// +// // ensure the box is updated as per the output orientation +// struct wlr_box transformed_box; +// int width, height; +// wlr_output_transformed_resolution(output, &width, &height); +// wlr_box_transform(&transformed_box, &shadow_box, +// wlr_output_transform_invert(output->transform), width, height); +// +// 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_box_shadow(fx_renderer, &transformed_box, surface_box, matrix, +// corner_radius, shadow_data); +// } +// +// damage_finish: +// pixman_region32_fini(&damage); +// pixman_region32_fini(&inner_region); +// } +// +// static void scene_node_render(struct fx_renderer *fx_renderer, struct wlr_scene_node *node, +// struct wlr_scene_output *scene_output, pixman_region32_t *damage) { +// int x, y; +// wlr_scene_node_coords(node, &x, &y); +// x -= scene_output->x; +// y -= scene_output->y; +// +// struct wlr_output *output = scene_output->output; +// +// pixman_region32_t render_region; +// pixman_region32_init(&render_region); +// pixman_region32_copy(&render_region, &node->visible); +// pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y); +// scale_output_damage(&render_region, output->scale); +// pixman_region32_intersect(&render_region, &render_region, damage); +// if (!pixman_region32_not_empty(&render_region)) { +// pixman_region32_fini(&render_region); +// return; +// } +// +// struct wlr_box dst_box = { +// .x = x, +// .y = y, +// }; +// scene_node_get_size(node, &dst_box.width, &dst_box.height); +// scale_box(&dst_box, output->scale); +// +// struct wlr_texture *texture; +// float matrix[9]; +// enum wl_output_transform transform; +// switch (node->type) { +// case WLR_SCENE_NODE_TREE: +// assert(false); +// break; +// case WLR_SCENE_NODE_RECT:; +// struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); +// +// render_rect(fx_renderer, output, &render_region, scene_rect->color, &dst_box, +// output->transform_matrix); +// break; +// case WLR_SCENE_NODE_BUFFER:; +// struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); +// assert(scene_buffer->buffer); +// +// struct wlr_renderer *renderer = output->renderer; +// texture = scene_buffer_get_texture(scene_buffer, renderer); +// +// transform = wlr_output_transform_invert(scene_buffer->transform); +// wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, +// output->transform_matrix); +// +// // Some surfaces (mostly GTK 4) decorate their windows with shadows +// // which extends the node size past the actual window size. This gets +// // the actual surface geometry, mostly ignoring CSD decorations +// // but only if we need to. +// if (scene_buffer->corner_radius != 0 || +// scene_buffer_has_shadow(&scene_buffer->shadow_data)) { +// struct wlr_scene_surface *scene_surface = NULL; +// if ((scene_surface = wlr_scene_surface_from_buffer(scene_buffer)) && +// wlr_surface_is_xdg_surface(scene_surface->surface)) { +// struct wlr_xdg_surface *xdg_surface = +// wlr_xdg_surface_from_wlr_surface(scene_surface->surface); +// +// struct wlr_box geometry; +// wlr_xdg_surface_get_geometry(xdg_surface, &geometry); +// dst_box.width = fmin(dst_box.width, geometry.width); +// dst_box.height = fmin(dst_box.height, geometry.height); +// dst_box.x = fmax(dst_box.x, geometry.x + x); +// dst_box.y = fmax(dst_box.y, geometry.y + y); +// } +// } +// +// // Shadow +// if (scene_buffer_has_shadow(&scene_buffer->shadow_data)) { +// // TODO: Compensate for SSD borders here +// render_box_shadow(fx_renderer, output, &render_region, &dst_box, +// scene_buffer->corner_radius, &scene_buffer->shadow_data); +// } +// +// // Clip the damage to the dst_box before rendering the texture +// pixman_region32_intersect_rect(&render_region, &render_region, +// dst_box.x, dst_box.y, dst_box.width, dst_box.height); +// +// render_texture(fx_renderer, output, &render_region, texture, &scene_buffer->src_box, +// &dst_box, matrix, scene_buffer->opacity, scene_buffer->corner_radius); +// +// wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output); +// break; +// } +// +// pixman_region32_fini(&render_region); +// } + +struct render_list_entry { + struct wlr_scene_node *node; + bool sent_dmabuf_feedback; int x, y; - wlr_scene_node_coords(node, &x, &y); - x -= scene_output->x; - y -= scene_output->y; +}; - struct wlr_output *output = scene_output->output; +static void scene_entry_render(struct render_list_entry *entry, const struct render_data *data) { + struct wlr_scene_node *node = entry->node; pixman_region32_t render_region; pixman_region32_init(&render_region); pixman_region32_copy(&render_region, &node->visible); - pixman_region32_translate(&render_region, -scene_output->x, -scene_output->y); - scale_output_damage(&render_region, output->scale); - pixman_region32_intersect(&render_region, &render_region, damage); + pixman_region32_translate(&render_region, -data->logical.x, -data->logical.y); + scale_output_damage(&render_region, data->scale); + pixman_region32_intersect(&render_region, &render_region, &data->damage); if (!pixman_region32_not_empty(&render_region)) { pixman_region32_fini(&render_region); return; } struct wlr_box dst_box = { - .x = x, - .y = y, + .x = entry->x - data->logical.x, + .y = entry->y - data->logical.y, }; scene_node_get_size(node, &dst_box.width, &dst_box.height); - scale_box(&dst_box, output->scale); + scale_box(&dst_box, data->scale); + + pixman_region32_t opaque; + pixman_region32_init(&opaque); + scene_node_opaque_region(node, dst_box.x, dst_box.y, &opaque); + scale_output_damage(&opaque, data->scale); + pixman_region32_subtract(&opaque, &render_region, &opaque); + + transform_output_box(&dst_box, data); + transform_output_damage(&render_region, data); - struct wlr_texture *texture; - float matrix[9]; - enum wl_output_transform transform; switch (node->type) { case WLR_SCENE_NODE_TREE: assert(false); break; case WLR_SCENE_NODE_RECT:; - struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); - - render_rect(fx_renderer, output, &render_region, scene_rect->color, &dst_box, - output->transform_matrix); + struct wlr_scene_rect *scene_rect = wlr_scene_rect_from_node(node); + + wlr_render_pass_add_rect(data->render_pass, &(struct wlr_render_rect_options){ + .box = dst_box, + .color = { + .r = scene_rect->color[0], + .g = scene_rect->color[1], + .b = scene_rect->color[2], + .a = scene_rect->color[3], + }, + .clip = &render_region, + }); break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node); assert(scene_buffer->buffer); - struct wlr_renderer *renderer = output->renderer; - texture = scene_buffer_get_texture(scene_buffer, renderer); - - transform = wlr_output_transform_invert(scene_buffer->transform); - wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, - output->transform_matrix); - - // Some surfaces (mostly GTK 4) decorate their windows with shadows - // which extends the node size past the actual window size. This gets - // the actual surface geometry, mostly ignoring CSD decorations - // but only if we need to. - if (scene_buffer->corner_radius != 0 || - scene_buffer_has_shadow(&scene_buffer->shadow_data)) { - struct wlr_scene_surface *scene_surface = NULL; - if ((scene_surface = wlr_scene_surface_from_buffer(scene_buffer)) && - wlr_surface_is_xdg_surface(scene_surface->surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(scene_surface->surface); - - struct wlr_box geometry; - wlr_xdg_surface_get_geometry(xdg_surface, &geometry); - dst_box.width = fmin(dst_box.width, geometry.width); - dst_box.height = fmin(dst_box.height, geometry.height); - dst_box.x = fmax(dst_box.x, geometry.x + x); - dst_box.y = fmax(dst_box.y, geometry.y + y); - } - } - - // Shadow - if (scene_buffer_has_shadow(&scene_buffer->shadow_data)) { - // TODO: Compensate for SSD borders here - render_box_shadow(fx_renderer, output, &render_region, &dst_box, - scene_buffer->corner_radius, &scene_buffer->shadow_data); + struct wlr_texture *texture = scene_buffer_get_texture(scene_buffer, + data->output->output->renderer); + if (texture == NULL) { + break; } - // Clip the damage to the dst_box before rendering the texture - pixman_region32_intersect_rect(&render_region, &render_region, - dst_box.x, dst_box.y, dst_box.width, dst_box.height); - - render_texture(fx_renderer, output, &render_region, texture, &scene_buffer->src_box, - &dst_box, matrix, scene_buffer->opacity, scene_buffer->corner_radius); - - wl_signal_emit_mutable(&scene_buffer->events.output_present, scene_output); + enum wl_output_transform transform = + wlr_output_transform_invert(scene_buffer->transform); + transform = wlr_output_transform_compose(transform, data->transform); + + wlr_render_pass_add_texture(data->render_pass, &(struct wlr_render_texture_options) { + .texture = texture, + .src_box = scene_buffer->src_box, + .dst_box = dst_box, + .transform = transform, + .clip = &render_region, + .alpha = &scene_buffer->opacity, + .filter_mode = scene_buffer->filter_mode, + .blend_mode = pixman_region32_not_empty(&opaque) ? + WLR_RENDER_BLEND_MODE_PREMULTIPLIED : WLR_RENDER_BLEND_MODE_NONE, + }); + + struct wlr_scene_output_sample_event sample_event = { + .output = data->output, + .direct_scanout = false, + }; + wl_signal_emit_mutable(&scene_buffer->events.output_sample, &sample_event); break; } + pixman_region32_fini(&opaque); pixman_region32_fini(&render_region); } @@ -1285,6 +1462,23 @@ void wlr_scene_set_presentation(struct wlr_scene *scene, wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); } +static void scene_handle_linux_dmabuf_v1_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, linux_dmabuf_v1_destroy); + wl_list_remove(&scene->linux_dmabuf_v1_destroy.link); + wl_list_init(&scene->linux_dmabuf_v1_destroy.link); + scene->linux_dmabuf_v1 = NULL; +} + +void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, + struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1) { + assert(scene->linux_dmabuf_v1 == NULL); + scene->linux_dmabuf_v1 = linux_dmabuf_v1; + scene->linux_dmabuf_v1_destroy.notify = scene_handle_linux_dmabuf_v1_destroy; + wl_signal_add(&linux_dmabuf_v1->events.destroy, &scene->linux_dmabuf_v1_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -1296,50 +1490,47 @@ static const struct wlr_addon_interface output_addon_impl = { .destroy = scene_output_handle_destroy, }; - static void scene_node_output_update(struct wlr_scene_node *node, - struct wl_list *outputs, struct wlr_scene_output *ignore) { + struct wl_list *outputs, struct wlr_scene_output *ignore, + struct wlr_scene_output *force) { if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { - scene_node_output_update(child, outputs, ignore); + scene_node_output_update(child, outputs, ignore, force); } return; } - update_node_update_outputs(node, outputs, ignore); + update_node_update_outputs(node, outputs, ignore, force); } -static void scene_output_update_geometry(struct wlr_scene_output *scene_output) { - int width, height; - wlr_output_transformed_resolution(scene_output->output, &width, &height); - wlr_damage_ring_set_bounds(&scene_output->damage_ring, width, height); +static void scene_output_update_geometry(struct wlr_scene_output *scene_output, + bool force_update) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); wlr_output_schedule_frame(scene_output->output); scene_node_output_update(&scene_output->scene->tree.node, - &scene_output->scene->outputs, NULL); + &scene_output->scene->outputs, NULL, force_update ? scene_output : NULL); } static void scene_output_handle_commit(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_commit); struct wlr_output_event_commit *event = data; + const struct wlr_output_state *state = event->state; - if (event->committed & (WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE | + bool force_update = state->committed & ( + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_SUBPIXEL); + + if (force_update || state->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_ENABLED)) { - scene_output_update_geometry(scene_output); + scene_output_update_geometry(scene_output, force_update); } } -static void scene_output_handle_mode(struct wl_listener *listener, void *data) { - struct wlr_scene_output *scene_output = wl_container_of(listener, - scene_output, output_mode); - scene_output_update_geometry(scene_output); -} - static void scene_output_handle_damage(struct wl_listener *listener, void *data) { struct wlr_scene_output *scene_output = wl_container_of(listener, scene_output, output_damage); @@ -1395,16 +1586,13 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, scene_output->output_commit.notify = scene_output_handle_commit; wl_signal_add(&output->events.commit, &scene_output->output_commit); - scene_output->output_mode.notify = scene_output_handle_mode; - wl_signal_add(&output->events.mode, &scene_output->output_mode); - scene_output->output_damage.notify = scene_output_handle_damage; wl_signal_add(&output->events.damage, &scene_output->output_damage); scene_output->output_needs_frame.notify = scene_output_handle_needs_frame; wl_signal_add(&output->events.needs_frame, &scene_output->output_needs_frame); - scene_output_update_geometry(scene_output); + scene_output_update_geometry(scene_output, false); return scene_output; } @@ -1423,18 +1611,21 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wl_signal_emit_mutable(&scene_output->events.destroy, NULL); scene_node_output_update(&scene_output->scene->tree.node, - &scene_output->scene->outputs, scene_output); + &scene_output->scene->outputs, scene_output, NULL); struct highlight_region *damage, *tmp_damage; wl_list_for_each_safe(damage, tmp_damage, &scene_output->damage_highlight_regions, link) { highlight_region_destroy(damage); } + struct fx_renderer *fx_renderer = + fx_renderer_addon_find(&scene_output->output->addons, scene_output->scene); + wlr_addon_finish(&fx_renderer->addon); + wlr_addon_finish(&scene_output->addon); wlr_damage_ring_finish(&scene_output->damage_ring); wl_list_remove(&scene_output->link); wl_list_remove(&scene_output->output_commit.link); - wl_list_remove(&scene_output->output_mode.link); wl_list_remove(&scene_output->output_damage.link); wl_list_remove(&scene_output->output_needs_frame.link); @@ -1463,14 +1654,14 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, scene_output->x = lx; scene_output->y = ly; - scene_output_update_geometry(scene_output); + scene_output_update_geometry(scene_output, false); } static bool scene_node_invisible(struct wlr_scene_node *node) { if (node->type == WLR_SCENE_NODE_TREE) { return true; } else if (node->type == WLR_SCENE_NODE_RECT) { - struct wlr_scene_rect *rect = scene_rect_from_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_BUFFER) { @@ -1500,7 +1691,7 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, // If we see a black rect, we can ignore rendering everything under the rect // and even the rect itself. if (node->type == WLR_SCENE_NODE_RECT && data->calculate_visibility) { - struct wlr_scene_rect *rect = scene_rect_from_node(node); + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); float *black = (float[4]){ 0.f, 0.f, 0.f, 1.f }; if (memcmp(rect->color, black, sizeof(float) * 4) == 0) { @@ -1520,16 +1711,66 @@ static bool construct_render_list_iterator(struct wlr_scene_node *node, pixman_region32_fini(&intersection); - struct wlr_scene_node **entry = wl_array_add(data->render_list, - sizeof(struct wlr_scene_node *)); - if (entry) { - *entry = node; + struct render_list_entry *entry = wl_array_add(data->render_list, sizeof(*entry)); + if (!entry) { + return false; } + + *entry = (struct render_list_entry){ + .node = node, + .x = lx, + .y = ly, + }; + return false; } -static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, - struct wlr_scene_output *scene_output, struct wlr_box *box) { +static void output_state_apply_damage(const struct render_data *data, + struct wlr_output_state *state) { + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + pixman_region32_copy(&frame_damage, &data->output->damage_ring.current); + transform_output_damage(&frame_damage, data); + wlr_output_state_set_damage(state, &frame_damage); + pixman_region32_fini(&frame_damage); +} + +static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, + struct wlr_scene_buffer *scene_buffer, + const struct wlr_linux_dmabuf_feedback_v1_init_options *options) { + if (!scene->linux_dmabuf_v1) { + return; + } + + struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(scene_buffer); + if (!surface) { + return; + } + + // compare to the previous options so that we don't send + // duplicate feedback events. + if (memcmp(options, &scene_buffer->prev_feedback_options, sizeof(*options)) == 0) { + return; + } + + scene_buffer->prev_feedback_options = *options; + + struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; + if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, options)) { + return; + } + + wlr_linux_dmabuf_v1_set_surface_feedback(scene->linux_dmabuf_v1, + surface->surface, &feedback); + + wlr_linux_dmabuf_feedback_v1_finish(&feedback); +} + +static bool scene_entry_try_direct_scanout(struct render_list_entry *entry, + struct wlr_output_state *state, const struct render_data *data) { + struct wlr_scene_output *scene_output = data->output; + struct wlr_scene_node *node = entry->node; + if (!scene_output->scene->direct_scanout) { return false; } @@ -1545,8 +1786,18 @@ static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, return false; } + if (state->committed & (WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_ENABLED | + WLR_OUTPUT_STATE_RENDER_FORMAT)) { + // Legacy DRM will explode if we try to modeset with a direct scanout buffer + return false; + } + + if (!wlr_output_is_direct_scanout_allowed(scene_output->output)) { + return false; + } + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - struct wlr_output *output = scene_output->output; struct wlr_fbox default_box = {0}; if (buffer->transform & WL_OUTPUT_TRANSFORM_90) { @@ -1562,75 +1813,396 @@ static bool scene_node_try_direct_scanout(struct wlr_scene_node *node, return false; } - if (buffer->transform != output->transform) { + if (buffer->transform != data->transform) { return false; } - struct wlr_box node_box; - wlr_scene_node_coords(node, &node_box.x, &node_box.y); + struct wlr_box node_box = { .x = entry->x, .y = entry->y }; scene_node_get_size(node, &node_box.width, &node_box.height); - if (!wlr_box_equal(box, &node_box)) { + if (!wlr_box_equal(&data->logical, &node_box)) { + return false; + } + + if (buffer->primary_output == scene_output) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = scene_output->output->renderer, + .scanout_primary_output = scene_output->output, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + entry->sent_dmabuf_feedback = true; + } + + struct wlr_output_state pending; + wlr_output_state_init(&pending); + if (!wlr_output_state_copy(&pending, state)) { return false; } - wlr_output_attach_buffer(output, buffer->buffer); - if (!wlr_output_test(output)) { - wlr_output_rollback(output); + wlr_output_state_set_buffer(&pending, buffer->buffer); + output_state_apply_damage(data, &pending); + + if (!wlr_output_test_state(scene_output->output, &pending)) { + wlr_output_state_finish(&pending); return false; } - return wlr_output_commit(output); + wlr_output_state_copy(state, &pending); + wlr_output_state_finish(&pending); + + struct wlr_scene_output_sample_event sample_event = { + .output = scene_output, + .direct_scanout = true, + }; + wl_signal_emit_mutable(&buffer->events.output_sample, &sample_event); + return true; } -bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { +// bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { +// struct wlr_output *output = scene_output->output; +// enum wlr_scene_debug_damage_option debug_damage = +// scene_output->scene->debug_damage_option; +// +// // Find the fx_renderer addon +// struct fx_renderer *renderer = +// fx_renderer_addon_find(&output->addons, scene_output->scene); +// assert(renderer != NULL); +// +// struct render_list_constructor_data list_con = { +// .box = { .x = scene_output->x, .y = scene_output->y }, +// .render_list = &scene_output->render_list, +// .calculate_visibility = scene_output->scene->calculate_visibility, +// }; +// wlr_output_effective_resolution(output, +// &list_con.box.width, &list_con.box.height); +// +// list_con.render_list->size = 0; +// scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, +// construct_render_list_iterator, &list_con); +// array_realloc(list_con.render_list, list_con.render_list->size); +// +// int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); +// struct wlr_scene_node **list_data = list_con.render_list->data; +// +// // if there is only one thing to render let's see if that thing can be +// // directly scanned out +// bool scanout = false; +// if (list_len == 1) { +// struct wlr_scene_node *node = list_data[0]; +// scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box); +// } +// +// if (scene_output->prev_scanout != scanout) { +// scene_output->prev_scanout = scanout; +// wlr_log(WLR_DEBUG, "Direct scan-out %s", +// scanout ? "enabled" : "disabled"); +// // When exiting direct scan-out, damage everything +// wlr_damage_ring_add_whole(&scene_output->damage_ring); +// } +// +// if (scanout) { +// struct wlr_scene_node *node = list_data[0]; +// +// assert(node->type == WLR_SCENE_NODE_BUFFER); +// struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); +// wl_signal_emit_mutable(&buffer->events.output_present, scene_output); +// return true; +// } +// +// if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_RERENDER) { +// wlr_damage_ring_add_whole(&scene_output->damage_ring); +// } +// +// struct timespec now; +// if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { +// struct wl_list *regions = &scene_output->damage_highlight_regions; +// clock_gettime(CLOCK_MONOTONIC, &now); +// +// // add the current frame's damage if there is damage +// if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { +// struct highlight_region *current_damage = +// calloc(1, sizeof(*current_damage)); +// if (current_damage) { +// pixman_region32_init(¤t_damage->region); +// pixman_region32_copy(¤t_damage->region, +// &scene_output->damage_ring.current); +// current_damage->when = now; +// wl_list_insert(regions, ¤t_damage->link); +// } +// } +// +// pixman_region32_t acc_damage; +// pixman_region32_init(&acc_damage); +// struct highlight_region *damage, *tmp_damage; +// wl_list_for_each_safe(damage, tmp_damage, regions, link) { +// // remove overlaping damage regions +// pixman_region32_subtract(&damage->region, &damage->region, &acc_damage); +// pixman_region32_union(&acc_damage, &acc_damage, &damage->region); +// +// // if this damage is too old or has nothing in it, get rid of it +// struct timespec time_diff; +// timespec_sub(&time_diff, &now, &damage->when); +// if (timespec_to_msec(&time_diff) >= HIGHLIGHT_DAMAGE_FADEOUT_TIME || +// !pixman_region32_not_empty(&damage->region)) { +// highlight_region_destroy(damage); +// } +// } +// +// wlr_damage_ring_add(&scene_output->damage_ring, &acc_damage); +// pixman_region32_fini(&acc_damage); +// } +// +// int buffer_age; +// if (!wlr_output_attach_render(output, &buffer_age)) { +// return false; +// } +// +// pixman_region32_t damage; +// pixman_region32_init(&damage); +// wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, +// buffer_age, &damage); +// if (!output->needs_frame && !pixman_region32_not_empty( +// &scene_output->damage_ring.current)) { +// pixman_region32_fini(&damage); +// wlr_output_rollback(output); +// return true; +// } +// +// fx_renderer_begin(renderer, output->width, output->height); +// +// pixman_region32_t background; +// pixman_region32_init(&background); +// pixman_region32_copy(&background, &damage); +// +// // Cull areas of the background that are occluded by opaque regions of +// // scene nodes above. Those scene nodes will just render atop having us +// // never see the background. +// if (scene_output->scene->calculate_visibility) { +// float output_scale = scene_output->output->scale; +// +// for (int i = list_len - 1; i >= 0; i--) { +// struct wlr_scene_node *node = list_data[i]; +// int x, y; +// wlr_scene_node_coords(node, &x, &y); +// +// // We must only cull opaque regions that are visible by the node. +// // The node's visibility will have the knowledge of a black rect +// // that may have been omitted from the render list via the black +// // rect optimization. In order to ensure we don't cull background +// // rendering in that black rect region, consider the node's visibility. +// pixman_region32_t opaque; +// pixman_region32_init(&opaque); +// scene_node_opaque_region(node, x, y, &opaque); +// pixman_region32_intersect(&opaque, &opaque, &node->visible); +// +// pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); +// wlr_region_scale(&opaque, &opaque, output_scale); +// pixman_region32_subtract(&background, &background, &opaque); +// pixman_region32_fini(&opaque); +// } +// +// if (floor(output_scale) != output_scale) { +// wlr_region_expand(&background, &background, 1); +// +// // reintersect with the damage because we never want to render +// // outside of the damage region +// pixman_region32_intersect(&background, &background, &damage); +// } +// } +// +// int nrects; +// pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects); +// for (int i = 0; i < nrects; ++i) { +// scissor_output(output, &rects[i]); +// fx_renderer_clear((float[4]){ 0.0, 0.0, 0.0, 1.0 }); +// } +// pixman_region32_fini(&background); +// +// for (int i = list_len - 1; i >= 0; i--) { +// struct wlr_scene_node *node = list_data[i]; +// scene_node_render(renderer, node, scene_output, &damage); +// } +// +// fx_renderer_scissor(NULL); +// +// if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { +// struct highlight_region *damage; +// wl_list_for_each(damage, &scene_output->damage_highlight_regions, link) { +// struct timespec time_diff; +// timespec_sub(&time_diff, &now, &damage->when); +// int64_t time_diff_ms = timespec_to_msec(&time_diff); +// float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; +// +// int nrects; +// pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects); +// for (int i = 0; i < nrects; ++i) { +// struct wlr_box box = { +// .x = rects[i].x1, +// .y = rects[i].y1, +// .width = rects[i].x2 - rects[i].x1, +// .height = rects[i].y2 - rects[i].y1, +// }; +// +// float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 }; +// fx_render_rect(renderer, &box, color, output->transform_matrix); +// } +// } +// } +// +// // Draw the software cursors +// wlr_renderer_begin(output->renderer, output->width, output->height); +// wlr_output_render_software_cursors(output, &damage); +// wlr_renderer_end(output->renderer); +// +// pixman_region32_fini(&damage); +// +// int tr_width, tr_height; +// wlr_output_transformed_resolution(output, &tr_width, &tr_height); +// +// enum wl_output_transform transform = +// wlr_output_transform_invert(output->transform); +// +// pixman_region32_t frame_damage; +// pixman_region32_init(&frame_damage); +// wlr_region_transform(&frame_damage, +// &scene_output->damage_ring.current, +// transform, tr_width, tr_height); +// wlr_output_set_damage(output, &frame_damage); +// pixman_region32_fini(&frame_damage); +// +// bool success = wlr_output_commit(output); +// +// if (success) { +// wlr_damage_ring_rotate(&scene_output->damage_ring); +// } +// +// if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && +// !wl_list_empty(&scene_output->damage_highlight_regions)) { +// wlr_output_schedule_frame(scene_output->output); +// } +// +// return success; +// } + +bool wlr_scene_output_commit(struct wlr_scene_output *scene_output, + const struct wlr_scene_output_state_options *options) { + if (!scene_output->output->needs_frame && !pixman_region32_not_empty( + &scene_output->damage_ring.current)) { + return true; + } + + bool ok = false; + struct wlr_output_state state; + wlr_output_state_init(&state); + if (!wlr_scene_output_build_state(scene_output, &state, options)) { + goto out; + } + + ok = wlr_output_commit_state(scene_output->output, &state); + if (!ok) { + goto out; + } + + wlr_damage_ring_rotate(&scene_output->damage_ring); + +out: + wlr_output_state_finish(&state); + return ok; +} + +bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, + struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { + struct wlr_scene_output_state_options default_options = {0}; + if (!options) { + options = &default_options; + } + struct wlr_scene_timer *timer = options->timer; + struct timespec start_time; + if (timer) { + clock_gettime(CLOCK_MONOTONIC, &start_time); + wlr_scene_timer_finish(timer); + *timer = (struct wlr_scene_timer){0}; + } + + if ((state->committed & WLR_OUTPUT_STATE_ENABLED) && !state->enabled) { + // if the state is being disabled, do nothing. + return true; + } + struct wlr_output *output = scene_output->output; enum wlr_scene_debug_damage_option debug_damage = scene_output->scene->debug_damage_option; - // Find the fx_renderer addon - struct fx_renderer *renderer = - fx_renderer_addon_find(&output->addons, scene_output->scene); - assert(renderer != NULL); + struct render_data render_data = { + .transform = output->transform, + .scale = output->scale, + .logical = { .x = scene_output->x, .y = scene_output->y }, + .output = scene_output, + }; + + output_pending_resolution(output, state, + &render_data.trans_width, &render_data.trans_height); + + if (state->committed & WLR_OUTPUT_STATE_TRANSFORM) { + if (render_data.transform != state->transform) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } + + render_data.transform = state->transform; + } + + if (state->committed & WLR_OUTPUT_STATE_SCALE) { + if (render_data.scale != state->scale) { + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } + + render_data.scale = state->scale; + } + + if (render_data.transform & WL_OUTPUT_TRANSFORM_90) { + int tmp = render_data.trans_width; + render_data.trans_width = render_data.trans_height; + render_data.trans_height = tmp; + } + + render_data.logical.width = render_data.trans_width / render_data.scale; + render_data.logical.height = render_data.trans_height / render_data.scale; struct render_list_constructor_data list_con = { - .box = { .x = scene_output->x, .y = scene_output->y }, + .box = render_data.logical, .render_list = &scene_output->render_list, .calculate_visibility = scene_output->scene->calculate_visibility, }; - wlr_output_effective_resolution(output, - &list_con.box.width, &list_con.box.height); list_con.render_list->size = 0; scene_nodes_in_box(&scene_output->scene->tree.node, &list_con.box, construct_render_list_iterator, &list_con); array_realloc(list_con.render_list, list_con.render_list->size); - int list_len = list_con.render_list->size / sizeof(struct wlr_scene_node *); - struct wlr_scene_node **list_data = list_con.render_list->data; + struct render_list_entry *list_data = list_con.render_list->data; + int list_len = list_con.render_list->size / sizeof(*list_data); - // if there is only one thing to render let's see if that thing can be - // directly scanned out - bool scanout = false; - if (list_len == 1) { - struct wlr_scene_node *node = list_data[0]; - scanout = scene_node_try_direct_scanout(node, scene_output, &list_con.box); - } + bool scanout = list_len == 1 && + scene_entry_try_direct_scanout(&list_data[0], state, &render_data); if (scene_output->prev_scanout != scanout) { scene_output->prev_scanout = scanout; wlr_log(WLR_DEBUG, "Direct scan-out %s", scanout ? "enabled" : "disabled"); - // When exiting direct scan-out, damage everything - wlr_damage_ring_add_whole(&scene_output->damage_ring); + if (!scanout) { + // When exiting direct scan-out, damage everything + wlr_damage_ring_add_whole(&scene_output->damage_ring); + } } if (scanout) { - struct wlr_scene_node *node = list_data[0]; - - assert(node->type == WLR_SCENE_NODE_BUFFER); - struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); - wl_signal_emit_mutable(&buffer->events.output_present, scene_output); + if (timer) { + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); + } return true; } @@ -1645,8 +2217,7 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { // add the current frame's damage if there is damage if (pixman_region32_not_empty(&scene_output->damage_ring.current)) { - struct highlight_region *current_damage = - calloc(1, sizeof(*current_damage)); + struct highlight_region *current_damage = calloc(1, sizeof(*current_damage)); if (current_damage) { pixman_region32_init(¤t_damage->region); pixman_region32_copy(¤t_damage->region, @@ -1677,38 +2248,52 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { pixman_region32_fini(&acc_damage); } + wlr_damage_ring_set_bounds(&scene_output->damage_ring, + render_data.trans_width, render_data.trans_height); + + if (!wlr_output_configure_primary_swapchain(output, state, &output->swapchain)) { + return false; + } + int buffer_age; - if (!wlr_output_attach_render(output, &buffer_age)) { + struct wlr_buffer *buffer = wlr_swapchain_acquire(output->swapchain, &buffer_age); + if (buffer == NULL) { return false; } - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, - buffer_age, &damage); - if (!output->needs_frame && !pixman_region32_not_empty( - &scene_output->damage_ring.current)) { - pixman_region32_fini(&damage); - wlr_output_rollback(output); - return true; + if (timer) { + timer->render_timer = wlr_render_timer_create(output->renderer); + + struct timespec end_time, duration; + clock_gettime(CLOCK_MONOTONIC, &end_time); + timespec_sub(&duration, &end_time, &start_time); + timer->pre_render_duration = timespec_to_nsec(&duration); } - fx_renderer_begin(renderer, output->width, output->height); + struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, + &(struct wlr_buffer_pass_options){ + .timer = timer ? timer->render_timer : NULL, + }); + if (render_pass == NULL) { + wlr_buffer_unlock(buffer); + return false; + } + + render_data.render_pass = render_pass; + pixman_region32_init(&render_data.damage); + wlr_damage_ring_get_buffer_damage(&scene_output->damage_ring, + buffer_age, &render_data.damage); pixman_region32_t background; pixman_region32_init(&background); - pixman_region32_copy(&background, &damage); + pixman_region32_copy(&background, &render_data.damage); // Cull areas of the background that are occluded by opaque regions of // scene nodes above. Those scene nodes will just render atop having us // never see the background. if (scene_output->scene->calculate_visibility) { - float output_scale = scene_output->output->scale; - for (int i = list_len - 1; i >= 0; i--) { - struct wlr_scene_node *node = list_data[i]; - int x, y; - wlr_scene_node_coords(node, &x, &y); + struct render_list_entry *entry = &list_data[i]; // We must only cull opaque regions that are visible by the node. // The node's visibility will have the knowledge of a black rect @@ -1717,38 +2302,49 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { // rendering in that black rect region, consider the node's visibility. pixman_region32_t opaque; pixman_region32_init(&opaque); - scene_node_opaque_region(node, x, y, &opaque); - pixman_region32_intersect(&opaque, &opaque, &node->visible); + scene_node_opaque_region(entry->node, entry->x, entry->y, &opaque); + pixman_region32_intersect(&opaque, &opaque, &entry->node->visible); pixman_region32_translate(&opaque, -scene_output->x, -scene_output->y); - wlr_region_scale(&opaque, &opaque, output_scale); + wlr_region_scale(&opaque, &opaque, render_data.scale); pixman_region32_subtract(&background, &background, &opaque); pixman_region32_fini(&opaque); } - if (floor(output_scale) != output_scale) { + if (floor(render_data.scale) != render_data.scale) { wlr_region_expand(&background, &background, 1); // reintersect with the damage because we never want to render // outside of the damage region - pixman_region32_intersect(&background, &background, &damage); + pixman_region32_intersect(&background, &background, &render_data.damage); } } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&background, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(output, &rects[i]); - fx_renderer_clear((float[4]){ 0.0, 0.0, 0.0, 1.0 }); - } + transform_output_damage(&background, &render_data); + wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ + .box = { .width = buffer->width, .height = buffer->height }, + .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, + .clip = &background, + }); pixman_region32_fini(&background); for (int i = list_len - 1; i >= 0; i--) { - struct wlr_scene_node *node = list_data[i]; - scene_node_render(renderer, node, scene_output, &damage); - } + struct render_list_entry *entry = &list_data[i]; + scene_entry_render(entry, &render_data); + + if (entry->node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(entry->node); - fx_renderer_scissor(NULL); + if (buffer->primary_output == scene_output && !entry->sent_dmabuf_feedback) { + struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = output->renderer, + .scanout_primary_output = NULL, + }; + + scene_buffer_send_dmabuf_feedback(scene_output->scene, buffer, &options); + } + } + } if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { struct highlight_region *damage; @@ -1758,55 +2354,48 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { int64_t time_diff_ms = timespec_to_msec(&time_diff); float alpha = 1.0 - (double)time_diff_ms / HIGHLIGHT_DAMAGE_FADEOUT_TIME; - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage->region, &nrects); - for (int i = 0; i < nrects; ++i) { - struct wlr_box box = { - .x = rects[i].x1, - .y = rects[i].y1, - .width = rects[i].x2 - rects[i].x1, - .height = rects[i].y2 - rects[i].y1, - }; - - float color[4] = { alpha * .5, 0.0, 0.0, alpha * .5 }; - fx_render_rect(renderer, &box, color, output->transform_matrix); - } + wlr_render_pass_add_rect(render_pass, &(struct wlr_render_rect_options){ + .box = { .width = buffer->width, .height = buffer->height }, + .color = { .r = alpha * 0.5, .g = 0, .b = 0, .a = alpha * 0.5 }, + .clip = &damage->region, + }); } } - // Draw the software cursors - wlr_renderer_begin(output->renderer, output->width, output->height); - wlr_output_render_software_cursors(output, &damage); - wlr_renderer_end(output->renderer); - - pixman_region32_fini(&damage); + wlr_output_add_software_cursors_to_render_pass(output, render_pass, &render_data.damage); - int tr_width, tr_height; - wlr_output_transformed_resolution(output, &tr_width, &tr_height); + pixman_region32_fini(&render_data.damage); - enum wl_output_transform transform = - wlr_output_transform_invert(output->transform); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - wlr_region_transform(&frame_damage, - &scene_output->damage_ring.current, - transform, tr_width, tr_height); - wlr_output_set_damage(output, &frame_damage); - pixman_region32_fini(&frame_damage); - - bool success = wlr_output_commit(output); - - if (success) { - wlr_damage_ring_rotate(&scene_output->damage_ring); + if (!wlr_render_pass_submit(render_pass)) { + wlr_buffer_unlock(buffer); + return false; } + wlr_output_state_set_buffer(state, buffer); + wlr_buffer_unlock(buffer); + output_state_apply_damage(&render_data, state); + if (debug_damage == WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT && !wl_list_empty(&scene_output->damage_highlight_regions)) { wlr_output_schedule_frame(scene_output->output); } - return success; + return true; +} + +int64_t wlr_scene_timer_get_duration_ns(struct wlr_scene_timer *timer) { + int64_t pre_render = timer->pre_render_duration; + if (!timer->render_timer) { + return pre_render; + } + int64_t render = wlr_render_timer_get_duration_ns(timer->render_timer); + return render != -1 ? pre_render + render : -1; +} + +void wlr_scene_timer_finish(struct wlr_scene_timer *timer) { + if (timer->render_timer) { + wlr_render_timer_destroy(timer->render_timer); + } } static void scene_node_send_frame_done(struct wlr_scene_node *node, @@ -1823,7 +2412,7 @@ static void scene_node_send_frame_done(struct wlr_scene_node *node, wlr_scene_buffer_send_frame_done(scene_buffer, now); } } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_node_send_frame_done(child, scene_output, now); @@ -1858,7 +2447,7 @@ static void scene_output_for_each_scene_buffer(const struct wlr_box *output_box, user_iterator(scene_buffer, lx, ly, user_data); } } else if (node->type == WLR_SCENE_NODE_TREE) { - struct wlr_scene_tree *scene_tree = scene_tree_from_node(node); + struct wlr_scene_tree *scene_tree = wlr_scene_tree_from_node(node); struct wlr_scene_node *child; wl_list_for_each(child, &scene_tree->children, link) { scene_output_for_each_scene_buffer(output_box, child, lx, ly, diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c deleted file mode 100644 index 1688348..0000000 --- a/types/scene/xdg_shell.c +++ /dev/null @@ -1,124 +0,0 @@ -#include <stdlib.h> -#include <wlr/types/wlr_scene.h> -#include <wlr/types/wlr_xdg_shell.h> - -struct wlr_scene_xdg_surface { - struct wlr_scene_tree *tree; - struct wlr_xdg_surface *xdg_surface; - struct wlr_scene_tree *surface_tree; - - struct wl_listener tree_destroy; - struct wl_listener xdg_surface_destroy; - struct wl_listener xdg_surface_map; - struct wl_listener xdg_surface_unmap; - struct wl_listener xdg_surface_commit; -}; - -static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, tree_destroy); - // tree and surface_node will be cleaned up by scene_node_finish - wl_list_remove(&scene_xdg_surface->tree_destroy.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); - wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); - free(scene_xdg_surface); -} - -static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy); - wlr_scene_node_destroy(&scene_xdg_surface->tree->node); -} - -static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_map); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); -} - -static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); -} - -static void scene_xdg_surface_update_position( - struct wlr_scene_xdg_surface *scene_xdg_surface) { - struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; - - struct wlr_box geo = {0}; - wlr_xdg_surface_get_geometry(xdg_surface, &geo); - wlr_scene_node_set_position(&scene_xdg_surface->surface_tree->node, - -geo.x, -geo.y); - - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { - struct wlr_xdg_popup *popup = xdg_surface->popup; - wlr_scene_node_set_position(&scene_xdg_surface->tree->node, - popup->current.geometry.x, popup->current.geometry.y); - } -} - -static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener, - void *data) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - wl_container_of(listener, scene_xdg_surface, xdg_surface_commit); - scene_xdg_surface_update_position(scene_xdg_surface); -} - -struct wlr_scene_tree *wlr_scene_xdg_surface_create( - struct wlr_scene_tree *parent, struct wlr_xdg_surface *xdg_surface) { - struct wlr_scene_xdg_surface *scene_xdg_surface = - calloc(1, sizeof(*scene_xdg_surface)); - if (scene_xdg_surface == NULL) { - return NULL; - } - - scene_xdg_surface->xdg_surface = xdg_surface; - - scene_xdg_surface->tree = wlr_scene_tree_create(parent); - if (scene_xdg_surface->tree == NULL) { - free(scene_xdg_surface); - return NULL; - } - - scene_xdg_surface->surface_tree = wlr_scene_subsurface_tree_create( - scene_xdg_surface->tree, xdg_surface->surface); - if (scene_xdg_surface->surface_tree == NULL) { - wlr_scene_node_destroy(&scene_xdg_surface->tree->node); - free(scene_xdg_surface); - return NULL; - } - - scene_xdg_surface->tree_destroy.notify = - scene_xdg_surface_handle_tree_destroy; - wl_signal_add(&scene_xdg_surface->tree->node.events.destroy, - &scene_xdg_surface->tree_destroy); - - scene_xdg_surface->xdg_surface_destroy.notify = - scene_xdg_surface_handle_xdg_surface_destroy; - wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); - - scene_xdg_surface->xdg_surface_map.notify = - scene_xdg_surface_handle_xdg_surface_map; - wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map); - - scene_xdg_surface->xdg_surface_unmap.notify = - scene_xdg_surface_handle_xdg_surface_unmap; - wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap); - - scene_xdg_surface->xdg_surface_commit.notify = - scene_xdg_surface_handle_xdg_surface_commit; - wl_signal_add(&xdg_surface->surface->events.commit, - &scene_xdg_surface->xdg_surface_commit); - - wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped); - scene_xdg_surface_update_position(scene_xdg_surface); - - return scene_xdg_surface->tree; -} @@ -19,7 +19,7 @@ bool env_parse_bool(const char *option) { return false; } -ssize_t env_parse_switch(const char *option, const char **switches) { +size_t env_parse_switch(const char *option, const char **switches) { const char *env = getenv(option); if (env) { wlr_log(WLR_INFO, "Loading %s option: %s", option, env); diff --git a/util/time.c b/util/time.c index 06e42b4..78faac5 100644 --- a/util/time.c +++ b/util/time.c @@ -10,12 +10,16 @@ int64_t timespec_to_msec(const struct timespec *a) { return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; } +int64_t timespec_to_nsec(const struct timespec *a) { + return (int64_t)a->tv_sec * NSEC_PER_SEC + a->tv_nsec; +} + void timespec_from_nsec(struct timespec *r, int64_t nsec) { r->tv_sec = nsec / NSEC_PER_SEC; r->tv_nsec = nsec % NSEC_PER_SEC; } -uint32_t get_current_time_msec(void) { +int64_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return timespec_to_msec(&now); |