Merge branch 'master' of github.com:swaywm/sway into feature/swap-workspace

This commit is contained in:
Fabian Specht 2024-01-24 15:03:35 +01:00
commit 1b4d5ba2f2
No known key found for this signature in database
GPG key ID: 3E324CC0E6232463
54 changed files with 2734 additions and 4397 deletions

View file

@ -31,7 +31,7 @@ Install dependencies:
* json-c
* pango
* cairo
* gdk-pixbuf2 (optional: system tray)
* gdk-pixbuf2 (optional: additional image formats for system tray)
* [swaybg] (optional: wallpaper)
* [scdoc] (optional: man pages) \*
* git (optional: version info) \*

View file

@ -1,13 +0,0 @@
#include <wlr/types/wlr_compositor.h>
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);

View file

@ -1,6 +1,7 @@
#ifndef _SWAY_TRANSACTION_H
#define _SWAY_TRANSACTION_H
#include <stdint.h>
#include <stdbool.h>
/**
* 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

View file

@ -3,6 +3,7 @@
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/util/edges.h>
@ -12,7 +13,6 @@
#include "sway/input/text_input.h"
struct sway_seat;
struct render_context;
struct sway_seatop_impl {
void (*button)(struct sway_seat *seat, uint32_t time_msec,
@ -52,7 +52,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 render_context *ctx);
bool allow_set_cursor;
};
@ -75,20 +74,6 @@ struct sway_seat_node {
struct wl_listener destroy;
};
struct sway_drag_icon {
struct sway_seat *seat;
struct wlr_drag_icon *wlr_drag_icon;
struct wl_list link; // sway_root::drag_icons
double x, y; // in layout-local coordinates
int dx, dy; // offset in surface-local coordinates
struct wl_listener surface_commit;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener destroy;
};
struct sway_drag {
struct sway_seat *seat;
struct wlr_drag *wlr_drag;
@ -99,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;
@ -257,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_icons_update_position(struct sway_seat *seat);
enum wlr_edges find_resize_edge(struct sway_container *cont,
struct wlr_surface *surface, struct sway_cursor *cursor);
@ -357,12 +351,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 render_context *ctx);
bool seatop_allows_set_cursor(struct sway_seat *seat);
/**

View file

@ -4,53 +4,30 @@
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_layer_shell_v1.h>
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 *tree;
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;
};
@ -61,7 +38,4 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
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

View file

@ -5,6 +5,7 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include "config.h"
#include "sway/tree/node.h"
#include "sway/tree/view.h"
@ -19,16 +20,31 @@ 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 timespec last_frame;
struct wlr_damage_ring damage_ring;
int lx, ly; // layout coords
int width, height; // transformed buffer size
enum wl_output_subpixel detected_subpixel;
@ -43,9 +59,7 @@ struct sway_output {
struct wl_listener destroy;
struct wl_listener commit;
struct wl_listener present;
struct wl_listener damage;
struct wl_listener frame;
struct wl_listener needs_frame;
struct wl_listener request_state;
struct {
@ -65,14 +79,6 @@ struct sway_output_non_desktop {
struct wl_listener destroy;
};
struct render_context {
struct sway_output *output;
struct wlr_renderer *renderer;
const pixman_region32_t *output_damage;
struct wlr_render_pass *pass;
};
struct sway_output *output_create(struct wlr_output *wlr_output);
void output_destroy(struct sway_output *output);
@ -91,19 +97,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);
bool output_match_name_or_id(struct sway_output *output,
const char *name_or_id);
@ -119,46 +112,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 render_context *ctx);
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);
@ -176,13 +131,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 render_context *ctx, 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);

View file

@ -0,0 +1,33 @@
/**
* Across a wayland compositor, there are multiple shells: It can be
* a toplevel, or a layer_shell, or even something more meta like a drag
* icon or highlight indicators when dragging windows around.
*
* This object lets us store values that represent these modes of operation
* and keep track of what object is being represented.
*/
#ifndef _SWAY_SCENE_DESCRIPTOR_H
#define _SWAY_SCENE_DESCRIPTOR_H
#include <wlr/types/wlr_scene.h>
enum sway_scene_descriptor_type {
SWAY_SCENE_DESC_BUFFER_TIMER,
SWAY_SCENE_DESC_NON_INTERACTIVE,
SWAY_SCENE_DESC_CONTAINER,
SWAY_SCENE_DESC_VIEW,
SWAY_SCENE_DESC_LAYER_SHELL,
SWAY_SCENE_DESC_XWAYLAND_UNMANAGED,
SWAY_SCENE_DESC_POPUP,
SWAY_SCENE_DESC_DRAG_ICON,
};
bool scene_descriptor_assign(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type, void *data);
void *scene_descriptor_try_get(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type);
void scene_descriptor_destroy(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type);
#endif

View file

@ -28,6 +28,19 @@
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;
struct wl_event_loop *wl_event_loop;
@ -41,7 +54,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;
@ -93,15 +105,9 @@ struct sway_server {
struct wl_listener gamma_control_set_gamma;
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;
@ -150,13 +156,7 @@ 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;
bool legacy_wl_drm; // Enable the legacy wl_drm interface
};
extern struct sway_debug debug;
@ -170,12 +170,15 @@ 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);
bool sway_session_lock_has_surface(struct sway_session_lock *lock,
struct wlr_surface *surface);
void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data);
#if HAVE_XWAYLAND
void handle_xwayland_surface(struct wl_listener *listener, void *data);

View file

@ -1,24 +0,0 @@
#ifndef _SWAY_SURFACE_H
#define _SWAY_SURFACE_H
#include <wlr/types/wlr_compositor.h>
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;
};
void surface_update_outputs(struct wlr_surface *surface);
void surface_enter_output(struct wlr_surface *surface,
struct sway_output *output);
void surface_leave_output(struct wlr_surface *surface,
struct sway_output *output);
#endif

View file

@ -0,0 +1,28 @@
#ifndef _SWAY_BUFFER_H
#define _SWAY_BUFFER_H
#include <wlr/types/wlr_scene.h>
struct sway_text_node {
int width;
int max_width;
int height;
int baseline;
bool pango_markup;
float color[4];
float background[4];
struct wlr_scene_node *node;
};
struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
char *text, float color[4], bool pango_markup);
void sway_text_node_set_color(struct sway_text_node *node, float color[4]);
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);
void sway_text_node_set_background(struct sway_text_node *node, float background[4]);
#endif

View file

@ -3,6 +3,7 @@
#include <stdint.h>
#include <sys/types.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#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_tree *border;
struct wlr_scene_tree *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.
@ -120,18 +141,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;
@ -151,19 +161,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);
struct sway_container *container_get_first_view(struct sway_container *container);
void container_for_each_child(struct sway_container *container,
@ -182,13 +179,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);
@ -224,11 +221,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.
*/
@ -291,26 +283,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,
@ -358,8 +336,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);
@ -383,4 +359,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

View file

@ -2,6 +2,7 @@
#define _SWAY_NODE_H
#include <wayland-server-core.h>
#include <stdbool.h>
#include <wlr/types/wlr_scene.h>
#include "list.h"
#define MIN_SANE_W 100
@ -75,4 +76,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

View file

@ -3,6 +3,7 @@
#include <wayland-server-core.h>
#include <wayland-util.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/render/wlr_texture.h>
#include "sway/tree/container.h"
#include "sway/tree/node.h"
@ -16,10 +17,44 @@ 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;
// tree containing all layers the compositor will render. Cursor handling
// will end up iterating this tree.
struct wlr_scene_tree *layer_tree;
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

