diff --git a/include/sway/desktop.h b/include/sway/desktop.h deleted file mode 100644 index 7f2f5b3eb..000000000 --- a/include/sway/desktop.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -struct sway_container; -struct sway_view; - -void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, - bool whole); - -void desktop_damage_whole_container(struct sway_container *con); - -void desktop_damage_box(struct wlr_box *box); - -void desktop_damage_view(struct sway_view *view); diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7dd58ba8c..17d41fa35 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -1,6 +1,7 @@ #ifndef _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H #include +#include /** * Transactions enable us to perform atomic layout updates. @@ -38,8 +39,11 @@ void transaction_commit_dirty_client(void); * Notify the transaction system that a view is ready for the new layout. * * When all views in the transaction are ready, the layout will be applied. + * + * A success boolean is returned denoting that this part of the transaction is + * ready. */ -void transaction_notify_view_ready_by_serial(struct sway_view *view, +bool transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial); /** @@ -47,8 +51,11 @@ void transaction_notify_view_ready_by_serial(struct sway_view *view, * identifying the instruction by geometry rather than by serial. * * This is used by xwayland views, as they don't have serials. + * + * A success boolean is returned denoting that this part of the transaction is + * ready. */ -void transaction_notify_view_ready_by_geometry(struct sway_view *view, +bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height); #endif diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index e3a468726..70f1f5f6d 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/input/tablet.h" @@ -42,8 +43,6 @@ struct sway_seatop_impl { uint32_t time_msec, enum wlr_tablet_tool_tip_state state); void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); - void (*render)(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage); bool allow_set_cursor; }; @@ -67,16 +66,12 @@ struct sway_seat_node { }; struct sway_drag_icon { - struct sway_seat *seat; struct wlr_drag_icon *wlr_drag_icon; - struct wl_list link; // sway_root::drag_icons + struct wlr_scene_tree *tree; + struct wlr_scene_tree *surface_tree; - double x, y; // in layout-local coordinates - - struct wl_listener surface_commit; - struct wl_listener map; - struct wl_listener unmap; struct wl_listener destroy; + struct wl_listener commit; }; struct sway_drag { @@ -89,6 +84,15 @@ struct sway_seat { struct wlr_seat *wlr_seat; struct sway_cursor *cursor; + // Seat scene tree structure + // - scene_tree + // - drag icons + // - drag icon 1 + // - drag icon 2 + // - seatop specific stuff + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *drag_icons; + bool has_focus; struct wl_list focus_stack; // list of containers in focus order struct sway_workspace *workspace; @@ -247,7 +251,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); -void drag_icon_update_position(struct sway_drag_icon *icon); +void drag_icon_update_position(struct sway_seat *seat, struct sway_drag_icon *icon); enum wlr_edges find_resize_edge(struct sway_container *cont, struct wlr_surface *surface, struct sway_cursor *cursor); @@ -332,13 +336,6 @@ void seatop_end(struct sway_seat *seat); */ void seatop_unref(struct sway_seat *seat, struct sway_container *con); -/** - * Instructs a seatop to render anything that it needs to render - * (eg. dropzone for move-tiling) - */ -void seatop_render(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage); - bool seatop_allows_set_cursor(struct sway_seat *seat); /** diff --git a/include/sway/layers.h b/include/sway/layers.h index f85084934..0cc1ea0a1 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -4,60 +4,32 @@ #include #include -enum layer_parent { - LAYER_PARENT_LAYER, - LAYER_PARENT_POPUP, -}; - struct sway_layer_surface { - struct wlr_layer_surface_v1 *layer_surface; - struct wl_list link; - - struct wl_listener destroy; struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; struct wl_listener output_destroy; + struct wl_listener node_destroy; struct wl_listener new_popup; - struct wl_listener new_subsurface; - struct wlr_box geo; bool mapped; - struct wlr_box extent; - enum zwlr_layer_shell_v1_layer layer; - struct wl_list subsurfaces; + struct sway_output *output; + struct wlr_scene_layer_surface_v1 *scene; + struct wlr_scene_tree *popups; + struct wlr_layer_surface_v1 *layer_surface; }; struct sway_layer_popup { struct wlr_xdg_popup *wlr_popup; - enum layer_parent parent_type; - union { - struct sway_layer_surface *parent_layer; - struct sway_layer_popup *parent_popup; - }; - struct wl_listener map; - struct wl_listener unmap; + struct wlr_scene_tree *scene; + struct sway_layer_surface *toplevel; + struct wl_listener destroy; - struct wl_listener commit; struct wl_listener new_popup; }; -struct sway_layer_subsurface { - struct wlr_subsurface *wlr_subsurface; - struct sway_layer_surface *layer_surface; - struct wl_list link; - - struct wl_listener map; - struct wl_listener unmap; - struct wl_listener destroy; - struct wl_listener commit; -}; - struct sway_output; void arrange_layers(struct sway_output *output); -struct sway_layer_surface *layer_from_wlr_layer_surface_v1( - struct wlr_layer_surface_v1 *layer_surface); - #endif diff --git a/include/sway/output.h b/include/sway/output.h index d72bf1b21..31b1e220f 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "config.h" #include "sway/tree/node.h" #include "sway/tree/view.h" @@ -18,15 +19,32 @@ struct sway_output_state { struct sway_output { struct sway_node node; + + struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *shell_top; + struct wlr_scene_tree *shell_overlay; + struct wlr_scene_tree *session_lock; + } layers; + + // when a container is fullscreen, in case the fullscreen surface is + // translucent (can see behind) we must make sure that the background is a + // solid color in order to conform to the wayland protocol. This rect + // ensures that when looking through a surface, all that will be seen + // is black. + struct wlr_scene_rect *fullscreen_background; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; struct sway_server *server; struct wl_list link; - struct wl_list layers[4]; // sway_layer_surface::link - struct wlr_box usable_area; + struct wlr_fbox usable_area; struct timespec last_frame; - struct wlr_output_damage *damage; int lx, ly; // layout coords int width, height; // transformed buffer size @@ -44,8 +62,7 @@ struct sway_output { struct wl_listener commit; struct wl_listener mode; struct wl_listener present; - struct wl_listener damage_destroy; - struct wl_listener damage_frame; + struct wl_listener frame_request; struct { struct wl_signal disable; @@ -81,19 +98,6 @@ typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct sway_view *view, struct wlr_surface *surface, struct wlr_box *box, void *user_data); -void output_damage_whole(struct sway_output *output); - -void output_damage_surface(struct sway_output *output, double ox, double oy, - struct wlr_surface *surface, bool whole); - -void output_damage_from_view(struct sway_output *output, - struct sway_view *view); - -void output_damage_box(struct sway_output *output, struct wlr_box *box); - -void output_damage_whole_container(struct sway_output *output, - struct sway_container *con); - // this ONLY includes the enabled outputs struct sway_output *output_by_name_or_id(const char *name_or_id); @@ -106,47 +110,8 @@ void output_enable(struct sway_output *output); void output_disable(struct sway_output *output); -bool output_has_opaque_overlay_layer_surface(struct sway_output *output); - struct sway_workspace *output_get_active_workspace(struct sway_output *output); -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage); - -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data); - -void output_view_for_each_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_view_for_each_popup_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_toplevel_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -void output_layer_for_each_popup_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data); - -#if HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct sway_output *output, - struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, - void *user_data); -#endif - -void output_drag_icons_for_each_surface(struct sway_output *output, - struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, - void *user_data); - void output_for_each_workspace(struct sway_output *output, void (*f)(struct sway_workspace *ws, void *data), void *data); @@ -164,14 +129,6 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4]); - -void premultiply_alpha(float color[4], float opacity); - -void scale_box(struct wlr_box *box, float scale); - enum wlr_direction opposite_direction(enum wlr_direction d); void handle_output_layout_change(struct wl_listener *listener, void *data); diff --git a/include/sway/scene_descriptor.h b/include/sway/scene_descriptor.h new file mode 100644 index 000000000..f750d4fdf --- /dev/null +++ b/include/sway/scene_descriptor.h @@ -0,0 +1,26 @@ +#ifndef _SWAY_SCENE_DESCRIPTOR_H +#define _SWAY_SCENE_DESCRIPTOR_H +#include + +enum sway_scene_descriptor_type { + SWAY_SCENE_DESC_BUFFER_TIMER, + SWAY_SCENE_DESC_NON_INTERACTIVE, + SWAY_SCENE_DESC_CONTAINER, + SWAY_SCENE_DESC_LAYER_SHELL, + SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, + SWAY_SCENE_DESC_POPUP, + SWAY_SCENE_DESC_DRAG_ICON, +}; + +struct sway_scene_descriptor { + struct wlr_scene_node *node; + enum sway_scene_descriptor_type type; + void *data; + + struct wl_listener destroy; +}; + +void scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data); + +#endif diff --git a/include/sway/server.h b/include/sway/server.h index 6a5a60c8c..452fd83e4 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -27,6 +27,18 @@ #endif struct sway_transaction; +struct sway_session_lock { + struct wlr_session_lock_v1 *lock; + struct wlr_surface *focused; + bool abandoned; + + struct wl_list outputs; // struct sway_session_lock_output + + // invalid if the session is abandoned + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; +}; struct sway_server { struct wl_display *wl_display; @@ -40,7 +52,6 @@ struct sway_server { struct wlr_allocator *allocator; struct wlr_compositor *compositor; - struct wl_listener compositor_new_surface; struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; @@ -92,15 +103,9 @@ struct sway_server { struct wl_listener output_manager_test; struct { - bool locked; + struct sway_session_lock *lock; struct wlr_session_lock_manager_v1 *manager; - struct wlr_session_lock_v1 *lock; - struct wlr_surface *focused; - struct wl_listener lock_new_surface; - struct wl_listener lock_unlock; - struct wl_listener lock_destroy; - struct wl_listener new_lock; struct wl_listener manager_destroy; } session_lock; @@ -139,13 +144,6 @@ struct sway_debug { bool noatomic; // Ignore atomic layout updates bool txn_timings; // Log verbose messages about transactions bool txn_wait; // Always wait for the timeout before applying - bool noscanout; // Disable direct scan-out - - enum { - DAMAGE_DEFAULT, // Default behaviour - DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged - DAMAGE_RERENDER, // Render the full output when any damage occurs - } damage; }; extern struct sway_debug debug; @@ -157,12 +155,13 @@ void server_run(struct sway_server *server); void restore_nofile_limit(void); -void handle_compositor_new_surface(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); void sway_session_lock_init(void); +void sway_session_lock_add_output(struct sway_session_lock *lock, + struct sway_output *output); void handle_xdg_shell_surface(struct wl_listener *listener, void *data); #if HAVE_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); diff --git a/include/sway/surface.h b/include/sway/surface.h deleted file mode 100644 index fb1cd7758..000000000 --- a/include/sway/surface.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _SWAY_SURFACE_H -#define _SWAY_SURFACE_H -#include - -struct sway_surface { - struct wlr_surface *wlr_surface; - - struct wl_listener destroy; - - /** - * This timer can be used for issuing delayed frame done callbacks (for - * example, to improve presentation latency). Its handler is set to a - * function that issues a frame done callback to this surface. - */ - struct wl_event_source *frame_done_timer; -}; - -#endif diff --git a/include/sway/sway_text_buffer.h b/include/sway/sway_text_buffer.h new file mode 100644 index 000000000..5248defc1 --- /dev/null +++ b/include/sway/sway_text_buffer.h @@ -0,0 +1,25 @@ +#ifndef _SWAY_BUFFER_H +#define _SWAY_BUFFER_H +#include + +struct sway_text_node { + int width; + int max_width; + int height; + int baseline; + bool pango_markup; + float color[4]; + + struct wlr_scene_node *node; +}; + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, const float *color, bool pango_markup); + +void sway_text_node_set_color(struct sway_text_node *node, const float *color); + +void sway_text_node_set_text(struct sway_text_node *node, char *text); + +void sway_text_node_set_max_width(struct sway_text_node *node, int max_width); + +#endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 751612e2c..b750bb35c 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "list.h" #include "sway/tree/node.h" @@ -68,11 +69,39 @@ struct sway_container { struct sway_node node; struct sway_view *view; + struct wlr_scene_tree *scene_tree; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *border; + struct wlr_scene_rect *background; + + struct sway_text_node *title_text; + struct sway_text_node *marks_text; + } title_bar; + + struct { + struct wlr_scene_tree *tree; + + struct wlr_scene_rect *top; + struct wlr_scene_rect *bottom; + struct wlr_scene_rect *left; + struct wlr_scene_rect *right; + } border; + + struct wlr_scene_tree *content_tree; + struct wlr_scene_buffer *output_handler; + + struct wl_listener output_enter; + struct wl_listener output_leave; + struct sway_container_state current; struct sway_container_state pending; char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar + int title_width; enum sway_container_layout prev_split_layout; @@ -100,14 +129,6 @@ struct sway_container { double child_total_width; double child_total_height; - // In most cases this is the same as the content x and y, but if the view - // refuses to resize to the content dimensions then it can be smaller. - // These are in layout coordinates. - double surface_x, surface_y; - - // Outputs currently being intersected - list_t *outputs; // struct sway_output - // Indicates that the container is a scratchpad container. // Both hidden and visible scratchpad containers have scratchpad=true. // Hidden scratchpad containers have a NULL parent. @@ -115,18 +136,7 @@ struct sway_container { float alpha; - struct wlr_texture *title_focused; - struct wlr_texture *title_focused_inactive; - struct wlr_texture *title_focused_tab_title; - struct wlr_texture *title_unfocused; - struct wlr_texture *title_urgent; - list_t *marks; // char * - struct wlr_texture *marks_focused; - struct wlr_texture *marks_focused_inactive; - struct wlr_texture *marks_focused_tab_title; - struct wlr_texture *marks_unfocused; - struct wlr_texture *marks_urgent; struct { struct wl_signal destroy; @@ -146,19 +156,6 @@ void container_begin_destroy(struct sway_container *con); struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data); -/** - * Find a container at the given coordinates. Returns the surface and - * surface-local coordinates of the given layout coordinates if the container - * is a view and the view contains a surface at those coordinates. - */ -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy); - -struct sway_container *tiling_container_at( - struct sway_node *parent, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy); - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data); @@ -175,13 +172,13 @@ bool container_has_ancestor(struct sway_container *container, void container_update_textures_recursive(struct sway_container *con); -void container_damage_whole(struct sway_container *container); - void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); -void container_update_title_textures(struct sway_container *container); +void container_update_title_bar(struct sway_container *container); + +void container_update_marks(struct sway_container *container); size_t container_build_representation(enum sway_container_layout layout, list_t *children, char *buffer); @@ -214,11 +211,6 @@ void container_set_geometry_from_content(struct sway_container *con); */ bool container_is_floating(struct sway_container *container); -/** - * Same as above, but for current container state. - */ -bool container_is_current_floating(struct sway_container *container); - /** * Get a container's box in layout coordinates. */ @@ -281,26 +273,12 @@ bool container_is_floating_or_child(struct sway_container *container); */ bool container_is_fullscreen_or_child(struct sway_container *container); -/** - * Return the output which will be used for scale purposes. - * This is the most recently entered output. - * If the container is not on any output, return NULL. - */ -struct sway_output *container_get_effective_output(struct sway_container *con); - -void container_discover_outputs(struct sway_container *con); - enum sway_container_layout container_parent_layout(struct sway_container *con); -enum sway_container_layout container_current_parent_layout( - struct sway_container *con); - list_t *container_get_siblings(struct sway_container *container); int container_sibling_index(struct sway_container *child); -list_t *container_get_current_siblings(struct sway_container *container); - void container_handle_fullscreen_reparent(struct sway_container *con); void container_add_child(struct sway_container *parent, @@ -348,8 +326,6 @@ bool container_has_mark(struct sway_container *container, char *mark); void container_add_mark(struct sway_container *container, char *mark); -void container_update_marks_textures(struct sway_container *container); - void container_raise_floating(struct sway_container *con); bool container_is_scratchpad_hidden(struct sway_container *con); @@ -373,4 +349,10 @@ bool container_is_sticky_or_child(struct sway_container *con); */ int container_squash(struct sway_container *con); +void container_arrange_title_bar(struct sway_container *con); + +void container_update(struct sway_container *con); + +void container_update_itself_and_parents(struct sway_container *con); + #endif diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 470ee3b5e..ce3a96b6a 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -1,6 +1,7 @@ #ifndef _SWAY_NODE_H #define _SWAY_NODE_H #include +#include #include "list.h" #define MIN_SANE_W 100 @@ -74,4 +75,15 @@ list_t *node_get_children(struct sway_node *node); bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); +// when destroying a sway tree, it's not known which order the tree will be +// destroyed. To prevent freeing of scene_nodes recursing up the tree, +// let's use this helper function to disown them to the staging node. +void scene_node_disown_children(struct wlr_scene_tree *tree); + +// a helper function used to allocate tree nodes. If an allocation failure +// occurs a flag is flipped that can be checked later to destroy a parent +// of this scene node preventing memory leaks. +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed); + #endif diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index af4124a18..50ce6010d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include "sway/tree/container.h" #include "sway/tree/node.h" #include "config.h" @@ -16,10 +16,40 @@ struct sway_root { struct wlr_output_layout *output_layout; struct wl_listener output_layout_change; + + // scene node layout: + // - root + // - staging + // - layer shell stuff + // - tiling + // - floating + // - fullscreen stuff + // - seat stuff + // - ext_session_lock + struct wlr_scene *root_scene; + + // since wlr_scene nodes can't be orphaned and must always + // have a parent, use this staging scene_tree so that a + // node always have a valid parent. Nothing in this + // staging node will be visible. + struct wlr_scene_tree *staging; + + struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *floating; + struct wlr_scene_tree *shell_top; + struct wlr_scene_tree *fullscreen; + struct wlr_scene_tree *fullscreen_global; #if HAVE_XWAYLAND - struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link + struct wlr_scene_tree *unmanaged; #endif - struct wl_list drag_icons; // sway_drag_icon::link + struct wlr_scene_tree *shell_overlay; + struct wlr_scene_tree *popup; + struct wlr_scene_tree *seat; + struct wlr_scene_tree *session_lock; + } layers; // Includes disabled outputs struct wl_list all_outputs; // sway_output::link diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 0dcbf1aa6..e0a81b18f 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -2,6 +2,7 @@ #define _SWAY_VIEW_H #include #include +#include #include "config.h" #if HAVE_XWAYLAND #include @@ -39,16 +40,12 @@ struct sway_view_impl { enum sway_view_prop prop); uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); uint32_t (*configure)(struct sway_view *view, double lx, double ly, - int width, int height); + double width, double height); void (*set_activated)(struct sway_view *view, bool activated); void (*set_tiled)(struct sway_view *view, bool tiled); void (*set_fullscreen)(struct sway_view *view, bool fullscreen); void (*set_resizing)(struct sway_view *view, bool resizing); bool (*wants_floating)(struct sway_view *view); - void (*for_each_surface)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - void (*for_each_popup_surface)(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); bool (*is_transient_for)(struct sway_view *child, struct sway_view *ancestor); void (*close)(struct sway_view *view); @@ -56,19 +53,14 @@ struct sway_view_impl { void (*destroy)(struct sway_view *view); }; -struct sway_saved_buffer { - struct wlr_client_buffer *buffer; - int x, y; - int width, height; - enum wl_output_transform transform; - struct wlr_fbox source_box; - struct wl_list link; // sway_view::saved_buffers -}; - struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *content_tree; + struct wlr_scene_tree *saved_surface_tree; + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; @@ -87,15 +79,9 @@ struct sway_view { bool allow_request_urgent; struct wl_event_source *urgent_timer; - struct wl_list saved_buffers; // sway_saved_buffer::link - // The geometry for whatever the client is committing, regardless of // transaction state. Updated on every commit. - struct wlr_box geometry; - - // The "old" geometry during a transaction. Used to damage the old location - // when a transaction is applied. - struct wlr_box saved_geometry; + struct wlr_fbox geometry; struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; @@ -118,8 +104,6 @@ struct sway_view { struct wl_signal unmap; } events; - struct wl_listener surface_new_subsurface; - int max_render_time; // In milliseconds enum seat_config_shortcuts_inhibit shortcuts_inhibit; @@ -144,6 +128,8 @@ struct sway_xdg_shell_view { struct sway_xwayland_view { struct sway_view view; + struct wlr_scene_surface *surface_scene; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -166,14 +152,12 @@ struct sway_xwayland_view { struct sway_xwayland_unmanaged { struct wlr_xwayland_surface *wlr_xwayland_surface; - struct wl_list link; - int lx, ly; + struct wlr_scene_surface *surface_scene; struct wl_listener request_activate; struct wl_listener request_configure; struct wl_listener request_fullscreen; - struct wl_listener commit; struct wl_listener set_geometry; struct wl_listener map; struct wl_listener unmap; @@ -184,7 +168,6 @@ struct sway_xwayland_unmanaged { struct sway_view_child; struct sway_view_child_impl { - void (*get_view_coords)(struct sway_view_child *child, int *sx, int *sy); void (*destroy)(struct sway_view_child *child); }; @@ -199,20 +182,9 @@ struct sway_view_child { struct sway_view_child *parent; struct wl_list children; // sway_view_child::link struct wlr_surface *surface; - bool mapped; - struct wl_listener surface_commit; - struct wl_listener surface_new_subsurface; - struct wl_listener surface_map; - struct wl_listener surface_unmap; - struct wl_listener surface_destroy; - struct wl_listener view_unmap; -}; - -struct sway_subsurface { - struct sway_view_child child; - - struct wl_listener destroy; + struct wlr_scene_tree *scene_tree; + struct wlr_scene_tree *xdg_surface_tree; }; struct sway_xdg_popup { @@ -245,8 +217,8 @@ const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height); -uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, - int height); +uint32_t view_configure(struct sway_view *view, double lx, double ly, + double width, double height); bool view_inhibit_idle(struct sway_view *view); @@ -288,23 +260,9 @@ void view_close(struct sway_view *view); void view_close_popups(struct sway_view *view); -void view_damage_from(struct sway_view *view); - -/** - * Iterate all surfaces of a view (toplevels + popups). - */ -void view_for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - -/** - * Iterate all popup surfaces of a view. - */ -void view_for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data); - // view implementation -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl); void view_destroy(struct sway_view *view); @@ -372,4 +330,6 @@ void view_save_buffer(struct sway_view *view); bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); +void view_send_frame_done(struct sway_view *view); + #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index b3d93a813..e64f219f0 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -2,6 +2,7 @@ #define _SWAY_WORKSPACE_H #include +#include #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -22,6 +23,12 @@ struct sway_workspace_state { struct sway_workspace { struct sway_node node; + + struct { + struct wlr_scene_tree *tiling; + struct wlr_scene_tree *fullscreen; + } layers; + struct sway_container *fullscreen; char *name; diff --git a/sway/commands/client.c b/sway/commands/client.c index 772631456..5ff949661 100644 --- a/sway/commands/client.c +++ b/sway/commands/client.c @@ -5,9 +5,8 @@ #include "sway/tree/container.h" #include "util.h" -static void rebuild_textures_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); - container_update_title_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_title_bar(con); } static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, @@ -51,12 +50,7 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, memcpy(class, &colors, sizeof(struct border_colors)); if (config->active) { - root_for_each_container(rebuild_textures_iterator, NULL); - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + root_for_each_container(title_bar_update_iterator, NULL); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/mark.c b/sway/commands/mark.c index aa5f185c8..30cf458c7 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -59,7 +59,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } free(mark); - container_update_marks_textures(container); + container_update_marks(container); if (container->view) { view_execute_criteria(container->view); } diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 96e6228ed..1c44a6468 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -37,6 +37,5 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { } con->alpha = val; - container_damage_whole(con); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 76f14bba3..82967ca7f 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -9,9 +9,8 @@ #include "list.h" #include "log.h" -static void rebuild_textures_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); - container_update_title_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_title_bar(con); } static void do_reload(void *data) { @@ -48,7 +47,7 @@ static void do_reload(void *data) { } list_free_items_and_destroy(bar_ids); - root_for_each_container(rebuild_textures_iterator, NULL); + root_for_each_container(title_bar_update_iterator, NULL); arrange_root(); } diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 0d373b80c..fecb5ade4 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -10,8 +10,8 @@ #include "stringop.h" #include "util.h" -static void rebuild_marks_iterator(struct sway_container *con, void *data) { - container_update_marks_textures(con); +static void title_bar_update_iterator(struct sway_container *con, void *data) { + container_update_marks(con); } struct cmd_results *cmd_show_marks(int argc, char **argv) { @@ -23,12 +23,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { config->show_marks = parse_boolean(argv[0], config->show_marks); if (config->show_marks) { - root_for_each_container(rebuild_marks_iterator, NULL); - } - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); + root_for_each_container(title_bar_update_iterator, NULL); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/title_align.c b/sway/commands/title_align.c index c30355deb..be298a290 100644 --- a/sway/commands/title_align.c +++ b/sway/commands/title_align.c @@ -4,6 +4,10 @@ #include "sway/tree/container.h" #include "sway/tree/root.h" +static void arrange_title_bar_iterator(struct sway_container *con, void *data) { + container_arrange_title_bar(con); +} + struct cmd_results *cmd_title_align(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "title_align", EXPECTED_AT_LEAST, 1))) { @@ -21,10 +25,7 @@ struct cmd_results *cmd_title_align(int argc, char **argv) { "Expected 'title_align left|center|right'"); } - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + root_for_each_container(arrange_title_bar_iterator, NULL); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 7c27c163a..fa3db3c50 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c @@ -27,7 +27,6 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { "Expected output to have a workspace"); } arrange_workspace(ws); - output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/titlebar_padding.c b/sway/commands/titlebar_padding.c index 29ce59ff0..6999f7a27 100644 --- a/sway/commands/titlebar_padding.c +++ b/sway/commands/titlebar_padding.c @@ -33,7 +33,6 @@ struct cmd_results *cmd_titlebar_padding(int argc, char **argv) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; arrange_workspace(output_get_active_workspace(output)); - output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 19274dfbf..c3a6ac4b9 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c @@ -8,9 +8,13 @@ #include "log.h" #include "stringop.h" -static void remove_all_marks_iterator(struct sway_container *con, void *data) { +static void remove_mark(struct sway_container *con) { container_clear_marks(con); - container_update_marks_textures(con); + container_update_marks(con); +} + +static void remove_all_marks_iterator(struct sway_container *con, void *data) { + remove_mark(con); } // unmark Remove all marks from all views @@ -38,8 +42,7 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { } } else if (con && !mark) { // Clear all marks from the given container - container_clear_marks(con); - container_update_marks_textures(con); + remove_mark(con); } else if (!con && mark) { // Remove mark from whichever container has it container_find_and_unmark(mark); diff --git a/sway/config/output.c b/sway/config/output.c index 9c7082d07..e59f092f3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -524,10 +524,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { return true; } - if (config->reloading) { - output_damage_whole(output); - } - if (oc) { enum scale_filter_mode scale_filter_old = output->scale_filter; switch (oc->scale_filter) { diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c deleted file mode 100644 index c8d4502cd..000000000 --- a/sway/desktop/desktop.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "sway/tree/container.h" -#include "sway/desktop.h" -#include "sway/output.h" - -void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, - bool whole) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, - output->wlr_output, &output_box); - output_damage_surface(output, lx - output_box.x, - ly - output_box.y, surface, whole); - } -} - -void desktop_damage_whole_container(struct sway_container *con) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole_container(output, con); - } -} - -void desktop_damage_box(struct wlr_box *box) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_box(output, box); - } -} - -void desktop_damage_view(struct sway_view *view) { - desktop_damage_whole_container(view->container); - struct wlr_box box = { - .x = view->container->current.content_x - view->geometry.x, - .y = view->container->current.content_y - view->geometry.y, - .width = view->surface->current.width, - .height = view->surface->current.height, - }; - desktop_damage_box(&box); -} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d44d6338e..8d18f981e 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -7,6 +7,7 @@ #include #include #include "log.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -16,245 +17,109 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" +#include -static void apply_exclusive(struct wlr_box *usable_area, - uint32_t anchor, int32_t exclusive, - int32_t margin_top, int32_t margin_right, - int32_t margin_bottom, int32_t margin_left) { - if (exclusive <= 0) { - return; - } - struct { - uint32_t singular_anchor; - uint32_t anchor_triplet; - int *positive_axis; - int *negative_axis; - int margin; - } edges[] = { - // Top - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - .positive_axis = &usable_area->y, - .negative_axis = &usable_area->height, - .margin = margin_top, - }, - // Bottom - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->height, - .margin = margin_bottom, - }, - // Left - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = &usable_area->x, - .negative_axis = &usable_area->width, - .margin = margin_left, - }, - // Right - { - .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT, - .anchor_triplet = - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, - .positive_axis = NULL, - .negative_axis = &usable_area->width, - .margin = margin_right, - }, - }; - for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { - if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet) - && exclusive + edges[i].margin > 0) { - if (edges[i].positive_axis) { - *edges[i].positive_axis += exclusive + edges[i].margin; - } - if (edges[i].negative_axis) { - *edges[i].negative_axis -= exclusive + edges[i].margin; - } - break; - } - } -} - -static void arrange_layer(struct sway_output *output, struct wl_list *list, - struct wlr_box *usable_area, bool exclusive) { - struct sway_layer_surface *sway_layer; - struct wlr_box full_area = { 0 }; - wlr_output_effective_resolution(output->wlr_output, - &full_area.width, &full_area.height); - wl_list_for_each(sway_layer, list, link) { - struct wlr_layer_surface_v1 *layer = sway_layer->layer_surface; - struct wlr_layer_surface_v1_state *state = &layer->current; - if (exclusive != (state->exclusive_zone > 0)) { +static void arrange_surface(struct sway_output *output, const struct wlr_fbox *full_area, + struct wlr_fbox *usable_area, struct wlr_scene_tree *tree) { + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + struct sway_scene_descriptor *desc = node->data; + + // the descriptor could be null during the node's destruction + if (!desc) { continue; } - 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 axis - const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT - | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - if (box.width == 0) { - box.x = bounds.x; - } else if ((state->anchor & both_horiz) == both_horiz) { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x = bounds.x; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x = bounds.x + (bounds.width - box.width); - } else { - box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); - } - // Vertical axis - const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP - | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (box.height == 0) { - box.y = bounds.y; - } else if ((state->anchor & both_vert) == both_vert) { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y = bounds.y; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y = bounds.y + (bounds.height - box.height); - } else { - box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); - } - // Margin - if (box.width == 0) { - box.x += state->margin.left; - box.width = bounds.width - - (state->margin.left + state->margin.right); - } else if ((state->anchor & both_horiz) == both_horiz) { - // don't apply margins - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { - box.x += state->margin.left; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - box.x -= state->margin.right; - } - if (box.height == 0) { - box.y += state->margin.top; - box.height = bounds.height - - (state->margin.top + state->margin.bottom); - } else if ((state->anchor & both_vert) == both_vert) { - // don't apply margins - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { - box.y += state->margin.top; - } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - box.y -= state->margin.bottom; - } - if (!sway_assert(box.width >= 0 && box.height >= 0, - "Expected layer surface to have positive size")) { - continue; - } - // Apply - sway_layer->geo = box; - apply_exclusive(usable_area, state->anchor, state->exclusive_zone, - state->margin.top, state->margin.right, - state->margin.bottom, state->margin.left); - wlr_layer_surface_v1_configure(layer, box.width, box.height); + + sway_assert(desc->type == SWAY_SCENE_DESC_LAYER_SHELL, + "Corrupted scene tree: expected a layer shell node"); + + struct sway_layer_surface *surface = desc->data; + wlr_scene_layer_surface_v1_configure(surface->scene, + full_area, usable_area); } } void arrange_layers(struct sway_output *output) { - struct wlr_box usable_area = { 0 }; + int output_width, output_height; wlr_output_effective_resolution(output->wlr_output, - &usable_area.width, &usable_area.height); + &output_width, &output_height); + struct wlr_fbox usable_area = { + .x = 0, + .y = 0, + .width = output_width, + .height = output_height, + }; + const struct wlr_fbox full_area = usable_area; - // Arrange exclusive surfaces from top->bottom - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, true); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); if (memcmp(&usable_area, &output->usable_area, - sizeof(struct wlr_box)) != 0) { + sizeof(struct wlr_fbox)) != 0) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); - memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); + memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_fbox)); arrange_output(output); } - - // Arrange non-exclusive surfaces from top->bottom - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - &usable_area, false); - arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - &usable_area, false); - - // Find topmost keyboard interactive layer, if such a layer exists - uint32_t layers_above_shell[] = { - ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, - }; - size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); - struct sway_layer_surface *layer, *topmost = NULL; - for (size_t i = 0; i < nlayers; ++i) { - wl_list_for_each_reverse(layer, - &output->layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive && - layer->layer_surface->mapped) { - topmost = layer; - break; - } - } - if (topmost != NULL) { - break; - } - } - - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (topmost != NULL) { - seat_set_focus_layer(seat, topmost->layer_surface); - } else if (seat->focused_layer && - !seat->focused_layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, NULL); - } - } +} + +static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, + enum zwlr_layer_shell_v1_layer type) { + switch (type) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + return output->layers.shell_background; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + return output->layers.shell_bottom; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + return output->layers.shell_top; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + return output->layers.shell_overlay; + } + + sway_assert(false, "unreachable"); + return NULL; +} + +static struct sway_layer_surface *sway_layer_surface_create( + struct wlr_scene_layer_surface_v1 *scene) { + struct sway_layer_surface *surface = calloc(1, sizeof(struct sway_layer_surface)); + if (!surface) { + sway_log(SWAY_ERROR, "Could not allocate a scene_layer surface"); + return NULL; + } + + struct wlr_scene_tree *popups = wlr_scene_tree_create(root->layers.popup); + if (!popups) { + sway_log(SWAY_ERROR, "Could not allocate a scene_layer popup node"); + free(surface); + return NULL; + } + + surface->scene = scene; + surface->layer_surface = scene->layer_surface; + surface->popups = popups; + + return surface; } static struct sway_layer_surface *find_mapped_layer_by_client( - struct wl_client *client, struct wlr_output *ignore_output) { + struct wl_client *client, struct sway_output *ignore_output) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - if (output->wlr_output == ignore_output) { + if (output == ignore_output) { continue; } // For now we'll only check the overlay layer - struct sway_layer_surface *lsurface; - wl_list_for_each(lsurface, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { - struct wl_resource *resource = lsurface->layer_surface->resource; + struct wlr_scene_node *node; + wl_list_for_each (node, &output->layers.shell_overlay->children, link) { + struct sway_scene_descriptor *desc = node->data; + struct sway_layer_surface *surface = desc->data; + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + struct wl_resource *resource = layer_surface->resource; if (wl_resource_get_client(resource) == client - && lsurface->layer_surface->mapped) { - return lsurface; + && layer_surface->mapped) { + return surface; } } } @@ -262,293 +127,131 @@ static struct sway_layer_surface *find_mapped_layer_by_client( } static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = - wl_container_of(listener, sway_layer, output_destroy); + struct sway_layer_surface *layer = + wl_container_of(listener, layer, output_destroy); + + layer->output = NULL; + wlr_scene_node_destroy(&layer->scene->tree->node); +} + +static void handle_node_destroy(struct wl_listener *listener, void *data) { + struct sway_layer_surface *layer = + wl_container_of(listener, layer, node_destroy); + // Determine if this layer is being used by an exclusive client. If it is, // try and find another layer owned by this client to pass focus to. struct sway_seat *seat = input_manager_get_default_seat(); struct wl_client *client = - wl_resource_get_client(sway_layer->layer_surface->resource); + wl_resource_get_client(layer->layer_surface->resource); bool set_focus = seat->exclusive_client == client; - if (set_focus) { - struct sway_layer_surface *layer = - find_mapped_layer_by_client(client, sway_layer->layer_surface->output); - if (layer) { - seat_set_focus_layer(seat, layer->layer_surface); + struct sway_layer_surface *consider_layer = + find_mapped_layer_by_client(client, layer->output); + if (consider_layer) { + seat_set_focus_layer(seat, consider_layer->layer_surface); } } - wlr_layer_surface_v1_destroy(sway_layer->layer_surface); + if (layer->output) { + arrange_layers(layer->output); + transaction_commit_dirty(); + } + + wlr_scene_node_destroy(&layer->popups->node); + + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->surface_commit.link); + wl_list_remove(&layer->node_destroy.link); + wl_list_remove(&layer->output_destroy.link); + + free(layer); } static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_layer_surface *layer = - wl_container_of(listener, layer, surface_commit); - struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; - struct wlr_output *wlr_output = layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - struct wlr_box old_extent = layer->extent; + struct sway_layer_surface *surface = + wl_container_of(listener, surface, surface_commit); - bool layer_changed = false; - if (layer_surface->current.committed != 0 - || layer->mapped != layer_surface->mapped) { - layer->mapped = layer_surface->mapped; - layer_changed = layer->layer != layer_surface->current.layer; - if (layer_changed) { - wl_list_remove(&layer->link); - wl_list_insert(&output->layers[layer_surface->current.layer], - &layer->link); - layer->layer = layer_surface->current.layer; - } - arrange_layers(output); + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + uint32_t committed = layer_surface->current.committed; + + if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; + struct wlr_scene_tree *output_layer = sway_layer_get_scene( + surface->output, layer_type); + wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); } - wlr_surface_get_extends(layer_surface->surface, &layer->extent); - layer->extent.x += layer->geo.x; - layer->extent.y += layer->geo.y; - - bool extent_changed = - memcmp(&old_extent, &layer->extent, sizeof(struct wlr_box)) != 0; - if (extent_changed || layer_changed) { - output_damage_box(output, &old_extent); - output_damage_surface(output, layer->geo.x, layer->geo.y, - layer_surface->surface, true); - } else { - output_damage_surface(output, layer->geo.x, layer->geo.y, - layer_surface->surface, false); + if (committed || layer_surface->mapped != surface->mapped) { + surface->mapped = layer_surface->mapped; + arrange_layers(surface->output); + transaction_commit_dirty(); } - transaction_commit_dirty(); + double lx, ly; + wlr_scene_node_coords(&surface->scene->tree->node, &lx, &ly); + wlr_scene_node_set_position(&surface->popups->node, lx, ly); } -static void unmap(struct sway_layer_surface *sway_layer) { +static void handle_map(struct wl_listener *listener, void *data) { + struct sway_layer_surface *surface = wl_container_of(listener, + surface, map); + + struct wlr_layer_surface_v1 *layer_surface = + surface->scene->layer_surface; + + // focus on new surface + if (layer_surface->current.keyboard_interactive && + (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || + layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + // but only if the currently focused layer has a lower precedence + if (!seat->focused_layer || + seat->focused_layer->current.layer >= layer_surface->current.layer) { + seat_set_focus_layer(seat, layer_surface); + } + } + arrange_layers(surface->output); + } + + cursor_rebase_all(); +} + +static void handle_unmap(struct wl_listener *listener, void *data) { + struct sway_layer_surface *surface = wl_container_of( + listener, surface, unmap); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - if (seat->focused_layer == sway_layer->layer_surface) { + if (seat->focused_layer == surface->layer_surface) { seat_set_focus_layer(seat, NULL); } } cursor_rebase_all(); - - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, - sway_layer->layer_surface->surface, true); -} - -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface); - -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = - wl_container_of(listener, sway_layer, destroy); - sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", - sway_layer->layer_surface->namespace); - if (sway_layer->layer_surface->mapped) { - unmap(sway_layer); - } - - struct sway_layer_subsurface *subsurface, *subsurface_tmp; - wl_list_for_each_safe(subsurface, subsurface_tmp, &sway_layer->subsurfaces, link) { - layer_subsurface_destroy(subsurface); - } - - wl_list_remove(&sway_layer->link); - wl_list_remove(&sway_layer->destroy.link); - wl_list_remove(&sway_layer->map.link); - wl_list_remove(&sway_layer->unmap.link); - wl_list_remove(&sway_layer->surface_commit.link); - wl_list_remove(&sway_layer->new_popup.link); - wl_list_remove(&sway_layer->new_subsurface.link); - - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - arrange_layers(output); - transaction_commit_dirty(); - wl_list_remove(&sway_layer->output_destroy.link); - sway_layer->layer_surface->output = NULL; - - free(sway_layer); -} - -static void handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = wl_container_of(listener, - sway_layer, map); - struct wlr_output *wlr_output = sway_layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, - sway_layer->layer_surface->surface, true); - wlr_surface_send_enter(sway_layer->layer_surface->surface, - sway_layer->layer_surface->output); - cursor_rebase_all(); -} - -static void handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer = wl_container_of( - listener, sway_layer, unmap); - unmap(sway_layer); -} - -static void subsurface_damage(struct sway_layer_subsurface *subsurface, - bool whole) { - struct sway_layer_surface *layer = subsurface->layer_surface; - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; - int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; - output_damage_surface( - output, ox, oy, subsurface->wlr_subsurface->surface, whole); -} - -static void subsurface_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, unmap); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, map); - subsurface_damage(subsurface, true); -} - -static void subsurface_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, commit); - subsurface_damage(subsurface, false); -} - -static void layer_subsurface_destroy(struct sway_layer_subsurface *subsurface) { - wl_list_remove(&subsurface->link); - wl_list_remove(&subsurface->map.link); - wl_list_remove(&subsurface->unmap.link); - wl_list_remove(&subsurface->destroy.link); - wl_list_remove(&subsurface->commit.link); - free(subsurface); -} - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_layer_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - layer_subsurface_destroy(subsurface); -} - -static struct sway_layer_subsurface *create_subsurface( - struct wlr_subsurface *wlr_subsurface, - struct sway_layer_surface *layer_surface) { - struct sway_layer_subsurface *subsurface = - calloc(1, sizeof(struct sway_layer_subsurface)); - if (subsurface == NULL) { - return NULL; - } - - subsurface->wlr_subsurface = wlr_subsurface; - subsurface->layer_surface = layer_surface; - wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); - - subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); - subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); - subsurface->destroy.notify = subsurface_handle_destroy; - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->commit.notify = subsurface_handle_commit; - wl_signal_add(&wlr_subsurface->surface->events.commit, &subsurface->commit); - - return subsurface; -} - -static void handle_new_subsurface(struct wl_listener *listener, void *data) { - struct sway_layer_surface *sway_layer_surface = - wl_container_of(listener, sway_layer_surface, new_subsurface); - struct wlr_subsurface *wlr_subsurface = data; - create_subsurface(wlr_subsurface, sway_layer_surface); -} - - -static struct sway_layer_surface *popup_get_layer( - struct sway_layer_popup *popup) { - while (popup->parent_type == LAYER_PARENT_POPUP) { - popup = popup->parent_popup; - } - return popup->parent_layer; -} - -static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { - struct wlr_xdg_popup *popup = layer_popup->wlr_popup; - struct wlr_surface *surface = popup->base->surface; - int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; - int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; - int ox = popup_sx, oy = popup_sy; - struct sway_layer_surface *layer; - while (true) { - if (layer_popup->parent_type == LAYER_PARENT_POPUP) { - layer_popup = layer_popup->parent_popup; - ox += layer_popup->wlr_popup->current.geometry.x; - oy += layer_popup->wlr_popup->current.geometry.y; - } else { - layer = layer_popup->parent_layer; - ox += layer->geo.x; - oy += layer->geo.y; - break; - } - } - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; - output_damage_surface(output, ox, oy, surface, whole); -} - -static void popup_handle_map(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, map); - struct sway_layer_surface *layer = popup_get_layer(popup); - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); - popup_damage(popup, true); -} - -static void popup_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, unmap); - popup_damage(popup, true); -} - -static void popup_handle_commit(struct wl_listener *listener, void *data) { - struct sway_layer_popup *popup = wl_container_of(listener, popup, commit); - popup_damage(popup, false); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { struct sway_layer_popup *popup = wl_container_of(listener, popup, destroy); - wl_list_remove(&popup->map.link); - wl_list_remove(&popup->unmap.link); wl_list_remove(&popup->destroy.link); - wl_list_remove(&popup->commit.link); + wl_list_remove(&popup->new_popup.link); free(popup); } static void popup_unconstrain(struct sway_layer_popup *popup) { - struct sway_layer_surface *layer = popup_get_layer(popup); struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + struct sway_output *output = popup->toplevel->output; - struct wlr_output *wlr_output = layer->layer_surface->output; - sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); - struct sway_output *output = wlr_output->data; + double lx, ly; + wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); // the output box expressed in the coordinate system of the toplevel parent // of the popup - struct wlr_box output_toplevel_sx_box = { - .x = -layer->geo.x, - .y = -layer->geo.y, + struct wlr_fbox output_toplevel_sx_box = { + .x = output->lx - lx, + .y = output->ly - ly, .width = output->width, .height = output->height, }; @@ -559,25 +262,25 @@ static void popup_unconstrain(struct sway_layer_popup *popup) { static void popup_handle_new_popup(struct wl_listener *listener, void *data); static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, - enum layer_parent parent_type, void *parent) { + struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) { struct sway_layer_popup *popup = calloc(1, sizeof(struct sway_layer_popup)); if (popup == NULL) { return NULL; } + popup->toplevel = toplevel; popup->wlr_popup = wlr_popup; - popup->parent_type = parent_type; - popup->parent_layer = parent; + popup->scene = wlr_scene_xdg_surface_create(parent, + wlr_popup->base); + + if (!popup->scene) { + free(popup); + return NULL; + } - popup->map.notify = popup_handle_map; - wl_signal_add(&wlr_popup->base->events.map, &popup->map); - popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); popup->destroy.notify = popup_handle_destroy; wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); - popup->commit.notify = popup_handle_commit; - wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); @@ -590,25 +293,20 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_layer_popup *sway_layer_popup = wl_container_of(listener, sway_layer_popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_POPUP, sway_layer_popup); + create_popup(wlr_popup, sway_layer_popup->toplevel, sway_layer_popup->scene); } static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer_surface = wl_container_of(listener, sway_layer_surface, new_popup); struct wlr_xdg_popup *wlr_popup = data; - create_popup(wlr_popup, LAYER_PARENT_LAYER, sway_layer_surface); -} - -struct sway_layer_surface *layer_from_wlr_layer_surface_v1( - struct wlr_layer_surface_v1 *layer_surface) { - return layer_surface->data; + create_popup(wlr_popup, sway_layer_surface, sway_layer_surface->popups); } void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct wlr_layer_surface_v1 *layer_surface = data; sway_log(SWAY_DEBUG, "new layer surface: namespace %s layer %d anchor %" PRIu32 - " size %" PRIu32 "x%" PRIu32 " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", + " size %lfx%lf margin %lf,%lf,%lf,%lf", layer_surface->namespace, layer_surface->pending.layer, layer_surface->pending.anchor, @@ -634,10 +332,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", layer_surface->namespace); - // Note that layer_surface->output can be NULL - // here, but none of our destroy callbacks are - // registered yet so we don't have to make them - // handle that case. wlr_layer_surface_v1_destroy(layer_surface); return; } @@ -646,39 +340,53 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->output = output->wlr_output; } - struct sway_layer_surface *sway_layer = - calloc(1, sizeof(struct sway_layer_surface)); - if (!sway_layer) { + struct sway_output *output = layer_surface->output->data; + + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; + struct wlr_scene_tree *output_layer = sway_layer_get_scene( + output, layer_type); + struct wlr_scene_layer_surface_v1 *scene_surface = + wlr_scene_layer_surface_v1_create(output_layer, layer_surface); + if (!scene_surface) { + sway_log(SWAY_ERROR, "Could not allocate a layer_surface_v1"); return; } - wl_list_init(&sway_layer->subsurfaces); + struct sway_layer_surface *surface = + sway_layer_surface_create(scene_surface); + if (!surface) { + wlr_layer_surface_v1_destroy(layer_surface); - sway_layer->surface_commit.notify = handle_surface_commit; + sway_log(SWAY_ERROR, "Could not allocate a sway_layer_surface"); + return; + } + + scene_descriptor_assign(&scene_surface->tree->node, + SWAY_SCENE_DESC_LAYER_SHELL, surface); + if (!scene_surface->tree->node.data) { + // destroying the layer_surface will also destroy its corresponding + // scene node + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + + surface->output = output; + + surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, - &sway_layer->surface_commit); + &surface->surface_commit); + surface->map.notify = handle_map; + wl_signal_add(&layer_surface->events.map, &surface->map); + surface->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->events.unmap, &surface->unmap); + surface->new_popup.notify = handle_new_popup; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); - sway_layer->destroy.notify = handle_destroy; - wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); - sway_layer->map.notify = handle_map; - wl_signal_add(&layer_surface->events.map, &sway_layer->map); - sway_layer->unmap.notify = handle_unmap; - wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); - sway_layer->new_popup.notify = handle_new_popup; - wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); - sway_layer->new_subsurface.notify = handle_new_subsurface; - wl_signal_add(&layer_surface->surface->events.new_subsurface, - &sway_layer->new_subsurface); + surface->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.disable, &surface->output_destroy); - sway_layer->layer_surface = layer_surface; - layer_surface->data = sway_layer; - - struct sway_output *output = layer_surface->output->data; - sway_layer->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->events.disable, &sway_layer->output_destroy); - - wl_list_insert(&output->layers[layer_surface->pending.layer], - &sway_layer->link); + surface->node_destroy.notify = handle_node_destroy; + wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); // Temporarily set the layer's current state to pending // So that we can easily arrange it diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3f3f9494a..db768ac44 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -24,8 +23,8 @@ #include "sway/input/seat.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/server.h" -#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -58,318 +57,6 @@ struct sway_output *all_output_by_name_or_id(const char *name_or_id) { return NULL; } -struct surface_iterator_data { - sway_surface_iterator_func_t user_iterator; - void *user_data; - - struct sway_output *output; - struct sway_view *view; - double ox, oy; - int width, height; -}; - -static bool get_surface_box(struct surface_iterator_data *data, - struct wlr_surface *surface, int sx, int sy, - struct wlr_box *surface_box) { - struct sway_output *output = data->output; - - if (!wlr_surface_has_buffer(surface)) { - return false; - } - - int sw = surface->current.width; - int sh = surface->current.height; - - struct wlr_box box = { - .x = floor(data->ox + sx), - .y = floor(data->oy + sy), - .width = sw, - .height = sh, - }; - if (surface_box != NULL) { - memcpy(surface_box, &box, sizeof(struct wlr_box)); - } - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - return wlr_box_intersection(&intersection, &output_box, &box); -} - -static void output_for_each_surface_iterator(struct wlr_surface *surface, - int sx, int sy, void *_data) { - struct surface_iterator_data *data = _data; - - struct wlr_box box; - bool intersects = get_surface_box(data, surface, sx, sy, &box); - if (!intersects) { - return; - } - - data->user_iterator(data->output, data->view, surface, &box, - data->user_data); -} - -void output_surface_for_each_surface(struct sway_output *output, - struct wlr_surface *surface, double ox, double oy, - sway_surface_iterator_func_t iterator, void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = ox, - .oy = oy, - .width = surface->current.width, - .height = surface->current.height, - }; - - wlr_surface_for_each_surface(surface, - output_for_each_surface_iterator, &data); -} - -void output_view_for_each_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = view, - .ox = view->container->surface_x - output->lx - - view->geometry.x, - .oy = view->container->surface_y - output->ly - - view->geometry.y, - .width = view->container->current.content_width, - .height = view->container->current.content_height, - }; - - view_for_each_surface(view, output_for_each_surface_iterator, &data); -} - -void output_view_for_each_popup_surface(struct sway_output *output, - struct sway_view *view, sway_surface_iterator_func_t iterator, - void *user_data) { - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = view, - .ox = view->container->surface_x - output->lx - - view->geometry.x, - .oy = view->container->surface_y - output->ly - - view->geometry.y, - .width = view->container->current.content_width, - .height = view->container->current.content_height, - }; - - view_for_each_popup_surface(view, output_for_each_surface_iterator, &data); -} - -void output_layer_for_each_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -void output_layer_for_each_toplevel_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - output_surface_for_each_surface(output, wlr_layer_surface_v1->surface, - layer_surface->geo.x, layer_surface->geo.y, iterator, - user_data); - } -} - - -void output_layer_for_each_popup_surface(struct sway_output *output, - struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_layer_surface *layer_surface; - wl_list_for_each(layer_surface, layer_surfaces, link) { - struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = - layer_surface->layer_surface; - struct wlr_surface *surface = wlr_layer_surface_v1->surface; - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - .ox = layer_surface->geo.x, - .oy = layer_surface->geo.y, - .width = surface->current.width, - .height = surface->current.height, - }; - wlr_layer_surface_v1_for_each_popup_surface(wlr_layer_surface_v1, - output_for_each_surface_iterator, &data); - } -} - -#if HAVE_XWAYLAND -void output_unmanaged_for_each_surface(struct sway_output *output, - struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->lx; - double oy = unmanaged_surface->ly - output->ly; - - output_surface_for_each_surface(output, xsurface->surface, ox, oy, - iterator, user_data); - } -} -#endif - -void output_drag_icons_for_each_surface(struct sway_output *output, - struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, - void *user_data) { - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->lx; - double oy = drag_icon->y - output->ly; - - if (drag_icon->wlr_drag_icon->mapped) { - output_surface_for_each_surface(output, - drag_icon->wlr_drag_icon->surface, ox, oy, - iterator, user_data); - } - } -} - -static void for_each_surface_container_iterator(struct sway_container *con, - void *_data) { - if (!con->view || !view_is_visible(con->view)) { - return; - } - - struct surface_iterator_data *data = _data; - output_view_for_each_surface(data->output, con->view, - data->user_iterator, data->user_data); -} - -static void output_for_each_surface(struct sway_output *output, - sway_surface_iterator_func_t iterator, void *user_data) { - if (server.session_lock.locked) { - if (server.session_lock.lock == NULL) { - return; - } - struct wlr_session_lock_surface_v1 *lock_surface; - wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { - if (lock_surface->output != output->wlr_output) { - continue; - } - if (!lock_surface->mapped) { - continue; - } - - output_surface_for_each_surface(output, lock_surface->surface, - 0.0, 0.0, iterator, user_data); - } - return; - } - - if (output_has_opaque_overlay_layer_surface(output)) { - goto overlay; - } - - struct surface_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .output = output, - .view = NULL, - }; - - struct sway_workspace *workspace = output_get_active_workspace(output); - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - if (!workspace) { - return; - } - fullscreen_con = workspace->current.fullscreen; - } - if (fullscreen_con) { - for_each_surface_container_iterator(fullscreen_con, &data); - container_for_each_child(fullscreen_con, - for_each_surface_container_iterator, &data); - - // TODO: Show transient containers for fullscreen global - if (fullscreen_con == workspace->current.fullscreen) { - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, fullscreen_con)) { - for_each_surface_container_iterator(floater, &data); - } - } - } -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, - iterator, user_data); -#endif - } else { - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - iterator, user_data); - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - iterator, user_data); - - workspace_for_each_container(workspace, - for_each_surface_container_iterator, &data); - -#if HAVE_XWAYLAND - output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, - iterator, user_data); -#endif - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - iterator, user_data); - } - -overlay: - output_layer_for_each_surface(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - iterator, user_data); - output_drag_icons_for_each_surface(output, &root->drag_icons, - iterator, user_data); -} - -static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); -} - -void scale_box(struct wlr_box *box, float scale) { - box->width = scale_length(box->width, box->x, scale); - box->height = scale_length(box->height, box->y, scale); - box->x = round(box->x * scale); - box->y = round(box->y * scale); -} struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); @@ -383,201 +70,130 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) { return focus->sway_workspace; } -bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { - struct sway_layer_surface *sway_layer_surface; - wl_list_for_each(sway_layer_surface, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { - struct wlr_surface *wlr_surface = sway_layer_surface->layer_surface->surface; - pixman_box32_t output_box = { - .x2 = output->width, - .y2 = output->height, - }; - pixman_region32_t surface_opaque_box; - pixman_region32_init(&surface_opaque_box); - pixman_region32_copy(&surface_opaque_box, &wlr_surface->opaque_region); - pixman_region32_translate(&surface_opaque_box, - sway_layer_surface->geo.x, sway_layer_surface->geo.y); - pixman_region_overlap_t contains = - pixman_region32_contains_rectangle(&surface_opaque_box, &output_box); - pixman_region32_fini(&surface_opaque_box); - - if (contains == PIXMAN_REGION_IN) { - return true; - } - } - return false; -} - struct send_frame_done_data { struct timespec when; int msec_until_refresh; + struct sway_output *output; }; -static void send_frame_done_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *user_data) { - int view_max_render_time = 0; - if (view != NULL) { - view_max_render_time = view->max_render_time; +struct buffer_timer { + struct wl_listener destroy; + struct wl_event_source *frame_done_timer; +}; + +static int handle_buffer_timer(void *data) { + struct wlr_scene_buffer *buffer = data; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_buffer_send_frame_done(buffer, &now); + return 0; +} + +static void handle_buffer_timer_destroy(struct wl_listener *listener, + void *data) { + struct buffer_timer *timer = wl_container_of(listener, timer, destroy); + + wl_list_remove(&timer->destroy.link); + wl_event_source_remove(timer->frame_done_timer); + free(timer); +} + +static struct buffer_timer *buffer_timer_assign(struct wlr_scene_buffer *buffer) { + struct buffer_timer *timer = calloc(1, sizeof(struct buffer_timer)); + if (!timer) { + return NULL; } + timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_buffer_timer, buffer); + if (!timer->frame_done_timer) { + free(timer); + return NULL; + } + + scene_descriptor_assign(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER, timer); + + timer->destroy.notify = handle_buffer_timer_destroy; + wl_signal_add(&buffer->node.events.destroy, &timer->destroy); + + return timer; +} + +static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, + double x, double y, void *user_data) { struct send_frame_done_data *data = user_data; + struct sway_output *output = data->output; + int view_max_render_time = 0; + + if (buffer->primary_output != data->output->scene_output) { + return; + } + + struct wlr_scene_node *current = &buffer->node; + + while (true) { + if (current->data) { + struct sway_scene_descriptor *desc = current->data; + + if (desc->type == SWAY_SCENE_DESC_CONTAINER) { + struct sway_container *con = desc->data; + + // We can be dealing with a title bar without owned by a + // container without a view. This will happen if you have a + // nested tabbed view for example. + if (con->view) { + view_max_render_time = con->view->max_render_time; + } + + break; + } + } + + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } int delay = data->msec_until_refresh - output->max_render_time - view_max_render_time; - if (output->max_render_time == 0 || view_max_render_time == 0 || delay < 1) { - wlr_surface_send_frame_done(surface, &data->when); - } else { - struct sway_surface *sway_surface = surface->data; - wl_event_source_timer_update(sway_surface->frame_done_timer, delay); - } -} + struct buffer_timer *timer = NULL; -static void send_frame_done(struct sway_output *output, struct send_frame_done_data *data) { - output_for_each_surface(output, send_frame_done_iterator, data); -} - -static void count_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *box, void *data) { - size_t *n = data; - (*n)++; -} - -static bool scan_out_fullscreen_view(struct sway_output *output, - struct sway_view *view) { - struct wlr_output *wlr_output = output->wlr_output; - struct sway_workspace *workspace = output->current.active_workspace; - if (!sway_assert(workspace, "Expected an active workspace")) { - return false; - } - - if (server.session_lock.locked) { - return false; - } - - if (!wl_list_empty(&view->saved_buffers)) { - return false; - } - - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, view->container)) { - return false; + if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { + if (buffer->node.data) { + struct sway_scene_descriptor *desc = buffer->node.data; + sway_assert(desc->type == SWAY_SCENE_DESC_BUFFER_TIMER, + "Corrupted scene tree: expected a buffer timer"); + timer = desc->data; + }else{ + timer = buffer_timer_assign(buffer); } } -#if HAVE_XWAYLAND - if (!wl_list_empty(&root->xwayland_unmanaged)) { - return false; - } -#endif - - if (!wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) { - return false; - } - if (!wl_list_empty(&root->drag_icons)) { - return false; - } - - struct wlr_surface *surface = view->surface; - if (surface == NULL) { - return false; - } - size_t n_surfaces = 0; - output_view_for_each_surface(output, view, - count_surface_iterator, &n_surfaces); - if (n_surfaces != 1) { - return false; - } - - if (surface->buffer == NULL) { - return false; - } - - if ((float)surface->current.scale != wlr_output->scale || - surface->current.transform != wlr_output->transform) { - return false; - } - - wlr_output_attach_buffer(wlr_output, &surface->buffer->base); - if (!wlr_output_test(wlr_output)) { - return false; - } - - wlr_presentation_surface_sampled_on_output(server.presentation, surface, - wlr_output); - - return wlr_output_commit(wlr_output); + if (timer) { + wl_event_source_timer_update(timer->frame_done_timer, delay); + } else { + wlr_scene_buffer_send_frame_done(buffer, &data->when); + } } static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; - if (output->wlr_output == NULL) { - return 0; + + if (output->enabled) { + output->wlr_output->frame_pending = false; + wlr_scene_output_commit(output->scene_output); } - output->wlr_output->frame_pending = false; - - struct sway_workspace *workspace = output->current.active_workspace; - if (workspace == NULL) { - return 0; - } - - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - fullscreen_con = workspace->current.fullscreen; - } - - if (fullscreen_con && fullscreen_con->view && !debug.noscanout) { - // Try to scan-out the fullscreen view - static bool last_scanned_out = false; - bool scanned_out = - scan_out_fullscreen_view(output, fullscreen_con->view); - - if (scanned_out && !last_scanned_out) { - sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", - output->wlr_output->name); - } - if (last_scanned_out && !scanned_out) { - sway_log(SWAY_DEBUG, "Stopping fullscreen view scan out on %s", - output->wlr_output->name); - output_damage_whole(output); - } - last_scanned_out = scanned_out; - - if (scanned_out) { - return 0; - } - } - - bool needs_frame; - pixman_region32_t damage; - pixman_region32_init(&damage); - if (!wlr_output_damage_attach_render(output->damage, - &needs_frame, &damage)) { - return 0; - } - - if (needs_frame) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - output_render(output, &now, &damage); - } else { - wlr_output_rollback(output->wlr_output); - } - - pixman_region32_fini(&damage); - return 0; } -static void damage_handle_frame(struct wl_listener *listener, void *user_data) { +static void handle_frame_request(struct wl_listener *listener, void *user_data) { struct sway_output *output = - wl_container_of(listener, output, damage_frame); + wl_container_of(listener, output, frame_request); if (!output->enabled || !output->wlr_output->enabled) { return; } @@ -637,116 +253,8 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) { struct send_frame_done_data data = {0}; clock_gettime(CLOCK_MONOTONIC, &data.when); data.msec_until_refresh = msec_until_refresh; - send_frame_done(output, &data); -} - -void output_damage_whole(struct sway_output *output) { - // The output can exist with no wlr_output if it's just been disconnected - // and the transaction to evacuate it has't completed yet. - if (output && output->wlr_output && output->damage) { - wlr_output_damage_add_whole(output->damage); - } -} - -static void damage_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - bool *data = _data; - bool whole = *data; - - struct wlr_box box = *_box; - scale_box(&box, output->wlr_output->scale); - - pixman_region32_t damage; - pixman_region32_init(&damage); - wlr_surface_get_effective_damage(surface, &damage); - wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceil(output->wlr_output->scale) > surface->current.scale) { - // When scaling up a surface, it'll become blurry so we need to - // expand the damage region - wlr_region_expand(&damage, &damage, - ceil(output->wlr_output->scale) - surface->current.scale); - } - pixman_region32_translate(&damage, box.x, box.y); - wlr_output_damage_add(output->damage, &damage); - pixman_region32_fini(&damage); - - if (whole) { - wlr_output_damage_add_box(output->damage, &box); - } - - if (!wl_list_empty(&surface->current.frame_callback_list)) { - wlr_output_schedule_frame(output->wlr_output); - } -} - -void output_damage_surface(struct sway_output *output, double ox, double oy, - struct wlr_surface *surface, bool whole) { - output_surface_for_each_surface(output, surface, ox, oy, - damage_surface_iterator, &whole); -} - -void output_damage_from_view(struct sway_output *output, - struct sway_view *view) { - if (!view_is_visible(view)) { - return; - } - bool whole = false; - output_view_for_each_surface(output, view, damage_surface_iterator, &whole); -} - -// Expecting an unscaled box in layout coordinates -void output_damage_box(struct sway_output *output, struct wlr_box *_box) { - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx; - box.y -= output->ly; - scale_box(&box, output->wlr_output->scale); - wlr_output_damage_add_box(output->damage, &box); -} - -static void damage_child_views_iterator(struct sway_container *con, - void *data) { - if (!con->view || !view_is_visible(con->view)) { - return; - } - struct sway_output *output = data; - bool whole = true; - output_view_for_each_surface(output, con->view, damage_surface_iterator, - &whole); -} - -void output_damage_whole_container(struct sway_output *output, - struct sway_container *con) { - // Pad the box by 1px, because the width is a double and might be a fraction - struct wlr_box box = { - .x = con->current.x - output->lx - 1, - .y = con->current.y - output->ly - 1, - .width = con->current.width + 2, - .height = con->current.height + 2, - }; - scale_box(&box, output->wlr_output->scale); - wlr_output_damage_add_box(output->damage, &box); - // Damage subsurfaces as well, which may extend outside the box - if (con->view) { - damage_child_views_iterator(con, output); - } else { - container_for_each_child(con, damage_child_views_iterator, output); - } -} - -static void damage_handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = - wl_container_of(listener, output, damage_destroy); - if (!output->enabled) { - return; - } - output_disable(output); - - wl_list_remove(&output->damage_destroy.link); - wl_list_remove(&output->damage_frame.link); - - transaction_commit_dirty(); + data.output = output; + wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); } static void update_output_manager_config(struct sway_server *server) { @@ -778,11 +286,12 @@ static void update_output_manager_config(struct sway_server *server) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_server *server = output->server; - output_begin_destroy(output); if (output->enabled) { output_disable(output); } + + output_begin_destroy(output); wl_list_remove(&output->link); @@ -790,7 +299,10 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->commit.link); wl_list_remove(&output->mode.link); wl_list_remove(&output->present.link); + wl_list_remove(&output->frame_request.link); + wlr_scene_output_destroy(output->scene_output); + output->scene_output = NULL; output->wlr_output->data = NULL; output->wlr_output = NULL; @@ -824,11 +336,6 @@ static void handle_mode(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } -static void update_textures(struct sway_container *con, void *data) { - container_update_title_textures(con); - container_update_marks_textures(con); -} - static void handle_commit(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, commit); struct wlr_output_event_commit *event = data; @@ -837,10 +344,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->committed & WLR_OUTPUT_STATE_SCALE) { - output_for_each_container(output, update_textures, NULL); - } - if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { arrange_layers(output); arrange_output(output); @@ -898,12 +401,24 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } - struct sway_output *output = output_create(wlr_output); - if (!output) { + // Create the scene output here so we're not accidentally creating one for + // the fallback output + struct wlr_scene_output *scene_output = + wlr_scene_output_create(root->root_scene, wlr_output); + if (!scene_output) { + sway_log(SWAY_ERROR, "Failed to create a scene output"); return; } + + struct sway_output *output = output_create(wlr_output); + if (!output) { + sway_log(SWAY_ERROR, "Failed to create a sway output"); + wlr_scene_output_destroy(scene_output); + return; + } + output->server = server; - output->damage = wlr_output_damage_create(wlr_output); + output->scene_output = scene_output; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; @@ -913,14 +428,16 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - output->damage_frame.notify = damage_handle_frame; - wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); - output->damage_destroy.notify = damage_handle_destroy; + wl_signal_add(&wlr_output->events.frame, &output->frame_request); + output->frame_request.notify = handle_frame_request; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); + if (server->session_lock.lock) { + sway_session_lock_add_output(server->session_lock.lock, output); + } + struct output_config *oc = find_output_config(output); apply_output_config(oc, output); free_output_config(oc); diff --git a/sway/desktop/render.c b/sway/desktop/render.c deleted file mode 100644 index ed9ad4906..000000000 --- a/sway/desktop/render.c +++ /dev/null @@ -1,1204 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "log.h" -#include "config.h" -#include "sway/config.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" -#include "sway/layers.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/tree/arrange.h" -#include "sway/tree/container.h" -#include "sway/tree/root.h" -#include "sway/tree/view.h" -#include "sway/tree/workspace.h" - -struct render_data { - pixman_region32_t *damage; - float alpha; - struct wlr_box *clip_box; -}; - -/** - * Apply scale to a width or height. - * - * One does not simply multiply the width by the scale. We allow fractional - * scaling, which means the resulting scaled width might be a decimal. - * So we round it. - * - * But even this can produce undesirable results depending on the X or Y offset - * of the box. For example, with a scale of 1.5, a box with width=1 should not - * scale to 2px if its X coordinate is 1, because the X coordinate would have - * scaled to 2px. - */ -static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); -} - -static void scissor_output(struct wlr_output *wlr_output, - pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_output->renderer; - assert(renderer); - - 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(wlr_output, &ow, &oh); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - wlr_renderer_scissor(renderer, &box); -} - -static void set_scale_filter(struct wlr_output *wlr_output, - struct wlr_texture *texture, enum scale_filter_mode scale_filter) { - if (!wlr_texture_is_gles2(texture)) { - return; - } - - struct wlr_gles2_texture_attribs attribs; - wlr_gles2_texture_get_attribs(texture, &attribs); - - glBindTexture(attribs.target, attribs.tex); - - switch (scale_filter) { - case SCALE_FILTER_LINEAR: - glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; - case SCALE_FILTER_NEAREST: - glTexParameteri(attribs.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - assert(false); // unreachable - } -} - -static void render_texture(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct wlr_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9], float alpha) { - struct wlr_renderer *renderer = wlr_output->renderer; - struct sway_output *output = wlr_output->data; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, dst_box->x, dst_box->y, - dst_box->width, dst_box->height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - set_scale_filter(wlr_output, texture, output->scale_filter); - if (src_box != NULL) { - wlr_render_subtexture_with_matrix(renderer, texture, src_box, matrix, alpha); - } else { - wlr_render_texture_with_matrix(renderer, texture, matrix, alpha); - } - } - -damage_finish: - pixman_region32_fini(&damage); -} - -static void render_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, - struct wlr_box *_box, void *_data) { - struct render_data *data = _data; - struct wlr_output *wlr_output = output->wlr_output; - pixman_region32_t *output_damage = data->damage; - float alpha = data->alpha; - - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (!texture) { - return; - } - - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - - struct wlr_box proj_box = *_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, - wlr_output->transform_matrix); - - struct wlr_box dst_box = *_box; - struct wlr_box *clip_box = data->clip_box; - if (clip_box != NULL) { - dst_box.width = fmin(dst_box.width, clip_box->width); - dst_box.height = fmin(dst_box.height, clip_box->height); - } - scale_box(&dst_box, wlr_output->scale); - - render_texture(wlr_output, output_damage, texture, - &src_box, &dst_box, matrix, alpha); - - wlr_presentation_surface_sampled_on_output(server.presentation, surface, - wlr_output); -} - -static void render_layer_toplevel(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_layer_for_each_toplevel_surface(output, layer_surfaces, - render_surface_iterator, &data); -} - -static void render_layer_popups(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_layer_for_each_popup_surface(output, layer_surfaces, - render_surface_iterator, &data); -} - -#if HAVE_XWAYLAND -static void render_unmanaged(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *unmanaged) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_unmanaged_for_each_surface(output, unmanaged, - render_surface_iterator, &data); -} -#endif - -static void render_drag_icons(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *drag_icons) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - output_drag_icons_for_each_surface(output, drag_icons, - render_surface_iterator, &data); -} - -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_rect(struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4]) { - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_renderer *renderer = wlr_output->renderer; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; - - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, box.x, box.y, - box.width, box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_render_rect(renderer, &box, color, - wlr_output->transform_matrix); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -void premultiply_alpha(float color[4], float opacity) { - color[3] *= opacity; - color[0] *= color[3]; - color[1] *= color[3]; - color[2] *= color[3]; -} - -static void render_view_toplevels(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .damage = damage, - .alpha = alpha, - }; - struct wlr_box clip_box; - if (!container_is_current_floating(view->container)) { - // As we pass the geometry offsets to the surface iterator, we will - // need to account for the offsets in the clip dimensions. - clip_box.width = view->container->current.content_width + view->geometry.x; - clip_box.height = view->container->current.content_height + view->geometry.y; - data.clip_box = &clip_box; - } - // Render all toplevels without descending into popups - double ox = view->container->surface_x - - output->lx - view->geometry.x; - double oy = view->container->surface_y - - output->ly - view->geometry.y; - output_surface_for_each_surface(output, view->surface, ox, oy, - render_surface_iterator, &data); -} - -static void render_view_popups(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct render_data data = { - .damage = damage, - .alpha = alpha, - }; - output_view_for_each_popup_surface(output, view, - render_surface_iterator, &data); -} - -static void render_saved_view(struct sway_view *view, - struct sway_output *output, pixman_region32_t *damage, float alpha) { - struct wlr_output *wlr_output = output->wlr_output; - - if (wl_list_empty(&view->saved_buffers)) { - return; - } - - bool floating = container_is_current_floating(view->container); - - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - if (!saved_buf->buffer->texture) { - continue; - } - - struct wlr_box proj_box = { - .x = saved_buf->x - view->saved_geometry.x - output->lx, - .y = saved_buf->y - view->saved_geometry.y - output->ly, - .width = saved_buf->width, - .height = saved_buf->height, - }; - - struct wlr_box output_box = { - .width = output->width, - .height = output->height, - }; - - struct wlr_box intersection; - bool intersects = wlr_box_intersection(&intersection, &output_box, &proj_box); - if (!intersects) { - continue; - } - - struct wlr_box dst_box = proj_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0, - wlr_output->transform_matrix); - - if (!floating) { - dst_box.width = fmin(dst_box.width, - view->container->current.content_width - - (saved_buf->x - view->container->current.content_x) + view->saved_geometry.x); - dst_box.height = fmin(dst_box.height, - view->container->current.content_height - - (saved_buf->y - view->container->current.content_y) + view->saved_geometry.y); - } - scale_box(&dst_box, wlr_output->scale); - - render_texture(wlr_output, damage, saved_buf->buffer->texture, - &saved_buf->source_box, &dst_box, matrix, alpha); - } - - // FIXME: we should set the surface that this saved buffer originates from - // as sampled here. - // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 -} - -/** - * Render a view's surface and left/bottom/right borders. - */ -static void render_view(struct sway_output *output, pixman_region32_t *damage, - struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->view; - if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(view, output, damage, view->container->alpha); - } else if (view->surface) { - render_view_toplevels(view, output, damage, view->container->alpha); - } - - if (con->current.border == B_NONE || con->current.border == B_CSD) { - return; - } - - struct wlr_box box; - float output_scale = output->wlr_output->scale; - float color[4]; - struct sway_container_state *state = &con->current; - - if (state->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->content_y); - box.width = state->border_thickness; - box.height = state->content_height; - scale_box(&box, output_scale); - render_rect(output, damage, &box, color); - } - - list_t *siblings = container_get_current_siblings(con); - enum sway_container_layout layout = - container_current_parent_layout(con); - - if (state->border_right) { - if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = floor(state->content_x + state->content_width); - box.y = floor(state->content_y); - box.width = state->border_thickness; - box.height = state->content_height; - scale_box(&box, output_scale); - render_rect(output, damage, &box, color); - } - - if (state->border_bottom) { - if (!container_is_current_floating(con) && siblings->length == 1 && layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->content_y + state->content_height); - box.width = state->width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(output, damage, &box, color); - } -} - -/** - * Render a titlebar. - * - * Care must be taken not to render over the same pixel multiple times, - * otherwise the colors will be incorrect when using opacity. - * - * The height is: 1px border, 3px padding, font height, 3px padding, 1px border - * The left side is: 1px border, 2px padding, title - */ -static void render_titlebar(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, - int x, int y, int width, - struct border_colors *colors, struct wlr_texture *title_texture, - struct wlr_texture *marks_texture) { - struct wlr_box box; - float color[4]; - float output_scale = output->wlr_output->scale; - double output_x = output->lx; - double output_y = output->ly; - int titlebar_border_thickness = config->titlebar_border_thickness; - int titlebar_h_padding = config->titlebar_h_padding; - int titlebar_v_padding = config->titlebar_v_padding; - enum alignment title_align = config->title_align; - - // Single pixel bar above title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x; - box.y = y; - box.width = width; - box.height = titlebar_border_thickness; - scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); - - // Single pixel bar below title - box.x = x; - box.y = y + container_titlebar_height() - titlebar_border_thickness; - box.width = width; - box.height = titlebar_border_thickness; - scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); - - // Single pixel left edge - box.x = x; - box.y = y + titlebar_border_thickness; - box.width = titlebar_border_thickness; - box.height = container_titlebar_height() - titlebar_border_thickness * 2; - scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); - - // Single pixel right edge - box.x = x + width - titlebar_border_thickness; - box.y = y + titlebar_border_thickness; - box.width = titlebar_border_thickness; - box.height = container_titlebar_height() - titlebar_border_thickness * 2; - scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); - - int inner_x = x - output_x + titlebar_h_padding; - int bg_y = y + titlebar_border_thickness; - size_t inner_width = width - titlebar_h_padding * 2; - - // output-buffer local - int ob_inner_x = round(inner_x * output_scale); - int ob_inner_width = scale_length(inner_width, inner_x, output_scale); - int ob_bg_height = scale_length( - (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height, bg_y, output_scale); - - // Marks - int ob_marks_x = 0; // output-buffer-local - int ob_marks_width = 0; // output-buffer-local - if (config->show_marks && marks_texture) { - struct wlr_box texture_box = { - .width = marks_texture->width, - .height = marks_texture->height, - }; - ob_marks_width = texture_box.width; - - // The marks texture might be shorter than the config->font_height, in - // which case we need to pad it as evenly as possible above and below. - int ob_padding_total = ob_bg_height - texture_box.height; - int ob_padding_above = floor(ob_padding_total / 2.0); - int ob_padding_below = ceil(ob_padding_total / 2.0); - - // Render texture. If the title is on the right, the marks will be on - // the left. Otherwise, they will be on the right. - if (title_align == ALIGN_RIGHT || texture_box.width > ob_inner_width) { - texture_box.x = ob_inner_x; - } else { - texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; - } - ob_marks_x = texture_box.x; - - texture_box.y = round((bg_y - output_y) * output_scale) + - ob_padding_above; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width < texture_box.width) { - texture_box.width = ob_inner_width; - } - render_texture(output->wlr_output, output_damage, marks_texture, - NULL, &texture_box, matrix, con->alpha); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; - box.height = ob_padding_above; - render_rect(output, output_damage, &box, color); - - // Padding below - box.y += ob_padding_above + texture_box.height; - box.height = ob_padding_below; - render_rect(output, output_damage, &box, color); - } - - // Title text - int ob_title_x = 0; // output-buffer-local - int ob_title_width = 0; // output-buffer-local - if (title_texture) { - struct wlr_box texture_box = { - .width = title_texture->width, - .height = title_texture->height, - }; - - // The effective output may be NULL when con is not on any output. - // This can happen because we render all children of containers, - // even those that are out of the bounds of any output. - struct sway_output *effective = container_get_effective_output(con); - float title_scale = effective ? effective->wlr_output->scale : output_scale; - texture_box.width = texture_box.width * output_scale / title_scale; - texture_box.height = texture_box.height * output_scale / title_scale; - ob_title_width = texture_box.width; - - // The title texture might be shorter than the config->font_height, - // in which case we need to pad it above and below. - int ob_padding_above = round((titlebar_v_padding - - titlebar_border_thickness) * output_scale); - int ob_padding_below = ob_bg_height - ob_padding_above - - texture_box.height; - - // Render texture - if (texture_box.width > ob_inner_width - ob_marks_width) { - texture_box.x = (title_align == ALIGN_RIGHT && ob_marks_width) - ? ob_marks_x + ob_marks_width : ob_inner_x; - } else if (title_align == ALIGN_LEFT) { - texture_box.x = ob_inner_x; - } else if (title_align == ALIGN_CENTER) { - // If there are marks visible, center between the edge and marks. - // Otherwise, center in the inner area. - if (ob_marks_width) { - texture_box.x = (ob_inner_x + ob_marks_x) / 2 - - texture_box.width / 2; - } else { - texture_box.x = ob_inner_x + ob_inner_width / 2 - - texture_box.width / 2; - } - } else { - texture_box.x = ob_inner_x + ob_inner_width - texture_box.width; - } - ob_title_x = texture_box.x; - - texture_box.y = - round((bg_y - output_y) * output_scale) + ob_padding_above; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width - ob_marks_width < texture_box.width) { - texture_box.width = ob_inner_width - ob_marks_width; - } - - render_texture(output->wlr_output, output_damage, title_texture, - NULL, &texture_box, matrix, con->alpha); - - // Padding above - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; - box.height = ob_padding_above; - render_rect(output, output_damage, &box, color); - - // Padding below - box.y += ob_padding_above + texture_box.height; - box.height = ob_padding_below; - render_rect(output, output_damage, &box, color); - } - - // Determine the left + right extends of the textures (output-buffer local) - int ob_left_x, ob_left_width, ob_right_x, ob_right_width; - if (ob_title_width == 0 && ob_marks_width == 0) { - ob_left_x = ob_inner_x; - ob_left_width = 0; - ob_right_x = ob_inner_x; - ob_right_width = 0; - } else if (ob_title_x < ob_marks_x) { - ob_left_x = ob_title_x; - ob_left_width = ob_title_width; - ob_right_x = ob_marks_x; - ob_right_width = ob_marks_width; - } else { - ob_left_x = ob_marks_x; - ob_left_width = ob_marks_width; - ob_right_x = ob_title_x; - ob_right_width = ob_title_width; - } - if (ob_left_x < ob_inner_x) { - ob_left_x = ob_inner_x; - } else if (ob_left_x + ob_left_width > ob_right_x + ob_right_width) { - ob_right_x = ob_left_x; - ob_right_width = ob_left_width; - } - - // Filler between title and marks - box.width = ob_right_x - ob_left_x - ob_left_width; - if (box.width > 0) { - box.x = ob_left_x + ob_left_width + round(output_x * output_scale); - box.y = round(bg_y * output_scale); - box.height = ob_bg_height; - render_rect(output, output_damage, &box, color); - } - - // Padding on left side - box.x = x + titlebar_border_thickness; - box.y = y + titlebar_border_thickness; - box.width = titlebar_h_padding - titlebar_border_thickness; - box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height; - scale_box(&box, output_scale); - int left_x = ob_left_x + round(output_x * output_scale); - if (box.x + box.width < left_x) { - box.width += left_x - box.x - box.width; - } - render_rect(output, output_damage, &box, color); - - // Padding on right side - box.x = x + width - titlebar_h_padding; - box.y = y + titlebar_border_thickness; - box.width = titlebar_h_padding - titlebar_border_thickness; - box.height = (titlebar_v_padding - titlebar_border_thickness) * 2 + - config->font_height; - scale_box(&box, output_scale); - int right_rx = ob_right_x + ob_right_width + round(output_x * output_scale); - if (right_rx < box.x) { - box.width += box.x - right_rx; - box.x = right_rx; - } - render_rect(output, output_damage, &box, color); -} - -/** - * Render the top border line for a view using "border pixel". - */ -static void render_top_border(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, - struct border_colors *colors) { - struct sway_container_state *state = &con->current; - if (!state->border_top) { - return; - } - struct wlr_box box; - float color[4]; - float output_scale = output->wlr_output->scale; - - // Child border - top edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = floor(state->x); - box.y = floor(state->y); - box.width = state->width; - box.height = state->border_thickness; - scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); -} - -struct parent_data { - enum sway_container_layout layout; - struct wlr_box box; - list_t *children; - bool focused; - struct sway_container *active_child; -}; - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool parent_focused); - -/** - * Render a container's children using a L_HORIZ or L_VERT layout. - * - * Wrap child views in borders and leave child containers borderless because - * they'll apply their own borders to their children. - */ -static void render_containers_linear(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - - if (child->view) { - struct sway_view *view = child->view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - struct sway_container_state *state = &child->current; - - if (view_is_urgent(view)) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (state->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, floor(state->x), - floor(state->y), state->width, colors, - title_texture, marks_texture); - } else if (state->border == B_PIXEL) { - render_top_border(output, damage, child, colors); - } - render_view(output, damage, child, colors); - } else { - render_container(output, damage, child, - parent->focused || child->current.focused); - } - } -} - -static bool container_is_focused(struct sway_container *con, void *data) { - return con->current.focused; -} - -static bool container_has_focused_child(struct sway_container *con) { - return container_find_child(con, container_is_focused, NULL); -} - -/** - * Render a container's children using the L_TABBED layout. - */ -static void render_containers_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { - if (!parent->children->length) { - return; - } - struct sway_container *current = parent->active_child; - struct border_colors *current_colors = &config->border_colors.unfocused; - int tab_width = parent->box.width / parent->children->length; - - // Render tabs - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - struct sway_view *view = child->view; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - bool urgent = view ? - view_is_urgent(view) : container_has_urgent_child(child); - - if (urgent) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (cstate->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (config->has_focused_tab_title && container_has_focused_child(child)) { - colors = &config->border_colors.focused_tab_title; - title_texture = child->title_focused_tab_title; - marks_texture = child->marks_focused_tab_title; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - int x = floor(cstate->x + tab_width * i); - - // Make last tab use the remaining width of the parent - if (i == parent->children->length - 1) { - tab_width = parent->box.width - tab_width * i; - } - - render_titlebar(output, damage, child, x, parent->box.y, tab_width, - colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current->view) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent->focused || current->current.focused); - } -} - -/** - * Render a container's children using the L_STACKED layout. - */ -static void render_containers_stacked(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { - if (!parent->children->length) { - return; - } - struct sway_container *current = parent->active_child; - struct border_colors *current_colors = &config->border_colors.unfocused; - size_t titlebar_height = container_titlebar_height(); - - // Render titles - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; - struct sway_view *view = child->view; - struct sway_container_state *cstate = &child->current; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - bool urgent = view ? - view_is_urgent(view) : container_has_urgent_child(child); - - if (urgent) { - colors = &config->border_colors.urgent; - title_texture = child->title_urgent; - marks_texture = child->marks_urgent; - } else if (cstate->focused || parent->focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->marks_focused; - } else if (config->has_focused_tab_title && container_has_focused_child(child)) { - colors = &config->border_colors.focused_tab_title; - title_texture = child->title_focused_tab_title; - marks_texture = child->marks_focused_tab_title; - } else if (child == parent->active_child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->marks_unfocused; - } - - int y = parent->box.y + titlebar_height * i; - render_titlebar(output, damage, child, parent->box.x, y, - parent->box.width, colors, title_texture, marks_texture); - - if (child == current) { - current_colors = colors; - } - } - - // Render surface and left/right/bottom borders - if (current->view) { - render_view(output, damage, current, current_colors); - } else { - render_container(output, damage, current, - parent->focused || current->current.focused); - } -} - -static void render_containers(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { - if (config->hide_lone_tab && parent->children->length == 1) { - struct sway_container *child = parent->children->items[0]; - if (child->view) { - render_containers_linear(output,damage, parent); - return; - } - } - - switch (parent->layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_containers_linear(output, damage, parent); - break; - case L_STACKED: - render_containers_stacked(output, damage, parent); - break; - case L_TABBED: - render_containers_tabbed(output, damage, parent); - break; - } -} - -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool focused) { - struct parent_data data = { - .layout = con->current.layout, - .box = { - .x = floor(con->current.x), - .y = floor(con->current.y), - .width = con->current.width, - .height = con->current.height, - }, - .children = con->current.children, - .focused = focused, - .active_child = con->current.focused_inactive_child, - }; - render_containers(output, damage, &data); -} - -static void render_workspace(struct sway_output *output, - pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { - struct parent_data data = { - .layout = ws->current.layout, - .box = { - .x = floor(ws->current.x), - .y = floor(ws->current.y), - .width = ws->current.width, - .height = ws->current.height, - }, - .children = ws->current.tiling, - .focused = focused, - .active_child = ws->current.focused_inactive_child, - }; - render_containers(output, damage, &data); -} - -static void render_floating_container(struct sway_output *soutput, - pixman_region32_t *damage, struct sway_container *con) { - if (con->view) { - struct sway_view *view = con->view; - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - - if (view_is_urgent(view)) { - colors = &config->border_colors.urgent; - title_texture = con->title_urgent; - marks_texture = con->marks_urgent; - } else if (con->current.focused) { - colors = &config->border_colors.focused; - title_texture = con->title_focused; - marks_texture = con->marks_focused; - } else { - colors = &config->border_colors.unfocused; - title_texture = con->title_unfocused; - marks_texture = con->marks_unfocused; - } - - if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, floor(con->current.x), - floor(con->current.y), con->current.width, colors, - title_texture, marks_texture); - } else if (con->current.border == B_PIXEL) { - render_top_border(soutput, damage, con, colors); - } - render_view(soutput, damage, con, colors); - } else { - render_container(soutput, damage, con, con->current.focused); - } -} - -static void render_floating(struct sway_output *soutput, - pixman_region32_t *damage) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->current.workspaces->length; ++j) { - struct sway_workspace *ws = output->current.workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - for (int k = 0; k < ws->current.floating->length; ++k) { - struct sway_container *floater = ws->current.floating->items[k]; - if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { - continue; - } - render_floating_container(soutput, damage, floater); - } - } - } -} - -static void render_seatops(struct sway_output *output, - pixman_region32_t *damage) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seatop_render(seat, output, damage); - } -} - -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_renderer *renderer = output->server->renderer; - - struct sway_workspace *workspace = output->current.active_workspace; - if (workspace == NULL) { - return; - } - - struct sway_container *fullscreen_con = root->fullscreen_global; - if (!fullscreen_con) { - fullscreen_con = workspace->current.fullscreen; - } - - wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); - - if (debug.damage == DAMAGE_RERENDER) { - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - pixman_region32_union_rect(damage, damage, 0, 0, width, height); - } - - if (!pixman_region32_not_empty(damage)) { - // Output isn't damaged but needs buffer swap - goto renderer_end; - } - - if (debug.damage == DAMAGE_HIGHLIGHT) { - wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); - } - - if (server.session_lock.locked) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - if (server.session_lock.lock == NULL) { - // abandoned lock -> red BG - clear_color[0] = 1.f; - } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - if (server.session_lock.lock != NULL) { - struct render_data data = { - .damage = damage, - .alpha = 1.0f, - }; - - struct wlr_session_lock_surface_v1 *lock_surface; - wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { - if (lock_surface->output != wlr_output) { - continue; - } - if (!lock_surface->mapped) { - continue; - } - - output_surface_for_each_surface(output, lock_surface->surface, - 0.0, 0.0, render_surface_iterator, &data); - } - } - goto renderer_end; - } - - if (output_has_opaque_overlay_layer_surface(output)) { - goto render_overlay; - } - - if (fullscreen_con) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - if (fullscreen_con->view) { - if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { - render_saved_view(fullscreen_con->view, output, damage, 1.0f); - } else if (fullscreen_con->view->surface) { - render_view_toplevels(fullscreen_con->view, - output, damage, 1.0f); - } - } else { - render_container(output, damage, fullscreen_con, - fullscreen_con->current.focused); - } - - for (int i = 0; i < workspace->current.floating->length; ++i) { - struct sway_container *floater = - workspace->current.floating->items[i]; - if (container_is_transient_for(floater, fullscreen_con)) { - render_floating_container(output, damage, floater); - } - } -#if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); -#endif - } else { - float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; - - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - wlr_renderer_clear(renderer, clear_color); - } - - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - - render_workspace(output, damage, workspace, workspace->current.focused); - render_floating(output, damage); -#if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); -#endif - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - } - - render_seatops(output, damage); - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - if (focus && focus->view) { - render_view_popups(focus->view, output, damage, focus->alpha); - } - -render_overlay: - render_layer_toplevel(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(output, damage, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root->drag_icons); - -renderer_end: - wlr_renderer_scissor(renderer, NULL); - wlr_output_render_software_cursors(wlr_output, damage); - wlr_renderer_end(renderer); - - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - - enum wl_output_transform transform = - wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, &output->damage->current, - transform, width, height); - - if (debug.damage != DAMAGE_DEFAULT) { - pixman_region32_union_rect(&frame_damage, &frame_damage, - 0, 0, wlr_output->width, wlr_output->height); - } - - wlr_output_set_damage(wlr_output, &frame_damage); - pixman_region32_fini(&frame_damage); - - if (!wlr_output_commit(wlr_output)) { - return; - } - output->last_frame = *when; -} diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c deleted file mode 100644 index 1d7b536da..000000000 --- a/sway/desktop/surface.c +++ /dev/null @@ -1,46 +0,0 @@ -#define _POSIX_C_SOURCE 200112L -#include -#include -#include -#include "sway/server.h" -#include "sway/surface.h" - -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_surface *surface = wl_container_of(listener, surface, destroy); - - surface->wlr_surface->data = NULL; - wl_list_remove(&surface->destroy.link); - - if (surface->frame_done_timer) { - wl_event_source_remove(surface->frame_done_timer); - } - - free(surface); -} - -static int surface_frame_done_timer_handler(void *data) { - struct sway_surface *surface = data; - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(surface->wlr_surface, &now); - - return 0; -} - -void handle_compositor_new_surface(struct wl_listener *listener, void *data) { - struct wlr_surface *wlr_surface = data; - - struct sway_surface *surface = calloc(1, sizeof(struct sway_surface)); - surface->wlr_surface = wlr_surface; - wlr_surface->data = surface; - - surface->destroy.notify = handle_destroy; - wl_signal_add(&wlr_surface->events.destroy, &surface->destroy); - - surface->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, - surface_frame_done_timer_handler, surface); - if (!surface->frame_done_timer) { - wl_resource_post_no_memory(wlr_surface->resource); - } -} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index f5a3a053f..067ea0cff 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -5,7 +5,7 @@ #include #include #include "sway/config.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -214,39 +214,20 @@ static void transaction_add_node(struct sway_transaction *transaction, static void apply_output_state(struct sway_output *output, struct sway_output_state *state) { - output_damage_whole(output); list_free(output->current.workspaces); memcpy(&output->current, state, sizeof(struct sway_output_state)); - output_damage_whole(output); } static void apply_workspace_state(struct sway_workspace *ws, struct sway_workspace_state *state) { - output_damage_whole(ws->current.output); list_free(ws->current.floating); list_free(ws->current.tiling); memcpy(&ws->current, state, sizeof(struct sway_workspace_state)); - output_damage_whole(ws->current.output); } static void apply_container_state(struct sway_container *container, struct sway_container_state *state) { struct sway_view *view = container->view; - // Damage the old location - desktop_damage_whole_container(container); - if (view && !wl_list_empty(&view->saved_buffers)) { - struct sway_saved_buffer *saved_buf; - wl_list_for_each(saved_buf, &view->saved_buffers, link) { - struct wlr_box box = { - .x = saved_buf->x - view->saved_geometry.x, - .y = saved_buf->y - view->saved_geometry.y, - .width = saved_buf->width, - .height = saved_buf->height, - }; - desktop_damage_box(&box); - } - } - // There are separate children lists for each instruction state, the // container's current state and the container's pending state // (ie. con->children). The list itself needs to be freed here. @@ -256,35 +237,464 @@ static void apply_container_state(struct sway_container *container, memcpy(&container->current, state, sizeof(struct sway_container_state)); - if (view && !wl_list_empty(&view->saved_buffers)) { - if (!container->node.destroying || container->node.ntxnrefs == 1) { - view_remove_saved_buffer(view); + if (view) { + if (view->saved_surface_tree) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { + view_remove_saved_buffer(view); + } + } + + // If the view hasn't responded to the configure, center it within + // the container. This is important for fullscreen views which + // refuse to resize to the size of the output. + if (view->surface) { + view_center_surface(view); + } + } +} + +static void arrange_title_bar(struct sway_container *con, + int x, int y, int width, int height) { + container_update(con); + + wlr_scene_node_set_enabled(&con->title_bar.tree->node, height); + if (height) { + int titlebar_border_thickness = config->titlebar_border_thickness; + + wlr_scene_rect_set_size(con->title_bar.border, width, height); + wlr_scene_rect_set_size(con->title_bar.background, + width - titlebar_border_thickness * 2, + height - titlebar_border_thickness * 2); + + wlr_scene_node_set_position(&con->title_bar.tree->node, x, y); + wlr_scene_node_set_position(&con->title_bar.background->node, + titlebar_border_thickness, titlebar_border_thickness); + + con->title_width = width; + container_arrange_title_bar(con); + } +} + +static void disable_container(struct sway_container *con) { + if (con->view) { + wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); + } else { + for (int i = 0; i < con->current.children->length; i++) { + struct sway_container *child = con->current.children->items[i]; + + wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); + + disable_container(child); + } + } +} + +static void arrange_container(struct sway_container *con, + int width, int height, bool title_bar, int gaps); + +static bool arrange_children(enum sway_container_layout layout, list_t *children, + struct sway_container *active, struct wlr_scene_tree *content, + int width, int height, int gaps) { + int title_bar_height = container_titlebar_height(); + + if (layout == L_TABBED) { + struct sway_container *first = children->length == 1 ? + ((struct sway_container *)children->items[0]) : NULL; + if (config->hide_lone_tab && first && first->view && + first->current.border != B_NORMAL) { + title_bar_height = 0; + } + + double w = (double) width / children->length; + int title_offset = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + bool activated = child == active; + int next_title_offset = round(w * i + w); + + arrange_title_bar(child, title_offset, -title_bar_height, + next_title_offset - title_offset, title_bar_height); + wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); + wlr_scene_node_reparent(&child->scene_tree->node, content); + + if (activated) { + arrange_container(child, width, height - title_bar_height, + false, 0); + } else { + disable_container(child); + } + + title_offset = next_title_offset; + } + } else if (layout == L_STACKED) { + struct sway_container *first = children->length == 1 ? + ((struct sway_container *)children->items[0]) : NULL; + if (config->hide_lone_tab && first && first->view && + first->current.border != B_NORMAL) { + title_bar_height = 0; + } + + int title_height = title_bar_height * children->length; + + int y = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + bool activated = child == active; + + arrange_title_bar(child, 0, y - title_height, width, title_bar_height); + wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); + wlr_scene_node_reparent(&child->scene_tree->node, content); + + if (activated) { + arrange_container(child, width, height - title_height, + false, 0); + } else { + disable_container(child); + } + + y += title_bar_height; + } + } else if (layout == L_VERT) { + int off = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + int cheight = child->current.height; + + wlr_scene_node_set_enabled(&child->border.tree->node, true); + wlr_scene_node_set_position(&child->scene_tree->node, 0, off); + wlr_scene_node_reparent(&child->scene_tree->node, content); + arrange_container(child, width, cheight, true, gaps); + off += cheight + gaps; + } + } else if (layout == L_HORIZ) { + int off = 0; + for (int i = 0; i < children->length; i++) { + struct sway_container *child = children->items[i]; + int cwidth = child->current.width; + + wlr_scene_node_set_enabled(&child->border.tree->node, true); + wlr_scene_node_set_position(&child->scene_tree->node, off, 0); + wlr_scene_node_reparent(&child->scene_tree->node, content); + arrange_container(child, cwidth, height, true, gaps); + off += cwidth + gaps; + } + } else { + return false; + } + + return true; +} + +static void arrange_container(struct sway_container *con, + int width, int height, bool title_bar, int gaps) { + // this container might have previously been in the scratchpad, + // make sure it's enabled for viewing + wlr_scene_node_set_enabled(&con->scene_tree->node, true); + + if (con->output_handler) { + wlr_scene_buffer_set_dest_size(con->output_handler, width, height); + } + + if (con->view) { + int border_top = container_titlebar_height(); + int border_width = con->current.border_thickness; + + if (title_bar && con->current.border != B_NORMAL) { + wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); + wlr_scene_node_set_enabled(&con->border.top->node, true); + } else { + wlr_scene_node_set_enabled(&con->border.top->node, false); + } + + if (con->current.border == B_NORMAL) { + if (title_bar) { + arrange_title_bar(con, 0, 0, width, border_top); + } else { + border_top = 0; + // should be handled by the parent container + } + } else if (con->current.border == B_PIXEL) { + container_update(con); + border_top = title_bar && con->current.border_top ? border_width : 0; + } else if (con->current.border == B_NONE) { + container_update(con); + border_top = 0; + border_width = 0; + } else if (con->current.border == B_CSD) { + border_top = 0; + border_width = 0; + } else { + sway_assert(false, "unreachable"); + } + + int border_bottom = con->current.border_bottom ? border_width : 0; + int border_left = con->current.border_left ? border_width : 0; + int border_right = con->current.border_right ? border_width : 0; + + wlr_scene_rect_set_size(con->border.top, width, border_top); + wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); + wlr_scene_rect_set_size(con->border.left, + border_left, height - border_top - border_bottom); + wlr_scene_rect_set_size(con->border.right, + border_right, height - border_top - border_bottom); + + wlr_scene_node_set_position(&con->border.top->node, 0, 0); + wlr_scene_node_set_position(&con->border.bottom->node, + 0, height - border_bottom); + wlr_scene_node_set_position(&con->border.left->node, + 0, border_top); + wlr_scene_node_set_position(&con->border.right->node, + width - border_right, border_top); + + // make sure to reparent, it's possible that the client just came out of + // fullscreen mode where the parent of the surface is not the container + wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); + wlr_scene_node_set_position(&con->view->scene_tree->node, + border_left, border_top); + } else { + // make sure to disable the title bar if the parent is not managing it + if (title_bar) { + wlr_scene_node_set_enabled(&con->title_bar.tree->node, false); + } + + if (!arrange_children(con->current.layout, con->current.children, + con->current.focused_inactive_child, con->content_tree, + width, height, gaps)) { + sway_assert(false, "unreachable"); + } + } +} + +static int container_get_gaps(struct sway_container *con) { + struct sway_workspace *ws = con->current.workspace; + struct sway_container *temp = con; + while (temp) { + enum sway_container_layout layout; + if (temp->current.parent) { + layout = temp->current.parent->current.layout; + } else { + layout = ws->current.layout; + } + if (layout == L_TABBED || layout == L_STACKED) { + return 0; + } + temp = temp->pending.parent; + } + return ws->gaps_inner; +} + +static void arrange_fullscreen(struct wlr_scene_tree *tree, + struct sway_container *fs, struct sway_workspace *ws, + int width, int height) { + struct wlr_scene_node *fs_node; + if (fs->view) { + fs_node = &fs->view->scene_tree->node; + + // if we only care about the view, disable any decorations + wlr_scene_node_set_enabled(&fs->scene_tree->node, false); + } else { + fs_node = &fs->scene_tree->node; + arrange_container(fs, width, height, true, container_get_gaps(fs)); + } + + wlr_scene_node_reparent(fs_node, tree); + wlr_scene_node_lower_to_bottom(fs_node); + wlr_scene_node_set_position(fs_node, 0, 0); +} + +static void arrange_workspace_floating(struct sway_workspace *ws) { + for (int i = 0; i < ws->current.floating->length; i++) { + struct sway_container *floater = ws->current.floating->items[i]; + struct wlr_scene_tree *layer = root->layers.floating; + + if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { + continue; + } + + if (root->fullscreen_global) { + if (container_is_transient_for(floater, root->fullscreen_global)) { + layer = root->layers.fullscreen_global; + } + } else { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + struct sway_workspace *active = output->current.active_workspace; + + if (active && active->fullscreen && + container_is_transient_for(floater, active->fullscreen)) { + layer = root->layers.fullscreen; + } + } + } + + wlr_scene_node_reparent(&floater->scene_tree->node, layer); + wlr_scene_node_set_position(&floater->scene_tree->node, + floater->current.x, floater->current.y); + wlr_scene_node_set_enabled(&floater->scene_tree->node, true); + + arrange_container(floater, floater->current.width, floater->current.height, + true, ws->gaps_inner); + } +} + +static void arrange_workspace_tiling(struct sway_workspace *ws, + int width, int height) { + if (!arrange_children(ws->current.layout, ws->current.tiling, + ws->current.focused_inactive_child, ws->layers.tiling, + width, height, ws->gaps_inner)) { + sway_assert(false, "unreachable"); + } +} + +static void disable_workspace(struct sway_workspace *ws) { + // if any containers were just moved to a disabled workspace it will + // have the parent of the old workspace. Move the workspace so that it won't + // be shown. + for (int i = 0; i < ws->current.tiling->length; i++) { + struct sway_container *child = ws->current.tiling->items[i]; + + wlr_scene_node_reparent(&child->scene_tree->node, ws->layers.tiling); + disable_container(child); + } + + for (int i = 0; i < ws->current.floating->length; i++) { + struct sway_container *floater = ws->current.floating->items[i]; + wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); + disable_container(floater); + wlr_scene_node_set_enabled(&floater->scene_tree->node, false); + } +} + +static void arrange_output(struct sway_output *output, int width, int height) { + for (int i = 0; i < output->current.workspaces->length; i++) { + struct sway_workspace *child = output->current.workspaces->items[i]; + + bool activated = output->current.active_workspace == child; + + wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); + wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); + + for (int i = 0; i < child->current.floating->length; i++) { + struct sway_container *floater = child->current.floating->items[i]; + wlr_scene_node_reparent(&floater->scene_tree->node, root->layers.floating); + wlr_scene_node_set_enabled(&floater->scene_tree->node, activated); + } + + if (activated) { + struct sway_container *fs = child->current.fullscreen; + wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); + wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); + + arrange_workspace_floating(child); + + wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); + wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); + wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); + + if (fs) { + wlr_scene_rect_set_size(output->fullscreen_background, width, height); + + arrange_fullscreen(child->layers.fullscreen, fs, child, + width, height); + } else { + struct wlr_fbox *area = &output->usable_area; + struct side_gaps *gaps = &child->current_gaps; + + wlr_scene_node_set_position(&child->layers.tiling->node, + gaps->left + area->x, gaps->top + area->y); + + arrange_workspace_tiling(child, + area->width - gaps->left - gaps->right, + area->height - gaps->top - gaps->bottom); + } + } else { + wlr_scene_node_set_enabled(&child->layers.tiling->node, false); + wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); + + disable_workspace(child); + } + } +} + +static void arrange_popup(struct wlr_scene_tree *popup) { + struct wlr_scene_node *node; + wl_list_for_each(node, &popup->children, link) { + struct sway_scene_descriptor *desc = node->data; + + // the popup layer may have popups from layer_shell surfaces, in this + // case those don't have a scene descriptor, so lets skip those here. + if (desc) { + sway_assert(desc->type == SWAY_SCENE_DESC_POPUP, + "Corrupted scene tree: expected a popup node"); + struct sway_xdg_popup *popup = desc->data; + struct wlr_scene_tree *tree = popup->child.view->content_tree; + + double lx, ly; + wlr_scene_node_coords(&tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->child.scene_tree->node, lx, ly); + } + } +} + +static void arrange_root(struct sway_root *root) { + struct sway_container *fs = root->fullscreen_global; + + wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); + wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); + wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); + wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); + wlr_scene_node_set_enabled(&root->layers.shell_overlay->node, !fs); + + // hide all contents in the scratchpad + for (int i = 0; i < root->scratchpad->length; i++) { + struct sway_container *con = root->scratchpad->items[i]; + + wlr_scene_node_set_enabled(&con->scene_tree->node, false); + } + + if (fs) { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + struct sway_workspace *ws = output->current.active_workspace; + + if (ws) { + arrange_workspace_floating(ws); + } + } + + arrange_fullscreen(root->layers.fullscreen_global, fs, NULL, + root->width, root->height); + } else { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + + wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); + + wlr_scene_node_reparent(&output->layers.shell_background->node, root->layers.shell_background); + wlr_scene_node_reparent(&output->layers.shell_bottom->node, root->layers.shell_bottom); + wlr_scene_node_reparent(&output->layers.tiling->node, root->layers.tiling); + wlr_scene_node_reparent(&output->layers.shell_top->node, root->layers.shell_top); + wlr_scene_node_reparent(&output->layers.shell_overlay->node, root->layers.shell_overlay); + wlr_scene_node_reparent(&output->layers.fullscreen->node, root->layers.fullscreen); + wlr_scene_node_reparent(&output->layers.session_lock->node, root->layers.session_lock); + + wlr_scene_node_set_position(&output->layers.shell_background->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_bottom->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.tiling->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.fullscreen->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_top->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.shell_overlay->node, output->lx, output->ly); + wlr_scene_node_set_position(&output->layers.session_lock->node, output->lx, output->ly); + + arrange_output(output, output->width, output->height); } } - // If the view hasn't responded to the configure, center it within - // the container. This is important for fullscreen views which - // refuse to resize to the size of the output. - if (view && view->surface) { - view_center_surface(view); - } - - // Damage the new location - desktop_damage_whole_container(container); - if (view && view->surface) { - struct wlr_surface *surface = view->surface; - struct wlr_box box = { - .x = container->current.content_x - view->geometry.x, - .y = container->current.content_y - view->geometry.y, - .width = surface->current.width, - .height = surface->current.height, - }; - desktop_damage_box(&box); - } - - if (!container->node.destroying) { - container_discover_outputs(container); - } + arrange_popup(root->layers.popup); } /** @@ -326,8 +736,6 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - - cursor_rebase_all(); } static void transaction_commit_pending(void); @@ -340,6 +748,8 @@ static void transaction_progress(void) { return; } transaction_apply(server.queued_transaction); + arrange_root(root); + cursor_rebase_all(); transaction_destroy(server.queued_transaction); server.queued_transaction = NULL; @@ -415,21 +825,11 @@ static void transaction_commit(struct sway_transaction *transaction) { ++transaction->num_waiting; } - // From here on we are rendering a saved buffer of the view, which - // means we can send a frame done event to make the client redraw it - // as soon as possible. Additionally, this is required if a view is - // mapping and its default geometry doesn't intersect an output. - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done( - node->sway_container->view->surface, &now); + view_send_frame_done(node->sway_container->view); } if (!hidden && node_is_view(node) && - wl_list_empty(&node->sway_container->view->saved_buffers)) { + !node->sway_container->view->saved_surface_tree) { view_save_buffer(node->sway_container->view); - memcpy(&node->sway_container->view->saved_geometry, - &node->sway_container->view->geometry, - sizeof(struct wlr_box)); } node->instruction = instruction; } @@ -499,16 +899,18 @@ static void set_instruction_ready( transaction_progress(); } -void transaction_notify_view_ready_by_serial(struct sway_view *view, +bool transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial) { struct sway_transaction_instruction *instruction = view->container->node.instruction; if (instruction != NULL && instruction->serial == serial) { set_instruction_ready(instruction); + return true; } + return false; } -void transaction_notify_view_ready_by_geometry(struct sway_view *view, +bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height) { struct sway_transaction_instruction *instruction = view->container->node.instruction; @@ -518,7 +920,9 @@ void transaction_notify_view_ready_by_geometry(struct sway_view *view, instruction->container_state.content_width == width && instruction->container_state.content_height == height) { set_instruction_ready(instruction); + return true; } + return false; } static void _transaction_commit_dirty(bool server_request) { diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 8da922d50..0cec74561 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -7,7 +7,7 @@ #include #include "log.h" #include "sway/decoration.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -21,17 +21,6 @@ static const struct sway_view_child_impl popup_impl; -static void popup_get_view_coords(struct sway_view_child *child, - int *sx, int *sy) { - struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; - struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup; - - wlr_xdg_popup_get_toplevel_coords(wlr_popup, - wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, - wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, - sx, sy); -} - static void popup_destroy(struct sway_view_child *child) { if (!sway_assert(child->impl == &popup_impl, "Expected an xdg_shell popup")) { @@ -40,22 +29,25 @@ static void popup_destroy(struct sway_view_child *child) { struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->destroy.link); + + wlr_scene_node_destroy(&child->scene_tree->node); + free(popup); } static const struct sway_view_child_impl popup_impl = { - .get_view_coords = popup_get_view_coords, .destroy = popup_destroy, }; static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view); + struct wlr_xdg_popup *wlr_popup, struct sway_view *view, + struct wlr_scene_tree *parent); static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, popup->child.view); + popup_create(wlr_popup, popup->child.view, popup->child.xdg_surface_tree); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { @@ -71,7 +63,7 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { // the output box expressed in the coordinate system of the toplevel parent // of the popup - struct wlr_box output_toplevel_sx_box = { + struct wlr_fbox output_toplevel_sx_box = { .x = output->lx - view->container->pending.content_x + view->geometry.x, .y = output->ly - view->container->pending.content_y + view->geometry.y, .width = output->width, @@ -82,7 +74,8 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { } static struct sway_xdg_popup *popup_create( - struct wlr_xdg_popup *wlr_popup, struct sway_view *view) { + struct wlr_xdg_popup *wlr_popup, struct sway_view *view, + struct wlr_scene_tree *parent) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; struct sway_xdg_popup *popup = @@ -90,19 +83,30 @@ static struct sway_xdg_popup *popup_create( if (popup == NULL) { return NULL; } + + struct wlr_scene_tree *scene_tree = wlr_scene_tree_create(parent); + if (!scene_tree) { + free(popup); + return NULL; + } + + scene_descriptor_assign(&scene_tree->node, SWAY_SCENE_DESC_POPUP, popup); + view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); popup->wlr_xdg_popup = xdg_surface->popup; + struct sway_xdg_shell_view *shell_view = wl_container_of(view, shell_view, view); + xdg_surface->data = shell_view; + + popup->child.scene_tree = scene_tree; + popup->child.xdg_surface_tree = + wlr_scene_xdg_surface_create(scene_tree, xdg_surface); wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); - wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); - popup_unconstrain(popup); - return popup; } @@ -142,7 +146,7 @@ static const char *get_string_prop(struct sway_view *view, } static uint32_t configure(struct sway_view *view, double lx, double ly, - int width, int height) { + double width, double height) { struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); if (xdg_shell_view == NULL) { @@ -194,24 +198,6 @@ static bool wants_floating(struct sway_view *view) { || toplevel->parent; } -static void for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator, - user_data); -} - -static void for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (xdg_shell_view_from_view(view) == NULL) { - return; - } - wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, - iterator, user_data); -} - static bool is_transient_for(struct sway_view *child, struct sway_view *ancestor) { if (xdg_shell_view_from_view(child) == NULL) { @@ -259,8 +245,6 @@ static const struct sway_view_impl view_impl = { .set_fullscreen = set_fullscreen, .set_resizing = set_resizing, .wants_floating = wants_floating, - .for_each_surface = for_each_surface, - .for_each_popup_surface = for_each_popup_surface, .is_transient_for = is_transient_for, .close = _close, .close_popups = close_popups, @@ -273,7 +257,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base; - struct wlr_box new_geo; + struct wlr_fbox new_geo; wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); bool new_size = new_geo.width != view->geometry.width || new_geo.height != view->geometry.height || @@ -284,23 +268,27 @@ static void handle_commit(struct wl_listener *listener, void *data) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - desktop_damage_view(view); - memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + memcpy(&view->geometry, &new_geo, sizeof(struct wlr_fbox)); if (container_is_floating(view->container)) { view_update_size(view); transaction_commit_dirty_client(); } else { view_center_surface(view); } - desktop_damage_view(view); } if (view->container->node.instruction) { - transaction_notify_view_ready_by_serial(view, + bool successful = transaction_notify_view_ready_by_serial(view, xdg_surface->current.configure_serial); - } - view_damage_from(view); + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } + } } static void handle_set_title(struct wl_listener *listener, void *data) { @@ -322,7 +310,16 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, &xdg_shell_view->view); + + struct sway_xdg_popup *popup = popup_create(wlr_popup, + &xdg_shell_view->view, root->layers.popup); + if (!popup) { + return; + } + + double lx, ly; + wlr_scene_node_coords(&popup->child.view->content_tree->node, &lx, &ly); + wlr_scene_node_set_position(&popup->child.scene_tree->node, lx, ly); } static void handle_request_maximize(struct wl_listener *listener, void *data) { @@ -519,8 +516,10 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { return; } - view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); - xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; + if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) { + free(xdg_shell_view); + return; + } xdg_shell_view->map.notify = handle_map; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); @@ -531,5 +530,8 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { xdg_shell_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); + xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel; + wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_surface); + xdg_surface->data = xdg_shell_view; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 7c5dde530..0f88ca6e0 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -5,15 +5,16 @@ #include #include #include +#include #include #include #include "log.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -43,29 +44,12 @@ static void unmanaged_handle_request_configure(struct wl_listener *listener, ev->width, ev->height); } -static void unmanaged_handle_commit(struct wl_listener *listener, void *data) { - struct sway_xwayland_unmanaged *surface = - wl_container_of(listener, surface, commit); - struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - false); -} - static void unmanaged_handle_set_geometry(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, set_geometry); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - if (xsurface->x != surface->lx || xsurface->y != surface->ly) { - // Surface has moved - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - true); - surface->lx = xsurface->x; - surface->ly = xsurface->y; - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, - true); - } + wlr_scene_node_set_position(&surface->surface_scene->buffer->node, xsurface->x, xsurface->y); } static void unmanaged_handle_map(struct wl_listener *listener, void *data) { @@ -73,17 +57,18 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { wl_container_of(listener, surface, map); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); + surface->surface_scene = wlr_scene_surface_create(root->layers.unmanaged, + xsurface->surface); - wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); - surface->set_geometry.notify = unmanaged_handle_set_geometry; + if (surface->surface_scene) { + scene_descriptor_assign(&surface->surface_scene->buffer->node, + SWAY_SCENE_DESC_XWAYLAND_UNMANAGED, surface); + wlr_scene_node_set_position(&surface->surface_scene->buffer->node, + xsurface->x, xsurface->y); - wl_signal_add(&xsurface->surface->events.commit, &surface->commit); - surface->commit.notify = unmanaged_handle_commit; - - surface->lx = xsurface->x; - surface->ly = xsurface->y; - desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); + wl_signal_add(&xsurface->events.set_geometry, &surface->set_geometry); + surface->set_geometry.notify = unmanaged_handle_set_geometry; + } if (wlr_xwayland_or_surface_wants_focus(xsurface)) { struct sway_seat *seat = input_manager_current_seat(); @@ -97,10 +82,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, unmap); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - desktop_damage_surface(xsurface->surface, xsurface->x, xsurface->y, true); - wl_list_remove(&surface->link); - wl_list_remove(&surface->set_geometry.link); - wl_list_remove(&surface->commit.link); + + if (surface->surface_scene) { + wl_list_remove(&surface->set_geometry.link); + } struct sway_seat *seat = input_manager_current_seat(); if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { @@ -247,8 +232,8 @@ static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { } } -static uint32_t configure(struct sway_view *view, double lx, double ly, int width, - int height) { +static uint32_t configure(struct sway_view *view, double lx, double ly, + double width, double height) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); if (xwayland_view == NULL) { return 0; @@ -396,17 +381,6 @@ static const struct sway_view_impl view_impl = { .destroy = destroy, }; -static void get_geometry(struct sway_view *view, struct wlr_box *box) { - box->x = box->y = 0; - if (view->surface) { - box->width = view->surface->current.width; - box->height = view->surface->current.height; - } else { - box->width = 0; - box->height = 0; - } -} - static void handle_commit(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit); @@ -414,18 +388,17 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; struct wlr_surface_state *state = &xsurface->surface->current; - struct wlr_box new_geo; - get_geometry(view, &new_geo); + struct wlr_box new_geo = {0}; + new_geo.width = state->width; + new_geo.height = state->height; + bool new_size = new_geo.width != view->geometry.width || - new_geo.height != view->geometry.height || - new_geo.x != view->geometry.x || - new_geo.y != view->geometry.y; + new_geo.height != view->geometry.height; if (new_size) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - desktop_damage_view(view); memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); @@ -433,15 +406,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { } else { view_center_surface(view); } - desktop_damage_view(view); } if (view->container->node.instruction) { - transaction_notify_view_ready_by_geometry(view, + bool successful = transaction_notify_view_ready_by_geometry(view, xsurface->x, xsurface->y, state->width, state->height); - } - view_damage_from(view); + // If we saved the view and this commit isn't what we're looking for + // that means the user will never actually see the buffers submitted to + // us here. Just send frame done events to these surfaces so they can + // commit another time for us. + if (view->saved_surface_tree && !successful) { + view_send_frame_done(view); + } + } } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -486,6 +464,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { view_unmap(view); + xwayland_view->surface_scene = NULL; + wl_list_remove(&xwayland_view->commit.link); } @@ -506,6 +486,9 @@ static void handle_map(struct wl_listener *listener, void *data) { // Put it back into the tree view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); + xwayland_view->surface_scene = wlr_scene_surface_create( + xwayland_view->view.content_tree, xsurface->surface); + transaction_commit_dirty(); } @@ -712,7 +695,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu return NULL; } - view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); + if (!view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl)) { + free(xwayland_view); + return NULL; + } xwayland_view->view.wlr_xwayland_surface = xsurface; wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 449aa4300..bb996304e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -19,12 +19,12 @@ #include "log.h" #include "util.h" #include "sway/commands.h" -#include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" @@ -37,165 +37,88 @@ static uint32_t get_current_time_msec(void) { return now.tv_sec * 1000 + now.tv_nsec / 1000000; } -static struct wlr_surface *layer_surface_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub) { - return sub; - } - } - return NULL; -} - -static bool surface_is_xdg_popup(struct wlr_surface *surface) { - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; - } - return false; -} - -static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, - struct wl_list *layer, double ox, double oy, double *sx, double *sy) { - struct sway_layer_surface *sway_layer; - wl_list_for_each_reverse(sway_layer, layer, link) { - double _sx = ox - sway_layer->geo.x; - double _sy = oy - sway_layer->geo.y; - struct wlr_surface *sub = wlr_layer_surface_v1_surface_at( - sway_layer->layer_surface, _sx, _sy, sx, sy); - if (sub && surface_is_xdg_popup(sub)) { - return sub; - } - } - return NULL; -} - /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). */ struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - // find the output the cursor is on + struct wlr_surface **_surface, double *sx, double *sy) { + struct wlr_scene_node *scene_node = NULL; + size_t num_layers = sizeof(root->layers) / sizeof(struct wlr_scene_tree *); + for (int i = num_layers - 1; i >= 0 && !scene_node; i--) { + struct wlr_scene_tree *layer = ((struct wlr_scene_tree **) &root->layers)[i]; + struct sway_scene_descriptor *desc = layer->node.data; + + if (!desc || desc->type != SWAY_SCENE_DESC_NON_INTERACTIVE) { + scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy); + } + } + + if (scene_node) { + // determine what wlr_surface we clicked on + if (scene_node->type == WLR_SCENE_NODE_BUFFER) { + struct wlr_scene_buffer *scene_buffer = + wlr_scene_buffer_from_node(scene_node); + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_buffer(scene_buffer); + + if (scene_surface) { + *_surface = scene_surface->surface; + } + } + + // determine what container we clicked on + struct wlr_scene_node *current = scene_node; + while (true) { + if (current->data) { + struct sway_scene_descriptor *desc = current->data; + + if (desc->type == SWAY_SCENE_DESC_CONTAINER) { + struct sway_container *con = desc->data; + return &con->node; + } else if (desc->type == SWAY_SCENE_DESC_LAYER_SHELL) { + // We don't want to feed through the current workspace on + // layer shells + return NULL; + } else if (desc->type == SWAY_SCENE_DESC_POPUP) { + struct sway_xdg_popup *popup = desc->data; + struct sway_container *con = popup->child.view->container; + return &con->node; + } +#if HAVE_XWAYLAND + else if (desc->type == SWAY_SCENE_DESC_XWAYLAND_UNMANAGED) { + return NULL; + } +#endif + } + + if (!current->parent) { + break; + } + + current = ¤t->parent->node; + } + } + + // if we aren't on a container, determine what workspace we are on struct wlr_output *wlr_output = wlr_output_layout_output_at( root->output_layout, lx, ly); if (wlr_output == NULL) { return NULL; } + struct sway_output *output = wlr_output->data; if (!output || !output->enabled) { // output is being destroyed or is being enabled return NULL; } - double ox = lx, oy = ly; - wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); - // layer surfaces on the overlay layer are rendered on top - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], - ox, oy, sx, sy))) { - return NULL; - } - - // check for unmanaged views -#if HAVE_XWAYLAND - struct wl_list *unmanaged = &root->xwayland_unmanaged; - struct sway_xwayland_unmanaged *unmanaged_surface; - wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { - struct wlr_xwayland_surface *xsurface = - unmanaged_surface->wlr_xwayland_surface; - - double _sx = lx - unmanaged_surface->lx; - double _sy = ly - unmanaged_surface->ly; - if (wlr_surface_point_accepts_input(xsurface->surface, _sx, _sy)) { - *surface = xsurface->surface; - *sx = _sx; - *sy = _sy; - return NULL; - } - } -#endif - - if (root->fullscreen_global) { - // Try fullscreen container - struct sway_container *con = tiling_container_at( - &root->fullscreen_global->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - - // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); if (!ws) { return NULL; } - if (ws->fullscreen) { - // Try transient containers - for (int i = 0; i < ws->floating->length; ++i) { - struct sway_container *floater = ws->floating->items[i]; - if (container_is_transient_for(floater, ws->fullscreen)) { - struct sway_container *con = tiling_container_at( - &floater->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - } - } - // Try fullscreen container - struct sway_container *con = - tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy); - if (con) { - return &con->node; - } - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_popup_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], - ox, oy, sx, sy))) { - return NULL; - } - - struct sway_container *c; - if ((c = container_at(ws, lx, ly, surface, sx, sy))) { - return &c->node; - } - - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], - ox, oy, sx, sy))) { - return NULL; - } - if ((*surface = layer_surface_at(output, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], - ox, oy, sx, sy))) { - return NULL; - } - return &ws->node; } @@ -541,11 +464,13 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { seat->touch_x = lx; seat->touch_y = ly; - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } + struct wlr_scene_node *node; + wl_list_for_each(node, &seat->drag_icons->children, link) { + struct sway_scene_descriptor *desc = node->data; + + sway_assert(desc && desc->type == SWAY_SCENE_DESC_DRAG_ICON, + "Corrupted scene tree: expected a drag icon"); + drag_icon_update_position(seat, desc->data); } } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 26eefc8a1..4063a8452 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -293,7 +293,7 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) struct sway_input_manager *input_manager = wl_container_of( listener, input_manager, inhibit_deactivate); struct sway_seat *seat; - if (server.session_lock.locked) { + if (server.session_lock.lock) { // Don't deactivate the grab of a screenlocker return; } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 5e5692f19..08b10efa4 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -405,7 +405,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, bool exact_identifier = keyboard->wlr->group != NULL; seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); bool input_inhibited = seat->exclusive_client != NULL || - server.session_lock.locked; + server.session_lock.lock; struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; diff --git a/sway/input/seat.c b/sway/input/seat.c index 43b207799..b1be09085 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -18,7 +18,7 @@ #include "list.h" #include "log.h" #include "sway/config.h" -#include "sway/desktop.h" +#include "sway/scene_descriptor.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" @@ -92,6 +92,7 @@ void seat_destroy(struct sway_seat *seat) { for (int i = 0; i < seat->deferred_bindings->length; i++) { free_sway_binding(seat->deferred_bindings->items[i]); } + wlr_scene_node_destroy(&seat->scene_tree->node); list_free(seat->deferred_bindings); free(seat->prev_workspace_name); free(seat); @@ -365,25 +366,15 @@ static void handle_new_node(struct wl_listener *listener, void *data) { seat_node_from_node(seat, node); } -static void drag_icon_damage_whole(struct sway_drag_icon *icon) { - if (!icon->wlr_drag_icon->mapped) { - return; - } - desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); -} - -void drag_icon_update_position(struct sway_drag_icon *icon) { - drag_icon_damage_whole(icon); - +void drag_icon_update_position(struct sway_seat *seat, struct sway_drag_icon *icon) { struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; - struct sway_seat *seat = icon->seat; struct wlr_cursor *cursor = seat->cursor->cursor; + switch (wlr_icon->drag->grab_type) { case WLR_DRAG_GRAB_KEYBOARD: return; case WLR_DRAG_GRAB_KEYBOARD_POINTER: - icon->x = cursor->x + wlr_icon->surface->sx; - icon->y = cursor->y + wlr_icon->surface->sy; + wlr_scene_node_set_position(&icon->tree->node, cursor->x, cursor->y); break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -391,38 +382,23 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x + wlr_icon->surface->sx; - icon->y = seat->touch_y + wlr_icon->surface->sy; + wlr_scene_node_set_position(&icon->tree->node, seat->touch_x, seat->touch_y); } - - drag_icon_damage_whole(icon); } -static void drag_icon_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_drag_icon *icon = - wl_container_of(listener, icon, surface_commit); - drag_icon_update_position(icon); -} - -static void drag_icon_handle_map(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, map); - drag_icon_damage_whole(icon); -} - -static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { - struct sway_drag_icon *icon = wl_container_of(listener, icon, unmap); - drag_icon_damage_whole(icon); +static void drag_icon_handle_commit(struct wl_listener *listener, void *data) { + struct sway_drag_icon *icon = wl_container_of(listener, icon, commit); + struct wlr_surface *surface = icon->wlr_drag_icon->surface; + wlr_scene_node_set_position(&icon->surface_tree->node, + surface->sx, surface->sy); } static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); icon->wlr_drag_icon->data = NULL; - wl_list_remove(&icon->link); - wl_list_remove(&icon->surface_commit.link); - wl_list_remove(&icon->unmap.link); - wl_list_remove(&icon->map.link); + wl_list_remove(&icon->commit.link); wl_list_remove(&icon->destroy.link); + wlr_scene_node_destroy(&icon->tree->node); free(icon); } @@ -500,22 +476,40 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "Allocation failed"); return; } - icon->seat = seat; + + icon->tree = wlr_scene_tree_create(seat->drag_icons); + if (!icon->tree) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree"); + free(icon); + return; + } + + icon->surface_tree = wlr_scene_subsurface_tree_create(icon->tree, + wlr_drag_icon->surface); + if (!icon->surface_tree) { + sway_log(SWAY_ERROR, "Failed to allocate a drag icon surface"); + wlr_scene_node_destroy(&icon->tree->node); + free(icon); + return; + } + + scene_descriptor_assign(&icon->tree->node, SWAY_SCENE_DESC_DRAG_ICON, icon); + if (!icon->tree->node.data) { + wlr_scene_node_destroy(&icon->tree->node); + free(icon); + return; + } + icon->wlr_drag_icon = wlr_drag_icon; wlr_drag_icon->data = icon; - icon->surface_commit.notify = drag_icon_handle_surface_commit; - wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); - icon->unmap.notify = drag_icon_handle_unmap; - wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); - icon->map.notify = drag_icon_handle_map; - wl_signal_add(&wlr_drag_icon->events.map, &icon->map); + icon->commit.notify = drag_icon_handle_commit; + wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->commit); + icon->destroy.notify = drag_icon_handle_destroy; wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); - wl_list_insert(&root->drag_icons, &icon->link); - - drag_icon_update_position(icon); + drag_icon_update_position(seat, icon); } seatop_begin_default(seat); } @@ -562,8 +556,18 @@ struct sway_seat *seat_create(const char *seat_name) { return NULL; } + bool alloc_failure = false; + seat->scene_tree = alloc_scene_tree(root->layers.seat, &alloc_failure); + seat->drag_icons = alloc_scene_tree(seat->scene_tree, &alloc_failure); + if (alloc_failure) { + wlr_scene_node_destroy(&seat->scene_tree->node); + free(seat); + return NULL; + } + seat->wlr_seat = wlr_seat_create(server.wl_display, seat_name); if (!sway_assert(seat->wlr_seat, "could not allocate seat")) { + wlr_scene_node_destroy(&seat->scene_tree->node); free(seat); return NULL; } @@ -571,6 +575,7 @@ struct sway_seat *seat_create(const char *seat_name) { seat->cursor = sway_cursor_create(seat); if (!seat->cursor) { + wlr_scene_node_destroy(&seat->scene_tree->node); wlr_seat_destroy(seat->wlr_seat); free(seat); return NULL; @@ -1075,7 +1080,7 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { struct wl_client *client = wl_resource_get_client(surface->resource); return seat->exclusive_client == client || - (seat->exclusive_client == NULL && !server.session_lock.locked); + (seat->exclusive_client == NULL && !server.session_lock.lock); } static void send_unfocus(struct sway_container *con, void *data) { @@ -1276,8 +1281,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } else { seat_set_workspace_focus(seat, node); } - if (server.session_lock.locked) { - seat_set_focus_surface(seat, server.session_lock.focused, false); + if (server.session_lock.lock) { + seat_set_focus_surface(seat, server.session_lock.lock->focused, false); } } @@ -1679,13 +1684,6 @@ void seatop_end(struct sway_seat *seat) { seat->seatop_impl = NULL; } -void seatop_render(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage) { - if (seat->seatop_impl->render) { - seat->seatop_impl->render(seat, output, damage); - } -} - bool seatop_allows_set_cursor(struct sway_seat *seat) { return seat->seatop_impl->allow_set_cursor; } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 84acefdf6..5eb3d8e8c 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -10,6 +10,7 @@ #include "sway/input/seat.h" #include "sway/input/tablet.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" @@ -53,6 +54,9 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { while (cont) { if (container_parent_layout(cont) == layout) { list_t *siblings = container_get_siblings(cont); + if (!siblings) { + return false; + } int index = list_find(siblings, cont); if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { return false; @@ -585,6 +589,17 @@ static void check_focus_follows_mouse(struct sway_seat *seat, } } +static void drag_icons_update_position(struct sway_seat *seat) { + struct wlr_scene_node *node; + wl_list_for_each(node, &seat->drag_icons->children, link) { + struct sway_scene_descriptor *desc = node->data; + + sway_assert(desc && desc->type == SWAY_SCENE_DESC_DRAG_ICON, + "Corrupted scene tree: expected drag icon"); + drag_icon_update_position(seat, desc->data); + } +} + static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_default_event *e = seat->seatop_data; struct sway_cursor *cursor = seat->cursor; @@ -608,12 +623,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); } - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + drag_icons_update_position(seat); e->previous_node = node; } @@ -643,12 +653,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); } - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } + drag_icons_update_position(seat); e->previous_node = node; } diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index ddcd4c53e..21d048ce2 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -1,6 +1,5 @@ #define _POSIX_C_SOURCE 200809L #include -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -39,9 +38,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_floating_event *e = seat->seatop_data; struct wlr_cursor *cursor = seat->cursor->cursor; - desktop_damage_whole_container(e->con); container_floating_move_to(e->con, cursor->x - e->dx, cursor->y - e->dy); - desktop_damage_whole_container(e->con); transaction_commit_dirty(); } diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 223c6c08c..c1abbdb8f 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -2,7 +2,6 @@ #include #include #include -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -24,28 +23,19 @@ struct seatop_move_tiling_event { struct sway_container *con; struct sway_node *target_node; enum wlr_edges target_edge; - struct wlr_box drop_box; double ref_lx, ref_ly; // cursor's x/y at start of op bool threshold_reached; bool split_target; bool insert_after_target; + struct wlr_scene_rect *indicator_rect; }; -static void handle_render(struct sway_seat *seat, - struct sway_output *output, pixman_region32_t *damage) { + +static void handle_end(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; - if (!e->threshold_reached) { - return; - } - if (e->target_node && node_get_output(e->target_node) == output) { - float color[4]; - memcpy(&color, config->border_colors.focused.indicator, - sizeof(float) * 4); - premultiply_alpha(color, 0.5); - struct wlr_box box; - memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); - scale_box(&box, output->wlr_output->scale); - render_rect(output, damage, &box, color); + if (e->indicator_rect) { + wlr_scene_node_destroy(&e->indicator_rect->node); + e->indicator_rect = NULL; } } @@ -67,6 +57,9 @@ static void handle_motion_prethreshold(struct sway_seat *seat) { // If the threshold has been exceeded, start the actual drag if ((cx - sx) * (cx - sx) + (cy - sy) * (cy - sy) > threshold) { + if (e->indicator_rect) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, true); + } e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } @@ -165,6 +158,13 @@ static bool split_titlebar(struct sway_node *node, struct sway_container *avoid, return false; } +static void update_indicator(struct seatop_move_tiling_event *e, struct wlr_box *box) { + if (e->indicator_rect) { + wlr_scene_node_set_position(&e->indicator_rect->node, box->x, box->y); + wlr_scene_rect_set_size(e->indicator_rect, box->width, box->height); + } +} + static void handle_motion_postthreshold(struct sway_seat *seat) { struct seatop_move_tiling_event *e = seat->seatop_data; e->split_target = false; @@ -173,8 +173,6 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { struct sway_cursor *cursor = seat->cursor; struct sway_node *node = node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - // Damage the old location - desktop_damage_box(&e->drop_box); if (!node) { // Eg. hovered over a layer surface such as swaybar @@ -187,8 +185,10 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Empty workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; - workspace_get_box(node->sway_workspace, &e->drop_box); - desktop_damage_box(&e->drop_box); + + struct wlr_box drop_box; + workspace_get_box(node->sway_workspace, &drop_box); + update_indicator(e, &drop_box); return; } @@ -201,11 +201,18 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { return; } + struct wlr_box drop_box = { + .x = con->pending.content_x, + .y = con->pending.content_y, + .width = con->pending.content_width, + .height = con->pending.content_height, + }; + // Check if the cursor is over a tilebar only if the destination // container is not a descendant of the source container. if (!surface && !container_has_ancestor(con, e->con) && split_titlebar(node, e->con, cursor->cursor, - &e->drop_box, &e->insert_after_target)) { + &drop_box, &e->insert_after_target)) { // Don't allow dropping over the source container's titlebar // to give users a chance to cancel a drag operation. if (con == e->con) { @@ -215,6 +222,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->split_target = true; } e->target_edge = WLR_EDGE_NONE; + update_indicator(e, &drop_box); return; } @@ -256,8 +264,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { e->target_node = node_get_parent(e->target_node); } e->target_edge = edge; - e->drop_box = box; - desktop_damage_box(&e->drop_box); + update_indicator(e, &box); return; } con = con->pending.parent; @@ -299,12 +306,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } e->target_node = node; - e->drop_box.x = con->pending.content_x; - e->drop_box.y = con->pending.content_y; - e->drop_box.width = con->pending.content_width; - e->drop_box.height = con->pending.content_height; - resize_box(&e->drop_box, e->target_edge, thickness); - desktop_damage_box(&e->drop_box); + resize_box(&drop_box, e->target_edge, thickness); + update_indicator(e, &drop_box); } static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { @@ -439,7 +442,7 @@ static const struct sway_seatop_impl seatop_impl = { .pointer_motion = handle_pointer_motion, .tablet_tool_tip = handle_tablet_tool_tip, .unref = handle_unref, - .render = handle_render, + .end = handle_end, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, @@ -451,6 +454,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, if (!e) { return; } + + const float *indicator = config->border_colors.focused.indicator; + const float color[] = { + indicator[0] * .5, + indicator[1] * .5, + indicator[2] * .5, + indicator[3] * .5 + }; + e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, + (const float *) &color); + if (e->indicator_rect) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, false); + } + e->con = con; e->ref_lx = seat->cursor->cursor->x; e->ref_ly = seat->cursor->cursor->y; @@ -468,6 +485,9 @@ void seatop_begin_move_tiling(struct sway_seat *seat, seatop_begin_move_tiling_threshold(seat, con); struct seatop_move_tiling_event *e = seat->seatop_data; if (e) { + if (e->indicator_rect) { + wlr_scene_node_set_enabled(&e->indicator_rect->node, true); + } e->threshold_reached = true; cursor_set_image(seat->cursor, "grab", NULL); } diff --git a/sway/input/switch.c b/sway/input/switch.c index fc7dfaff7..9001f0da8 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -36,7 +36,7 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, static void execute_binding(struct sway_switch *sway_switch) { struct sway_seat* seat = sway_switch->seat_device->sway_seat; bool input_inhibited = seat->exclusive_client != NULL || - server.session_lock.locked; + server.session_lock.lock; list_t *bindings = config->current_mode->switch_bindings; struct sway_switch_binding *matched_binding = NULL; diff --git a/sway/lock.c b/sway/lock.c index 3c7c06cf4..d2e990010 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,24 +1,35 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include "log.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "sway/output.h" #include "sway/server.h" -struct sway_session_lock_surface { - struct wlr_session_lock_surface_v1 *lock_surface; +struct sway_session_lock_output { + struct wlr_scene_tree *tree; + struct wlr_scene_rect *background; + struct sway_session_lock *lock; + struct sway_output *output; - struct wlr_surface *surface; - struct wl_listener map; + + struct wl_list link; // sway_session_lock::outputs + struct wl_listener destroy; - struct wl_listener surface_commit; - struct wl_listener output_mode; - struct wl_listener output_commit; + struct wl_listener commit; + struct wl_listener mode; + + struct wlr_session_lock_surface_v1 *surface; + + // invalid if surface is NULL + struct wl_listener surface_destroy; + struct wl_listener surface_map; }; -static void set_lock_focused_surface(struct wlr_surface *focused) { - server.session_lock.focused = focused; +static void focus_surface(struct sway_session_lock *lock, + struct wlr_surface *focused) { + lock->focused = focused; struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { @@ -26,99 +37,198 @@ static void set_lock_focused_surface(struct wlr_surface *focused) { } } -static void handle_surface_map(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); - if (server.session_lock.focused == NULL) { - set_lock_focused_surface(surf->surface); +static void refocus_output(struct sway_session_lock_output *output) { + // Move the seat focus to another surface if one is available + if (output->lock->focused == output->surface->surface) { + struct wlr_surface *next_focus = NULL; + + struct sway_session_lock_output *candidate; + wl_list_for_each(candidate, &output->lock->outputs, link) { + if (candidate == output || !candidate->surface) { + continue; + } + + if (candidate->surface->mapped) { + next_focus = candidate->surface->surface; + break; + } + } + + focus_surface(output->lock, next_focus); } - output_damage_whole(surf->output); } -static void handle_surface_commit(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); - output_damage_surface(surf->output, 0, 0, surf->surface, false); -} - -static void handle_output_mode(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode); - wlr_session_lock_surface_v1_configure(surf->lock_surface, - surf->output->width, surf->output->height); -} - -static void handle_output_commit(struct wl_listener *listener, void *data) { - struct wlr_output_event_commit *event = data; - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); - if (event->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_SCALE | - WLR_OUTPUT_STATE_TRANSFORM)) { - wlr_session_lock_surface_v1_configure(surf->lock_surface, - surf->output->width, surf->output->height); +static void handle_surface_map(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); + if (surf->lock->focused == NULL) { + focus_surface(surf->lock, surf->surface->surface); } } static void handle_surface_destroy(struct wl_listener *listener, void *data) { - struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); + struct sway_session_lock_output *output = + wl_container_of(listener, output, surface_destroy); + refocus_output(output); - // Move the seat focus to another surface if one is available - if (server.session_lock.focused == surf->surface) { - struct wlr_surface *next_focus = NULL; + sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); + output->surface = NULL; + wl_list_remove(&output->surface_destroy.link); + wl_list_remove(&output->surface_map.link); +} - struct wlr_session_lock_surface_v1 *other; - wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { - if (other != surf->lock_surface && other->mapped) { - next_focus = other->surface; - break; - } - } - set_lock_focused_surface(next_focus); +static void lock_output_reconfigure(struct sway_session_lock_output *output) { + int width = output->output->width; + int height = output->output->height; + + wlr_scene_rect_set_size(output->background, width, height); + + if (output->surface) { + wlr_session_lock_surface_v1_configure(output->surface, width, height); } - - wl_list_remove(&surf->map.link); - wl_list_remove(&surf->destroy.link); - wl_list_remove(&surf->surface_commit.link); - wl_list_remove(&surf->output_mode.link); - wl_list_remove(&surf->output_commit.link); - output_damage_whole(surf->output); - free(surf); } static void handle_new_surface(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); struct wlr_session_lock_surface_v1 *lock_surface = data; - struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); - if (surf == NULL) { - return; - } + struct sway_output *output = lock_surface->output->data; sway_log(SWAY_DEBUG, "new lock layer surface"); - struct sway_output *output = lock_surface->output->data; - wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); + struct sway_session_lock_output *current_lock_output, *lock_output = NULL; + wl_list_for_each(current_lock_output, &lock->outputs, link) { + if (current_lock_output->output == output) { + lock_output = current_lock_output; + break; + } + } + sway_assert(lock_output, "Couldn't find output to lock"); + sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); - surf->lock_surface = lock_surface; - surf->surface = lock_surface->surface; - surf->output = output; - surf->map.notify = handle_surface_map; - wl_signal_add(&lock_surface->events.map, &surf->map); - surf->destroy.notify = handle_surface_destroy; - wl_signal_add(&lock_surface->events.destroy, &surf->destroy); - surf->surface_commit.notify = handle_surface_commit; - wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); - surf->output_mode.notify = handle_output_mode; - wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode); - surf->output_commit.notify = handle_output_commit; - wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); + lock_output->surface = lock_surface; + + wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); + + lock_output->surface_destroy.notify = handle_surface_destroy; + wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); + lock_output->surface_map.notify = handle_surface_map; + wl_signal_add(&lock_surface->events.map, &lock_output->surface_map); + + lock_output_reconfigure(lock_output); +} + +static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { + if (output->surface) { + refocus_output(output); + wl_list_remove(&output->surface_destroy.link); + wl_list_remove(&output->surface_map.link); + } + + wl_list_remove(&output->mode.link); + wl_list_remove(&output->commit.link); + wl_list_remove(&output->destroy.link); + wl_list_remove(&output->link); + + free(output); +} + +static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *output = + wl_container_of(listener, output, destroy); + sway_session_lock_output_destroy(output); +} + +static void lock_output_handle_mode(struct wl_listener *listener, void *data) { + struct sway_session_lock_output *output = + wl_container_of(listener, output, mode); + lock_output_reconfigure(output); +} + +static void lock_output_handle_commit(struct wl_listener *listener, void *data) { + struct wlr_output_event_commit *event = data; + struct sway_session_lock_output *output = + wl_container_of(listener, output, commit); + if (event->committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_SCALE | + WLR_OUTPUT_STATE_TRANSFORM)) { + lock_output_reconfigure(output); + } +} + +static struct sway_session_lock_output *session_lock_output_create( + struct sway_session_lock *lock, struct sway_output *output) { + struct sway_session_lock_output *lock_output = + calloc(1, sizeof(struct sway_session_lock_output)); + if (!lock_output) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output"); + return NULL; + } + + struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); + if (!tree) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); + free(lock_output); + return NULL; + } + + float *color = (float[4]){ 0.f, 0.f, 0.f, 1.f }; + if (lock->abandoned) { + color[0] = 1.f; + } + + struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, color); + if (!background) { + sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); + wlr_scene_node_destroy(&tree->node); + free(lock_output); + return NULL; + } + + lock_output->output = output; + lock_output->tree = tree; + lock_output->background = background; + lock_output->lock = lock; + + lock_output->destroy.notify = lock_node_handle_destroy; + wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); + + lock_output->commit.notify = lock_output_handle_commit; + wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); + lock_output->mode.notify = lock_output_handle_mode; + wl_signal_add(&output->wlr_output->events.mode, &lock_output->mode); + + lock_output_reconfigure(lock_output); + + wl_list_insert(&lock->outputs, &lock_output->link); + + return lock_output; +} + +static void sway_session_lock_destroy(struct sway_session_lock* lock) { + struct sway_session_lock_output *lock_output, *tmp_lock_output; + wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { + // destroying the node will also destroy the whole lock output + wlr_scene_node_destroy(&lock_output->tree->node); + } + + if (server.session_lock.lock == lock) { + server.session_lock.lock = NULL; + } + + if (!lock->abandoned) { + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); + } + + free(lock); } static void handle_unlock(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); sway_log(SWAY_DEBUG, "session unlocked"); - server.session_lock.locked = false; - server.session_lock.lock = NULL; - server.session_lock.focused = NULL; - wl_list_remove(&server.session_lock.lock_new_surface.link); - wl_list_remove(&server.session_lock.lock_unlock.link); - wl_list_remove(&server.session_lock.lock_destroy.link); + sway_session_lock_destroy(lock); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { @@ -131,33 +241,27 @@ static void handle_unlock(struct wl_listener *listener, void *data) { seat_set_focus(seat, previous); } } - - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } } static void handle_abandon(struct wl_listener *listener, void *data) { + struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); sway_log(SWAY_INFO, "session lock abandoned"); - server.session_lock.lock = NULL; - server.session_lock.focused = NULL; - - wl_list_remove(&server.session_lock.lock_new_surface.link); - wl_list_remove(&server.session_lock.lock_unlock.link); - wl_list_remove(&server.session_lock.lock_destroy.link); struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat->exclusive_client = NULL; } - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &lock->outputs, link) { + wlr_scene_rect_set_color(lock_output->background, + (float[4]){ 1.f, 0.f, 0.f, 1.f }); } + + lock->abandoned = true; + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); } static void handle_session_lock(struct wl_listener *listener, void *data) { @@ -165,44 +269,78 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { struct wl_client *client = wl_resource_get_client(lock->resource); if (server.session_lock.lock) { + if (server.session_lock.lock->abandoned) { + sway_log(SWAY_INFO, "Replacing abandoned lock"); + sway_session_lock_destroy(server.session_lock.lock); + } else { + sway_log(SWAY_ERROR, "Cannot lock an already locked session"); + wlr_session_lock_v1_destroy(lock); + return; + } + } + + struct sway_session_lock *sway_lock = + calloc(1, sizeof(struct sway_session_lock)); + if (!sway_lock) { + sway_log(SWAY_ERROR, "failed to allocate a session lock object"); wlr_session_lock_v1_destroy(lock); return; } + wl_list_init(&sway_lock->outputs); + sway_log(SWAY_DEBUG, "session locked"); - server.session_lock.locked = true; - server.session_lock.lock = lock; struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat_set_exclusive_client(seat, client); } - wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); - wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); - wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + sway_session_lock_add_output(sway_lock, output); + } + + sway_lock->new_surface.notify = handle_new_surface; + wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); + sway_lock->unlock.notify = handle_unlock; + wl_signal_add(&lock->events.unlock, &sway_lock->unlock); + sway_lock->destroy.notify = handle_abandon; + wl_signal_add(&lock->events.destroy, &sway_lock->destroy); wlr_session_lock_v1_send_locked(lock); - - // redraw everything - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole(output); - } + server.session_lock.lock = sway_lock; } static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { - assert(server.session_lock.lock == NULL); + // if the server shuts down while a lock is active, destroy the lock + if (server.session_lock.lock) { + sway_session_lock_destroy(server.session_lock.lock); + } + wl_list_remove(&server.session_lock.new_lock.link); wl_list_remove(&server.session_lock.manager_destroy.link); + + server.session_lock.manager = NULL; +} + +void sway_session_lock_add_output(struct sway_session_lock *lock, + struct sway_output *output) { + struct sway_session_lock_output *lock_output = + session_lock_output_create(lock, output); + + // if we run out of memory while trying to lock the screen, the best we + // can do is kill the sway process. Security conscious users will have + // the sway session fall back to a login shell. + if (!lock_output) { + sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); + abort(); + } } void sway_session_lock_init(void) { server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); - server.session_lock.lock_new_surface.notify = handle_new_surface; - server.session_lock.lock_unlock.notify = handle_unlock; - server.session_lock.lock_destroy.notify = handle_abandon; server.session_lock.new_lock.notify = handle_session_lock; server.session_lock.manager_destroy.notify = handle_session_lock_destroy; wl_signal_add(&server.session_lock.manager->events.new_lock, diff --git a/sway/main.c b/sway/main.c index 85bc2f1c9..513d35651 100644 --- a/sway/main.c +++ b/sway/main.c @@ -192,11 +192,7 @@ void restore_nofile_limit(void) { } void enable_debug_flag(const char *flag) { - if (strcmp(flag, "damage=highlight") == 0) { - debug.damage = DAMAGE_HIGHLIGHT; - } else if (strcmp(flag, "damage=rerender") == 0) { - debug.damage = DAMAGE_RERENDER; - } else if (strcmp(flag, "noatomic") == 0) { + if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; } else if (strcmp(flag, "txn-wait") == 0) { debug.txn_wait = true; @@ -204,8 +200,6 @@ void enable_debug_flag(const char *flag) { debug.txn_timings = true; } else if (strncmp(flag, "txn-timeout=", 12) == 0) { server.txn_timeout_ms = atoi(&flag[12]); - } else if (strcmp(flag, "noscanout") == 0) { - debug.noscanout = true; } else { sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); } @@ -381,6 +375,8 @@ int main(int argc, char **argv) { return 1; } + wlr_scene_set_presentation(root->root_scene, server.presentation); + if (validate) { bool valid = load_main_config(config_path, false, true); free(config_path); diff --git a/sway/meson.build b/sway/meson.build index 4d7dccfa8..089570fab 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -8,17 +8,16 @@ sway_sources = files( 'lock.c', 'main.c', 'realtime.c', + 'scene_descriptor.c', 'server.c', + 'sway_text_buffer.c', 'swaynag.c', 'xdg_activation_v1.c', 'xdg_decoration.c', - 'desktop/desktop.c', 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', - 'desktop/render.c', - 'desktop/surface.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c new file mode 100644 index 000000000..18710dbd7 --- /dev/null +++ b/sway/scene_descriptor.c @@ -0,0 +1,33 @@ +#include +#include "log.h" +#include "sway/scene_descriptor.h" + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_scene_descriptor *desc = + wl_container_of(listener, desc, destroy); + + desc->node->data = NULL; + wl_list_remove(&desc->destroy.link); + + free(desc); +} + +void scene_descriptor_assign(struct wlr_scene_node *node, + enum sway_scene_descriptor_type type, void *data) { + struct sway_scene_descriptor *desc = + calloc(1, sizeof(struct sway_scene_descriptor)); + + if (!desc) { + sway_log(SWAY_ERROR, "Could not allocate a scene descriptor"); + return; + } + + desc->type = type; + desc->data = data; + desc->node = node; + + desc->destroy.notify = handle_destroy; + wl_signal_add(&node->events.destroy, &desc->destroy); + + node->data = desc; +} diff --git a/sway/server.c b/sway/server.c index f67207557..1804a38c8 100644 --- a/sway/server.c +++ b/sway/server.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -97,9 +98,6 @@ bool server_init(struct sway_server *server) { server->compositor = wlr_compositor_create(server->wl_display, server->renderer); - server->compositor_new_surface.notify = handle_compositor_new_surface; - wl_signal_add(&server->compositor->events.new_surface, - &server->compositor_new_surface); wlr_subcompositor_create(server->wl_display); @@ -204,6 +202,8 @@ bool server_init(struct sway_server *server) { wlr_viewporter_create(server->wl_display); wlr_single_pixel_buffer_manager_v1_create(server->wl_display); + wlr_fractional_scale_manager_v1_create(server->wl_display); + struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); diff --git a/sway/sway_text_buffer.c b/sway/sway_text_buffer.c new file mode 100644 index 000000000..85ee4233c --- /dev/null +++ b/sway/sway_text_buffer.c @@ -0,0 +1,340 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include "cairo_util.h" +#include "pango.h" +#include "sway/sway_text_buffer.h" +#include "sway/config.h" +#include "log.h" + +struct cairo_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; + cairo_t *cairo; +}; + +static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + + cairo_surface_destroy(buffer->surface); + cairo_destroy(buffer->cairo); + free(buffer); +} + +static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, void **data, uint32_t *format, size_t *stride) { + struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base); + *data = cairo_image_surface_get_data(buffer->surface); + *stride = cairo_image_surface_get_stride(buffer->surface); + *format = DRM_FORMAT_ARGB8888; + return true; +} + +static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // This space is intentionally left blank +} + +static const struct wlr_buffer_impl cairo_buffer_impl = { + .destroy = cairo_buffer_handle_destroy, + .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access, + .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access, +}; + +struct text_buffer { + struct wlr_scene_buffer *buffer_node; + char *text; + struct sway_text_node props; + + float scale; + enum wl_output_subpixel subpixel; + + struct wl_list outputs; // text_buffer_output.link + + struct wl_listener output_enter; + struct wl_listener output_leave; + struct wl_listener destroy; +}; + +struct text_buffer_output { + struct wl_list link; + struct wlr_output *output; + struct text_buffer *text_buffer; + + struct wl_listener commit; +}; + +static int get_text_width(struct sway_text_node *props) { + if (props->max_width) { + return MIN(props->max_width, props->width); + } + + return props->width; +} + +static void update_source_box(struct text_buffer *buffer) { + struct sway_text_node *props = &buffer->props; + struct wlr_fbox source_box = { + .x = 0, + .y = 0, + .width = ceil(get_text_width(props) * buffer->scale), + .height = ceil(props->height * buffer->scale), + }; + + wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); +} + +static void render_backing_buffer(struct text_buffer *buffer) { + float scale = buffer->scale; + int width = ceil(buffer->props.width * scale); + int height = ceil(buffer->props.height * scale); + float *color = (float *) &buffer->props.color; + PangoContext *pango = NULL; + + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); + enum wl_output_subpixel subpixel = buffer->subpixel; + if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); + } else { + cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel)); + } + + cairo_surface_t *surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, width, height); + cairo_status_t status = cairo_surface_status(surface); + if (status != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", + cairo_status_to_string(status)); + goto err; + } + + cairo_t *cairo = cairo_create(surface); + cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); + cairo_set_font_options(cairo, fo); + pango = pango_cairo_create_context(cairo); + cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]); + cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale); + + render_text(cairo, config->font_description, scale, buffer->props.pango_markup, + "%s", buffer->text); + + cairo_surface_flush(surface); + + struct cairo_buffer *cairo_buffer = calloc(1, sizeof(struct cairo_buffer)); + wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height); + cairo_buffer->surface = surface; + cairo_buffer->cairo = cairo; + + wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); + wlr_buffer_drop(&cairo_buffer->base); + update_source_box(buffer); + +err: + if (pango) g_object_unref(pango); + cairo_font_options_destroy(fo); +} + +static void ensure_backing_buffer(struct text_buffer *buffer) { + float scale = 0; + enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + + struct text_buffer_output *output; + wl_list_for_each(output, &buffer->outputs, link) { + if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) { + subpixel = output->output->subpixel; + } else if (subpixel != output->output->subpixel) { + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } + + if (scale != 0 && scale != output->output->scale) { + // drop down to gray scale if we encounter outputs with different + // scales or else we will have chromatic aberations + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } + + if (scale < output->output->scale) { + scale = output->output->scale; + } + } + + // no outputs + if (scale == 0) { + return; + } + + if (scale != buffer->scale || subpixel != buffer->subpixel) { + buffer->scale = scale; + buffer->subpixel = subpixel; + render_backing_buffer(buffer); + } +} + +static void handle_output_commit(struct wl_listener *listener, void *data) { + struct text_buffer_output *output = wl_container_of(listener, output, commit); + struct wlr_output_event_commit *event = data; + + if (event->committed & (WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_SUBPIXEL)) { + ensure_backing_buffer(output->text_buffer); + } +} + +static void handle_output_enter(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, output_enter); + struct wlr_scene_output *output = data; + struct text_buffer_output *buffer_output = + calloc(1, sizeof(struct text_buffer_output)); + if (!buffer_output) { + return; + } + + buffer_output->text_buffer = buffer; + + buffer_output->commit.notify = handle_output_commit; + wl_signal_add(&output->output->events.commit, &buffer_output->commit); + + buffer_output->output = output->output; + wl_list_insert(&buffer->outputs, &buffer_output->link); + ensure_backing_buffer(buffer); +} + +static void text_buffer_output_destroy(struct text_buffer_output *output) { + if (!output) { + return; + } + + wl_list_remove(&output->link); + wl_list_remove(&output->commit.link); + free(output); +} + +static struct text_buffer_output *get_text_output_from_wlr_output( + struct text_buffer *buffer, struct wlr_output *wlr_output) { + struct text_buffer_output *output; + wl_list_for_each(output, &buffer->outputs, link) { + if (output->output == wlr_output) { + return output; + } + } + return NULL; +} + +static void handle_output_leave(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, output_leave); + struct wlr_scene_output *scene_output = data; + + struct text_buffer_output *output = get_text_output_from_wlr_output( + buffer, scene_output->output); + text_buffer_output_destroy(output); + ensure_backing_buffer(buffer); +} + +static void handle_destroy(struct wl_listener *listener, void *data) { + struct text_buffer *buffer = wl_container_of(listener, buffer, destroy); + + wl_list_remove(&buffer->output_enter.link); + wl_list_remove(&buffer->output_leave.link); + wl_list_remove(&buffer->destroy.link); + + struct text_buffer_output *output, *tmp_output; + wl_list_for_each_safe(output, tmp_output, &buffer->outputs, link) { + text_buffer_output_destroy(output); + } + + free(buffer->text); + free(buffer); +} + +static void text_calc_size(struct text_buffer *buffer) { + struct sway_text_node *props = &buffer->props; + + cairo_t *c = cairo_create(NULL); + cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); + get_text_size(c, config->font_description, &props->width, NULL, + &props->baseline, 1, props->pango_markup, "%s", buffer->text); + cairo_destroy(c); + + wlr_scene_buffer_set_dest_size(buffer->buffer_node, + get_text_width(props), props->height); +} + +struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, + char *text, const float *color, bool pango_markup) { + struct text_buffer *buffer = calloc(1, sizeof(struct text_buffer)); + if (buffer == NULL) { + return NULL; + } + + struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL); + if (!node) { + free(buffer); + return NULL; + } + + buffer->buffer_node = node; + buffer->props.node = &node->node; + buffer->text = strdup(text); + if (!buffer->text) { + free(buffer); + wlr_scene_node_destroy(&node->node); + return NULL; + } + + wl_list_init(&buffer->outputs); + + buffer->props.height = config->font_height; + buffer->props.pango_markup = pango_markup; + memcpy(&buffer->props.color, color, sizeof(float) * 4); + + buffer->destroy.notify = handle_destroy; + wl_signal_add(&node->node.events.destroy, &buffer->destroy); + buffer->output_enter.notify = handle_output_enter; + wl_signal_add(&node->events.output_enter, &buffer->output_enter); + buffer->output_leave.notify = handle_output_leave; + wl_signal_add(&node->events.output_leave, &buffer->output_leave); + + text_calc_size(buffer); + + return &buffer->props; +} + +void sway_text_node_set_color(struct sway_text_node *node, const float *color) { + if (memcmp(&node->color, color, sizeof(float) * 4) == 0) { + return; + } + + memcpy(&node->color, color, sizeof(float) * 4); + struct text_buffer *buffer = wl_container_of(node, buffer, props); + + render_backing_buffer(buffer); +} + +void sway_text_node_set_text(struct sway_text_node *node, char *text) { + struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (strcmp(buffer->text, text) == 0) { + return; + } + + char *new_text = strdup(text); + if (!new_text) { + return; + } + + free(buffer->text); + buffer->text = new_text; + + text_calc_size(buffer); + render_backing_buffer(buffer); +} + +void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { + struct text_buffer *buffer = wl_container_of(node, buffer, props); + buffer->props.max_width = max_width; + wlr_scene_buffer_set_dest_size(buffer->buffer_node, + get_text_width(&buffer->props), buffer->props.height); + update_source_box(buffer); +} diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 9c1a11e55..6ce1bdd64 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -259,8 +259,8 @@ void arrange_workspace(struct sway_workspace *workspace) { return; } struct sway_output *output = workspace->output; - struct wlr_box *area = &output->usable_area; - sway_log(SWAY_DEBUG, "Usable area for ws: %dx%d@%d,%d", + struct wlr_fbox *area = &output->usable_area; + sway_log(SWAY_DEBUG, "Usable area for ws: %lfx%lf@%lf,%lf", area->width, area->height, area->x, area->y); bool first_arrange = workspace->width == 0 && workspace->height == 0; diff --git a/sway/tree/container.c b/sway/tree/container.c index 718dd0e0d..18533c026 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -5,21 +5,23 @@ #include #include #include +#include +#include #include +#include #include #include #include #include #include #include "linux-dmabuf-unstable-v1-protocol.h" -#include "cairo_util.h" -#include "pango.h" #include "sway/config.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/scene_descriptor.h" +#include "sway/sway_text_buffer.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -30,6 +32,52 @@ #include "log.h" #include "stringop.h" +static void handle_output_enter( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_enter); + struct wlr_scene_output *output = data; + + if (con->view->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_output_enter( + con->view->foreign_toplevel, output->output); + } +} + +static void handle_output_leave( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_leave); + struct wlr_scene_output *output = data; + + if (con->view->foreign_toplevel) { + wlr_foreign_toplevel_handle_v1_output_leave( + con->view->foreign_toplevel, output->output); + } +} + +static bool handle_point_accepts_input( + struct wlr_scene_buffer *buffer, double x, double y) { + return false; +} + +static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, + bool *failed) { + if (*failed) { + return NULL; + } + + // just pass in random values. These can and will be overwritten when + // they need to be used. + struct wlr_scene_rect *rect = wlr_scene_rect_create( + parent, 0, 0, (float[4]){0.f, 0.f, 0.f, 1}); + if (!rect) { + *failed = true; + } + + return rect; +} + struct sway_container *container_create(struct sway_view *view) { struct sway_container *c = calloc(1, sizeof(struct sway_container)); if (!c) { @@ -37,23 +85,323 @@ struct sway_container *container_create(struct sway_view *view) { return NULL; } node_init(&c->node, N_CONTAINER, c); - c->pending.layout = L_NONE; - c->view = view; - c->alpha = 1.0f; - if (!view) { + // Container tree structure + // - scene tree + // - title bar + // - border + // - background + // - title text + // - marks text + // - border + // - border top/bottom/left/right + // - content_tree (we put the content node here so when we disable the + // border everything gets disabled. We only render the content iff there + // is a border as well) + // - buffer used for output enter/leave events for foreign_toplevel + bool alloc_failure = false; + c->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); + + c->title_bar.tree = alloc_scene_tree(c->scene_tree, &alloc_failure); + c->title_bar.border = alloc_rect_node(c->title_bar.tree, &alloc_failure); + c->title_bar.background = alloc_rect_node(c->title_bar.tree, &alloc_failure); + + c->border.tree = alloc_scene_tree(c->scene_tree, &alloc_failure); + c->content_tree = alloc_scene_tree(c->border.tree, &alloc_failure); + + if (view) { + // only containers with views can have borders + c->border.top = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.bottom = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.left = alloc_rect_node(c->border.tree, &alloc_failure); + c->border.right = alloc_rect_node(c->border.tree, &alloc_failure); + + c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); + if (!c->output_handler) { + alloc_failure = true; + } + + if (!alloc_failure) { + c->output_enter.notify = handle_output_enter; + wl_signal_add(&c->output_handler->events.output_enter, + &c->output_enter); + c->output_leave.notify = handle_output_leave; + wl_signal_add(&c->output_handler->events.output_leave, + &c->output_leave); + c->output_handler->point_accepts_input = handle_point_accepts_input; + } + } else { c->pending.children = create_list(); c->current.children = create_list(); } + + scene_descriptor_assign(&c->scene_tree->node, SWAY_SCENE_DESC_CONTAINER, c); + if (!c->scene_tree->node.data) { + alloc_failure = true; + } + + // also assign the scene descriptor to the view. On fullscreen clients + // the container will not be part of the scene graph, only the view itself + // So to not confuse input logic, we need to assign this. + if (view) { + scene_descriptor_assign(&view->scene_tree->node, SWAY_SCENE_DESC_CONTAINER, c); + if (!view->scene_tree->node.data) { + alloc_failure = true; + } + } + + if (alloc_failure) { + wlr_scene_node_destroy(&c->scene_tree->node); + free(c); + return NULL; + } + + c->pending.layout = L_NONE; + c->view = view; + c->alpha = 1.0f; c->marks = create_list(); - c->outputs = create_list(); wl_signal_init(&c->events.destroy); wl_signal_emit_mutable(&root->events.new_node, &c->node); + container_update(c); + return c; } +static bool container_is_focused(struct sway_container *con, void *data) { + return con->current.focused; +} + +static bool container_has_focused_child(struct sway_container *con) { + return container_find_child(con, container_is_focused, NULL); +} + +static bool container_is_current_parent_focused(struct sway_container *con) { + if (con->current.parent) { + struct sway_container *parent = con->current.parent; + return parent->current.focused || container_is_current_parent_focused(parent); + } else if (con->current.workspace) { + struct sway_workspace *ws = con->current.workspace; + return ws->current.focused; + } + + return false; +} + +static struct border_colors *container_get_current_colors( + struct sway_container *con) { + struct border_colors *colors; + + bool urgent = con->view ? + view_is_urgent(con->view) : container_has_urgent_child(con); + struct sway_container *active_child; + + if (con->current.parent) { + active_child = con->current.parent->current.focused_inactive_child; + } else if (con->current.workspace) { + active_child = con->current.workspace->current.focused_inactive_child; + } else { + active_child = NULL; + } + + if (urgent) { + colors = &config->border_colors.urgent; + } else if (con->current.focused || container_is_current_parent_focused(con)) { + colors = &config->border_colors.focused; + } else if (config->has_focused_tab_title && container_has_focused_child(con)) { + colors = &config->border_colors.focused_tab_title; + } else if (con == active_child) { + colors = &config->border_colors.focused_inactive; + } else { + colors = &config->border_colors.unfocused; + } + + return colors; +} + +static bool container_is_current_floating(struct sway_container *container) { + if (!container->current.parent && container->current.workspace && + list_find(container->current.workspace->floating, container) != -1) { + return true; + } + if (container->scratchpad) { + return true; + } + return false; +} + +void container_update(struct sway_container *con) { + struct border_colors *colors = container_get_current_colors(con); + list_t *siblings = NULL; + enum sway_container_layout layout = L_NONE; + + if (con->current.parent) { + siblings = con->current.parent->current.children; + layout = con->current.parent->current.layout; + } else if (con->current.workspace) { + siblings = con->current.workspace->current.tiling; + layout = con->current.workspace->current.layout; + } + + const float *bottom, *right; + bottom = (const float *) colors->child_border; + right = (const float *) colors->child_border; + + if (!container_is_current_floating(con) && siblings && siblings->length == 1) { + if (layout == L_HORIZ) { + right = (const float *) colors->indicator; + } else if (layout == L_VERT) { + bottom = (const float *) colors->indicator; + } + } + + wlr_scene_rect_set_color(con->title_bar.border, (const float *) colors->border); + wlr_scene_rect_set_color(con->title_bar.background, (const float *) colors->background); + + if (con->view) { + wlr_scene_rect_set_color(con->border.top, (const float *) colors->child_border); + wlr_scene_rect_set_color(con->border.bottom, bottom); + wlr_scene_rect_set_color(con->border.left, (const float *) colors->child_border); + wlr_scene_rect_set_color(con->border.right, right); + } + + if (con->title_bar.title_text) { + sway_text_node_set_color(con->title_bar.title_text, colors->text); + } + + if (con->title_bar.marks_text) { + sway_text_node_set_color(con->title_bar.marks_text, colors->text); + } +} + +void container_update_itself_and_parents(struct sway_container *con) { + container_update(con); + + if (con->current.parent) { + container_update_itself_and_parents(con->current.parent); + } +} + +void container_arrange_title_bar(struct sway_container *con) { + enum alignment title_align = config->title_align; + int marks_buffer_width = 0; + int width = con->title_width; + int height = container_titlebar_height(); + + if (con->title_bar.marks_text) { + struct sway_text_node *node = con->title_bar.marks_text; + marks_buffer_width = node->width; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = config->titlebar_h_padding; + } else { + h_padding = width - config->titlebar_h_padding - marks_buffer_width; + } + + h_padding = MAX(h_padding, 0); + + int alloc_width = MIN((int) node->width, width - h_padding); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + } + + if (con->title_bar.title_text) { + struct sway_text_node *node = con->title_bar.title_text; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = width - config->titlebar_h_padding - node->width; + } else if (title_align == ALIGN_CENTER) { + h_padding = ((int) width - marks_buffer_width - node->width) >> 1; + } else { + h_padding = config->titlebar_h_padding; + } + + h_padding = MAX(h_padding, 0); + + int alloc_width = MIN((int) node->width, width - h_padding); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + } +} + +void container_update_marks(struct sway_container *con) { + char *buffer = NULL; + + if (config->show_marks && con->marks->length) { + size_t len = 0; + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + len += strlen(mark) + 2; + } + } + buffer = calloc(len + 1, 1); + char *part = malloc(len + 1); + + if (!sway_assert(buffer && part, "Unable to allocate memory")) { + free(buffer); + return; + } + + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + snprintf(part, len + 1, "[%s]", mark); + strcat(buffer, part); + } + } + free(part); + } + + if (!buffer) { + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + } else if (!con->title_bar.marks_text) { + struct border_colors *colors = container_get_current_colors(con); + + con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, + buffer, colors->text, false); + } else { + sway_text_node_set_text(con->title_bar.marks_text, buffer); + } + + container_arrange_title_bar(con); + free(buffer); +} + +void container_update_title_bar(struct sway_container *con) { + if (!con->formatted_title) { + return; + } + + struct border_colors *colors = container_get_current_colors(con); + + if (con->title_bar.title_text) { + wlr_scene_node_destroy(con->title_bar.title_text->node); + con->title_bar.title_text = NULL; + } + + con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, + con->formatted_title, colors->text, config->pango_markup); + + // we always have to remake these text buffers completely for text font + // changes etc... + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + + container_update_marks(con); + container_arrange_title_bar(con); +} + void container_destroy(struct sway_container *con) { if (!sway_assert(con->node.destroying, "Tried to free container which wasn't marked as destroying")) { @@ -65,29 +413,21 @@ void container_destroy(struct sway_container *con) { } free(con->title); free(con->formatted_title); - wlr_texture_destroy(con->title_focused); - wlr_texture_destroy(con->title_focused_inactive); - wlr_texture_destroy(con->title_unfocused); - wlr_texture_destroy(con->title_urgent); - wlr_texture_destroy(con->title_focused_tab_title); list_free(con->pending.children); list_free(con->current.children); - list_free(con->outputs); list_free_items_and_destroy(con->marks); - wlr_texture_destroy(con->marks_focused); - wlr_texture_destroy(con->marks_focused_inactive); - wlr_texture_destroy(con->marks_unfocused); - wlr_texture_destroy(con->marks_urgent); - wlr_texture_destroy(con->marks_focused_tab_title); if (con->view && con->view->container == con) { con->view->container = NULL; + wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); } } + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); free(con); } @@ -174,265 +514,6 @@ struct sway_container *container_find_child(struct sway_container *container, return NULL; } -static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(con->view, "Expected a view")) { - return NULL; - } - struct sway_view *view = con->view; - double view_sx = lx - con->surface_x + view->geometry.x; - double view_sy = ly - con->surface_y + view->geometry.y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - switch (view->type) { -#if HAVE_XWAYLAND - case SWAY_VIEW_XWAYLAND: - _surface = wlr_surface_surface_at(view->surface, - view_sx, view_sy, &_sx, &_sy); - break; -#endif - case SWAY_VIEW_XDG_SHELL: - _surface = wlr_xdg_surface_surface_at( - view->wlr_xdg_toplevel->base, - view_sx, view_sy, &_sx, &_sy); - break; - } - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return con; - } - return NULL; -} - -/** - * container_at for a container with layout L_TABBED. - */ -static struct sway_container *container_at_tabbed(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - if (!children->length) { - return NULL; - } - - // Tab titles - int title_height = container_titlebar_height(); - if (ly < box.y + title_height) { - int tab_width = box.width / children->length; - int child_index = (lx - box.x) / tab_width; - if (child_index >= children->length) { - child_index = children->length - 1; - } - struct sway_container *child = children->items[child_index]; - return child; - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_STACKED. - */ -static struct sway_container *container_at_stacked(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct wlr_box box; - node_get_box(parent, &box); - if (lx < box.x || lx > box.x + box.width || - ly < box.y || ly > box.y + box.height) { - return NULL; - } - struct sway_seat *seat = input_manager_current_seat(); - list_t *children = node_get_children(parent); - - // Title bars - int title_height = container_titlebar_height(); - if (title_height > 0) { - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; - } - } - - // Surfaces - struct sway_node *current = seat_get_active_tiling_child(seat, parent); - return current ? tiling_container_at(current, lx, ly, surface, sx, sy) : NULL; -} - -/** - * container_at for a container with layout L_HORIZ or L_VERT. - */ -static struct sway_container *container_at_linear(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - list_t *children = node_get_children(parent); - for (int i = 0; i < children->length; ++i) { - struct sway_container *child = children->items[i]; - struct sway_container *container = - tiling_container_at(&child->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - return NULL; -} - -static struct sway_container *floating_container_at(double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - // For outputs with floating containers that overhang the output bounds, - // those at the end of the output list appear on top of floating - // containers from other outputs, so iterate the list in reverse. - for (int i = root->outputs->length - 1; i >= 0; --i) { - struct sway_output *output = root->outputs->items[i]; - for (int j = 0; j < output->workspaces->length; ++j) { - struct sway_workspace *ws = output->workspaces->items[j]; - if (!workspace_is_visible(ws)) { - continue; - } - // Items at the end of the list are on top, so iterate the list in - // reverse. - for (int k = ws->floating->length - 1; k >= 0; --k) { - struct sway_container *floater = ws->floating->items[k]; - struct sway_container *container = - tiling_container_at(&floater->node, lx, ly, surface, sx, sy); - if (container) { - return container; - } - } - } - } - return NULL; -} - -static struct sway_container *view_container_content_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.content_x, - .y = container->pending.content_y, - .width = container->pending.content_width, - .height = container->pending.content_height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -static struct sway_container *view_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(node_is_view(parent), "Expected a view")) { - return NULL; - } - - struct sway_container *container = parent->sway_container; - struct wlr_box box = { - .x = container->pending.x, - .y = container->pending.y, - .width = container->pending.width, - .height = container->pending.height, - }; - - if (wlr_box_contains_point(&box, lx, ly)) { - surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); - return container; - } - - return NULL; -} - -struct sway_container *tiling_container_at(struct sway_node *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (node_is_view(parent)) { - return view_container_at(parent, lx, ly, surface, sx, sy); - } - if (!node_get_children(parent)) { - return NULL; - } - switch (node_get_layout(parent)) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_NONE: - return NULL; - } - return NULL; -} - -static bool surface_is_popup(struct wlr_surface *surface) { - while (!wlr_surface_is_xdg_surface(surface)) { - if (!wlr_surface_is_subsurface(surface)) { - return false; - } - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - surface = subsurface->parent; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; -} - -struct sway_container *container_at(struct sway_workspace *workspace, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - struct sway_container *c; - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_container *focus = seat_get_focused_container(seat); - bool is_floating = focus && container_is_floating_or_child(focus); - // Focused view's popups - if (focus && focus->view) { - c = surface_at_view(focus, lx, ly, surface, sx, sy); - if (c && surface_is_popup(*surface)) { - return c; - } - *surface = NULL; - } - // Floating - if ((c = floating_container_at(lx, ly, surface ,sx ,sy))) { - return c; - } - // Tiling (focused) - if (focus && focus->view && !is_floating) { - if ((c = view_container_content_at(&focus->node, lx, ly, surface, sx, sy))) { - return c; - } - } - // Tiling (non-focused) - if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { - return c; - } - return NULL; -} - void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { @@ -478,127 +559,6 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } -void container_damage_whole(struct sway_container *container) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_whole_container(output, container); - } -} - -/** - * Return the output which will be used for scale purposes. - * This is the most recently entered output. - */ -struct sway_output *container_get_effective_output(struct sway_container *con) { - if (con->outputs->length == 0) { - return NULL; - } - return con->outputs->items[con->outputs->length - 1]; -} - -static void render_titlebar_text_texture(struct sway_output *output, - struct sway_container *con, struct wlr_texture **texture, - struct border_colors *class, bool pango_markup, char *text) { - double scale = output->wlr_output->scale; - int width = 0; - int height = config->font_height * scale; - int baseline; - - // We must use a non-nil cairo_t for cairo_set_font_options to work. - // Therefore, we cannot use cairo_create(NULL). - cairo_surface_t *dummy_surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, 0, 0); - cairo_t *c = cairo_create(dummy_surface); - cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); - cairo_font_options_t *fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); - if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); - } else { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_subpixel_order(fo, - to_cairo_subpixel_order(output->wlr_output->subpixel)); - } - cairo_set_font_options(c, fo); - get_text_size(c, config->font_description, &width, NULL, &baseline, scale, - config->pango_markup, "%s", text); - cairo_surface_destroy(dummy_surface); - cairo_destroy(c); - - if (width == 0 || height == 0) { - return; - } - - if (height > config->font_height * scale) { - height = config->font_height * scale; - } - - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, width, height); - cairo_status_t status = cairo_surface_status(surface); - if (status != CAIRO_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", - cairo_status_to_string(status)); - return; - } - - cairo_t *cairo = cairo_create(surface); - cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); - cairo_set_font_options(cairo, fo); - cairo_font_options_destroy(fo); - cairo_set_source_rgba(cairo, class->background[0], class->background[1], - class->background[2], class->background[3]); - cairo_paint(cairo); - PangoContext *pango = pango_cairo_create_context(cairo); - cairo_set_source_rgba(cairo, class->text[0], class->text[1], - class->text[2], class->text[3]); - cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - - render_text(cairo, config->font_description, scale, pango_markup, "%s", text); - - cairo_surface_flush(surface); - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_image_surface_get_stride(surface); - struct wlr_renderer *renderer = output->wlr_output->renderer; - *texture = wlr_texture_from_pixels( - renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} - -static void update_title_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->formatted_title) { - return; - } - - render_titlebar_text_texture(output, con, texture, class, - config->pango_markup, con->formatted_title); -} - -void container_update_title_textures(struct sway_container *container) { - update_title_texture(container, &container->title_focused, - &config->border_colors.focused); - update_title_texture(container, &container->title_focused_inactive, - &config->border_colors.focused_inactive); - update_title_texture(container, &container->title_unfocused, - &config->border_colors.unfocused); - update_title_texture(container, &container->title_urgent, - &config->border_colors.urgent); - update_title_texture(container, &container->title_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(container); -} - /** * Calculate and return the length of the tree representation. * An example tree representation is: V[Terminal, Firefox] @@ -664,7 +624,12 @@ void container_update_representation(struct sway_container *con) { } container_build_representation(con->pending.layout, con->pending.children, con->formatted_title); - container_update_title_textures(con); + + if (con->title_bar.title_text) { + sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); + } else { + container_update_title_bar(con); + } } if (con->pending.parent) { container_update_representation(con->pending.parent); @@ -935,17 +900,6 @@ bool container_is_floating(struct sway_container *container) { return false; } -bool container_is_current_floating(struct sway_container *container) { - if (!container->current.parent && container->current.workspace && - list_find(container->current.workspace->floating, container) != -1) { - return true; - } - if (container->scratchpad) { - return true; - } - return false; -} - void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->pending.x; box->y = container->pending.y; @@ -1319,72 +1273,6 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { return false; } -static void surface_send_enter_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_enter(surface, wlr_output); -} - -static void surface_send_leave_iterator(struct wlr_surface *surface, - int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_leave(surface, wlr_output); -} - -void container_discover_outputs(struct sway_container *con) { - struct wlr_box con_box = { - .x = con->current.x, - .y = con->current.y, - .width = con->current.width, - .height = con->current.height, - }; - struct sway_output *old_output = container_get_effective_output(con); - - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - struct wlr_box output_box; - output_get_box(output, &output_box); - struct wlr_box intersection; - bool intersects = - wlr_box_intersection(&intersection, &con_box, &output_box); - int index = list_find(con->outputs, output); - - if (intersects && index == -1) { - // Send enter - sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); - if (con->view) { - view_for_each_surface(con->view, - surface_send_enter_iterator, output->wlr_output); - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_enter( - con->view->foreign_toplevel, output->wlr_output); - } - } - list_add(con->outputs, output); - } else if (!intersects && index != -1) { - // Send leave - sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); - if (con->view) { - view_for_each_surface(con->view, - surface_send_leave_iterator, output->wlr_output); - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_leave( - con->view->foreign_toplevel, output->wlr_output); - } - } - list_del(con->outputs, index); - } - } - struct sway_output *new_output = container_get_effective_output(con); - double old_scale = old_output && old_output->enabled ? - old_output->wlr_output->scale : -1; - double new_scale = new_output ? new_output->wlr_output->scale : -1; - if (old_scale != new_scale) { - container_update_title_textures(con); - container_update_marks_textures(con); - } -} - enum sway_container_layout container_parent_layout(struct sway_container *con) { if (con->pending.parent) { return con->pending.parent->pending.layout; @@ -1395,14 +1283,6 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { return L_NONE; } -enum sway_container_layout container_current_parent_layout( - struct sway_container *con) { - if (con->current.parent) { - return con->current.parent->current.layout; - } - return con->current.workspace->current.layout; -} - list_t *container_get_siblings(struct sway_container *container) { if (container->pending.parent) { return container->pending.parent->pending.children; @@ -1410,6 +1290,9 @@ list_t *container_get_siblings(struct sway_container *container) { if (container_is_scratchpad_hidden(container)) { return NULL; } + if (!container->pending.workspace) { + return NULL; + } if (list_find(container->pending.workspace->tiling, container) != -1) { return container->pending.workspace->tiling; } @@ -1420,13 +1303,6 @@ int container_sibling_index(struct sway_container *child) { return list_find(container_get_siblings(child), child); } -list_t *container_get_current_siblings(struct sway_container *container) { - if (container->current.parent) { - return container->current.parent->current.children; - } - return container->current.workspace->current.tiling; -} - void container_handle_fullscreen_reparent(struct sway_container *con) { if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || con->pending.workspace->fullscreen == con) { @@ -1641,7 +1517,7 @@ bool container_find_and_unmark(char *mark) { if (strcmp(con_mark, mark) == 0) { free(con_mark); list_del(con->marks, i); - container_update_marks_textures(con); + container_update_marks(con); ipc_event_window(con, "mark"); return true; } @@ -1672,70 +1548,15 @@ void container_add_mark(struct sway_container *con, char *mark) { ipc_event_window(con, "mark"); } -static void update_marks_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->marks->length) { - return; - } - - size_t len = 0; - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - len += strlen(mark) + 2; - } - } - char *buffer = calloc(len + 1, 1); - char *part = malloc(len + 1); - - if (!sway_assert(buffer && part, "Unable to allocate memory")) { - free(buffer); - return; - } - - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - snprintf(part, len + 1, "[%s]", mark); - strcat(buffer, part); - } - } - free(part); - - render_titlebar_text_texture(output, con, texture, class, false, buffer); - - free(buffer); -} - -void container_update_marks_textures(struct sway_container *con) { - if (!config->show_marks) { - return; - } - update_marks_texture(con, &con->marks_focused, - &config->border_colors.focused); - update_marks_texture(con, &con->marks_focused_inactive, - &config->border_colors.focused_inactive); - update_marks_texture(con, &con->marks_unfocused, - &config->border_colors.unfocused); - update_marks_texture(con, &con->marks_urgent, - &config->border_colors.urgent); - update_marks_texture(con, &con->marks_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(con); -} - void container_raise_floating(struct sway_container *con) { // Bring container to front by putting it at the end of the floating list. struct sway_container *floater = container_toplevel_ancestor(con); if (container_is_floating(floater) && floater->pending.workspace) { + // it's okay to just raise the scene directly instead of waiting + // for the transaction to go through. We won't be reconfiguring + // surfaces + wlr_scene_node_raise_to_top(&floater->scene_tree->node); + list_move_to_end(floater->pending.workspace->floating, floater); node_set_dirty(&floater->pending.workspace->node); } diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa59..a08df5096 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -159,3 +159,26 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { } return false; } + +void scene_node_disown_children(struct wlr_scene_tree *tree) { + struct wlr_scene_node *child, *tmp_child; + wl_list_for_each_safe(child, tmp_child, &tree->children, link) { + wlr_scene_node_reparent(child, root->staging); + } +} + +struct wlr_scene_tree *alloc_scene_tree(struct wlr_scene_tree *parent, + bool *failed) { + // fallthrough + if (*failed) { + return NULL; + } + + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + if (!tree) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + *failed = true; + } + + return tree; +} diff --git a/sway/tree/output.c b/sway/tree/output.c index 368f0541c..362d02d28 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -88,10 +87,53 @@ static void restore_workspaces(struct sway_output *output) { output_sort_workspaces(output); } +static void destroy_scene_layers(struct sway_output *output) { + wlr_scene_node_destroy(&output->fullscreen_background->node); + + scene_node_disown_children(output->layers.tiling); + scene_node_disown_children(output->layers.fullscreen); + + size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *); + for (size_t i = 0; i < num_layers; i++) { + struct wlr_scene_tree *tree = + ((struct wlr_scene_tree **) &output->layers)[i]; + + if (tree) { + wlr_scene_node_destroy(&tree->node); + } + } +} + struct sway_output *output_create(struct wlr_output *wlr_output) { struct sway_output *output = calloc(1, sizeof(struct sway_output)); node_init(&output->node, N_OUTPUT, output); + + bool alloc_failure = false; + size_t num_layers = sizeof(output->layers) / sizeof(struct wlr_scene_node *); + for (size_t i = 0; i < num_layers; i++) { + ((struct wlr_scene_tree **) &output->layers)[i] = + alloc_scene_tree(root->staging, &alloc_failure); + } + + if (!alloc_failure) { + output->fullscreen_background = wlr_scene_rect_create( + output->layers.fullscreen, 0, 0, (float[4]){0., 0., 0., 1.}); + + if (!output->fullscreen_background) { + sway_log(SWAY_ERROR, "Unable to allocate a background rect"); + alloc_failure = true; + } + } + + if (alloc_failure) { + destroy_scene_layers(output); + wlr_scene_output_destroy(output->scene_output); + free(output); + return NULL; + } + output->wlr_output = wlr_output; + wlr_output->data = output; output->detected_subpixel = wlr_output->subpixel; output->scale_filter = SCALE_FILTER_NEAREST; @@ -103,11 +145,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { output->workspaces = create_list(); output->current.workspaces = create_list(); - size_t len = sizeof(output->layers) / sizeof(output->layers[0]); - for (size_t i = 0; i < len; ++i) { - wl_list_init(&output->layers[i]); - } - return output; } @@ -239,20 +276,14 @@ void output_destroy(struct sway_output *output) { "which is still referenced by transactions")) { return; } + + destroy_scene_layers(output); list_free(output->workspaces); list_free(output->current.workspaces); wl_event_source_remove(output->repaint_timer); free(output); } -static void untrack_output(struct sway_container *con, void *data) { - struct sway_output *output = data; - int index = list_find(con->outputs, output); - if (index != -1) { - list_del(con->outputs, index); - } -} - void output_disable(struct sway_output *output) { if (!sway_assert(output->enabled, "Expected an enabled output")) { return; @@ -267,8 +298,6 @@ void output_disable(struct sway_output *output) { output_evacuate(output); - root_for_each_container(untrack_output, output); - list_del(root->outputs, index); output->enabled = false; diff --git a/sway/tree/root.c b/sway/tree/root.c index 7df0b2378..28db0e997 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -3,10 +3,12 @@ #include #include #include +#include #include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scene_descriptor.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" @@ -29,13 +31,42 @@ struct sway_root *root_create(void) { sway_log(SWAY_ERROR, "Unable to allocate sway_root"); return NULL; } + + struct wlr_scene *root_scene = wlr_scene_create(); + if (!root_scene) { + sway_log(SWAY_ERROR, "Unable to allocate root scene node"); + free(root); + return NULL; + } + node_init(&root->node, N_ROOT, root); + root->root_scene = root_scene; + + bool alloc_failure = false; + root->staging = alloc_scene_tree(&root_scene->tree, &alloc_failure); + + size_t num_layers = sizeof(root->layers) / sizeof(struct wlr_scene_tree *); + for (size_t i = 0; i < num_layers; i++) { + ((struct wlr_scene_tree **) &root->layers)[i] = + alloc_scene_tree(&root_scene->tree, &alloc_failure); + } + + scene_descriptor_assign(&root->layers.seat->node, + SWAY_SCENE_DESC_NON_INTERACTIVE, NULL); + if (!root->layers.seat->node.data) { + alloc_failure = true; + } + + if (alloc_failure) { + wlr_scene_node_destroy(&root_scene->tree.node); + free(root); + return NULL; + } + + wlr_scene_node_set_enabled(&root->staging->node, false); + root->output_layout = wlr_output_layout_create(); wl_list_init(&root->all_outputs); -#if HAVE_XWAYLAND - wl_list_init(&root->xwayland_unmanaged); -#endif - wl_list_init(&root->drag_icons); wl_signal_init(&root->events.new_node); root->outputs = create_list(); root->non_desktop_outputs = create_list(); @@ -51,6 +82,7 @@ void root_destroy(struct sway_root *root) { wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); list_free(root->outputs); + wlr_scene_node_destroy(&root->root_scene->tree.node); wlr_output_layout_destroy(root->output_layout); free(root); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 589a3f7ef..e2dfeb146 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -16,7 +16,6 @@ #include "log.h" #include "sway/criteria.h" #include "sway/commands.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/cursor.h" @@ -24,6 +23,7 @@ #include "sway/output.h" #include "sway/input/seat.h" #include "sway/server.h" +#include "sway/sway_text_buffer.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -33,15 +33,23 @@ #include "pango.h" #include "stringop.h" -void view_init(struct sway_view *view, enum sway_view_type type, +bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { + bool alloc_failure = false; + view->scene_tree = alloc_scene_tree(root->staging, &alloc_failure); + view->content_tree = alloc_scene_tree(view->scene_tree, &alloc_failure); + if (alloc_failure) { + wlr_scene_node_destroy(&view->scene_tree->node); + return false; + } + view->type = type; view->impl = impl; view->executed_criteria = create_list(); - wl_list_init(&view->saved_buffers); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; wl_signal_init(&view->events.unmap); + return true; } void view_destroy(struct sway_view *view) { @@ -58,11 +66,9 @@ void view_destroy(struct sway_view *view) { return; } wl_list_remove(&view->events.unmap.listener_list); - if (!wl_list_empty(&view->saved_buffers)) { - view_remove_saved_buffer(view); - } list_free(view->executed_criteria); + wlr_scene_node_destroy(&view->scene_tree->node); free(view->title_format); if (view->impl->destroy) { @@ -164,8 +170,8 @@ void view_get_constraints(struct sway_view *view, double *min_width, } } -uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, - int height) { +uint32_t view_configure(struct sway_view *view, double lx, double ly, + double width, double height) { if (view->impl->configure) { return view->impl->configure(view, lx, ly, width, height); } @@ -433,52 +439,6 @@ void view_close_popups(struct sway_view *view) { } } -void view_damage_from(struct sway_view *view) { - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - output_damage_from_view(output, view); - } -} - -void view_for_each_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_surface) { - view->impl->for_each_surface(view, iterator, user_data); - } else { - wlr_surface_for_each_surface(view->surface, iterator, user_data); - } -} - -void view_for_each_popup_surface(struct sway_view *view, - wlr_surface_iterator_func_t iterator, void *user_data) { - if (!view->surface) { - return; - } - if (view->impl->for_each_popup_surface) { - view->impl->for_each_popup_surface(view, iterator, user_data); - } -} - -static void view_subsurface_create(struct sway_view *view, - struct wlr_subsurface *subsurface); - -static void view_init_subsurfaces(struct sway_view *view, - struct wlr_surface *surface); - -static void view_child_init_subsurfaces(struct sway_view_child *view_child, - struct wlr_surface *surface); - -static void view_handle_surface_new_subsurface(struct wl_listener *listener, - void *data) { - struct sway_view *view = - wl_container_of(listener, view, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - view_subsurface_create(view, subsurface); -} - static bool view_has_executed_criteria(struct sway_view *view, struct criteria *criteria) { for (int i = 0; i < view->executed_criteria->length; ++i) { @@ -777,11 +737,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } ipc_event_window(view->container, "new"); - view_init_subsurfaces(view, wlr_surface); - wl_signal_add(&wlr_surface->events.new_subsurface, - &view->surface_new_subsurface); - view->surface_new_subsurface.notify = view_handle_surface_new_subsurface; - if (decoration) { view_update_csd_from_client(view, decoration); } @@ -849,8 +804,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, void view_unmap(struct sway_view *view) { wl_signal_emit_mutable(&view->events.unmap, view); - wl_list_remove(&view->surface_new_subsurface.link); - if (view->urgent_timer) { wl_event_source_remove(view->urgent_timer); view->urgent_timer = NULL; @@ -906,187 +859,10 @@ void view_center_surface(struct sway_view *view) { struct sway_container *con = view->container; // We always center the current coordinates rather than the next, as the // geometry immediately affects the currently active rendering. - con->surface_x = fmax(con->current.content_x, con->current.content_x + - (con->current.content_width - view->geometry.width) / 2); - con->surface_y = fmax(con->current.content_y, con->current.content_y + - (con->current.content_height - view->geometry.height) / 2); -} + int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); + int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); -static const struct sway_view_child_impl subsurface_impl; - -static void subsurface_get_view_coords(struct sway_view_child *child, - int *sx, int *sy) { - struct wlr_surface *surface = child->surface; - if (child->parent && child->parent->impl && - child->parent->impl->get_view_coords) { - child->parent->impl->get_view_coords(child->parent, sx, sy); - } else { - *sx = *sy = 0; - } - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - *sx += subsurface->current.x; - *sy += subsurface->current.y; -} - -static void subsurface_destroy(struct sway_view_child *child) { - if (!sway_assert(child->impl == &subsurface_impl, - "Expected a subsurface")) { - return; - } - struct sway_subsurface *subsurface = (struct sway_subsurface *)child; - wl_list_remove(&subsurface->destroy.link); - free(subsurface); -} - -static const struct sway_view_child_impl subsurface_impl = { - .get_view_coords = subsurface_get_view_coords, - .destroy = subsurface_destroy, -}; - -static void subsurface_handle_destroy(struct wl_listener *listener, - void *data) { - struct sway_subsurface *subsurface = - wl_container_of(listener, subsurface, destroy); - struct sway_view_child *child = &subsurface->child; - view_child_destroy(child); -} - -static void view_child_damage(struct sway_view_child *child, bool whole); - -static void view_subsurface_create(struct sway_view *view, - struct wlr_subsurface *wlr_subsurface) { - struct sway_subsurface *subsurface = - calloc(1, sizeof(struct sway_subsurface)); - if (subsurface == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - view_child_init(&subsurface->child, &subsurface_impl, view, - wlr_subsurface->surface); - - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->destroy.notify = subsurface_handle_destroy; - - subsurface->child.mapped = true; - - view_child_damage(&subsurface->child, true); -} - -static void view_child_subsurface_create(struct sway_view_child *child, - struct wlr_subsurface *wlr_subsurface) { - struct sway_subsurface *subsurface = - calloc(1, sizeof(struct sway_subsurface)); - if (subsurface == NULL) { - sway_log(SWAY_ERROR, "Allocation failed"); - return; - } - subsurface->child.parent = child; - wl_list_insert(&child->children, &subsurface->child.link); - view_child_init(&subsurface->child, &subsurface_impl, child->view, - wlr_subsurface->surface); - - wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - subsurface->destroy.notify = subsurface_handle_destroy; - - subsurface->child.mapped = true; - - view_child_damage(&subsurface->child, true); -} - -static bool view_child_is_mapped(struct sway_view_child *child) { - while (child) { - if (!child->mapped) { - return false; - } - child = child->parent; - } - return true; -} - -static void view_child_damage(struct sway_view_child *child, bool whole) { - if (!child || !view_child_is_mapped(child) || !child->view || !child->view->container) { - return; - } - int sx, sy; - child->impl->get_view_coords(child, &sx, &sy); - desktop_damage_surface(child->surface, - child->view->container->pending.content_x - - child->view->geometry.x + sx, - child->view->container->pending.content_y - - child->view->geometry.y + sy, whole); -} - -static void view_child_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_commit); - view_child_damage(child, false); -} - -static void view_child_handle_surface_new_subsurface( - struct wl_listener *listener, void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_new_subsurface); - struct wlr_subsurface *subsurface = data; - view_child_subsurface_create(child, subsurface); -} - -static void view_child_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_destroy); - view_child_destroy(child); -} - -static void view_init_subsurfaces(struct sway_view *view, - struct wlr_surface *surface) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - view_subsurface_create(view, subsurface); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - view_subsurface_create(view, subsurface); - } -} - -static void view_child_init_subsurfaces(struct sway_view_child *view_child, - struct wlr_surface *surface) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - view_child_subsurface_create(view_child, subsurface); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - view_child_subsurface_create(view_child, subsurface); - } -} - -static void view_child_handle_surface_map(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_map); - child->mapped = true; - view_child_damage(child, true); -} - -static void view_child_handle_surface_unmap(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, surface_unmap); - view_child_damage(child, true); - child->mapped = false; -} - -static void view_child_handle_view_unmap(struct wl_listener *listener, - void *data) { - struct sway_view_child *child = - wl_container_of(listener, child, view_unmap); - view_child_damage(child, true); - child->mapped = false; + wlr_scene_node_set_position(&view->content_tree->node, x, y); } void view_child_init(struct sway_view_child *child, @@ -1096,41 +872,9 @@ void view_child_init(struct sway_view_child *child, child->view = view; child->surface = surface; wl_list_init(&child->children); - - wl_signal_add(&surface->events.commit, &child->surface_commit); - child->surface_commit.notify = view_child_handle_surface_commit; - wl_signal_add(&surface->events.new_subsurface, - &child->surface_new_subsurface); - child->surface_new_subsurface.notify = - view_child_handle_surface_new_subsurface; - wl_signal_add(&surface->events.destroy, &child->surface_destroy); - child->surface_destroy.notify = view_child_handle_surface_destroy; - - // Not all child views have a map/unmap event - child->surface_map.notify = view_child_handle_surface_map; - wl_list_init(&child->surface_map.link); - child->surface_unmap.notify = view_child_handle_surface_unmap; - wl_list_init(&child->surface_unmap.link); - - wl_signal_add(&view->events.unmap, &child->view_unmap); - child->view_unmap.notify = view_child_handle_view_unmap; - - struct sway_container *container = child->view->container; - if (container != NULL) { - struct sway_workspace *workspace = container->pending.workspace; - if (workspace) { - wlr_surface_send_enter(child->surface, workspace->output->wlr_output); - } - } - - view_child_init_subsurfaces(child, surface); } void view_child_destroy(struct sway_view_child *child) { - if (view_child_is_mapped(child) && child->view->container != NULL) { - view_child_damage(child, true); - } - if (child->parent != NULL) { wl_list_remove(&child->link); child->parent = NULL; @@ -1140,18 +884,8 @@ void view_child_destroy(struct sway_view_child *child) { wl_list_for_each_safe(subchild, tmpchild, &child->children, link) { wl_list_remove(&subchild->link); subchild->parent = NULL; - // The subchild lost its parent link, so it cannot see that the parent - // is unmapped. Unmap it directly. - subchild->mapped = false; } - wl_list_remove(&child->surface_commit.link); - wl_list_remove(&child->surface_destroy.link); - wl_list_remove(&child->surface_map.link); - wl_list_remove(&child->surface_unmap.link); - wl_list_remove(&child->view_unmap.link); - wl_list_remove(&child->surface_new_subsurface.link); - if (child->impl && child->impl->destroy) { child->impl->destroy(child); } else { @@ -1300,7 +1034,13 @@ void view_update_title(struct sway_view *view, bool force) { view->container->title = title ? strdup(title) : NULL; // Update title after the global font height is updated - container_update_title_textures(view->container); + if (view->container->title_bar.title_text && len) { + sway_text_node_set_text(view->container->title_bar.title_text, + view->container->formatted_title); + container_arrange_title_bar(view->container); + } else { + container_update_title_bar(view->container); + } ipc_event_window(view->container, "title"); @@ -1367,6 +1107,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { return; } clock_gettime(CLOCK_MONOTONIC, &view->urgent); + container_update_itself_and_parents(view->container); } else { view->urgent = (struct timespec){ 0 }; if (view->urgent_timer) { @@ -1374,7 +1115,6 @@ void view_set_urgent(struct sway_view *view, bool enable) { view->urgent_timer = NULL; } } - container_damage_whole(view->container); ipc_event_window(view->container, "urgent"); @@ -1388,40 +1128,47 @@ bool view_is_urgent(struct sway_view *view) { } void view_remove_saved_buffer(struct sway_view *view) { - if (!sway_assert(!wl_list_empty(&view->saved_buffers), "Expected a saved buffer")) { + if (!sway_assert(view->saved_surface_tree, "Expected a saved buffer")) { return; } - struct sway_saved_buffer *saved_buf, *tmp; - wl_list_for_each_safe(saved_buf, tmp, &view->saved_buffers, link) { - wlr_buffer_unlock(&saved_buf->buffer->base); - wl_list_remove(&saved_buf->link); - free(saved_buf); - } + + wlr_scene_node_destroy(&view->saved_surface_tree->node); + view->saved_surface_tree = NULL; + wlr_scene_node_set_enabled(&view->content_tree->node, true); } -static void view_save_buffer_iterator(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct sway_view *view = data; +static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, + double sx, double sy, void *data) { + struct wlr_scene_tree *tree = data; - if (surface && wlr_surface_has_buffer(surface)) { - wlr_buffer_lock(&surface->buffer->base); - struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); - saved_buffer->buffer = surface->buffer; - saved_buffer->width = surface->current.width; - saved_buffer->height = surface->current.height; - saved_buffer->x = view->container->surface_x + sx; - saved_buffer->y = view->container->surface_y + sy; - saved_buffer->transform = surface->current.transform; - wlr_surface_get_buffer_source_box(surface, &saved_buffer->source_box); - wl_list_insert(view->saved_buffers.prev, &saved_buffer->link); + struct wlr_scene_buffer *sbuf = wlr_scene_buffer_create(tree, NULL); + if (!sbuf) { + sway_log(SWAY_ERROR, "Could not allocate a scene buffer when saving a surface"); + return; } + + wlr_scene_buffer_set_dest_size(sbuf, + buffer->dst_width, buffer->dst_height); + wlr_scene_buffer_set_buffer(sbuf, buffer->buffer); + wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); + wlr_scene_node_set_position(&sbuf->node, sx, sy); + wlr_scene_buffer_set_transform(sbuf, buffer->transform); } void view_save_buffer(struct sway_view *view) { - if (!sway_assert(wl_list_empty(&view->saved_buffers), "Didn't expect saved buffer")) { + if (!sway_assert(!view->saved_surface_tree, "Didn't expect saved buffer")) { view_remove_saved_buffer(view); } - view_for_each_surface(view, view_save_buffer_iterator, view); + + view->saved_surface_tree = wlr_scene_tree_create(view->scene_tree); + if (!view->saved_surface_tree) { + sway_log(SWAY_ERROR, "Could not allocate a scene tree node when saving a surface"); + return; + } + + wlr_scene_node_for_each_buffer(&view->content_tree->node, + view_save_buffer_iterator, view->saved_surface_tree); + wlr_scene_node_set_enabled(&view->content_tree->node, false); } bool view_is_transient_for(struct sway_view *child, @@ -1429,3 +1176,19 @@ bool view_is_transient_for(struct sway_view *child, return child->impl->is_transient_for && child->impl->is_transient_for(child, ancestor); } + +static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, + double x, double y, void *data) { + struct timespec *when = data; + wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); +} + +void view_send_frame_done(struct sway_view *view) { + struct timespec when; + clock_gettime(CLOCK_MONOTONIC, &when); + + struct wlr_scene_node *node; + wl_list_for_each(node, &view->content_tree->children, link) { + wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when); + } +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 0c4e97a3a..1b956bee0 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -69,6 +69,18 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); + + bool alloc_failure = false; + ws->layers.tiling = alloc_scene_tree(root->staging, &alloc_failure); + ws->layers.fullscreen = alloc_scene_tree(root->staging, &alloc_failure); + + if (alloc_failure) { + wlr_scene_node_destroy(&ws->layers.tiling->node); + wlr_scene_node_destroy(&ws->layers.fullscreen->node); + free(ws); + return NULL; + } + ws->name = name ? strdup(name) : NULL; ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); @@ -129,6 +141,11 @@ void workspace_destroy(struct sway_workspace *workspace) { return; } + scene_node_disown_children(workspace->layers.tiling); + scene_node_disown_children(workspace->layers.fullscreen); + wlr_scene_node_destroy(&workspace->layers.tiling->node); + wlr_scene_node_destroy(&workspace->layers.fullscreen->node); + free(workspace->name); free(workspace->representation); list_free_items_and_destroy(workspace->output_priority); @@ -684,7 +701,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { if (workspace->urgent != new_urgent) { workspace->urgent = new_urgent; ipc_event_workspace(NULL, workspace, "urgent"); - output_damage_whole(workspace->output); } }