View file

@ -2,6 +2,7 @@
#define _SWAY_VIEW_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#include "sway/config.h"
#if HAVE_XWAYLAND
#include <wlr/xwayland.h>
@ -45,10 +46,6 @@ struct sway_view_impl {
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;
@ -88,16 +80,10 @@ 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_foreign_toplevel_handle_v1 *foreign_toplevel;
struct wl_listener foreign_activate_request;
struct wl_listener foreign_fullscreen_request;
@ -119,8 +105,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;
@ -145,6 +129,8 @@ struct sway_xdg_shell_view {
struct sway_xwayland_view {
struct sway_view view;
struct wlr_scene_tree *surface_tree;
struct wl_listener commit;
struct wl_listener request_move;
struct wl_listener request_resize;
@ -166,18 +152,18 @@ struct sway_xwayland_view {
struct wl_listener unmap;
struct wl_listener destroy;
struct wl_listener override_redirect;
struct wl_listener surface_tree_destroy;
};
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 associate;
struct wl_listener dissociate;
@ -187,43 +173,12 @@ struct sway_xwayland_unmanaged {
struct wl_listener override_redirect;
};
#endif
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);
};
/**
* A view child is a surface in the view tree, such as a subsurface or a popup.
*/
struct sway_view_child {
const struct sway_view_child_impl *impl;
struct wl_list link;
struct sway_view *view;
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 sway_xdg_popup {
struct sway_view_child child;
struct sway_view *view;
struct wlr_scene_tree *scene_tree;
struct wlr_scene_tree *xdg_surface_tree;
struct wlr_xdg_popup *wlr_xdg_popup;
struct wl_listener surface_commit;
@ -295,23 +250,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);
@ -333,14 +274,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
void view_unmap(struct sway_view *view);
void view_update_size(struct sway_view *view);
void view_center_surface(struct sway_view *view);
void view_child_init(struct sway_view_child *child,
const struct sway_view_child_impl *impl, struct sway_view *view,
struct wlr_surface *surface);
void view_child_destroy(struct sway_view_child *child);
void view_center_and_clip_surface(struct sway_view *view);
struct sway_view *view_from_wlr_xdg_surface(
struct wlr_xdg_surface *xdg_surface);
@ -381,4 +315,6 @@ bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
void view_send_frame_done(struct sway_view *view);
#endif

View file

@ -2,6 +2,7 @@
#define _SWAY_WORKSPACE_H
#include <stdbool.h>
#include <wlr/types/wlr_scene.h>
#include "sway/config.h"
#include "sway/tree/container.h"
#include "sway/tree/node.h"
@ -23,6 +24,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;

View file

@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for
option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag')
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray')
option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg')
option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray')
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library')

View file

@ -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 container_update_iterator(struct sway_container *con, void *data) {
container_update(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(container_update_iterator, NULL);
}
return cmd_results_new(CMD_SUCCESS, NULL);

View file

@ -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);
}

View file

@ -37,6 +37,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
}
con->alpha = val;
container_damage_whole(con);
container_update(con);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -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();
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -538,10 +538,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) {
@ -558,6 +554,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
if (scale_filter_old != output->scale_filter) {
sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name,
sway_output_scale_filter_to_string(output->scale_filter));
wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
}
}

View file

@ -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);
}

View file

@ -6,6 +6,7 @@
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_subcompositor.h>
#include "log.h"
#include "sway/scene_descriptor.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/input/input-manager.h"
@ -13,9 +14,9 @@
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/surface.h"
#include "sway/tree/arrange.h"
#include "sway/tree/workspace.h"
#include <wlr/types/wlr_scene.h>
struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
struct wlr_surface *surface) {
@ -50,162 +51,22 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
} while (true);
}
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_surface(struct sway_output *output, const struct wlr_box *full_area,
struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
struct wlr_scene_node *node;
wl_list_for_each(node, &tree->children, link) {
struct sway_layer_surface *surface = scene_descriptor_try_get(node,
SWAY_SCENE_DESC_LAYER_SHELL);
// surface could be null during destruction
if (!surface) {
continue;
}
}
}
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)) {
if (!surface->scene->layer_surface->initialized) {
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);
wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
}
}
@ -213,84 +74,81 @@ void arrange_layers(struct sway_output *output) {
struct wlr_box usable_area = { 0 };
wlr_output_effective_resolution(output->wlr_output,
&usable_area.width, &usable_area.height);
const struct wlr_box 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) {
if (!wlr_box_equal(&usable_area, &output->usable_area)) {
sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
output->usable_area = usable_area;
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
== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE &&
layer->layer_surface->surface->mapped) {
topmost = layer;
break;
}
}
if (topmost != NULL) {
break;
}
}
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat->has_exclusive_layer = false;
if (topmost != NULL) {
seat_set_focus_layer(seat, topmost->layer_surface);
} else if (seat->focused_layer &&
seat->focused_layer->current.keyboard_interactive
!= ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
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(*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->tree = scene->tree;
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_layer_surface *surface = scene_descriptor_try_get(node,
SWAY_SCENE_DESC_LAYER_SHELL);
if (!surface) {
continue;
}
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->surface->mapped) {
return lsurface;
&& layer_surface->surface->mapped) {
return surface;
}
}
}
@ -298,262 +156,144 @@ 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);
// destroy the scene descriptor straight away if it exists, otherwise
// we will try to reflow still considering the destroyed node.
scene_descriptor_destroy(&layer->tree->node, SWAY_SCENE_DESC_LAYER_SHELL);
// 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);
if (!server.session_lock.locked) {
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);
wl_resource_get_client(layer->layer_surface->resource);
if (!server.session_lock.lock) {
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->surface->mapped) {
layer->mapped = layer_surface->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;
if (!layer_surface->initialized) {
return;
}
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) {
old_extent.x += output->lx;
old_extent.y += output->ly;
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);
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);
}
transaction_commit_dirty();
if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) {
surface->mapped = layer_surface->surface->mapped;
arrange_layers(surface->output);
transaction_commit_dirty();
}
int 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 popup_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_layer_popup *popup =
wl_container_of(listener, popup, destroy);
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->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);
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->surface->events.map, &subsurface->map);
subsurface->unmap.notify = subsurface_handle_unmap;
wl_signal_add(&wlr_subsurface->surface->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);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
wl_list_remove(&popup->commit.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;
// if a client tries to create a popup while we are in the process of destroying
// its output, don't crash.
if (!output) {
return;
}
int 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,
.x = output->lx - lx,
.y = output->ly - ly,
.width = output->width,
.height = output->height,
};
@ -561,63 +301,38 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
}
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");
surface_enter_output(popup->wlr_popup->base->surface, wlr_output->data);
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);
if (popup->wlr_popup->base->initial_commit) {
popup_unconstrain(popup);
}
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);
free(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_popup *popup =
calloc(1, sizeof(struct sway_layer_popup));
struct sway_layer_surface *toplevel, struct wlr_scene_tree *parent) {
struct sway_layer_popup *popup = calloc(1, sizeof(*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->surface->events.map, &popup->map);
popup->unmap.notify = popup_handle_unmap;
wl_signal_add(&wlr_popup->base->surface->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);
popup->commit.notify = popup_handle_commit;
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
return popup;
}
@ -626,19 +341,14 @@ 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) {
@ -670,10 +380,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;
}
@ -682,46 +388,51 @@ 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;
}
if (!scene_descriptor_assign(&scene_surface->tree->node,
SWAY_SCENE_DESC_LAYER_SHELL, surface)) {
sway_log(SWAY_ERROR, "Failed to allocate a layer surface descriptor");
// 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->surface->events.map, &surface->map);
surface->unmap.notify = handle_unmap;
wl_signal_add(&layer_surface->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->surface->events.map, &sway_layer->map);
sway_layer->unmap.notify = handle_unmap;
wl_signal_add(&layer_surface->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_enter_output(layer_surface->surface, output);
// Temporarily set the layer's current state to pending
// So that we can easily arrange it
struct wlr_layer_surface_v1_state old_state = layer_surface->current;
layer_surface->current = layer_surface->pending;
arrange_layers(output);
layer_surface->current = old_state;
surface->node_destroy.notify = handle_node_destroy;
wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy);
}

View file

@ -26,8 +26,8 @@
#include "sway/ipc-server.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"
@ -71,318 +71,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->surface->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->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 roundf((offset + length) * scale) - roundf(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 = roundf(box->x * scale);
box->y = roundf(box->y * scale);
}
struct sway_workspace *output_get_active_workspace(struct sway_output *output) {
struct sway_seat *seat = input_manager_current_seat();
@ -396,304 +84,186 @@ 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_get_or_create(struct wlr_scene_buffer *buffer) {
struct buffer_timer *timer =
scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER);
if (timer) {
return 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,
int x, int 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) {
struct sway_view *view = scene_descriptor_try_get(current,
SWAY_SCENE_DESC_VIEW);
if (view) {
view_max_render_time = view->max_render_time;
break;
}
if (!current->parent) {
break;
}
current = &current->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);
struct buffer_timer *timer = NULL;
if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) {
timer = buffer_timer_get_or_create(buffer);
}
if (timer) {
wl_event_source_timer_update(timer->frame_done_timer, delay);
} else {
struct sway_surface *sway_surface = surface->data;
wl_event_source_timer_update(sway_surface->frame_done_timer, delay);
wlr_scene_buffer_send_frame_done(buffer, &data->when);
}
}
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 enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) {
switch (output->scale_filter) {
case SCALE_FILTER_LINEAR:
return WLR_SCALE_FILTER_BILINEAR;
case SCALE_FILTER_NEAREST:
return WLR_SCALE_FILTER_NEAREST;
default:
abort(); // unreachable
}
}
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 wlr_output_state *pending, 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;
static void output_configure_scene(struct sway_output *output,
struct wlr_scene_node *node, float opacity) {
if (!node->enabled) {
return;
}
if (server.session_lock.locked) {
return false;
struct sway_container *con =
scene_descriptor_try_get(node, SWAY_SCENE_DESC_CONTAINER);
if (con) {
opacity = con->alpha;
}
if (!wl_list_empty(&view->saved_buffers)) {
return false;
}
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
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;
// hack: don't call the scene setter because that will damage all outputs
// We don't want to damage outputs that aren't our current output that
// we're configuring
buffer->filter_mode = get_scale_filter(output);
wlr_scene_buffer_set_opacity(buffer, opacity);
} else if (node->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *node;
wl_list_for_each(node, &tree->children, link) {
output_configure_scene(output, node, opacity);
}
}
#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;
}
size_t n_popups = 0;
output_view_for_each_popup_surface(output, view,
count_surface_iterator, &n_popups);
if (n_popups > 0) {
return false;
}
if (surface->buffer == NULL) {
return false;
}
if ((float)surface->current.scale != wlr_output->scale ||
surface->current.transform != wlr_output->transform) {
return false;
}
if (!wlr_output_is_direct_scanout_allowed(wlr_output)) {
return false;
}
wlr_output_state_set_buffer(pending, &surface->buffer->base);
if (!wlr_output_test_state(wlr_output, pending)) {
return false;
}
wlr_presentation_surface_scanned_out_on_output(surface,
wlr_output);
return wlr_output_commit_state(wlr_output, pending);
}
static void get_frame_damage(struct sway_output *output,
pixman_region32_t *frame_damage) {
struct wlr_output *wlr_output = output->wlr_output;
int width, height;
wlr_output_transformed_resolution(wlr_output, &width, &height);
pixman_region32_init(frame_damage);
enum wl_output_transform transform =
wlr_output_transform_invert(wlr_output->transform);
wlr_region_transform(frame_damage, &output->damage_ring.current,
transform, width, height);
if (debug.damage != DAMAGE_DEFAULT) {
pixman_region32_union_rect(frame_damage, frame_damage,
0, 0, wlr_output->width, wlr_output->height);
}
}
static int output_repaint_timer_handler(void *data) {
struct sway_output *output = data;
struct wlr_output *wlr_output = output->wlr_output;
if (wlr_output == NULL) {
if (!output->enabled) {
return 0;
}
wlr_output->frame_pending = false;
output->wlr_output->frame_pending = false;
if (!wlr_output->needs_frame &&
!output->gamma_lut_changed &&
!pixman_region32_not_empty(&output->damage_ring.current)) {
return 0;
}
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;
}
struct wlr_output_state pending = {0};
output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
if (output->gamma_lut_changed) {
struct wlr_output_state pending;
wlr_output_state_init(&pending);
if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) {
return 0;
}
output->gamma_lut_changed = false;
struct wlr_gamma_control_v1 *gamma_control =
wlr_gamma_control_manager_v1_get_control(
server.gamma_control_manager_v1, wlr_output);
server.gamma_control_manager_v1, output->wlr_output);
if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
goto out;
}
if (!wlr_output_test_state(wlr_output, &pending)) {
wlr_output_state_finish(&pending);
pending = (struct wlr_output_state){0};
return 0;
}
if (!wlr_output_commit_state(output->wlr_output, &pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
wlr_output_state_finish(&pending);
return 0;
}
wlr_output_state_finish(&pending);
return 0;
}
pending.committed |= WLR_OUTPUT_STATE_DAMAGE;
get_frame_damage(output, &pending.damage);
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, &pending, 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) {
goto out;
}
}
if (!wlr_output_configure_primary_swapchain(wlr_output, &pending, &wlr_output->swapchain)) {
goto out;
}
int buffer_age;
struct wlr_buffer *buffer = wlr_swapchain_acquire(wlr_output->swapchain, &buffer_age);
if (buffer == NULL) {
goto out;
}
struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(
wlr_output->renderer, buffer, NULL);
if (render_pass == NULL) {
wlr_buffer_unlock(buffer);
goto out;
}
pixman_region32_t damage;
pixman_region32_init(&damage);
wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage);
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);
}
struct render_context ctx = {
.output_damage = &damage,
.renderer = wlr_output->renderer,
.output = output,
.pass = render_pass,
};
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
output_render(&ctx);
pixman_region32_fini(&damage);
if (!wlr_render_pass_submit(render_pass)) {
wlr_buffer_unlock(buffer);
goto out;
}
wlr_output_state_set_buffer(&pending, buffer);
wlr_buffer_unlock(buffer);
if (!wlr_output_commit_state(wlr_output, &pending)) {
goto out;
}
wlr_damage_ring_rotate(&output->damage_ring);
output->last_frame = now;
out:
wlr_output_state_finish(&pending);
wlr_scene_output_commit(output->scene_output, NULL);
return 0;
}
static void handle_damage(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, damage);
struct wlr_output_event_damage *event = user_data;
if (wlr_damage_ring_add(&output->damage_ring, event->damage)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
static void handle_frame(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, frame);
@ -754,117 +324,8 @@ static void 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);
}
static void handle_needs_frame(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, needs_frame);
wlr_output_schedule_frame(output->wlr_output);
}
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 != NULL && output->wlr_output != NULL) {
wlr_damage_ring_add_whole(&output->damage_ring);
wlr_output_schedule_frame(output->wlr_output);
}
}
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 (ceilf(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,
ceilf(output->wlr_output->scale) - surface->current.scale);
}
pixman_region32_translate(&damage, box.x, box.y);
if (wlr_damage_ring_add(&output->damage_ring, &damage)) {
wlr_output_schedule_frame(output->wlr_output);
}
pixman_region32_fini(&damage);
if (whole) {
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
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);
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
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);
if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
// 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);
}
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) {
@ -907,13 +368,11 @@ static void begin_destroy(struct sway_output *output) {
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->commit.link);
wl_list_remove(&output->present.link);
wl_list_remove(&output->damage.link);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->needs_frame.link);
wl_list_remove(&output->request_state.link);
wlr_damage_ring_finish(&output->damage_ring);
wlr_scene_output_destroy(output->scene_output);
output->scene_output = NULL;
output->wlr_output->data = NULL;
output->wlr_output = NULL;
@ -932,17 +391,6 @@ static void handle_layout_destroy(struct wl_listener *listener, void *data) {
begin_destroy(output);
}
static void update_textures(struct sway_container *con, void *data) {
container_update_title_textures(con);
container_update_marks_textures(con);
}
static void update_output_scale_iterator(struct sway_output *output,
struct sway_view *view, struct wlr_surface *surface,
struct wlr_box *box, void *user_data) {
surface_update_outputs(surface);
}
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;
@ -951,11 +399,6 @@ static void handle_commit(struct wl_listener *listener, void *data) {
return;
}
if (event->state->committed & WLR_OUTPUT_STATE_SCALE) {
output_for_each_container(output, update_textures, NULL);
output_for_each_surface(output, update_output_scale_iterator, NULL);
}
if (event->state->committed & (
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_TRANSFORM |
@ -967,15 +410,6 @@ static void handle_commit(struct wl_listener *listener, void *data) {
update_output_manager_config(output->server);
}
if (event->state->committed & (
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_TRANSFORM)) {
int width, height;
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
wlr_output_schedule_frame(output->wlr_output);
}
// Next time the output is enabled, try to re-apply the gamma LUT
if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) {
output->gamma_lut_changed = true;
@ -1039,12 +473,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;
wlr_damage_ring_init(&output->damage_ring);
output->scene_output = scene_output;
wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy);
output->layout_destroy.notify = handle_layout_destroy;
@ -1054,27 +500,24 @@ void handle_new_output(struct wl_listener *listener, void *data) {
output->commit.notify = handle_commit;
wl_signal_add(&wlr_output->events.present, &output->present);
output->present.notify = handle_present;
wl_signal_add(&wlr_output->events.damage, &output->damage);
output->damage.notify = handle_damage;
wl_signal_add(&wlr_output->events.frame, &output->frame);
output->frame.notify = handle_frame;
wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame);
output->needs_frame.notify = handle_needs_frame;
wl_signal_add(&wlr_output->events.request_state, &output->request_state);
output->request_state.notify = handle_request_state;
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);
transaction_commit_dirty();
int width, height;
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
update_output_manager_config(server);
}

File diff suppressed because it is too large Load diff

View file

@ -1,72 +0,0 @@
#define _POSIX_C_SOURCE 200112L
#include <stdlib.h>
#include <time.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include "sway/server.h"
#include "sway/surface.h"
#include "sway/output.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);
}
}
void surface_update_outputs(struct wlr_surface *surface) {
float scale = 1;
struct wlr_surface_output *surface_output;
wl_list_for_each(surface_output, &surface->current_outputs, link) {
if (surface_output->output->scale > scale) {
scale = surface_output->output->scale;
}
}
wlr_fractional_scale_v1_notify_scale(surface, scale);
wlr_surface_set_preferred_buffer_scale(surface, ceil(scale));
}
void surface_enter_output(struct wlr_surface *surface,
struct sway_output *output) {
wlr_surface_send_enter(surface, output->wlr_output);
surface_update_outputs(surface);
}
void surface_leave_output(struct wlr_surface *surface,
struct sway_output *output) {
wlr_surface_send_leave(surface, output->wlr_output);
surface_update_outputs(surface);
}

View file

@ -5,7 +5,7 @@
#include <time.h>
#include <wlr/types/wlr_buffer.h>
#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,449 @@ 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_and_clip_surface(view);
}
}
}
static void arrange_title_bar(struct sway_container *con,
int x, int y, int width, int height) {
container_update(con);
bool has_title_bar = height > 0;
wlr_scene_node_set_enabled(&con->title_bar.tree->node, has_title_bar);
if (!has_title_bar) {
return;
}
wlr_scene_node_set_position(&con->title_bar.tree->node, x, y);
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 void 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 {
sway_assert(false, "unreachable");
}
}
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);
}
arrange_children(con->current.layout, con->current.children,
con->current.focused_inactive_child, con->content_tree,
width, height, gaps);
}
}
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) {
arrange_children(ws->current.layout, ws->current.tiling,
ws->current.focused_inactive_child, ws->layers.tiling,
width, height, ws->gaps_inner);
}
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_box *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_xdg_popup *popup = scene_descriptor_try_get(node,
SWAY_SCENE_DESC_POPUP);
// 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 (popup) {
struct wlr_scene_tree *tree = popup->view->content_tree;
int lx, ly;
wlr_scene_node_coords(&tree->node, &lx, &ly);
wlr_scene_node_set_position(&popup->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);
// 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 +721,6 @@ static void transaction_apply(struct sway_transaction *transaction) {
node->instruction = NULL;
}
cursor_rebase_all();
}
static void transaction_commit_pending(void);
@ -340,6 +733,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 +810,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 +884,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 +905,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) {

View file

@ -7,7 +7,7 @@
#include <wlr/util/edges.h>
#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"
@ -19,41 +19,29 @@
#include "sway/tree/workspace.h"
#include "sway/xdg_decoration.h"
static const struct sway_view_child_impl popup_impl;
static struct sway_xdg_popup *popup_create(
struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
struct wlr_scene_tree *parent);
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_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->view, popup->xdg_surface_tree);
}
static void popup_destroy(struct sway_view_child *child) {
if (!sway_assert(child->impl == &popup_impl,
"Expected an xdg_shell popup")) {
return;
}
struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
wl_list_remove(&popup->surface_commit.link);
static void popup_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->new_popup.link);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->surface_commit.link);
wlr_scene_node_destroy(&popup->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);
static void popup_unconstrain(struct sway_xdg_popup *popup) {
struct sway_view *view = popup->child.view;
struct sway_view *view = popup->view;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
struct sway_workspace *workspace = view->container->pending.workspace;
@ -83,29 +71,44 @@ static void popup_handle_surface_commit(struct wl_listener *listener, void *data
}
}
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);
}
static void popup_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xdg_popup *popup = wl_container_of(listener, popup, destroy);
view_child_destroy(&popup->child);
}
static struct sway_xdg_popup *popup_create(
struct wlr_xdg_popup *wlr_popup, struct sway_view *view) {
static struct sway_xdg_popup *popup_create(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 =
calloc(1, sizeof(struct sway_xdg_popup));
if (popup == NULL) {
struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup));
if (!popup) {
return NULL;
}
view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
popup->wlr_xdg_popup = wlr_popup;
popup->view = view;
popup->scene_tree = wlr_scene_tree_create(parent);
if (!popup->scene_tree) {
free(popup);
return NULL;
}
popup->xdg_surface_tree = wlr_scene_xdg_surface_create(
popup->scene_tree, xdg_surface);
if (!popup->xdg_surface_tree) {
wlr_scene_node_destroy(&popup->scene_tree->node);
free(popup);
return NULL;
}
if (!scene_descriptor_assign(&popup->scene_tree->node,
SWAY_SCENE_DESC_POPUP, popup)) {
sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor");
wlr_scene_node_destroy(&popup->scene_tree->node);
free(popup);
return NULL;
}
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;
wl_signal_add(&xdg_surface->surface->events.commit, &popup->surface_commit);
popup->surface_commit.notify = popup_handle_surface_commit;
@ -114,9 +117,6 @@ static struct sway_xdg_popup *popup_create(
wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&xdg_surface->surface->events.map, &popup->child.surface_map);
wl_signal_add(&xdg_surface->surface->events.unmap, &popup->child.surface_unmap);
return popup;
}
@ -214,24 +214,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) {
@ -279,8 +261,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,
@ -317,7 +297,6 @@ 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));
if (container_is_floating(view->container)) {
view_update_size(view);
@ -327,18 +306,23 @@ static void handle_commit(struct wl_listener *listener, void *data) {
view->geometry.height);
}
transaction_commit_dirty_client();
} else {
view_center_surface(view);
}
desktop_damage_view(view);
view_center_and_clip_surface(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) {
@ -360,7 +344,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;
}
int lx, ly;
wlr_scene_node_coords(&popup->view->content_tree->node, &lx, &ly);
wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly);
}
static void handle_request_maximize(struct wl_listener *listener, void *data) {
@ -548,7 +541,10 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
return;
}
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
if (!view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl)) {
free(xdg_shell_view);
return;
}
xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
xdg_shell_view->map.notify = handle_map;
@ -564,5 +560,7 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
xdg_shell_view->destroy.notify = handle_destroy;
wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy);
wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base);
xdg_toplevel->base->data = xdg_shell_view;
}

View file

@ -6,15 +6,16 @@
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/xwayland.h>
#include <xcb/xcb_icccm.h>
#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/server.h"
@ -45,29 +46,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) {
@ -75,17 +59,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();
@ -99,10 +84,13 @@ 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);
wlr_scene_node_destroy(&surface->surface_scene->buffer->node);
surface->surface_scene = NULL;
}
struct sway_seat *seat = input_manager_current_seat();
if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) {
@ -426,17 +414,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);
@ -444,34 +421,38 @@ 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);
transaction_commit_dirty_client();
} else {
view_center_surface(view);
}
desktop_damage_view(view);
view_center_and_clip_surface(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) {
@ -515,9 +496,21 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
return;
}
view_unmap(view);
wl_list_remove(&xwayland_view->commit.link);
wl_list_remove(&xwayland_view->surface_tree_destroy.link);
if (xwayland_view->surface_tree) {
wlr_scene_node_destroy(&xwayland_view->surface_tree->node);
xwayland_view->surface_tree = NULL;
}
view_unmap(view);
}
static void handle_surface_tree_destroy(struct wl_listener *listener, void *data) {
struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view,
surface_tree_destroy);
xwayland_view->surface_tree = NULL;
}
static void handle_map(struct wl_listener *listener, void *data) {
@ -537,6 +530,15 @@ 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_tree = wlr_scene_subsurface_tree_create(
xwayland_view->view.content_tree, xsurface->surface);
if (xwayland_view->surface_tree) {
xwayland_view->surface_tree_destroy.notify = handle_surface_tree_destroy;
wl_signal_add(&xwayland_view->surface_tree->node.events.destroy,
&xwayland_view->surface_tree_destroy);
}
transaction_commit_dirty();
}
@ -796,7 +798,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);

View file

@ -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,43 +37,6 @@ 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) {
struct wlr_xdg_surface *xdg_surface =
wlr_xdg_surface_try_from_wlr_surface(surface);
return xdg_surface != NULL && xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP &&
xdg_surface->popup != NULL;
}
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).
@ -81,137 +44,101 @@ static struct wlr_surface *layer_surface_popup_at(struct sway_output *output,
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_scene_node *scene_node = NULL;
struct wlr_scene_node *node;
wl_list_for_each_reverse(node, &root->layer_tree->children, link) {
struct wlr_scene_tree *layer = wlr_scene_tree_from_node(node);
bool non_interactive = scene_descriptor_try_get(&layer->node,
SWAY_SCENE_DESC_NON_INTERACTIVE);
if (non_interactive) {
continue;
}
scene_node = wlr_scene_node_at(&layer->node, lx, ly, sx, sy);
if (scene_node) {
break;
}
}
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_try_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) {
struct sway_container *con = scene_descriptor_try_get(current,
SWAY_SCENE_DESC_CONTAINER);
if (!con) {
struct sway_view *view = scene_descriptor_try_get(current,
SWAY_SCENE_DESC_VIEW);
if (view) {
con = view->container;
}
}
if (!con) {
struct sway_xdg_popup *popup =
scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP);
if (popup) {
con = popup->view->container;
}
}
if (con && (!con->view || con->view->surface)) {
return &con->node;
}
if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_LAYER_SHELL)) {
// We don't want to feed through the current workspace on
// layer shells
return NULL;
}
#if HAVE_XWAYLAND
if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) {
return NULL;
}
#endif
if (!current->parent) {
break;
}
current = &current->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);
if (server.session_lock.locked) {
if (server.session_lock.lock == NULL) {
return NULL;
}
struct wlr_session_lock_surface_v1 *lock_surf;
wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) {
if (lock_surf->output != wlr_output) {
continue;
}
*surface = wlr_surface_surface_at(lock_surf->surface, ox, oy, sx, sy);
if (*surface != NULL) {
return NULL;
}
}
return NULL;
}
// 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;
}
@ -543,12 +470,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
if (seat->touch_id == event->touch_id) {
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);
}
}
drag_icons_update_position(seat);
}
if (cursor->simulating_pointer_from_touch) {

View file

@ -405,7 +405,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
char *device_identifier = input_device_get_identifier(wlr_device);
bool exact_identifier = keyboard->wlr->group != NULL;
seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
bool locked = server.session_lock.locked;
bool 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;

View file

@ -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);
@ -353,25 +354,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->surface->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);
struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon;
struct sway_seat *seat = icon->seat;
static void drag_icon_update_position(struct sway_seat *seat, struct wlr_scene_node *node) {
struct wlr_drag_icon *wlr_icon = scene_descriptor_try_get(node, SWAY_SCENE_DESC_DRAG_ICON);
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 + icon->dx;
icon->y = cursor->y + icon->dy;
wlr_scene_node_set_position(node, cursor->x, cursor->y);
break;
case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
struct wlr_touch_point *point =
@ -379,42 +370,15 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
if (point == NULL) {
return;
}
icon->x = seat->touch_x + icon->dx;
icon->y = seat->touch_y + icon->dy;
wlr_scene_node_set_position(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);
struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon;
icon->dx += wlr_icon->surface->current.dx;
icon->dy += wlr_icon->surface->current.dy;
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_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->destroy.link);
free(icon);
void drag_icons_update_position(struct sway_seat *seat) {
struct wlr_scene_node *node;
wl_list_for_each(node, &seat->drag_icons->children, link) {
drag_icon_update_position(seat, node);
}
}
static void drag_handle_destroy(struct wl_listener *listener, void *data) {
@ -486,27 +450,20 @@ static void handle_start_drag(struct wl_listener *listener, void *data) {
struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon;
if (wlr_drag_icon != NULL) {
struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon));
if (icon == NULL) {
sway_log(SWAY_ERROR, "Allocation failed");
struct wlr_scene_tree *tree = wlr_scene_drag_icon_create(seat->drag_icons, wlr_drag_icon);
if (!tree) {
sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene tree");
return;
}
icon->seat = seat;
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->surface->events.unmap, &icon->unmap);
icon->map.notify = drag_icon_handle_map;
wl_signal_add(&wlr_drag_icon->surface->events.map, &icon->map);
icon->destroy.notify = drag_icon_handle_destroy;
wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy);
if (!scene_descriptor_assign(&tree->node, SWAY_SCENE_DESC_DRAG_ICON,
wlr_drag_icon)) {
sway_log(SWAY_ERROR, "Failed to allocate a drag icon scene descriptor");
wlr_scene_node_destroy(&tree->node);
return;
}
wl_list_insert(&root->drag_icons, &icon->link);
drag_icon_update_position(icon);
drag_icon_update_position(seat, &tree->node);
}
seatop_begin_default(seat);
}
@ -553,8 +510,18 @@ struct sway_seat *seat_create(const char *seat_name) {
return NULL;
}
bool failed = false;
seat->scene_tree = alloc_scene_tree(root->layers.seat, &failed);
seat->drag_icons = alloc_scene_tree(seat->scene_tree, &failed);
if (failed) {
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;
}
@ -562,6 +529,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;
@ -1092,19 +1060,10 @@ void seat_configure_xcursor(struct sway_seat *seat) {
bool seat_is_input_allowed(struct sway_seat *seat,
struct wlr_surface *surface) {
if (!server.session_lock.locked) {
return true;
if (server.session_lock.lock) {
return sway_session_lock_has_surface(server.session_lock.lock, surface);
}
if (server.session_lock.lock == NULL) {
return false;
}
struct wlr_session_lock_surface_v1 *lock_surf;
wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) {
if (lock_surf->surface == surface) {
return true;
}
}
return false;
return true;
}
static void send_unfocus(struct sway_container *con, void *data) {
@ -1309,8 +1268,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);
}
}
@ -1732,12 +1691,6 @@ void seatop_end(struct sway_seat *seat) {
seat->seatop_impl = NULL;
}
void seatop_render(struct sway_seat *seat, struct render_context *ctx) {
if (seat->seatop_impl->render) {
seat->seatop_impl->render(seat, ctx);
}
}
bool seatop_allows_set_cursor(struct sway_seat *seat) {
return seat->seatop_impl->allow_set_cursor;
}

View file

@ -12,6 +12,7 @@
#include "sway/input/tablet.h"
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/scene_descriptor.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "log.h"
@ -55,6 +56,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;
@ -620,12 +624,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;
}
@ -655,12 +654,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;
}
@ -802,8 +796,9 @@ static void handle_pointer_axis(struct sway_seat *seat,
if (!handled) {
wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
event->orientation, scroll_factor * event->delta,
roundf(scroll_factor * event->delta_discrete), event->source);
event->orientation, scroll_factor * event->delta,
roundf(scroll_factor * event->delta_discrete), event->source,
event->relative_direction);
}
}

View file

@ -137,7 +137,8 @@ static void handle_pointer_axis(struct sway_seat *seat,
wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec,
event->orientation, scroll_factor * event->delta,
roundf(scroll_factor * event->delta_discrete), event->source);
roundf(scroll_factor * event->delta_discrete), event->source,
event->relative_direction);
}
static void handle_button(struct sway_seat *seat, uint32_t time_msec,

View file

@ -1,6 +1,5 @@
#define _POSIX_C_SOURCE 200809L
#include <wlr/types/wlr_cursor.h>
#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();
}

View file

@ -2,7 +2,6 @@
#include <limits.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/util/edges.h>
#include "sway/desktop.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/input/seat.h"
@ -24,28 +23,17 @@ 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 render_context *ctx) {
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) == ctx->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, ctx->output->wlr_output->scale);
render_rect(ctx, &box, color);
}
wlr_scene_node_destroy(&e->indicator_rect->node);
e->indicator_rect = NULL;
}
static void handle_motion_prethreshold(struct sway_seat *seat) {
@ -66,6 +54,7 @@ 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) {
wlr_scene_node_set_enabled(&e->indicator_rect->node, true);
e->threshold_reached = true;
cursor_set_image(seat->cursor, "grab", NULL);
}
@ -164,6 +153,11 @@ 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) {
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;
@ -172,8 +166,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
@ -186,8 +178,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;
}
@ -200,11 +194,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) {
@ -214,6 +215,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;
}
@ -255,8 +257,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;
@ -298,12 +299,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) {
@ -438,7 +435,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,
@ -450,6 +447,20 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat,
if (!e) {
return;
}
const float *indicator = config->border_colors.focused.indicator;
float color[4] = {
indicator[0] * .5,
indicator[1] * .5,
indicator[2] * .5,
indicator[3] * .5,
};
e->indicator_rect = wlr_scene_rect_create(seat->scene_tree, 0, 0, color);
if (!e->indicator_rect) {
free(e);
return;
}
e->con = con;
e->ref_lx = seat->cursor->cursor->x;
e->ref_ly = seat->cursor->cursor->y;

View file

@ -34,7 +34,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 locked = server.session_lock.locked;
bool locked = server.session_lock.lock;
list_t *bindings = config->current_mode->switch_bindings;
struct sway_switch_binding *matched_binding = NULL;

View file

@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <wlr/types/wlr_scene.h>
#include "log.h"
#include "sway/input/cursor.h"
#include "sway/input/keyboard.h"
@ -7,21 +8,29 @@
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/surface.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_commit;
struct wl_listener output_destroy;
struct wl_listener commit;
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) {
@ -29,104 +38,189 @@ static void set_lock_focused_surface(struct wlr_surface *focused) {
}
}
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->surface->mapped) {
next_focus = candidate->surface->surface;
break;
}
}
focus_surface(output->lock, next_focus);
}
}
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);
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);
}
cursor_rebase_all();
surface_enter_output(surf->surface, surf->output);
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_surface_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_output *output =
wl_container_of(listener, output, surface_destroy);
refocus_output(output);
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);
}
static void handle_output_commit(struct wl_listener *listener, void *data) {
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);
}
}
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_output *output = lock_surface->output->data;
sway_log(SWAY_DEBUG, "new lock layer surface");
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");
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->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->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_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);
struct sway_session_lock_output *output =
wl_container_of(listener, output, commit);
if (event->state->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);
lock_output_reconfigure(output);
}
}
static void destroy_lock_surface(struct sway_session_lock_surface *surf) {
// Move the seat focus to another surface if one is available
if (server.session_lock.focused == surf->surface) {
struct wlr_surface *next_focus = NULL;
struct wlr_session_lock_surface_v1 *other;
wl_list_for_each(other, &server.session_lock.lock->surfaces, link) {
if (other != surf->lock_surface && other->surface->mapped) {
next_focus = other->surface;
break;
}
}
set_lock_focused_surface(next_focus);
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(*lock_output));
if (!lock_output) {
sway_log(SWAY_ERROR, "failed to allocate a session lock output");
return NULL;
}
wl_list_remove(&surf->map.link);
wl_list_remove(&surf->destroy.link);
wl_list_remove(&surf->surface_commit.link);
wl_list_remove(&surf->output_commit.link);
wl_list_remove(&surf->output_destroy.link);
output_damage_whole(surf->output);
free(surf);
}
static void handle_surface_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy);
destroy_lock_surface(surf);
}
static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf =
wl_container_of(listener, surf, output_destroy);
destroy_lock_surface(surf);
}
static void handle_new_surface(struct wl_listener *listener, void *data) {
struct wlr_session_lock_surface_v1 *lock_surface = data;
struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf));
if (surf == NULL) {
return;
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;
}
sway_log(SWAY_DEBUG, "new lock layer surface");
struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){
lock->abandoned ? 1.f : 0.f,
0.f,
0.f,
1.f,
});
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;
}
struct sway_output *output = lock_surface->output->data;
wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height);
lock_output->output = output;
lock_output->tree = tree;
lock_output->background = background;
lock_output->lock = lock;
surf->lock_surface = lock_surface;
surf->surface = lock_surface->surface;
surf->output = output;
surf->map.notify = handle_surface_map;
wl_signal_add(&lock_surface->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_commit.notify = handle_output_commit;
wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit);
surf->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->node.events.destroy, &surf->output_destroy);
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_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) {
@ -145,28 +239,22 @@ static void handle_unlock(struct wl_listener *listener, void *data) {
struct sway_output *output = root->outputs->items[i];
arrange_layers(output);
}
// 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);
// 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) {
@ -174,44 +262,89 @@ 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(*sway_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_unfocus_unless_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();
}
}
bool sway_session_lock_has_surface(struct sway_session_lock *lock,
struct wlr_surface *surface) {
struct sway_session_lock_output *lock_output;
wl_list_for_each(lock_output, &lock->outputs, link) {
if (lock_output->surface && lock_output->surface->surface == surface) {
return true;
}
}
return false;
}
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,

View file

@ -154,11 +154,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;
@ -166,8 +162,8 @@ 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 if (strcmp(flag, "legacy-wl-drm") == 0) {
debug.legacy_wl_drm = true;
} else {
sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag);
}
@ -340,6 +336,10 @@ int main(int argc, char **argv) {
return 1;
}
if (server.linux_dmabuf_v1) {
wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1);
}
if (validate) {
bool valid = load_main_config(config_path, false, true);
free(config_path);

View file

@ -8,17 +8,16 @@ sway_sources = files(
'lock.c',
'main.c',
'realtime.c',
'scene_descriptor.c',
'server.c',
'sway_text_node.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',
'desktop/launcher.c',

66
sway/scene_descriptor.c Normal file
View file

@ -0,0 +1,66 @@
#include <stdlib.h>
#include <wlr/util/addon.h>
#include "log.h"
#include "sway/scene_descriptor.h"
struct scene_descriptor {
void *data;
struct wlr_addon addon;
};
static const struct wlr_addon_interface addon_interface;
static struct scene_descriptor *scene_node_get_descriptor(
struct wlr_scene_node *node, enum sway_scene_descriptor_type type) {
struct wlr_addon *addon = wlr_addon_find(&node->addons, (void *)type, &addon_interface);
if (!addon) {
return NULL;
}
struct scene_descriptor *desc = wl_container_of(addon, desc, addon);
return desc;
}
static void descriptor_destroy(struct scene_descriptor *desc) {
wlr_addon_finish(&desc->addon);
free(desc);
}
void *scene_descriptor_try_get(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type) {
struct scene_descriptor *desc = scene_node_get_descriptor(node, type);
if (!desc) {
return NULL;
}
return desc->data;
}
void scene_descriptor_destroy(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type) {
struct scene_descriptor *desc = scene_node_get_descriptor(node, type);
descriptor_destroy(desc);
}
static void addon_handle_destroy(struct wlr_addon *addon) {
struct scene_descriptor *desc = wl_container_of(addon, desc, addon);
descriptor_destroy(desc);
}
static const struct wlr_addon_interface addon_interface = {
.name = "sway_scene_descriptor",
.destroy = addon_handle_destroy,
};
bool scene_descriptor_assign(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type, void *data) {
struct scene_descriptor *desc = calloc(1, sizeof(*desc));
if (!desc) {
sway_log(SWAY_ERROR, "Could not allocate a scene descriptor");
return false;
}
wlr_addon_init(&desc->addon, &node->addons, (void *)type, &addon_interface);
desc->data = data;
return true;
}

View file

@ -13,6 +13,7 @@
#include <wlr/types/wlr_content_type_v1.h>
#include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
@ -191,6 +192,10 @@ bool server_init(struct sway_server *server) {
server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer(
server->wl_display, 4, server->renderer);
}
if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL &&
debug.legacy_wl_drm) {
wlr_drm_create(server->wl_display, server->renderer);
}
server->allocator = wlr_allocator_autocreate(server->backend,
server->renderer);
@ -201,9 +206,6 @@ bool server_init(struct sway_server *server) {
server->compositor = wlr_compositor_create(server->wl_display, 6,
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);

303
sway/sway_text_node.c Normal file
View file

@ -0,0 +1,303 @@
#define _POSIX_C_SOURCE 200809L
#include <drm_fourcc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/interfaces/wlr_buffer.h>
#include "cairo_util.h"
#include "log.h"
#include "pango.h"
#include "sway/config.h"
#include "sway/sway_text_node.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;
bool visible;
float scale;
enum wl_output_subpixel subpixel;
struct wl_listener outputs_update;
struct wl_listener destroy;
};
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) {
if (!buffer->visible) {
return;
}
float scale = buffer->scale;
int width = ceil(buffer->props.width * scale);
int height = ceil(buffer->props.height * scale);
float *color = (float *)&buffer->props.color;
float *background = (float *)&buffer->props.background;
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;
}
struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer));
if (!cairo_buffer) {
sway_log(SWAY_ERROR, "cairo_buffer allocation failed");
goto err;
}
cairo_t *cairo = cairo_create(surface);
if (!cairo) {
sway_log(SWAY_ERROR, "cairo_create failed");
free(cairo_buffer);
goto err;
}
cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
cairo_set_font_options(cairo, fo);
pango = pango_cairo_create_context(cairo);
cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]);
cairo_rectangle(cairo, 0, 0, width, height);
cairo_fill(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);
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);
pixman_region32_t opaque;
pixman_region32_init(&opaque);
if (background[3] == 1) {
pixman_region32_union_rect(&opaque, &opaque, 0, 0,
buffer->props.width, buffer->props.height);
}
wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque);
pixman_region32_fini(&opaque);
err:
if (pango) g_object_unref(pango);
cairo_font_options_destroy(fo);
}
static void handle_outputs_update(struct wl_listener *listener, void *data) {
struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update);
struct wlr_scene_outputs_update_event *event = data;
float scale = 0;
enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
for (size_t i = 0; i < event->size; i++) {
struct wlr_scene_output *output = event->active[i];
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;
}
}
buffer->visible = event->size > 0;
if (scale != buffer->scale || subpixel != buffer->subpixel) {
buffer->scale = scale;
buffer->subpixel = subpixel;
render_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->outputs_update.link);
wl_list_remove(&buffer->destroy.link);
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);
if (!c) {
sway_log(SWAY_ERROR, "cairo_t allocation failed");
return;
}
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, float color[4], bool pango_markup) {
struct text_buffer *buffer = calloc(1, sizeof(*buffer));
if (!buffer) {
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;
}
buffer->props.height = config->font_height;
buffer->props.pango_markup = pango_markup;
memcpy(&buffer->props.color, color, sizeof(*color) * 4);
buffer->destroy.notify = handle_destroy;
wl_signal_add(&node->node.events.destroy, &buffer->destroy);
buffer->outputs_update.notify = handle_outputs_update;
wl_signal_add(&node->events.outputs_update, &buffer->outputs_update);
text_calc_size(buffer);
return &buffer->props;
}
void sway_text_node_set_color(struct sway_text_node *node, float color[4]) {
if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) {
return;
}
memcpy(&node->color, color, sizeof(*color) * 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);
render_backing_buffer(buffer);
}
void sway_text_node_set_background(struct sway_text_node *node, float background[4]) {
struct text_buffer *buffer = wl_container_of(node, buffer, props);
memcpy(&node->background, background, sizeof(*background) * 4);
render_backing_buffer(buffer);
}

File diff suppressed because it is too large Load diff

View file

@ -159,3 +159,32 @@ bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) {
}
return false;
}
void scene_node_disown_children(struct wlr_scene_tree *tree) {
// this function can be called as part of destruction code that will be invoked
// upon an allocation failure. Let's not crash on NULL due to an allocation error.
if (!tree) {
return;
}
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;
}

View file

@ -87,9 +87,51 @@ 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);
wlr_scene_node_destroy(&output->layers.shell_background->node);
wlr_scene_node_destroy(&output->layers.shell_bottom->node);
wlr_scene_node_destroy(&output->layers.tiling->node);
wlr_scene_node_destroy(&output->layers.fullscreen->node);
wlr_scene_node_destroy(&output->layers.shell_top->node);
wlr_scene_node_destroy(&output->layers.shell_overlay->node);
wlr_scene_node_destroy(&output->layers.session_lock->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 failed = false;
output->layers.shell_background = alloc_scene_tree(root->staging, &failed);
output->layers.shell_bottom = alloc_scene_tree(root->staging, &failed);
output->layers.tiling = alloc_scene_tree(root->staging, &failed);
output->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
output->layers.shell_top = alloc_scene_tree(root->staging, &failed);
output->layers.shell_overlay = alloc_scene_tree(root->staging, &failed);
output->layers.session_lock = alloc_scene_tree(root->staging, &failed);
if (!failed) {
output->fullscreen_background = wlr_scene_rect_create(
output->layers.fullscreen, 0, 0, (float[4]){0.f, 0.f, 0.f, 1.f});
if (!output->fullscreen_background) {
sway_log(SWAY_ERROR, "Unable to allocate a background rect");
failed = true;
}
}
if (failed) {
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;
@ -102,11 +144,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;
}
@ -238,20 +275,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;
@ -266,8 +297,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;

View file

@ -3,11 +3,13 @@
#include <stdlib.h>
#include <string.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/transform.h>
#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"
@ -30,13 +32,51 @@ struct sway_root *root_create(struct wl_display *wl_display) {
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 failed = false;
root->staging = alloc_scene_tree(&root_scene->tree, &failed);
root->layer_tree = alloc_scene_tree(&root_scene->tree, &failed);
root->layers.shell_background = alloc_scene_tree(root->layer_tree, &failed);
root->layers.shell_bottom = alloc_scene_tree(root->layer_tree, &failed);
root->layers.tiling = alloc_scene_tree(root->layer_tree, &failed);
root->layers.floating = alloc_scene_tree(root->layer_tree, &failed);
root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed);
root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed);
root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed);
#if HAVE_XWAYLAND
root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed);
#endif
root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed);
root->layers.popup = alloc_scene_tree(root->layer_tree, &failed);
root->layers.seat = alloc_scene_tree(root->layer_tree, &failed);
root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed);
if (!failed && !scene_descriptor_assign(&root->layers.seat->node,
SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {
failed = true;
}
if (failed) {
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_display);
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();
@ -53,6 +93,7 @@ void root_destroy(struct sway_root *root) {
list_free(root->scratchpad);
list_free(root->non_desktop_outputs);
list_free(root->outputs);
wlr_scene_node_destroy(&root->root_scene->tree.node);
free(root);
}

View file

@ -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/desktop/launcher.h"
@ -24,8 +23,9 @@
#include "sway/ipc-server.h"
#include "sway/output.h"
#include "sway/input/seat.h"
#include "sway/scene_descriptor.h"
#include "sway/server.h"
#include "sway/surface.h"
#include "sway/sway_text_node.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
@ -35,15 +35,29 @@
#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 failed = false;
view->scene_tree = alloc_scene_tree(root->staging, &failed);
view->content_tree = alloc_scene_tree(view->scene_tree, &failed);
if (!failed && !scene_descriptor_assign(&view->scene_tree->node,
SWAY_SCENE_DESC_VIEW, view)) {
failed = true;
}
if (failed) {
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) {
@ -60,13 +74,10 @@ 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);
view_assign_ctx(view, NULL);
wlr_scene_node_destroy(&view->scene_tree->node);
free(view->title_format);
if (view->impl->destroy) {
@ -443,52 +454,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) {
@ -809,11 +774,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);
}
@ -880,8 +840,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);
view->executed_criteria->length = 0;
if (view->urgent_timer) {
@ -935,260 +893,28 @@ void view_update_size(struct sway_view *view) {
container_set_geometry_from_content(con);
}
void view_center_surface(struct sway_view *view) {
void view_center_and_clip_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);
}
static const struct sway_view_child_impl subsurface_impl;
if (container_is_floating(con)) {
// We always center the current coordinates rather than the next, as the
// geometry immediately affects the currently active rendering.
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 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);
wlr_scene_node_set_position(&view->content_tree->node, x, y);
} else {
*sx = *sy = 0;
}
struct wlr_subsurface *subsurface =
wlr_subsurface_try_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;
}
void view_child_init(struct sway_view_child *child,
const struct sway_view_child_impl *impl, struct sway_view *view,
struct wlr_surface *surface) {
child->impl = impl;
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) {
surface_enter_output(child->surface, workspace->output);
}
wlr_scene_node_set_position(&view->content_tree->node, 0, 0);
}
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;
}
struct sway_view_child *subchild, *tmpchild;
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 {
free(child);
// only make sure to clip the content if there is content to clip
if (!wl_list_empty(&con->view->content_tree->children)) {
wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){
.x = con->view->geometry.x,
.y = con->view->geometry.y,
.width = con->current.content_width,
.height = con->current.content_height,
});
}
}
@ -1321,7 +1047,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");
@ -1388,6 +1120,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) {
@ -1395,7 +1128,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");
@ -1409,40 +1141,54 @@ 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,
static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer,
int sx, int sy, void *data) {
struct sway_view *view = data;
struct wlr_scene_tree *tree = data;
if (surface && surface->buffer) {
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_opaque_region(sbuf, &buffer->opaque_region);
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);
wlr_scene_buffer_set_buffer(sbuf, buffer->buffer);
}
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;
}
// Enable and disable the saved surface tree like so to atomitaclly update
// the tree. This will prevent over damaging or other weirdness.
wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false);
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);
wlr_scene_node_set_enabled(&view->saved_surface_tree->node, true);
}
bool view_is_transient_for(struct sway_view *child,
@ -1450,3 +1196,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,
int x, int 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);
}
}

View file

@ -71,6 +71,18 @@ struct sway_workspace *workspace_create(struct sway_output *output,
return NULL;
}
node_init(&ws->node, N_WORKSPACE, ws);
bool failed = false;
ws->layers.tiling = alloc_scene_tree(root->staging, &failed);
ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
if (failed) {
wlr_scene_node_destroy(&ws->layers.tiling->node);
wlr_scene_node_destroy(&ws->layers.fullscreen->node);
free(ws);
return NULL;
}
ws->name = strdup(name);
ws->prev_split_layout = L_NONE;
ws->layout = output_get_default_layout(output);
@ -131,6 +143,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);
@ -669,7 +686,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);
}
}