From 7586f150c058997d9dde387ea7c091ffa7a3c3c7 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 30 Aug 2018 21:00:10 +1000 Subject: [PATCH] Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once. --- include/sway/config.h | 7 +- include/sway/desktop/transaction.h | 4 +- include/sway/input/input-manager.h | 4 +- include/sway/input/seat.h | 35 +- include/sway/ipc-json.h | 4 +- include/sway/ipc-server.h | 4 +- include/sway/output.h | 59 +- include/sway/server.h | 2 +- include/sway/tree/arrange.h | 9 +- include/sway/tree/container.h | 148 ++--- include/sway/tree/node.h | 74 +++ include/sway/tree/root.h | 28 +- include/sway/tree/view.h | 4 +- include/sway/tree/workspace.h | 107 +++- sway/commands.c | 27 +- sway/commands/border.c | 13 +- sway/commands/floating.c | 18 +- sway/commands/focus.c | 266 ++++----- sway/commands/fullscreen.c | 18 +- sway/commands/gaps.c | 35 +- sway/commands/hide_edge_borders.c | 4 +- sway/commands/kill.c | 22 +- sway/commands/layout.c | 186 +++--- sway/commands/mark.c | 7 +- sway/commands/move.c | 881 ++++++++++++++--------------- sway/commands/opacity.c | 3 +- sway/commands/reload.c | 2 +- sway/commands/rename.c | 11 +- sway/commands/resize.c | 86 +-- sway/commands/scratchpad.c | 42 +- sway/commands/seat/cursor.c | 4 +- sway/commands/show_marks.c | 10 +- sway/commands/smart_gaps.c | 2 +- sway/commands/split.c | 23 +- sway/commands/sticky.c | 27 +- sway/commands/swap.c | 76 ++- sway/commands/title_format.c | 7 +- sway/commands/unmark.c | 13 +- sway/commands/urgent.c | 7 +- sway/commands/workspace.c | 2 +- sway/config.c | 2 +- sway/config/bar.c | 14 +- sway/config/output.c | 47 +- sway/criteria.c | 41 +- sway/debug-tree.c | 111 ++-- sway/desktop/desktop.c | 31 +- sway/desktop/layer_shell.c | 23 +- sway/desktop/output.c | 192 +++---- sway/desktop/render.c | 273 +++++---- sway/desktop/transaction.c | 376 +++++++----- sway/desktop/xdg_shell.c | 47 +- sway/desktop/xdg_shell_v6.c | 44 +- sway/desktop/xwayland.c | 50 +- sway/input/cursor.c | 158 +++--- sway/input/input-manager.c | 14 +- sway/input/seat.c | 514 +++++++++-------- sway/ipc-json.c | 191 ++++--- sway/ipc-server.c | 55 +- sway/main.c | 6 +- sway/meson.build | 1 + sway/server.c | 7 +- sway/tree/arrange.c | 110 ++-- sway/tree/container.c | 875 +++++++++++----------------- sway/tree/node.c | 151 +++++ sway/tree/output.c | 454 ++++++++------- sway/tree/root.c | 184 +++--- sway/tree/view.c | 279 ++++----- sway/tree/workspace.c | 493 ++++++++-------- 68 files changed, 3515 insertions(+), 3509 deletions(-) create mode 100644 include/sway/tree/node.h create mode 100644 sway/tree/node.c diff --git a/include/sway/config.h b/include/sway/config.h index 6024f0f60..2fef0081a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -407,7 +407,9 @@ struct sway_config { struct output_config *output_config; struct seat_config *seat_config; struct sway_seat *seat; - struct sway_container *current_container; + struct sway_node *node; + struct sway_container *container; + struct sway_workspace *workspace; bool using_criteria; struct { int argc; @@ -486,8 +488,7 @@ struct output_config *new_output_config(const char *name); void merge_output_config(struct output_config *dst, struct output_config *src); -void apply_output_config(struct output_config *oc, - struct sway_container *output); +void apply_output_config(struct output_config *oc, struct sway_output *output); struct output_config *store_output_config(struct output_config *oc); diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 7ac924e7e..66e8c9a25 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -1,7 +1,6 @@ #ifndef _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H -#include -#include "sway/tree/container.h" +#include /** * Transactions enable us to perform atomic layout updates. @@ -21,6 +20,7 @@ */ struct sway_transaction_instruction; +struct sway_view; /** * Find all dirty containers, create and commit a transaction containing them, diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index aa2f6f19b..bde3cf46c 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -37,10 +37,10 @@ struct sway_input_manager { struct sway_input_manager *input_manager_create(struct sway_server *server); bool input_manager_has_focus(struct sway_input_manager *input, - struct sway_container *container); + struct sway_node *node); void input_manager_set_focus(struct sway_input_manager *input, - struct sway_container *container); + struct sway_node *node); void input_manager_configure_xcursor(struct sway_input_manager *input); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 5c404ecd3..8a7e5450a 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -13,9 +13,9 @@ struct sway_seat_device { struct wl_list link; // sway_seat::devices }; -struct sway_seat_container { +struct sway_seat_node { struct sway_seat *seat; - struct sway_container *container; + struct sway_node *node; struct wl_list link; // sway_seat::focus_stack @@ -76,7 +76,7 @@ struct sway_seat { uint32_t last_button_serial; struct wl_listener focus_destroy; - struct wl_listener new_container; + struct wl_listener new_node; struct wl_listener new_drag_icon; struct wl_list devices; // sway_seat_device::link @@ -100,10 +100,10 @@ void seat_remove_device(struct sway_seat *seat, void seat_configure_xcursor(struct sway_seat *seat); -void seat_set_focus(struct sway_seat *seat, struct sway_container *container); +void seat_set_focus(struct sway_seat *seat, struct sway_node *node); void seat_set_focus_warp(struct sway_seat *seat, - struct sway_container *container, bool warp, bool notify); + struct sway_node *node, bool warp, bool notify); void seat_set_focus_surface(struct sway_seat *seat, struct wlr_surface *surface, bool unfocus); @@ -114,7 +114,11 @@ void seat_set_focus_layer(struct sway_seat *seat, void seat_set_exclusive_client(struct sway_seat *seat, struct wl_client *client); -struct sway_container *seat_get_focus(struct sway_seat *seat); +struct sway_node *seat_get_focus(struct sway_seat *seat); + +struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat); + +struct sway_container *seat_get_focused_container(struct sway_seat *seat); /** * Return the last container to be focused for the seat (or the most recently @@ -125,32 +129,31 @@ struct sway_container *seat_get_focus(struct sway_seat *seat); * is destroyed, or focus moves to a container with children and we need to * descend into the next leaf in focus order. */ -struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, - struct sway_container *container); +struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, + struct sway_node *node); struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, - struct sway_container *container); + struct sway_workspace *workspace); /** * Descend into the focus stack to find the focus-inactive view. Useful for * container placement when they change position in the tree. */ struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, - struct sway_container *container); + struct sway_node *ancestor); /** * Return the immediate child of container which was most recently focused. */ -struct sway_container *seat_get_active_child(struct sway_seat *seat, - struct sway_container *container); +struct sway_node *seat_get_active_child(struct sway_seat *seat, + struct sway_node *parent); /** * Iterate over the focus-inactive children of the container calling the * function on each. */ -void seat_focus_inactive_children_for_each(struct sway_seat *seat, - struct sway_container *container, - void (*f)(struct sway_container *container, void *data), void *data); +void seat_for_each_node(struct sway_seat *seat, + void (*f)(struct sway_node *node, void *data), void *data); void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); @@ -173,7 +176,7 @@ void seat_begin_resize_tiling(struct sway_seat *seat, struct sway_container *con, uint32_t button, enum wlr_edges edge); struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, - struct sway_container *container); + struct sway_workspace *workspace); void seat_end_mouse_operation(struct sway_seat *seat); diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index eaaa21645..fef243e38 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h @@ -7,8 +7,8 @@ json_object *ipc_json_get_version(); json_object *ipc_json_describe_disabled_output(struct sway_output *o); -json_object *ipc_json_describe_container(struct sway_container *c); -json_object *ipc_json_describe_container_recursive(struct sway_container *c); +json_object *ipc_json_describe_node(struct sway_node *node); +json_object *ipc_json_describe_node_recursive(struct sway_node *node); json_object *ipc_json_describe_input(struct sway_input_device *device); json_object *ipc_json_describe_seat(struct sway_seat *seat); json_object *ipc_json_describe_bar_config(struct bar_config *bar); diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index 4b6d0e256..80180ec4e 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -11,8 +11,8 @@ void ipc_init(struct sway_server *server); struct sockaddr_un *ipc_user_sockaddr(void); -void ipc_event_workspace(struct sway_container *old, - struct sway_container *new, const char *change); +void ipc_event_workspace(struct sway_workspace *old, + struct sway_workspace *new, const char *change); void ipc_event_window(struct sway_container *window, const char *change); void ipc_event_barconfig_update(struct bar_config *bar); void ipc_event_mode(const char *mode, bool pango); diff --git a/include/sway/output.h b/include/sway/output.h index 651fdfe77..540ed8a03 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -6,14 +6,20 @@ #include #include #include "config.h" +#include "sway/tree/node.h" #include "sway/tree/view.h" struct sway_server; struct sway_container; +struct sway_output_state { + list_t *workspaces; + struct sway_workspace *active_workspace; +}; + struct sway_output { + struct sway_node node; struct wlr_output *wlr_output; - struct sway_container *swayc; struct sway_server *server; struct wl_list layers[4]; // sway_layer_surface::link @@ -22,11 +28,15 @@ struct sway_output { struct timespec last_frame; struct wlr_output_damage *damage; + bool enabled; + list_t *workspaces; + + struct sway_output_state current; + struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; struct wl_listener scale; - struct wl_listener damage_destroy; struct wl_listener damage_frame; @@ -39,13 +49,19 @@ struct sway_output { } events; }; -struct sway_container *output_create(struct sway_output *sway_output); +struct sway_output *output_create(struct wlr_output *wlr_output); -void output_destroy(struct sway_container *output); +void output_destroy(struct sway_output *output); -void output_begin_destroy(struct sway_container *output); +void output_begin_destroy(struct sway_output *output); -struct sway_container *output_from_wlr_output(struct wlr_output *output); +struct sway_output *output_from_wlr_output(struct wlr_output *output); + +struct sway_output *output_get_in_direction(struct sway_output *reference, + enum movement_direction direction); + +void output_add_workspace(struct sway_output *output, + struct sway_workspace *workspace); typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, struct wlr_surface *surface, struct wlr_box *box, float rotation, @@ -64,15 +80,19 @@ 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); -struct sway_container *output_by_name(const char *name); +struct sway_output *output_by_name(const char *name); -void output_sort_workspaces(struct sway_container *output); +void output_sort_workspaces(struct sway_output *output); -void output_enable(struct sway_output *output); +struct output_config *output_find_config(struct sway_output *output); + +void output_enable(struct sway_output *output, struct output_config *oc); + +void output_disable(struct sway_output *output); bool output_has_opaque_overlay_layer_surface(struct sway_output *output); -struct sway_container *output_get_active_workspace(struct sway_output *output); +struct sway_workspace *output_get_active_workspace(struct sway_output *output); void output_render(struct sway_output *output, struct timespec *when, pixman_region32_t *damage); @@ -103,16 +123,23 @@ 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_container *output, +void output_for_each_workspace(struct sway_output *output, + void (*f)(struct sway_workspace *ws, void *data), void *data); + +void output_for_each_container(struct sway_output *output, void (*f)(struct sway_container *con, void *data), void *data); -void output_for_each_container(struct sway_container *output, - void (*f)(struct sway_container *con, void *data), void *data); +struct sway_workspace *output_find_workspace(struct sway_output *output, + bool (*test)(struct sway_workspace *ws, void *data), void *data); -struct sway_container *output_find_workspace(struct sway_container *output, +struct sway_container *output_find_container(struct sway_output *output, bool (*test)(struct sway_container *con, void *data), void *data); -struct sway_container *output_find_container(struct sway_container *output, - bool (*test)(struct sway_container *con, void *data), void *data); +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 output_add_listeners(struct sway_output *output); #endif diff --git a/include/sway/server.h b/include/sway/server.h index 1e20f2c8d..07e0949a0 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -56,7 +56,7 @@ struct sway_server { size_t txn_timeout_ms; list_t *transactions; - list_t *dirty_containers; + list_t *dirty_nodes; }; struct sway_server server; diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h index f47e8db5e..06a2279c1 100644 --- a/include/sway/tree/arrange.h +++ b/include/sway/tree/arrange.h @@ -1,16 +1,19 @@ #ifndef _SWAY_ARRANGE_H #define _SWAY_ARRANGE_H +struct sway_output; +struct sway_workspace; struct sway_container; +struct sway_node; void arrange_container(struct sway_container *container); -void arrange_workspace(struct sway_container *workspace); +void arrange_workspace(struct sway_workspace *workspace); -void arrange_output(struct sway_container *output); +void arrange_output(struct sway_output *output); void arrange_root(void); -void arrange_windows(struct sway_container *container); +void arrange_node(struct sway_node *node); #endif diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index e4071cfea..c51425c93 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -5,8 +5,7 @@ #include #include #include "list.h" - -extern struct sway_container root_container; +#include "sway/tree/node.h" struct sway_view; struct sway_seat; @@ -17,23 +16,6 @@ struct sway_seat; #define TITLEBAR_H_PADDING 3 #define TITLEBAR_V_PADDING 4 -/** - * Different kinds of containers. - * - * This enum is in order. A container will never be inside of a container below - * it on this list. - */ -enum sway_container_type { - C_ROOT, - C_OUTPUT, - C_WORKSPACE, - C_CONTAINER, - C_VIEW, - - // Keep last - C_TYPES, -}; - enum sway_container_layout { L_NONE, L_HORIZ, @@ -57,18 +39,14 @@ enum movement_direction; enum wlr_direction; struct sway_container_state { - // Container/swayc properties + // Container properties enum sway_container_layout layout; - double swayc_x, swayc_y; - double swayc_width, swayc_height; + double con_x, con_y; + double con_width, con_height; bool is_fullscreen; - bool has_gaps; - double current_gaps; - double gaps_inner; - double gaps_outer; - + struct sway_workspace *workspace; struct sway_container *parent; list_t *children; @@ -86,35 +64,19 @@ struct sway_container_state { bool border_left; bool border_right; bool using_csd; - - // Workspace properties - struct sway_container *ws_fullscreen; - list_t *ws_floating; }; struct sway_container { - union { - // TODO: Encapsulate state for other node types as well like C_CONTAINER - struct sway_root *sway_root; - struct sway_output *sway_output; - struct sway_workspace *sway_workspace; - struct sway_view *sway_view; - }; - - /** - * A unique ID to identify this container. Primarily used in the - * get_tree JSON output. - */ - size_t id; + struct sway_node node; + struct sway_view *view; // The pending state is the main container properties, and the current state is in the below struct. // This means most places of the code can refer to the main variables (pending state) and it'll just work. struct sway_container_state current; - char *name; // The view's title (unformatted) + char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar - enum sway_container_type type; enum sway_container_layout layout; enum sway_container_layout prev_split_layout; @@ -132,14 +94,13 @@ struct sway_container { // The gaps currently applied to the container. double current_gaps; - bool has_gaps; double gaps_inner; double gaps_outer; - list_t *children; - - struct sway_container *parent; + struct sway_workspace *workspace; // NULL when hidden in the scratchpad + struct sway_container *parent; // NULL if container in root of workspace + list_t *children; // struct sway_container // Outputs currently being intersected list_t *outputs; // struct sway_output @@ -157,42 +118,17 @@ struct sway_container { struct wlr_texture *title_urgent; size_t title_height; - // The number of transactions which reference this container. - size_t ntxnrefs; - - // If this container is a view and is waiting for the client to respond to a - // configure then this will be populated, otherwise NULL. - struct sway_transaction_instruction *instruction; - - bool destroying; - - // If true, indicates that the container has pending state that differs from - // the current. - bool dirty; - struct { struct wl_signal destroy; } events; }; -struct sway_container *container_create(enum sway_container_type type); - -const char *container_type_to_str(enum sway_container_type type); - -/* - * Create a new view container. A view can be a child of a workspace container - * or a container container and are rendered in the order and structure of - * how they are attached to the tree. - */ -struct sway_container *container_view_create( - struct sway_container *sibling, struct sway_view *sway_view); +struct sway_container *container_create(struct sway_view *view); void container_destroy(struct sway_container *con); void container_begin_destroy(struct sway_container *con); -struct sway_container *container_close(struct sway_container *container); - /** * Search a container's descendants a container based on test criteria. Returns * the first container that passes the test. @@ -200,23 +136,17 @@ struct sway_container *container_close(struct sway_container *container); struct sway_container *container_find_child(struct sway_container *container, bool (*test)(struct sway_container *view, void *data), void *data); -/** - * Finds a parent container with the given struct sway_containerype. - */ -struct sway_container *container_parent(struct sway_container *container, - enum sway_container_type type); - /** * Find a container at the given coordinates. Returns the 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_container *workspace, +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_container *con, double lx, double ly, + struct sway_node *parent, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); void container_for_each_child(struct sway_container *container, @@ -228,16 +158,11 @@ void container_for_each_child(struct sway_container *container, bool container_has_ancestor(struct sway_container *container, struct sway_container *ancestor); -int container_count_descendants_of_type(struct sway_container *con, - enum sway_container_type type); - -void container_create_notify(struct sway_container *container); - void container_update_textures_recursive(struct sway_container *con); void container_damage_whole(struct sway_container *container); -struct sway_container *container_reap_empty(struct sway_container *con); +void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); @@ -248,11 +173,10 @@ void container_update_title_textures(struct sway_container *container); */ void container_calculate_title_height(struct sway_container *container); -/** - * Notify a container that a tree modification has changed in its children, - * so the container can update its tree representation. - */ -void container_notify_subtree_changed(struct sway_container *container); +size_t container_build_representation(enum sway_container_layout layout, + list_t *children, char *buffer); + +void container_update_representation(struct sway_container *container); /** * Return the height of a regular title bar. @@ -288,8 +212,7 @@ void container_floating_translate(struct sway_container *con, /** * Choose an output for the floating container's new position. */ -struct sway_container *container_floating_find_output( - struct sway_container *con); +struct sway_output *container_floating_find_output(struct sway_container *con); /** * Move a floating container to a new layout-local position. @@ -302,12 +225,6 @@ void container_floating_move_to(struct sway_container *con, */ void container_floating_move_to_center(struct sway_container *con); -/** - * Mark a container as dirty if it isn't already. Dirty containers will be - * included in the next transaction then unmarked as dirty. - */ -void container_set_dirty(struct sway_container *container); - bool container_has_urgent_child(struct sway_container *container); /** @@ -342,10 +259,18 @@ void container_remove_gaps(struct sway_container *container); void container_add_gaps(struct sway_container *container); +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(const struct sway_container *container); + int container_sibling_index(const struct sway_container *child); -void container_handle_fullscreen_reparent(struct sway_container *con, - struct sway_container *old_parent); +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, struct sway_container *child); @@ -353,19 +278,16 @@ void container_add_child(struct sway_container *parent, void container_insert_child(struct sway_container *parent, struct sway_container *child, int i); -struct sway_container *container_add_sibling(struct sway_container *parent, - struct sway_container *child); +void container_add_sibling(struct sway_container *parent, + struct sway_container *child, int offset); -struct sway_container *container_remove_child(struct sway_container *child); +void container_detach(struct sway_container *child); -struct sway_container *container_replace_child(struct sway_container *child, - struct sway_container *new_child); +void container_replace(struct sway_container *container, + struct sway_container *replacement); bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out); -enum sway_container_layout container_get_default_layout( - struct sway_container *con); - struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout); diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h new file mode 100644 index 000000000..5b8c19093 --- /dev/null +++ b/include/sway/tree/node.h @@ -0,0 +1,74 @@ +#ifndef _SWAY_NODE_H +#define _SWAY_NODE_H +#include +#include "list.h" + +struct sway_root; +struct sway_output; +struct sway_workspace; +struct sway_container; +struct sway_transaction_instruction; +struct wlr_box; + +enum sway_node_type { + N_ROOT, + N_OUTPUT, + N_WORKSPACE, + N_CONTAINER, +}; + +struct sway_node { + enum sway_node_type type; + union { + struct sway_root *sway_root; + struct sway_output *sway_output; + struct sway_workspace *sway_workspace; + struct sway_container *sway_container; + }; + + /** + * A unique ID to identify this node. + * Primarily used in the get_tree JSON output. + */ + size_t id; + + struct sway_transaction_instruction *instruction; + size_t ntxnrefs; + bool destroying; + + // If true, indicates that the container has pending state that differs from + // the current. + bool dirty; + + struct { + struct wl_signal destroy; + } events; +}; + +void node_init(struct sway_node *node, enum sway_node_type type, void *thing); + +const char *node_type_to_str(enum sway_node_type type); + +/** + * Mark a node as dirty if it isn't already. Dirty nodes will be included in the + * next transaction then unmarked as dirty. + */ +void node_set_dirty(struct sway_node *node); + +bool node_is_view(struct sway_node *node); + +char *node_get_name(struct sway_node *node); + +void node_get_box(struct sway_node *node, struct wlr_box *box); + +struct sway_output *node_get_output(struct sway_node *node); + +enum sway_container_layout node_get_layout(struct sway_node *node); + +struct sway_node *node_get_parent(struct sway_node *node); + +list_t *node_get_children(struct sway_node *node); + +bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor); + +#endif diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index ec6516c99..a2d464f95 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -5,12 +5,14 @@ #include #include #include "sway/tree/container.h" +#include "sway/tree/node.h" #include "config.h" #include "list.h" -extern struct sway_container root_container; +extern struct sway_root *root; struct sway_root { + struct sway_node node; struct wlr_output_layout *output_layout; struct wl_listener output_layout_change; @@ -24,17 +26,21 @@ struct sway_root { // Includes disabled outputs struct wl_list all_outputs; // sway_output::link + double x, y; + double width, height; + + list_t *outputs; // struct sway_output list_t *scratchpad; // struct sway_container list_t *saved_workspaces; // For when there's no connected outputs struct { - struct wl_signal new_container; + struct wl_signal new_node; } events; }; -void root_create(void); +struct sway_root *root_create(void); -void root_destroy(void); +void root_destroy(struct sway_root *root); /** * Move a container to the scratchpad. @@ -56,23 +62,25 @@ void root_scratchpad_show(struct sway_container *con); */ void root_scratchpad_hide(struct sway_container *con); -struct sway_container *root_workspace_for_pid(pid_t pid); +struct sway_workspace *root_workspace_for_pid(pid_t pid); void root_record_workspace_pid(pid_t pid); -void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), +void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), void *data); void root_for_each_container(void (*f)(struct sway_container *con, void *data), void *data); -struct sway_container *root_find_output( - bool (*test)(struct sway_container *con, void *data), void *data); +struct sway_output *root_find_output( + bool (*test)(struct sway_output *output, void *data), void *data); -struct sway_container *root_find_workspace( - bool (*test)(struct sway_container *con, void *data), void *data); +struct sway_workspace *root_find_workspace( + bool (*test)(struct sway_workspace *ws, void *data), void *data); struct sway_container *root_find_container( bool (*test)(struct sway_container *con, void *data), void *data); +void root_get_box(struct sway_root *root, struct wlr_box *box); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 30d3e742d..439dc1bf6 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -58,7 +58,7 @@ struct sway_view { enum sway_view_type type; const struct sway_view_impl *impl; - struct sway_container *swayc; // NULL for unmapped views + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views // Geometry of the view itself (excludes borders) in layout coordinates @@ -254,7 +254,7 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height); /** - * Configure the view's position and size based on the swayc's position and + * Configure the view's position and size based on the container's position and * size, taking borders into consideration. */ void view_autoconfigure(struct sway_view *view); diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 043259191..af9a071ab 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -3,66 +3,98 @@ #include #include "sway/tree/container.h" +#include "sway/tree/node.h" struct sway_view; -struct sway_workspace { - struct sway_container *swayc; +struct sway_workspace_state { struct sway_container *fullscreen; - list_t *floating; // struct sway_container + double x, y; + int width, height; + enum sway_container_layout layout; + struct sway_output *output; + list_t *floating; + list_t *tiling; + + struct sway_container *focused_inactive_child; + bool focused; +}; + +struct sway_workspace { + struct sway_node node; + struct sway_container *fullscreen; + + char *name; + char *representation; + + double x, y; + int width, height; + enum sway_container_layout layout; + enum sway_container_layout prev_split_layout; + + double current_gaps; + bool has_gaps; + double gaps_inner; + double gaps_outer; + + struct sway_output *output; // NULL if no outputs are connected + list_t *floating; // struct sway_container + list_t *tiling; // struct sway_container list_t *output_priority; bool urgent; + + struct sway_workspace_state current; }; extern char *prev_workspace_name; -struct sway_container *workspace_get_initial_output(const char *name); +struct sway_output *workspace_get_initial_output(const char *name); -struct sway_container *workspace_create(struct sway_container *output, +struct sway_workspace *workspace_create(struct sway_output *output, const char *name); -void workspace_destroy(struct sway_container *workspace); +void workspace_destroy(struct sway_workspace *workspace); -void workspace_begin_destroy(struct sway_container *workspace); +void workspace_begin_destroy(struct sway_workspace *workspace); -void workspace_consider_destroy(struct sway_container *ws); +void workspace_consider_destroy(struct sway_workspace *ws); char *workspace_next_name(const char *output_name); -bool workspace_switch(struct sway_container *workspace, +bool workspace_switch(struct sway_workspace *workspace, bool no_auto_back_and_forth); -struct sway_container *workspace_by_number(const char* name); +struct sway_workspace *workspace_by_number(const char* name); -struct sway_container *workspace_by_name(const char*); +struct sway_workspace *workspace_by_name(const char*); -struct sway_container *workspace_output_next(struct sway_container *current); +struct sway_workspace *workspace_output_next(struct sway_workspace *current); -struct sway_container *workspace_next(struct sway_container *current); +struct sway_workspace *workspace_next(struct sway_workspace *current); -struct sway_container *workspace_output_prev(struct sway_container *current); +struct sway_workspace *workspace_output_prev(struct sway_workspace *current); -struct sway_container *workspace_prev(struct sway_container *current); +struct sway_workspace *workspace_prev(struct sway_workspace *current); -bool workspace_is_visible(struct sway_container *ws); +bool workspace_is_visible(struct sway_workspace *ws); -bool workspace_is_empty(struct sway_container *ws); +bool workspace_is_empty(struct sway_workspace *ws); -void workspace_output_raise_priority(struct sway_container *workspace, - struct sway_container *old_output, struct sway_container *new_output); +void workspace_output_raise_priority(struct sway_workspace *workspace, + struct sway_output *old_output, struct sway_output *new_output); -void workspace_output_add_priority(struct sway_container *workspace, - struct sway_container *output); +void workspace_output_add_priority(struct sway_workspace *workspace, + struct sway_output *output); -struct sway_container *workspace_output_get_highest_available( - struct sway_container *ws, struct sway_container *exclude); +struct sway_output *workspace_output_get_highest_available( + struct sway_workspace *ws, struct sway_output *exclude); -void workspace_detect_urgent(struct sway_container *workspace); +void workspace_detect_urgent(struct sway_workspace *workspace); -void workspace_for_each_container(struct sway_container *ws, +void workspace_for_each_container(struct sway_workspace *ws, void (*f)(struct sway_container *con, void *data), void *data); -struct sway_container *workspace_find_container(struct sway_container *ws, +struct sway_container *workspace_find_container(struct sway_workspace *ws, bool (*test)(struct sway_container *con, void *data), void *data); /** @@ -70,13 +102,28 @@ struct sway_container *workspace_find_container(struct sway_container *ws, * The new container will be the only direct tiling child of the workspace. * The new container is returned. */ -struct sway_container *workspace_wrap_children(struct sway_container *ws); +struct sway_container *workspace_wrap_children(struct sway_workspace *ws); -void workspace_add_floating(struct sway_container *workspace, +void workspace_detach(struct sway_workspace *workspace); + +void workspace_add_tiling(struct sway_workspace *workspace, struct sway_container *con); -void workspace_remove_gaps(struct sway_container *ws); +void workspace_add_floating(struct sway_workspace *workspace, + struct sway_container *con); -void workspace_add_gaps(struct sway_container *ws); +void workspace_insert_tiling(struct sway_workspace *workspace, + struct sway_container *con, int index); + +void workspace_remove_gaps(struct sway_workspace *ws); + +void workspace_add_gaps(struct sway_workspace *ws); + +struct sway_container *workspace_split(struct sway_workspace *workspace, + enum sway_container_layout layout); + +void workspace_update_representation(struct sway_workspace *ws); + +void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box); #endif diff --git a/sway/commands.c b/sway/commands.c index e72b89167..b32628cdc 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -212,6 +212,24 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers, return res; } +static void set_config_node(struct sway_node *node) { + config->handler_context.node = node; + switch (node->type) { + case N_CONTAINER: + config->handler_context.container = node->sway_container; + config->handler_context.workspace = node->sway_container->workspace; + break; + case N_WORKSPACE: + config->handler_context.container = NULL; + config->handler_context.workspace = node->sway_workspace; + break; + default: + config->handler_context.container = NULL; + config->handler_context.workspace = NULL; + break; + } +} + struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { // Even though this function will process multiple commands we will only // return the last error, if any (for now). (Since we have access to an @@ -295,12 +313,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { if (!config->handler_context.using_criteria) { // without criteria, the command acts upon the focused // container - config->handler_context.current_container = - seat_get_focus_inactive(seat, &root_container); - if (!sway_assert(config->handler_context.current_container, - "could not get focus-inactive for root container")) { - return NULL; - } + set_config_node(seat_get_focus_inactive(seat, &root->node)); struct cmd_results *res = handler->handle(argc-1, argv+1); if (res->status != CMD_SUCCESS) { free_argv(argc, argv); @@ -314,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } else { for (int i = 0; i < views->length; ++i) { struct sway_view *view = views->items[i]; - config->handler_context.current_container = view->swayc; + set_config_node(&view->container->node); struct cmd_results *res = handler->handle(argc-1, argv+1); if (res->status != CMD_SUCCESS) { free_argv(argc, argv); diff --git a/sway/commands/border.c b/sway/commands/border.c index 9502c877f..95498b2f8 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -13,13 +13,12 @@ struct cmd_results *cmd_border(int argc, char **argv) { return error; } - struct sway_container *container = - config->handler_context.current_container; - if (container->type != C_VIEW) { + struct sway_container *container = config->handler_context.container; + if (!container->view) { return cmd_results_new(CMD_INVALID, "border", "Only views can have borders"); } - struct sway_view *view = container->sway_view; + struct sway_view *view = container->view; if (strcmp(argv[0], "none") == 0) { view->border = B_NONE; @@ -38,11 +37,11 @@ struct cmd_results *cmd_border(int argc, char **argv) { view->border_thickness = atoi(argv[1]); } - if (container_is_floating(view->swayc)) { - container_set_geometry_from_floating_view(view->swayc); + if (container_is_floating(view->container)) { + container_set_geometry_from_floating_view(view->container); } - arrange_windows(view->swayc); + arrange_container(view->container); struct sway_seat *seat = input_manager_current_seat(input_manager); if (seat->cursor) { diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 436376e3b..d8729094c 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -15,24 +15,23 @@ struct cmd_results *cmd_floating(int argc, char **argv) { if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) { return error; } - struct sway_container *container = - config->handler_context.current_container; - if (container->type == C_WORKSPACE && container->children->length == 0) { + struct sway_container *container = config->handler_context.container; + struct sway_workspace *workspace = config->handler_context.workspace; + if (!container && workspace->tiling->length == 0) { return cmd_results_new(CMD_INVALID, "floating", "Can't float an empty workspace"); } - if (container->type == C_WORKSPACE) { + if (!container) { // Wrap the workspace's children in a container so we can float it - struct sway_container *workspace = container; - container = workspace_wrap_children(container); + container = workspace_wrap_children(workspace); workspace->layout = L_HORIZ; - seat_set_focus(config->handler_context.seat, container); + seat_set_focus(config->handler_context.seat, &container->node); } // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(container)) { - while (container->parent->type != C_WORKSPACE) { + while (container->parent) { container = container->parent; } } @@ -51,8 +50,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); - struct sway_container *workspace = container_parent(container, C_WORKSPACE); - arrange_windows(workspace); + arrange_workspace(container->workspace); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/focus.c b/sway/commands/focus.c index f342e524b..e31898af3 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -34,211 +34,139 @@ static bool parse_movement_direction(const char *name, } /** - * Get swayc in the direction of newly entered output. + * Get node in the direction of newly entered output. */ -static struct sway_container *get_swayc_in_output_direction( - struct sway_container *output, enum movement_direction dir, - struct sway_seat *seat) { - if (!output) { - return NULL; +static struct sway_node *get_node_in_output_direction( + struct sway_output *output, enum movement_direction dir) { + struct sway_seat *seat = config->handler_context.seat; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws->fullscreen) { + return seat_get_focus_inactive(seat, &ws->fullscreen->node); } + struct sway_container *container = NULL; - struct sway_container *ws = seat_get_focus_inactive(seat, output); - if (ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } - - if (ws == NULL) { - wlr_log(WLR_ERROR, "got an output without a workspace"); - return NULL; - } - - if (ws->children->length > 0) { + if (ws->tiling->length > 0) { switch (dir) { case MOVE_LEFT: if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { // get most right child of new output - return ws->children->items[ws->children->length-1]; + container = ws->tiling->items[ws->tiling->length-1]; } else { - return seat_get_focus_inactive(seat, ws); + container = seat_get_focus_inactive_tiling(seat, ws); } + return &container->node; case MOVE_RIGHT: if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { // get most left child of new output - return ws->children->items[0]; + container = ws->tiling->items[0]; } else { - return seat_get_focus_inactive(seat, ws); + container = seat_get_focus_inactive_tiling(seat, ws); } + return &container->node; case MOVE_UP: - case MOVE_DOWN: { - struct sway_container *focused = - seat_get_focus_inactive(seat, ws); - if (focused && focused->parent) { - struct sway_container *parent = focused->parent; - if (parent->layout == L_VERT) { - if (dir == MOVE_UP) { - // get child furthest down on new output - int idx = parent->children->length - 1; - return parent->children->items[idx]; - } else if (dir == MOVE_DOWN) { - // get child furthest up on new output - return parent->children->items[0]; - } - } - return focused; + if (ws->layout == L_VERT || ws->layout == L_STACKED) { + // get most bottom child of new output + container = ws->tiling->items[ws->tiling->length-1]; + } else { + container = seat_get_focus_inactive_tiling(seat, ws); } - break; + return &container->node; + case MOVE_DOWN: { + if (ws->layout == L_VERT || ws->layout == L_STACKED) { + // get most top child of new output + container = ws->tiling->items[0]; + } else { + container = seat_get_focus_inactive_tiling(seat, ws); + } + return &container->node; } default: break; } } - return ws; + return &ws->node; } -static struct sway_container *container_get_in_direction( - struct sway_container *container, struct sway_seat *seat, - enum movement_direction dir) { - struct sway_container *parent = container->parent; - +static struct sway_node *node_get_in_direction(struct sway_container *container, + struct sway_seat *seat, enum movement_direction dir) { if (dir == MOVE_CHILD) { - return seat_get_focus_inactive(seat, container); + return seat_get_active_child(seat, &container->node); } if (container->is_fullscreen) { if (dir == MOVE_PARENT) { return NULL; } - container = container_parent(container, C_OUTPUT); - parent = container->parent; - } else { - if (dir == MOVE_PARENT) { - if (parent->type == C_OUTPUT || container_is_floating(container)) { - return NULL; - } else { - return parent; - } - } + // Fullscreen container with a direction - go straight to outputs + struct sway_output *output = container->workspace->output; + struct sway_output *new_output = output_get_in_direction(output, dir); + return get_node_in_output_direction(new_output, dir); + } + if (dir == MOVE_PARENT) { + return node_get_parent(&container->node); } struct sway_container *wrap_candidate = NULL; - while (true) { + struct sway_container *current = container; + while (current) { bool can_move = false; int desired; - int idx = list_find(container->parent->children, container); - if (idx == -1) { - return NULL; - } - if (parent->type == C_ROOT) { - enum wlr_direction wlr_dir = 0; - if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir), - "got invalid direction: %d", dir)) { - return NULL; - } - int lx = container->x + container->width / 2; - int ly = container->y + container->height / 2; - struct wlr_output_layout *layout = - root_container.sway_root->output_layout; - struct wlr_output *wlr_adjacent = - wlr_output_layout_adjacent_output(layout, wlr_dir, - container->sway_output->wlr_output, lx, ly); - struct sway_container *adjacent = - output_from_wlr_output(wlr_adjacent); + int idx = container_sibling_index(current); + enum sway_container_layout parent_layout = + container_parent_layout(current); + list_t *siblings = container_get_siblings(current); - if (!adjacent || adjacent == container) { - if (!wrap_candidate) { - return NULL; - } - return seat_get_focus_inactive_view(seat, wrap_candidate); - } - struct sway_container *next = - get_swayc_in_output_direction(adjacent, dir, seat); - if (next == NULL) { - return NULL; - } - struct sway_container *next_workspace = next; - if (next_workspace->type != C_WORKSPACE) { - next_workspace = container_parent(next_workspace, C_WORKSPACE); - } - sway_assert(next_workspace, "Next container has no workspace"); - if (next_workspace->sway_workspace->fullscreen) { - return seat_get_focus_inactive(seat, - next_workspace->sway_workspace->fullscreen); - } - if (next->children && next->children->length) { - // TODO consider floating children as well - return seat_get_focus_inactive_view(seat, next); - } else { - return next; + if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { + if (parent_layout == L_HORIZ || parent_layout == L_TABBED) { + can_move = true; + desired = idx + (dir == MOVE_LEFT ? -1 : 1); } } else { - if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { - if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { - can_move = true; - desired = idx + (dir == MOVE_LEFT ? -1 : 1); - } - } else { - if (parent->layout == L_VERT || parent->layout == L_STACKED) { - can_move = true; - desired = idx + (dir == MOVE_UP ? -1 : 1); - } + if (parent_layout == L_VERT || parent_layout == L_STACKED) { + can_move = true; + desired = idx + (dir == MOVE_UP ? -1 : 1); } } if (can_move) { - // TODO handle floating - if (desired < 0 || desired >= parent->children->length) { + if (desired < 0 || desired >= siblings->length) { can_move = false; - int len = parent->children->length; + int len = siblings->length; if (config->focus_wrapping != WRAP_NO && !wrap_candidate && len > 1) { if (desired < 0) { - wrap_candidate = parent->children->items[len-1]; + wrap_candidate = siblings->items[len-1]; } else { - wrap_candidate = parent->children->items[0]; + wrap_candidate = siblings->items[0]; } if (config->focus_wrapping == WRAP_FORCE) { - return seat_get_focus_inactive_view(seat, - wrap_candidate); + struct sway_container *c = seat_get_focus_inactive_view( + seat, &wrap_candidate->node); + return &c->node; } } } else { - struct sway_container *desired_con = - parent->children->items[desired]; - wlr_log(WLR_DEBUG, - "cont %d-%p dir %i sibling %d: %p", idx, - container, dir, desired, desired_con); - return seat_get_focus_inactive_view(seat, desired_con); + struct sway_container *desired_con = siblings->items[desired]; + struct sway_container *c = seat_get_focus_inactive_view( + seat, &desired_con->node); + return &c->node; } } - if (!can_move) { - container = parent; - parent = parent->parent; - if (!parent) { - // wrapping is the last chance - if (!wrap_candidate) { - return NULL; - } - return seat_get_focus_inactive_view(seat, wrap_candidate); - } - } + current = current->parent; } + + // Check a different output + struct sway_output *output = container->workspace->output; + struct sway_output *new_output = output_get_in_direction(output, dir); + if (new_output) { + return get_node_in_output_direction(new_output, dir); + } + return NULL; } -static struct cmd_results *focus_mode(struct sway_container *con, +static struct cmd_results *focus_mode(struct sway_workspace *ws, struct sway_seat *seat, bool floating) { - struct sway_container *ws = con->type == C_WORKSPACE ? - con : container_parent(con, C_WORKSPACE); - - // If the container is in a floating split container, - // operate on the split container instead of the child. - if (container_is_floating_or_child(con)) { - while (con->parent->type != C_WORKSPACE) { - con = con->parent; - } - } - struct sway_container *new_focus = NULL; if (floating) { new_focus = seat_get_focus_inactive_floating(seat, ws); @@ -246,7 +174,7 @@ static struct cmd_results *focus_mode(struct sway_container *con, new_focus = seat_get_focus_inactive_tiling(seat, ws); } if (new_focus) { - seat_set_focus(seat, new_focus); + seat_set_focus(seat, &new_focus->node); } else { return cmd_results_new(CMD_FAILURE, "focus", "Failed to find a %s container in workspace", @@ -255,14 +183,14 @@ static struct cmd_results *focus_mode(struct sway_container *con, return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static struct cmd_results *focus_output(struct sway_container *con, - struct sway_seat *seat, int argc, char **argv) { +static struct cmd_results *focus_output(struct sway_seat *seat, + int argc, char **argv) { if (!argc) { return cmd_results_new(CMD_INVALID, "focus", "Expected 'focus output '"); } char *identifier = join_args(argv, argc); - struct sway_container *output = output_by_name(identifier); + struct sway_output *output = output_by_name(identifier); if (!output) { enum movement_direction direction; @@ -272,14 +200,13 @@ static struct cmd_results *focus_output(struct sway_container *con, return cmd_results_new(CMD_INVALID, "focus", "There is no output with that name"); } - struct sway_container *focus = seat_get_focus(seat); - focus = container_parent(focus, C_OUTPUT); - output = container_get_in_direction(focus, seat, direction); + struct sway_workspace *ws = seat_get_focused_workspace(seat); + output = output_get_in_direction(ws->output, direction); } free(identifier); if (output) { - seat_set_focus(seat, seat_get_focus_inactive(seat, output)); + seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node)); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); @@ -289,29 +216,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) { if (config->reading || !config->active) { return cmd_results_new(CMD_DEFER, NULL, NULL); } - struct sway_container *con = config->handler_context.current_container; + struct sway_node *node = config->handler_context.node; + struct sway_container *container = config->handler_context.container; + struct sway_workspace *workspace = config->handler_context.workspace; struct sway_seat *seat = config->handler_context.seat; - if (con->type < C_WORKSPACE) { + if (node->type < N_WORKSPACE) { return cmd_results_new(CMD_FAILURE, "focus", "Command 'focus' cannot be used above the workspace level"); } if (argc == 0) { - seat_set_focus(seat, con); + seat_set_focus(seat, node); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } if (strcmp(argv[0], "floating") == 0) { - return focus_mode(con, seat, true); + return focus_mode(workspace, seat, true); } else if (strcmp(argv[0], "tiling") == 0) { - return focus_mode(con, seat, false); + return focus_mode(workspace, seat, false); } else if (strcmp(argv[0], "mode_toggle") == 0) { - return focus_mode(con, seat, !container_is_floating_or_child(con)); + bool floating = container && container_is_floating_or_child(container); + return focus_mode(workspace, seat, !floating); } if (strcmp(argv[0], "output") == 0) { argc--; argv++; - return focus_output(con, seat, argc, argv); + return focus_output(seat, argc, argv); } enum movement_direction direction = 0; @@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) { "or 'focus output '"); } - struct sway_container *next_focus = container_get_in_direction( - con, seat, direction); + if (node->type == N_WORKSPACE) { + // A workspace is focused, so just jump to the next output + struct sway_output *new_output = + output_get_in_direction(workspace->output, direction); + struct sway_node *node = + get_node_in_output_direction(new_output, direction); + seat_set_focus(seat, node); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); + } + + struct sway_node *next_focus = + node_get_in_direction(container, seat, direction); if (next_focus) { seat_set_focus(seat, next_focus); } diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index ac65dffbf..3bbe00c5e 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -12,18 +12,18 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) { return error; } - struct sway_container *container = - config->handler_context.current_container; - if (container->type == C_WORKSPACE && container->children->length == 0) { + struct sway_node *node = config->handler_context.node; + struct sway_container *container = config->handler_context.container; + struct sway_workspace *workspace = config->handler_context.workspace; + if (node->type == N_WORKSPACE && workspace->tiling->length == 0) { return cmd_results_new(CMD_INVALID, "fullscreen", "Can't fullscreen an empty workspace"); } - if (container->type == C_WORKSPACE) { + if (node->type == N_WORKSPACE) { // Wrap the workspace's children in a container so we can fullscreen it - struct sway_container *workspace = container; - container = workspace_wrap_children(container); + container = workspace_wrap_children(workspace); workspace->layout = L_HORIZ; - seat_set_focus(config->handler_context.seat, container); + seat_set_focus(config->handler_context.seat, &container->node); } bool enable = !container->is_fullscreen; @@ -32,9 +32,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { } container_set_fullscreen(container, enable); - - struct sway_container *workspace = container_parent(container, C_WORKSPACE); - arrange_windows(workspace->parent); + arrange_workspace(workspace); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 3906eb709..d676e475b 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -2,6 +2,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/tree/arrange.h" +#include "sway/tree/workspace.h" #include "log.h" #include "stringop.h" #include @@ -43,7 +44,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "gaps", "gaps edge_gaps on|off|toggle"); } - arrange_windows(&root_container); + arrange_root(); } else { int amount_idx = 0; // the current index in argv enum gaps_op op = GAPS_OP_SET; @@ -124,7 +125,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { if (amount_idx == 0) { // gaps config->gaps_inner = val; config->gaps_outer = val; - arrange_windows(&root_container); + arrange_root(); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } // Other variants. The middle-length variant (gaps inner|outer ) @@ -155,21 +156,27 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { } else { config->gaps_outer = total; } - arrange_windows(&root_container); + arrange_root(); } else { - struct sway_container *c = - config->handler_context.current_container; - if (scope == GAPS_SCOPE_WORKSPACE && c->type != C_WORKSPACE) { - c = container_parent(c, C_WORKSPACE); - } - c->has_gaps = true; - if (inner) { - c->gaps_inner = total; + if (scope == GAPS_SCOPE_WORKSPACE) { + struct sway_workspace *ws = config->handler_context.workspace; + ws->has_gaps = true; + if (inner) { + ws->gaps_inner = total; + } else { + ws->gaps_outer = total; + } + arrange_workspace(ws); } else { - c->gaps_outer = total; + struct sway_container *c = config->handler_context.container; + c->has_gaps = true; + if (inner) { + c->gaps_inner = total; + } else { + c->gaps_outer = total; + } + arrange_workspace(c->workspace); } - - arrange_windows(c->parent ? c->parent : &root_container); } } diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index e494f6aa5..0a5c7f28a 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -5,8 +5,8 @@ #include "sway/tree/view.h" static void _configure_view(struct sway_container *con, void *data) { - if (con->type == C_VIEW) { - view_autoconfigure(con->sway_view); + if (con->view) { + view_autoconfigure(con->view); } } diff --git a/sway/commands/kill.c b/sway/commands/kill.c index f3fa52f14..85ca0f339 100644 --- a/sway/commands/kill.c +++ b/sway/commands/kill.c @@ -2,15 +2,27 @@ #include "log.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" -#include "sway/tree/view.h" #include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "sway/commands.h" -struct cmd_results *cmd_kill(int argc, char **argv) { - struct sway_container *con = - config->handler_context.current_container; +static void close_container_iterator(struct sway_container *con, void *data) { + if (con->view) { + view_close(con->view); + } +} - container_close(con); +struct cmd_results *cmd_kill(int argc, char **argv) { + struct sway_container *con = config->handler_context.container; + struct sway_workspace *ws = config->handler_context.workspace; + + if (con) { + close_container_iterator(con, NULL); + container_for_each_child(con, close_container_iterator, NULL); + } else { + workspace_for_each_container(ws, close_container_iterator, NULL); + } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/layout.c b/sway/commands/layout.c index a06832de7..8fa1ce98a 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -4,21 +4,20 @@ #include "sway/commands.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/tree/workspace.h" #include "log.h" -static bool parse_layout_string(char *s, enum sway_container_layout *ptr) { +static enum sway_container_layout parse_layout_string(char *s) { if (strcasecmp(s, "splith") == 0) { - *ptr = L_HORIZ; + return L_HORIZ; } else if (strcasecmp(s, "splitv") == 0) { - *ptr = L_VERT; + return L_VERT; } else if (strcasecmp(s, "tabbed") == 0) { - *ptr = L_TABBED; + return L_TABBED; } else if (strcasecmp(s, "stacking") == 0) { - *ptr = L_STACKED; - } else { - return false; + return L_STACKED; } - return true; + return L_NONE; } static const char* expected_syntax = @@ -26,85 +25,130 @@ static const char* expected_syntax = "'layout toggle [split|all]' or " "'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; +static enum sway_container_layout get_layout_toggle(int argc, char **argv, + enum sway_container_layout layout, + enum sway_container_layout prev_split_layout) { + // "layout toggle" + if (argc == 0) { + return layout == L_HORIZ ? L_VERT : L_HORIZ; + } + + if (argc == 2) { + // "layout toggle split" (same as "layout toggle") + if (strcasecmp(argv[1], "split") == 0) { + return layout == L_HORIZ ? L_VERT : L_HORIZ; + } + // "layout toggle all" + if (strcasecmp(argv[1], "all") == 0) { + return layout == L_HORIZ ? L_VERT : + layout == L_VERT ? L_STACKED : + layout == L_STACKED ? L_TABBED : L_HORIZ; + } + return L_NONE; + } + + enum sway_container_layout parsed; + int curr = 1; + for (; curr < argc; curr++) { + parsed = parse_layout_string(argv[curr]); + if (parsed == layout || (strcmp(argv[curr], "split") == 0 && + (layout == L_VERT || layout == L_HORIZ))) { + break; + } + } + for (int i = curr + 1; i != curr; ++i) { + // cycle round to find next valid layout + if (i >= argc) { + i = 1; + } + parsed = parse_layout_string(argv[i]); + if (parsed != L_NONE) { + return parsed; + } + if (strcmp(argv[i], "split") == 0) { + return layout == L_HORIZ ? L_VERT : + layout == L_VERT ? L_HORIZ : prev_split_layout; + } + // invalid layout strings are silently ignored + } + return L_NONE; +} + +static enum sway_container_layout get_layout(int argc, char **argv, + enum sway_container_layout layout, + enum sway_container_layout prev_split_layout) { + // Check if assigned directly + enum sway_container_layout parsed = parse_layout_string(argv[0]); + if (parsed != L_NONE) { + return parsed; + } + + if (strcasecmp(argv[0], "default") == 0) { + return prev_split_layout; + } + + if (strcasecmp(argv[0], "toggle") == 0) { + argc--; argv++; + return get_layout_toggle(argc, argv, layout, prev_split_layout); + } + + return L_NONE; +} + struct cmd_results *cmd_layout(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { return error; } - struct sway_container *parent = config->handler_context.current_container; + struct sway_container *container = config->handler_context.container; + struct sway_workspace *workspace = config->handler_context.workspace; - if (container_is_floating(parent)) { + if (container && container_is_floating(container)) { return cmd_results_new(CMD_FAILURE, "layout", "Unable to change layout of floating windows"); } - while (parent->type == C_VIEW) { - parent = parent->parent; + // Typically we change the layout of the current container, but if the + // current container is a view (it usually is) then we'll change the layout + // of the parent instead, as it doesn't make sense for views to have layout. + if (container && container->view) { + container = container->parent; } - enum sway_container_layout prev = parent->layout; - bool assigned_directly = parse_layout_string(argv[0], &parent->layout); - if (!assigned_directly) { - if (strcasecmp(argv[0], "default") == 0) { - parent->layout = parent->prev_split_layout; - } else if (strcasecmp(argv[0], "toggle") == 0) { - if (argc == 1) { - parent->layout = - parent->layout == L_STACKED ? L_TABBED : - parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; - } else if (argc == 2) { - if (strcasecmp(argv[1], "all") == 0) { - parent->layout = - parent->layout == L_HORIZ ? L_VERT : - parent->layout == L_VERT ? L_STACKED : - parent->layout == L_STACKED ? L_TABBED : L_HORIZ; - } else if (strcasecmp(argv[1], "split") == 0) { - parent->layout = - parent->layout == L_HORIZ ? L_VERT : - parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; - } else { - return cmd_results_new(CMD_INVALID, "layout", expected_syntax); - } - } else { - enum sway_container_layout parsed_layout; - int curr = 1; - for (; curr < argc; curr++) { - bool valid = parse_layout_string(argv[curr], &parsed_layout); - if ((valid && parsed_layout == parent->layout) || - (strcmp(argv[curr], "split") == 0 && - (parent->layout == L_VERT || parent->layout == L_HORIZ))) { - break; - } - } - for (int i = curr + 1; i != curr; ++i) { - // cycle round to find next valid layout - if (i >= argc) { - i = 1; - } - if (parse_layout_string(argv[i], &parent->layout)) { - break; - } else if (strcmp(argv[i], "split") == 0) { - parent->layout = - parent->layout == L_HORIZ ? L_VERT : - parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; - break; - } // invalid layout strings are silently ignored - } + // We could be working with a container OR a workspace. These are different + // structures, so we set up pointers to they layouts so we can refer them in + // an abstract way. + enum sway_container_layout new_layout = L_NONE; + enum sway_container_layout old_layout = L_NONE; + if (container) { + old_layout = container->layout; + new_layout = get_layout(argc, argv, + container->layout, container->prev_split_layout); + } else { + old_layout = workspace->layout; + new_layout = get_layout(argc, argv, + workspace->layout, workspace->prev_split_layout); + } + if (new_layout == L_NONE) { + return cmd_results_new(CMD_INVALID, "layout", expected_syntax); + } + if (new_layout != old_layout) { + if (container) { + if (old_layout != L_TABBED && old_layout != L_STACKED) { + container->prev_split_layout = old_layout; } + container->layout = new_layout; + container_update_representation(container); + arrange_container(container); } else { - return cmd_results_new(CMD_INVALID, "layout", expected_syntax); + if (old_layout != L_TABBED && old_layout != L_STACKED) { + workspace->prev_split_layout = old_layout; + } + workspace->layout = new_layout; + workspace_update_representation(workspace); + arrange_workspace(workspace); } } - if (parent->layout == L_NONE) { - parent->layout = container_get_default_layout(parent); - } - if (prev != parent->layout) { - if (prev != L_TABBED && prev != L_STACKED) { - parent->prev_split_layout = prev; - } - container_notify_subtree_changed(parent); - arrange_windows(parent->parent); - } return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 9ea8c3015..fb95a7d03 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -18,13 +18,12 @@ struct cmd_results *cmd_mark(int argc, char **argv) { if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) { return error; } - struct sway_container *container = - config->handler_context.current_container; - if (container->type != C_VIEW) { + struct sway_container *container = config->handler_context.container; + if (!container->view) { return cmd_results_new(CMD_INVALID, "mark", "Only views can have marks"); } - struct sway_view *view = container->sway_view; + struct sway_view *view = container->view; bool add = false, toggle = false; while (argc > 0 && strncmp(*argv, "--", 2) == 0) { diff --git a/sway/commands/move.c b/sway/commands/move.c index 4426f24ef..1b2e830c6 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -40,7 +40,7 @@ enum wlr_direction opposite_direction(enum wlr_direction d) { } } -static struct sway_container *output_in_direction(const char *direction_string, +static struct sway_output *output_in_direction(const char *direction_string, struct wlr_output *reference, int ref_lx, int ref_ly) { struct { char *name; @@ -63,113 +63,22 @@ static struct sway_container *output_in_direction(const char *direction_string, if (direction) { struct wlr_output *target = wlr_output_layout_adjacent_output( - root_container.sway_root->output_layout, - direction, reference, ref_lx, ref_ly); + root->output_layout, direction, reference, ref_lx, ref_ly); if (!target) { target = wlr_output_layout_farthest_output( - root_container.sway_root->output_layout, - opposite_direction(direction), reference, ref_lx, ref_ly); + root->output_layout, opposite_direction(direction), + reference, ref_lx, ref_ly); } if (target) { - struct sway_output *sway_output = target->data; - return sway_output->swayc; + return target->data; } } return output_by_name(direction_string); } -static void container_move_to(struct sway_container *container, - struct sway_container *destination) { - if (!sway_assert(container->type == C_CONTAINER || - container->type == C_VIEW, "Expected a container or view")) { - return; - } - if (container == destination - || container_has_ancestor(container, destination)) { - return; - } - struct sway_container *old_parent = NULL; - struct sway_container *new_parent = NULL; - if (container_is_floating(container)) { - // Resolve destination into a workspace - struct sway_container *new_ws = NULL; - if (destination->type == C_OUTPUT) { - new_ws = output_get_active_workspace(destination->sway_output); - } else if (destination->type == C_WORKSPACE) { - new_ws = destination; - } else { - new_ws = container_parent(destination, C_WORKSPACE); - } - if (!new_ws) { - // This can happen if the user has run "move container to mark foo", - // where mark foo is on a hidden scratchpad container. - return; - } - struct sway_container *old_output = - container_parent(container, C_OUTPUT); - old_parent = container_remove_child(container); - workspace_add_floating(new_ws, container); - container_handle_fullscreen_reparent(container, old_parent); - // If changing output, center it within the workspace - if (old_output != new_ws->parent && !container->is_fullscreen) { - container_floating_move_to_center(container); - } - } else { - old_parent = container_remove_child(container); - container->width = container->height = 0; - container->saved_width = container->saved_height = 0; - - if (destination->type == C_VIEW) { - new_parent = container_add_sibling(destination, container); - } else { - new_parent = destination; - container_add_child(destination, container); - } - } - - if (container->type == C_VIEW) { - ipc_event_window(container, "move"); - } - container_notify_subtree_changed(old_parent); - container_notify_subtree_changed(new_parent); - - // If view was moved to a fullscreen workspace, refocus the fullscreen view - struct sway_container *new_workspace = container; - if (new_workspace->type != C_WORKSPACE) { - new_workspace = container_parent(new_workspace, C_WORKSPACE); - } - if (new_workspace->sway_workspace->fullscreen) { - struct sway_seat *seat; - struct sway_container *focus, *focus_ws; - wl_list_for_each(seat, &input_manager->seats, link) { - focus = seat_get_focus(seat); - focus_ws = focus; - if (focus_ws->type != C_WORKSPACE) { - focus_ws = container_parent(focus_ws, C_WORKSPACE); - } - if (focus_ws == new_workspace) { - struct sway_container *new_focus = seat_get_focus_inactive(seat, - new_workspace->sway_workspace->fullscreen); - seat_set_focus(seat, new_focus); - } - } - } - // Update workspace urgent state - struct sway_container *old_workspace = old_parent; - if (old_workspace->type != C_WORKSPACE) { - old_workspace = container_parent(old_workspace, C_WORKSPACE); - } - if (new_workspace != old_workspace) { - workspace_detect_urgent(new_workspace); - if (old_workspace) { - workspace_detect_urgent(old_workspace); - } - } -} - static bool is_parallel(enum sway_container_layout layout, enum movement_direction dir) { switch (layout) { @@ -184,326 +93,337 @@ static bool is_parallel(enum sway_container_layout layout, } } -static enum movement_direction invert_movement(enum movement_direction dir) { - switch (dir) { - case MOVE_LEFT: - return MOVE_RIGHT; - case MOVE_RIGHT: - return MOVE_LEFT; - case MOVE_UP: - return MOVE_DOWN; - case MOVE_DOWN: - return MOVE_UP; - default: - sway_assert(0, "This function expects left|right|up|down"); - return MOVE_LEFT; +/** + * Ensures all seats focus the fullscreen container if needed. + */ +static void workspace_focus_fullscreen(struct sway_workspace *workspace) { + if (!workspace->fullscreen) { + return; + } + struct sway_seat *seat; + struct sway_workspace *focus_ws; + wl_list_for_each(seat, &input_manager->seats, link) { + focus_ws = seat_get_focused_workspace(seat); + if (focus_ws == workspace) { + struct sway_node *new_focus = + seat_get_focus_inactive(seat, &workspace->fullscreen->node); + seat_set_focus(seat, new_focus); + } } } -static int move_offs(enum movement_direction move_dir) { - return move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; +static void container_move_to_container_from_direction( + struct sway_container *container, struct sway_container *destination, + enum movement_direction move_dir) { + if (destination->view) { + if (destination->parent == container->parent) { + wlr_log(WLR_DEBUG, "Swapping siblings"); + list_t *siblings = container_get_siblings(container); + int container_index = list_find(siblings, container); + int destination_index = list_find(siblings, destination); + list_swap(siblings, container_index, destination_index); + } else { + wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); + int offset = move_dir == MOVE_LEFT || move_dir == MOVE_UP; + int index = container_sibling_index(destination) + offset; + if (destination->parent) { + container_insert_child(destination->parent, container, index); + } else { + workspace_insert_tiling(destination->workspace, + container, index); + } + container->width = container->height = 0; + } + return; + } + + if (is_parallel(destination->layout, move_dir)) { + wlr_log(WLR_DEBUG, "Reparenting container (parallel)"); + int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ? + 0 : destination->children->length; + container_insert_child(destination, container, index); + container->width = container->height = 0; + return; + } + + wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); + struct sway_node *focus_inactive = seat_get_active_child( + config->handler_context.seat, &destination->node); + if (!focus_inactive || focus_inactive == &destination->node) { + // The container has no children + container_add_child(destination, container); + return; + } + + // Try again but with the child + container_move_to_container_from_direction(container, + focus_inactive->sway_container, move_dir); } -/* Gets the index of the most extreme member based on the movement offset */ -static int container_limit(struct sway_container *container, +static void container_move_to_workspace_from_direction( + struct sway_container *container, struct sway_workspace *workspace, enum movement_direction move_dir) { - return move_offs(move_dir) < 0 ? 0 : container->children->length; + if (is_parallel(workspace->layout, move_dir)) { + wlr_log(WLR_DEBUG, "Reparenting container (parallel)"); + int index = move_dir == MOVE_RIGHT || move_dir == MOVE_DOWN ? + 0 : workspace->tiling->length; + workspace_insert_tiling(workspace, container, index); + return; + } + + wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); + struct sway_container *focus_inactive = seat_get_focus_inactive_tiling( + config->handler_context.seat, workspace); + if (!focus_inactive) { + // The workspace has no tiling children + workspace_add_tiling(workspace, container); + return; + } + while (focus_inactive->parent) { + focus_inactive = focus_inactive->parent; + } + container_move_to_container_from_direction(container, focus_inactive, + move_dir); +} + +static void container_move_to_workspace(struct sway_container *container, + struct sway_workspace *workspace) { + if (container->workspace == workspace) { + return; + } + struct sway_workspace *old_workspace = container->workspace; + if (container_is_floating(container)) { + struct sway_output *old_output = container->workspace->output; + container_detach(container); + workspace_add_floating(workspace, container); + container_handle_fullscreen_reparent(container); + // If changing output, center it within the workspace + if (old_output != workspace->output && !container->is_fullscreen) { + container_floating_move_to_center(container); + } + } else { + container_detach(container); + container->width = container->height = 0; + container->saved_width = container->saved_height = 0; + workspace_add_tiling(workspace, container); + container_update_representation(container); + } + if (container->view) { + ipc_event_window(container, "move"); + } + workspace_detect_urgent(old_workspace); + workspace_detect_urgent(workspace); + workspace_focus_fullscreen(workspace); +} + +static void container_move_to_container(struct sway_container *container, + struct sway_container *destination) { + if (container == destination + || container_has_ancestor(container, destination) + || container_has_ancestor(destination, container)) { + return; + } + if (container_is_floating(container)) { + return; + } + struct sway_workspace *old_workspace = container->workspace; + + container_detach(container); + container->width = container->height = 0; + container->saved_width = container->saved_height = 0; + + if (destination->view) { + container_add_sibling(destination, container, 1); + } else { + container_add_child(destination, container); + } + + if (container->view) { + ipc_event_window(container, "move"); + } + + workspace_focus_fullscreen(destination->workspace); + + // Update workspace urgent state + workspace_detect_urgent(destination->workspace); + if (old_workspace != destination->workspace) { + workspace_detect_urgent(old_workspace); + } } /* Takes one child, sets it aside, wraps the rest of the children in a new * container, switches the layout of the workspace, and drops the child back in. * In other words, rejigger it. */ -static void workspace_rejigger(struct sway_container *ws, +static void workspace_rejigger(struct sway_workspace *ws, struct sway_container *child, enum movement_direction move_dir) { - struct sway_container *original_parent = child->parent; - struct sway_container *new_parent = - container_split(ws, ws->layout); - - container_remove_child(child); - for (int i = 0; i < ws->children->length; ++i) { - struct sway_container *_child = ws->children->items[i]; - container_move_to(new_parent, _child); + if (!sway_assert(child->parent == NULL, "Expected a root child")) { + return; } + container_detach(child); + workspace_wrap_children(ws); - int index = move_offs(move_dir); - container_insert_child(ws, child, index < 0 ? 0 : 1); + int index = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? 0 : 1; + workspace_insert_tiling(ws, child, index); ws->layout = move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; - - container_flatten(ws); - container_reap_empty(original_parent); - container_create_notify(new_parent); + workspace_update_representation(ws); } static void move_out_of_tabs_stacks(struct sway_container *container, struct sway_container *current, enum movement_direction move_dir, int offs) { - if (container->parent == current->parent - && current->parent->children->length == 1) { - wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); - current->parent->layout = move_dir == - MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; + enum sway_container_layout layout = move_dir == + MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; + list_t *siblings = container_get_siblings(container); + if (container == current && siblings->length == 1) { + wlr_log(WLR_DEBUG, "Changing layout of parent"); + if (container->parent) { + container->parent->layout = layout; + container_update_representation(container); + } else { + container->workspace->layout = layout; + workspace_update_representation(container->workspace); + } return; } wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); - bool is_workspace = current->parent->type == C_WORKSPACE; - struct sway_container *new_parent = container_split(current->parent, - move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); - if (is_workspace) { - container_insert_child(new_parent->parent, container, offs < 0 ? 0 : 1); - } else { + if (container->parent) { + struct sway_container *new_parent = + container_split(current->parent, layout); container_insert_child(new_parent, container, offs < 0 ? 0 : 1); - container_reap_empty(new_parent->parent); - container_flatten(new_parent->parent); + container_reap_empty(new_parent); + container_flatten(new_parent); + } else { + // Changing a workspace + struct sway_workspace *workspace = container->workspace; + workspace_split(workspace, layout); + workspace_insert_tiling(workspace, container, offs < 0 ? 0 : 1); } - container_create_notify(new_parent); - container_notify_subtree_changed(new_parent); } -static void container_move(struct sway_container *container, - enum movement_direction move_dir, int move_amt) { - if (!sway_assert( - container->type != C_CONTAINER || container->type != C_VIEW, - "Can only move containers and views")) { - return; - } - int offs = move_offs(move_dir); - - struct sway_container *sibling = NULL; - struct sway_container *current = container; - struct sway_container *parent = current->parent; - struct sway_container *top = &root_container; - +// Returns true if moved +static bool container_move_in_direction(struct sway_container *container, + enum movement_direction move_dir) { // If moving a fullscreen view, only consider outputs if (container->is_fullscreen) { - current = container_parent(container, C_OUTPUT); - } else if (container_is_fullscreen_or_child(container) || - container_is_floating_or_child(container)) { - // If we've fullscreened a split container, only allow the child to move - // around within the fullscreen parent. - // Same with floating a split container. - struct sway_container *ws = container_parent(container, C_WORKSPACE); - top = ws->sway_workspace->fullscreen; + struct sway_output *new_output = + output_get_in_direction(container->workspace->output, move_dir); + if (!new_output) { + return false; + } + struct sway_workspace *ws = output_get_active_workspace(new_output); + container_move_to_workspace(container, ws); + return true; } - struct sway_container *new_parent = container_flatten(parent); - if (new_parent != parent) { - // Special case: we were the last one in this container, so leave - return; + // If container is in a split container by itself, move out of the split + if (container->parent) { + struct sway_container *new_parent = + container_flatten(container->parent); + if (new_parent != container->parent) { + return true; + } } - while (!sibling) { - if (current == top) { - return; - } + // Look for a suitable *container* sibling or parent. + // The below loop stops once we hit the workspace because current->parent + // is NULL for the topmost containers in a workspace. + struct sway_container *current = container; + int offs = move_dir == MOVE_LEFT || move_dir == MOVE_UP ? -1 : 1; - parent = current->parent; - wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, - container_type_to_str(current->type), current->name); + while (current) { + struct sway_container *parent = current->parent; + list_t *siblings = container_get_siblings(current); + enum sway_container_layout layout = container_parent_layout(current); + int index = list_find(siblings, current); + int desired = index + offs; - int index = container_sibling_index(current); - - switch (current->type) { - case C_OUTPUT: { - enum wlr_direction wlr_dir = 0; - if (!sway_assert(sway_dir_to_wlr(move_dir, &wlr_dir), - "got invalid direction: %d", move_dir)) { - return; - } - double ref_lx = current->x + current->width / 2; - double ref_ly = current->y + current->height / 2; - struct wlr_output *next = wlr_output_layout_adjacent_output( - root_container.sway_root->output_layout, wlr_dir, - current->sway_output->wlr_output, ref_lx, ref_ly); - if (!next) { - wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); - return; - } - struct sway_output *next_output = next->data; - current = next_output->swayc; - wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); - // Select workspace and get outta here - current = seat_get_focus_inactive( - config->handler_context.seat, current); - if (current->type != C_WORKSPACE) { - current = container_parent(current, C_WORKSPACE); - } - sibling = current; - break; - } - case C_WORKSPACE: - if (!is_parallel(current->layout, move_dir)) { - if (current->children->length >= 2) { - wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", - current->children->length); - workspace_rejigger(current, container, move_dir); - return; - } else { - wlr_log(WLR_DEBUG, "Selecting output"); - current = current->parent; - } - } else if (current->layout == L_TABBED - || current->layout == L_STACKED) { - wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); - workspace_rejigger(current, container, move_dir); - } else { - wlr_log(WLR_DEBUG, "Selecting output"); - current = current->parent; - } - break; - case C_CONTAINER: - case C_VIEW: - if (is_parallel(parent->layout, move_dir)) { - if ((index == parent->children->length - 1 && offs > 0) - || (index == 0 && offs < 0)) { - if (current->parent == container->parent) { - if (!parent->is_fullscreen && - (parent->layout == L_TABBED || - parent->layout == L_STACKED)) { - move_out_of_tabs_stacks(container, current, - move_dir, offs); - return; - } else { - wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); - current = current->parent; - } + if (is_parallel(layout, move_dir)) { + if (desired == -1 || desired == siblings->length) { + if (current->parent == container->parent) { + if (!(parent && parent->is_fullscreen) && + (layout == L_TABBED || layout == L_STACKED)) { + move_out_of_tabs_stacks(container, current, + move_dir, offs); + return true; } else { - wlr_log(WLR_DEBUG, "Hit limit, " - "promoting descendant to sibling"); - // Special case + current = current->parent; + continue; + } + } else { + // Special case + if (current->parent) { container_insert_child(current->parent, container, index + (offs < 0 ? 0 : 1)); - container->width = container->height = 0; - return; + } else { + workspace_insert_tiling(current->workspace, container, + index + (offs < 0 ? 0 : 1)); } - } else { - sibling = parent->children->items[index + offs]; - wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); + return true; } - } else if (!parent->is_fullscreen && (parent->layout == L_TABBED || - parent->layout == L_STACKED)) { - move_out_of_tabs_stacks(container, current, move_dir, offs); - return; } else { - wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); - current = current->parent; + // Container can move within its siblings + container_move_to_container_from_direction(container, + siblings->items[desired], move_dir); + return true; } - break; - default: - sway_assert(0, "Not expecting to see container of type %s here", - container_type_to_str(current->type)); - return; + } else if (!(parent && parent->is_fullscreen) && + (layout == L_TABBED || layout == L_STACKED)) { + move_out_of_tabs_stacks(container, current, move_dir, offs); + return true; + } + + current = current->parent; + + // Don't allow containers to move out of their + // fullscreen or floating parent + if (current && + (current->is_fullscreen || container_is_floating(current))) { + return false; } } - // Part two: move stuff around - int index = container_sibling_index(container); - struct sway_container *old_parent = container->parent; - - while (sibling) { - switch (sibling->type) { - case C_VIEW: - if (sibling->parent == container->parent) { - wlr_log(WLR_DEBUG, "Swapping siblings"); - sibling->parent->children->items[index + offs] = container; - sibling->parent->children->items[index] = sibling; - } else { - wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); - container_insert_child(sibling->parent, container, - container_sibling_index(sibling) + (offs > 0 ? 0 : 1)); - container->width = container->height = 0; - } - sibling = NULL; - break; - case C_WORKSPACE: // Note: only in the case of moving between outputs - case C_CONTAINER: - if (is_parallel(sibling->layout, move_dir)) { - int limit = container_limit(sibling, invert_movement(move_dir)); - wlr_log(WLR_DEBUG, "limit: %d", limit); - wlr_log(WLR_DEBUG, - "Reparenting container (parallel) to index %d " - "(move dir: %d)", limit, move_dir); - container_insert_child(sibling, container, limit); - container->width = container->height = 0; - sibling = NULL; - } else { - wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); - struct sway_container *focus_inactive = seat_get_focus_inactive( - config->handler_context.seat, sibling); - if (focus_inactive && focus_inactive != sibling) { - while (focus_inactive->parent != sibling) { - focus_inactive = focus_inactive->parent; - } - wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", - focus_inactive->id); - sibling = focus_inactive; - continue; - } else if (sibling->children->length) { - wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); - container_remove_child(container); - container_add_sibling(sibling->children->items[0], container); - } else { - wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); - container_remove_child(container); - container_add_child(sibling, container); - } - container->width = container->height = 0; - sibling = NULL; - } - break; - default: - sway_assert(0, "Not expecting to see container of type %s here", - container_type_to_str(sibling->type)); - return; + // Maybe rejigger the workspace + struct sway_workspace *ws = container->workspace; + if (!is_parallel(ws->layout, move_dir)) { + if (ws->tiling->length >= 2) { + workspace_rejigger(ws, container, move_dir); + return true; } + } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { + workspace_rejigger(ws, container, move_dir); + return true; } - container_notify_subtree_changed(old_parent); - container_notify_subtree_changed(container->parent); - - if (container->type == C_VIEW) { - ipc_event_window(container, "move"); + // Try adjacent output + struct sway_output *output = + output_get_in_direction(container->workspace->output, move_dir); + if (output) { + struct sway_workspace *ws = output_get_active_workspace(output); + container_move_to_workspace_from_direction(container, ws, move_dir); + return true; } - - if (old_parent) { - seat_set_focus(config->handler_context.seat, old_parent); - seat_set_focus(config->handler_context.seat, container); - } - - struct sway_container *last_ws = old_parent; - struct sway_container *next_ws = container->parent; - if (last_ws && last_ws->type != C_WORKSPACE) { - last_ws = container_parent(last_ws, C_WORKSPACE); - } - if (next_ws && next_ws->type != C_WORKSPACE) { - next_ws = container_parent(next_ws, C_WORKSPACE); - } - if (last_ws && next_ws && last_ws != next_ws) { - ipc_event_workspace(last_ws, next_ws, "focus"); - workspace_detect_urgent(last_ws); - workspace_detect_urgent(next_ws); - } - container_end_mouse_operation(container); + wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); + return false; } -static struct cmd_results *cmd_move_container(struct sway_container *current, - int argc, char **argv) { +static struct cmd_results *cmd_move_container(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 3))) { return error; } - if (current->type == C_WORKSPACE) { - if (current->children->length == 0) { + struct sway_node *node = config->handler_context.node; + struct sway_workspace *workspace = config->handler_context.workspace; + struct sway_container *container = config->handler_context.container; + if (node->type == N_WORKSPACE) { + if (workspace->tiling->length == 0) { return cmd_results_new(CMD_FAILURE, "move", "Can't move an empty workspace"); } - current = workspace_wrap_children(current); - } else if (current->type != C_CONTAINER && current->type != C_VIEW) { - return cmd_results_new(CMD_FAILURE, "move", - "Can only move containers and views."); + container = workspace_wrap_children(workspace); } bool no_auto_back_and_forth = false; @@ -530,15 +450,15 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, } struct sway_seat *seat = config->handler_context.seat; - struct sway_container *old_parent = current->parent; - struct sway_container *old_ws = container_parent(current, C_WORKSPACE); - struct sway_container *old_output = container_parent(current, C_OUTPUT); - struct sway_container *destination = NULL; + struct sway_container *old_parent = container->parent; + struct sway_workspace *old_ws = container->workspace; + struct sway_output *old_output = old_ws->output; + struct sway_node *destination = NULL; // determine destination if (strcasecmp(argv[1], "workspace") == 0) { // move container to workspace x - struct sway_container *ws = NULL; + struct sway_workspace *ws = NULL; char *ws_name = NULL; if (strcasecmp(argv[2], "next") == 0 || strcasecmp(argv[2], "prev") == 0 || @@ -588,8 +508,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, // We have to create the workspace, but if the container is // sticky and the workspace is going to be created on the same // output, we'll bail out first. - if (current->is_sticky) { - struct sway_container *new_output = + if (container->is_sticky) { + struct sway_output *new_output = workspace_get_initial_output(ws_name); if (old_output == new_output) { free(ws_name); @@ -601,105 +521,113 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, ws = workspace_create(NULL, ws_name); } free(ws_name); - destination = seat_get_focus_inactive(seat, ws); + destination = seat_get_focus_inactive(seat, &ws->node); } else if (strcasecmp(argv[1], "output") == 0) { - struct sway_container *dest_output = output_in_direction(argv[2], - old_output->sway_output->wlr_output, current->x, current->y); - if (!dest_output) { + struct sway_output *new_output = output_in_direction(argv[2], + old_output->wlr_output, container->x, container->y); + if (!new_output) { return cmd_results_new(CMD_FAILURE, "move workspace", "Can't find output with name/direction '%s'", argv[2]); } - destination = seat_get_focus_inactive(seat, dest_output); - if (!destination) { - // We've never been to this output before - destination = dest_output->children->items[0]; - } + destination = seat_get_focus_inactive(seat, &new_output->node); } else if (strcasecmp(argv[1], "mark") == 0) { struct sway_view *dest_view = view_find_mark(argv[2]); if (dest_view == NULL) { return cmd_results_new(CMD_FAILURE, "move", "Mark '%s' not found", argv[2]); } - destination = dest_view->swayc; + destination = &dest_view->container->node; } else { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } - struct sway_container *new_output = destination->type == C_OUTPUT ? - destination : container_parent(destination, C_OUTPUT); - if (current->is_sticky && old_output == new_output) { + if (container->is_sticky && + node_has_ancestor(destination, &old_output->node)) { return cmd_results_new(CMD_FAILURE, "move", "Can't move sticky " "container to another workspace on the same output"); } - struct sway_container *new_output_last_ws = old_output == new_output ? - NULL : seat_get_active_child(seat, new_output); - struct sway_container *new_workspace = destination->type == C_WORKSPACE ? - destination : container_parent(destination, C_WORKSPACE); + struct sway_output *new_output = node_get_output(destination); + struct sway_workspace *new_output_last_ws = old_output == new_output ? + NULL : output_get_active_workspace(new_output); // move container, arrange windows and return focus - container_move_to(current, destination); + switch (destination->type) { + case N_WORKSPACE: + container_move_to_workspace(container, destination->sway_workspace); + break; + case N_OUTPUT: { + struct sway_output *output = destination->sway_output; + struct sway_workspace *ws = output_get_active_workspace(output); + container_move_to_workspace(container, ws); + } + break; + case N_CONTAINER: + container_move_to_container(container, destination->sway_container); + break; + case N_ROOT: + break; + } + struct sway_workspace *new_workspace = + output_get_active_workspace(new_output); if (new_output_last_ws && new_output_last_ws != new_workspace) { // change focus on destination output back to its last active workspace - struct sway_container *new_output_last_focus = - seat_get_focus_inactive(seat, new_output_last_ws); + struct sway_node *new_output_last_focus = + seat_get_focus_inactive(seat, &new_output_last_ws->node); seat_set_focus_warp(seat, new_output_last_focus, false, false); } - struct sway_container *focus = seat_get_focus_inactive(seat, old_parent); - seat_set_focus_warp(seat, focus, true, false); - container_reap_empty(old_parent); - container_reap_empty(destination->parent); - // TODO: Ideally we would arrange the surviving parent after reaping, - // but container_reap_empty does not return it, so we arrange the - // workspace instead. - arrange_windows(old_ws); - arrange_windows(destination->parent); + struct sway_node *focus = NULL; + if (old_parent) { + focus = seat_get_focus_inactive(seat, &old_parent->node); + } else { + focus = seat_get_focus_inactive(seat, &old_ws->node); + } + seat_set_focus_warp(seat, focus, true, false); + + if (old_parent) { + container_reap_empty(old_parent); + } else { + workspace_consider_destroy(old_ws); + } + + arrange_workspace(old_ws); + arrange_node(node_get_parent(destination)); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static void workspace_move_to_output(struct sway_container *workspace, - struct sway_container *output) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { +static void workspace_move_to_output(struct sway_workspace *workspace, + struct sway_output *output) { + if (workspace->output == output) { return; } - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return; - } - if (workspace->parent == output) { - return; - } - struct sway_container *old_output = container_remove_child(workspace); - struct sway_seat *seat = input_manager_get_default_seat(input_manager); - struct sway_container *new_output_focus = - seat_get_focus_inactive(seat, output); + struct sway_output *old_output = workspace->output; + workspace_detach(workspace); + struct sway_workspace *new_output_old_ws = + output_get_active_workspace(output); - container_add_child(output, workspace); + output_add_workspace(output, workspace); // If moving the last workspace from the old output, create a new workspace // on the old output - if (old_output->children->length == 0) { - char *ws_name = workspace_next_name(old_output->name); - struct sway_container *ws = workspace_create(old_output, ws_name); + struct sway_seat *seat = config->handler_context.seat; + if (old_output->workspaces->length == 0) { + char *ws_name = workspace_next_name(old_output->wlr_output->name); + struct sway_workspace *ws = workspace_create(old_output, ws_name); free(ws_name); - seat_set_focus(seat, ws); + seat_set_focus(seat, &ws->node); } - // Try to remove an empty workspace from the destination output. - container_reap_empty(new_output_focus); + workspace_consider_destroy(new_output_old_ws); output_sort_workspaces(output); - seat_set_focus(seat, output); + seat_set_focus(seat, &output->node); workspace_output_raise_priority(workspace, old_output, output); ipc_event_workspace(NULL, workspace, "move"); - - container_notify_subtree_changed(old_output); - container_notify_subtree_changed(output); } -static struct cmd_results *cmd_move_workspace(struct sway_container *current, - int argc, char **argv) { +static struct cmd_results *cmd_move_workspace(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 2))) { return error; @@ -716,27 +644,25 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current, return cmd_results_new(CMD_INVALID, "move", expected_syntax); } - struct sway_container *source = container_parent(current, C_OUTPUT); - int center_x = current->width / 2 + current->x, - center_y = current->height / 2 + current->y; - struct sway_container *destination = output_in_direction(argv[2], - source->sway_output->wlr_output, center_x, center_y); - if (!destination) { + struct sway_workspace *workspace = config->handler_context.workspace; + struct sway_output *old_output = workspace->output; + int center_x = workspace->width / 2 + workspace->x, + center_y = workspace->height / 2 + workspace->y; + struct sway_output *new_output = output_in_direction(argv[2], + old_output->wlr_output, center_x, center_y); + if (!new_output) { return cmd_results_new(CMD_FAILURE, "move workspace", "Can't find output with name/direction '%s'", argv[2]); } - if (current->type != C_WORKSPACE) { - current = container_parent(current, C_WORKSPACE); - } - workspace_move_to_output(current, destination); + workspace_move_to_output(workspace, new_output); - arrange_windows(source); - arrange_windows(destination); + arrange_output(old_output); + arrange_output(new_output); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static struct cmd_results *move_in_direction(struct sway_container *container, +static struct cmd_results *cmd_move_in_direction( enum movement_direction direction, int argc, char **argv) { int move_amt = 10; if (argc > 1) { @@ -748,7 +674,8 @@ static struct cmd_results *move_in_direction(struct sway_container *container, } } - if (container->type == C_WORKSPACE) { + struct sway_container *container = config->handler_context.container; + if (!container) { return cmd_results_new(CMD_FAILURE, "move", "Cannot move workspaces in a direction"); } @@ -780,20 +707,34 @@ static struct cmd_results *move_in_direction(struct sway_container *container, container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } - // For simplicity, we'll arrange the entire workspace. The reason for this - // is moving the container might reap the old parent, and container_move - // does not return a surviving parent. - // TODO: Make container_move return the surviving parent so we can arrange - // just that. - struct sway_container *old_ws = container_parent(container, C_WORKSPACE); - container_move(container, direction, move_amt); - struct sway_container *new_ws = container_parent(container, C_WORKSPACE); + struct sway_workspace *old_ws = container->workspace; - arrange_windows(old_ws); - if (new_ws != old_ws) { - arrange_windows(new_ws); + if (!container_move_in_direction(container, direction)) { + // Container didn't move + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } + struct sway_workspace *new_ws = container->workspace; + + arrange_workspace(old_ws); + if (new_ws != old_ws) { + arrange_workspace(new_ws); + } + + if (container->view) { + ipc_event_window(container, "move"); + } + + seat_set_focus(config->handler_context.seat, &new_ws->node); + seat_set_focus(config->handler_context.seat, &container->node); + + if (old_ws != new_ws) { + ipc_event_workspace(old_ws, new_ws, "focus"); + workspace_detect_urgent(old_ws); + workspace_detect_urgent(new_ws); + } + container_end_mouse_operation(container); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -802,9 +743,9 @@ static const char *expected_position_syntax = "'move [absolute] position center' or " "'move position cursor|mouse|pointer'"; -static struct cmd_results *move_to_position(struct sway_container *container, - int argc, char **argv) { - if (!container_is_floating(container)) { +static struct cmd_results *cmd_move_to_position(int argc, char **argv) { + struct sway_container *container = config->handler_context.container; + if (!container || !container_is_floating(container)) { return cmd_results_new(CMD_FAILURE, "move", "Only floating containers " "can be moved to an absolute position"); @@ -842,10 +783,10 @@ static struct cmd_results *move_to_position(struct sway_container *container, } else if (strcmp(argv[0], "center") == 0) { double lx, ly; if (absolute) { - lx = root_container.x + (root_container.width - container->width) / 2; - ly = root_container.y + (root_container.height - container->height) / 2; + lx = root->x + (root->width - container->width) / 2; + ly = root->y + (root->height - container->height) / 2; } else { - struct sway_container *ws = container_parent(container, C_WORKSPACE); + struct sway_workspace *ws = container->workspace; lx = ws->x + (ws->width - container->width) / 2; ly = ws->y + (ws->height - container->height) / 2; } @@ -881,30 +822,31 @@ static struct cmd_results *move_to_position(struct sway_container *container, } if (!absolute) { - struct sway_container *ws = container_parent(container, C_WORKSPACE); - lx += ws->x; - ly += ws->y; + lx += container->workspace->x; + ly += container->workspace->y; } container_floating_move_to(container, lx, ly); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } -static struct cmd_results *move_to_scratchpad(struct sway_container *con) { - if (con->type == C_WORKSPACE && con->children->length == 0) { +static struct cmd_results *cmd_move_to_scratchpad(void) { + struct sway_node *node = config->handler_context.node; + struct sway_container *con = config->handler_context.container; + struct sway_workspace *ws = config->handler_context.workspace; + if (node->type == N_WORKSPACE && ws->tiling->length == 0) { return cmd_results_new(CMD_INVALID, "move", "Can't move an empty workspace to the scratchpad"); } - if (con->type == C_WORKSPACE) { + if (node->type == N_WORKSPACE) { // Wrap the workspace's children in a container - struct sway_container *workspace = con; - con = workspace_wrap_children(con); - workspace->layout = L_HORIZ; + con = workspace_wrap_children(ws); + ws->layout = L_HORIZ; } // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { - while (con->parent->type != C_WORKSPACE) { + while (con->parent) { con = con->parent; } } @@ -922,32 +864,31 @@ struct cmd_results *cmd_move(int argc, char **argv) { if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { return error; } - struct sway_container *current = config->handler_context.current_container; if (strcasecmp(argv[0], "left") == 0) { - return move_in_direction(current, MOVE_LEFT, argc, argv); + return cmd_move_in_direction(MOVE_LEFT, argc, argv); } else if (strcasecmp(argv[0], "right") == 0) { - return move_in_direction(current, MOVE_RIGHT, argc, argv); + return cmd_move_in_direction(MOVE_RIGHT, argc, argv); } else if (strcasecmp(argv[0], "up") == 0) { - return move_in_direction(current, MOVE_UP, argc, argv); + return cmd_move_in_direction(MOVE_UP, argc, argv); } else if (strcasecmp(argv[0], "down") == 0) { - return move_in_direction(current, MOVE_DOWN, argc, argv); + return cmd_move_in_direction(MOVE_DOWN, argc, argv); } else if ((strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) || - (strcasecmp(argv[0], "--no-auto-back-and-forth") && - (strcasecmp(argv[0], "container") == 0 - || strcasecmp(argv[0], "window") == 0))) { - return cmd_move_container(current, argc, argv); + (strcasecmp(argv[0], "--no-auto-back-and-forth") && argc >= 2 + && (strcasecmp(argv[1], "container") == 0 + || strcasecmp(argv[1], "window") == 0))) { + return cmd_move_container(argc, argv); } else if (strcasecmp(argv[0], "workspace") == 0) { - return cmd_move_workspace(current, argc, argv); + return cmd_move_workspace(argc, argv); } else if (strcasecmp(argv[0], "scratchpad") == 0 || (strcasecmp(argv[0], "to") == 0 && argc == 2 && strcasecmp(argv[1], "scratchpad") == 0)) { - return move_to_scratchpad(current); + return cmd_move_to_scratchpad(); } else if (strcasecmp(argv[0], "position") == 0) { - return move_to_position(current, argc, argv); + return cmd_move_to_position(argc, argv); } else if (strcasecmp(argv[0], "absolute") == 0) { - return move_to_position(current, argc, argv); + return cmd_move_to_position(argc, argv); } else { return cmd_results_new(CMD_INVALID, "move", expected_syntax); } diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 68fd9f427..9cdaad7f4 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -19,8 +19,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { return error; } - struct sway_container *con = - config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; float opacity = 0.0f; diff --git a/sway/commands/reload.c b/sway/commands/reload.c index f8ca374d0..36fb90920 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -42,7 +42,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { } list_free(bar_ids); - arrange_windows(&root_container); + arrange_root(); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 21d2aa642..d982f9415 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -25,14 +25,11 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } int argn = 1; - struct sway_container *workspace; + struct sway_workspace *workspace = NULL; if (strcasecmp(argv[1], "to") == 0) { // 'rename workspace to new_name' - workspace = config->handler_context.current_container; - if (workspace->type != C_WORKSPACE) { - workspace = container_parent(workspace, C_WORKSPACE); - } + workspace = config->handler_context.workspace; } else if (strcasecmp(argv[1], "number") == 0) { // 'rename workspace number x to new_name' if (!isdigit(argv[2][0])) { @@ -78,7 +75,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "rename", "Cannot use special workspace name '%s'", argv[argn]); } - struct sway_container *tmp_workspace = workspace_by_name(new_name); + struct sway_workspace *tmp_workspace = workspace_by_name(new_name); if (tmp_workspace) { free(new_name); return cmd_results_new(CMD_INVALID, "rename", @@ -89,7 +86,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { free(workspace->name); workspace->name = new_name; - output_sort_workspaces(workspace->parent); + output_sort_workspaces(workspace->output); ipc_event_workspace(NULL, workspace, "rename"); return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index ad659ef56..99e9dbda3 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -10,6 +10,7 @@ #include "sway/commands.h" #include "sway/tree/arrange.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "log.h" static const int MIN_SANE_W = 100, MIN_SANE_H = 60; @@ -75,7 +76,7 @@ static int parse_resize_amount(int argc, char **argv, static void calculate_constraints(int *min_width, int *max_width, int *min_height, int *max_height) { - struct sway_container *con = config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; if (config->floating_minimum_width == -1) { // no minimum *min_width = 0; @@ -96,8 +97,7 @@ static void calculate_constraints(int *min_width, int *max_width, if (config->floating_maximum_width == -1) { // no maximum *max_width = INT_MAX; } else if (config->floating_maximum_width == 0) { // automatic - struct sway_container *ws = container_parent(con, C_WORKSPACE); - *max_width = ws->width; + *max_width = con->workspace->width; } else { *max_width = config->floating_maximum_width; } @@ -105,8 +105,7 @@ static void calculate_constraints(int *min_width, int *max_width, if (config->floating_maximum_height == -1) { // no maximum *max_height = INT_MAX; } else if (config->floating_maximum_height == 0) { // automatic - struct sway_container *ws = container_parent(con, C_WORKSPACE); - *max_height = ws->height; + *max_height = con->workspace->height; } else { *max_height = config->floating_maximum_height; } @@ -191,11 +190,11 @@ static void resize_tiled(struct sway_container *parent, int amount, normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT; int minor_weight = 0; int major_weight = 0; - while (parent->parent) { - struct sway_container *next = parent->parent; - if (next->layout == parallel_layout) { - for (int i = 0; i < next->children->length; i++) { - struct sway_container *sibling = next->children->items[i]; + while (parent) { + list_t *siblings = container_get_siblings(parent); + if (container_parent_layout(parent) == parallel_layout) { + for (int i = 0; i < siblings->length; i++) { + struct sway_container *sibling = siblings->items[i]; int sibling_pos = parallel_coord(sibling, axis); int focused_pos = parallel_coord(focused, axis); @@ -213,17 +212,13 @@ static void resize_tiled(struct sway_container *parent, int amount, break; } } - parent = next; + parent = parent->parent; } - - if (parent->type == C_ROOT) { + if (!parent) { + // Can't resize in this direction return; } - wlr_log(WLR_DEBUG, - "Found the proper parent: %p. It has %d l conts, and %d r conts", - parent->parent, minor_weight, major_weight); - // Implement up/down/left/right direction by zeroing one of the weights, // then setting the axis to be horizontal or vertical if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) { @@ -237,9 +232,10 @@ static void resize_tiled(struct sway_container *parent, int amount, //TODO: Ensure rounding is done in such a way that there are NO pixel leaks // ^ ????? + list_t *siblings = container_get_siblings(parent); - for (int i = 0; i < parent->parent->children->length; i++) { - struct sway_container *sibling = parent->parent->children->items[i]; + for (int i = 0; i < siblings->length; i++) { + struct sway_container *sibling = siblings->items[i]; int sibling_pos = parallel_coord(sibling, axis); int focused_pos = parallel_coord(focused, axis); @@ -277,8 +273,8 @@ static void resize_tiled(struct sway_container *parent, int amount, enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ? WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM; - for (int i = 0; i < parent->parent->children->length; i++) { - struct sway_container *sibling = parent->parent->children->items[i]; + for (int i = 0; i < siblings->length; i++) { + struct sway_container *sibling = siblings->items[i]; int sibling_pos = parallel_coord(sibling, axis); int focused_pos = parallel_coord(focused, axis); @@ -316,7 +312,11 @@ static void resize_tiled(struct sway_container *parent, int amount, } } - arrange_windows(parent->parent); + if (parent->parent) { + arrange_container(parent->parent); + } else { + arrange_workspace(parent->workspace); + } } void container_resize_tiled(struct sway_container *parent, @@ -346,7 +346,7 @@ void container_resize_tiled(struct sway_container *parent, */ static struct cmd_results *resize_adjust_floating(enum resize_axis axis, struct resize_amount *amount) { - struct sway_container *con = config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; int grow_width = 0, grow_height = 0; switch (axis) { case RESIZE_AXIS_HORIZONTAL: @@ -400,15 +400,15 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis, con->width += grow_width; con->height += grow_height; - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; + if (con->view) { + struct sway_view *view = con->view; view->x += grow_x; view->y += grow_y; view->width += grow_width; view->height += grow_height; } - arrange_windows(con); + arrange_container(con); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -418,7 +418,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis, */ static struct cmd_results *resize_adjust_tiled(enum resize_axis axis, struct resize_amount *amount) { - struct sway_container *current = config->handler_context.current_container; + struct sway_container *current = config->handler_context.container; if (amount->unit == RESIZE_UNIT_DEFAULT) { amount->unit = RESIZE_UNIT_PPT; @@ -456,13 +456,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, width->unit == RESIZE_UNIT_DEFAULT) { // Convert to px struct sway_container *parent = con->parent; - while (parent->type >= C_WORKSPACE && parent->layout != L_HORIZ) { + while (parent && parent->layout != L_HORIZ) { parent = parent->parent; } - if (parent->type >= C_WORKSPACE) { + if (parent) { width->amount = parent->width * width->amount / 100; - width->unit = RESIZE_UNIT_PX; + } else { + width->amount = con->workspace->width * width->amount / 100; } + width->unit = RESIZE_UNIT_PX; } if (width->unit == RESIZE_UNIT_PX) { resize_tiled(con, width->amount - con->width, @@ -475,13 +477,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, height->unit == RESIZE_UNIT_DEFAULT) { // Convert to px struct sway_container *parent = con->parent; - while (parent->type >= C_WORKSPACE && parent->layout != L_VERT) { + while (parent && parent->layout != L_VERT) { parent = parent->parent; } - if (parent->type >= C_WORKSPACE) { + if (parent) { height->amount = parent->height * height->amount / 100; - height->unit = RESIZE_UNIT_PX; + } else { + height->amount = con->workspace->height * height->amount / 100; } + height->unit = RESIZE_UNIT_PX; } if (height->unit == RESIZE_UNIT_PX) { resize_tiled(con, height->amount - con->height, @@ -508,15 +512,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con, con->width = width->amount; con->height = height->amount; - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; + if (con->view) { + struct sway_view *view = con->view; view->x -= grow_width / 2; view->y -= grow_height / 2; view->width += grow_width; view->height += grow_height; } - arrange_windows(con); + arrange_container(con); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -555,7 +559,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { } // If 0, don't resize that dimension - struct sway_container *con = config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; if (width.amount <= 0) { width.amount = con->width; } @@ -624,7 +628,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, first_amount.amount *= multiplier; second_amount.amount *= multiplier; - struct sway_container *con = config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; if (container_is_floating(con)) { // Floating containers can only resize in px. Choose an amount which // uses px, with fallback to an amount that specified no unit. @@ -657,14 +661,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, } struct cmd_results *cmd_resize(int argc, char **argv) { - struct sway_container *current = config->handler_context.current_container; + struct sway_container *current = config->handler_context.container; if (!current) { return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); } - if (current->type != C_VIEW && current->type != C_CONTAINER) { - return cmd_results_new(CMD_INVALID, "resize", - "Can only resize views/containers"); - } struct cmd_results *error; if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 7da200154..d8bae615f 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -9,36 +9,34 @@ static void scratchpad_toggle_auto(void) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *ws = focus->type == C_WORKSPACE ? - focus : container_parent(focus, C_WORKSPACE); + struct sway_container *focus = seat_get_focused_container(seat); + struct sway_workspace *ws = seat_get_focused_workspace(seat); // If the focus is in a floating split container, // operate on the split container instead of the child. - if (container_is_floating_or_child(focus)) { - while (focus->parent->type != C_WORKSPACE) { + if (focus && container_is_floating_or_child(focus)) { + while (focus->parent) { focus = focus->parent; } } - // Check if the currently focused window is a scratchpad window and should // be hidden again. - if (focus->scratchpad) { + if (focus && focus->scratchpad) { wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", - focus->name); + focus->title); root_scratchpad_hide(focus); return; } // Check if there is an unfocused scratchpad window on the current workspace // and focus it. - for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { - struct sway_container *floater = ws->sway_workspace->floating->items[i]; + for (int i = 0; i < ws->floating->length; ++i) { + struct sway_container *floater = ws->floating->items[i]; if (floater->scratchpad && focus != floater) { wlr_log(WLR_DEBUG, "Focusing other scratchpad window (%s) in this workspace", - floater->name); + floater->title); root_scratchpad_show(floater); return; } @@ -46,25 +44,23 @@ static void scratchpad_toggle_auto(void) { // Check if there is a visible scratchpad window on another workspace. // In this case we move it to the current workspace. - for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { - struct sway_container *con = - root_container.sway_root->scratchpad->items[i]; + for (int i = 0; i < root->scratchpad->length; ++i) { + struct sway_container *con = root->scratchpad->items[i]; if (con->parent) { wlr_log(WLR_DEBUG, "Moving a visible scratchpad window (%s) to this workspace", - con->name); + con->title); root_scratchpad_show(con); return; } } // Take the container at the bottom of the scratchpad list - if (!sway_assert(root_container.sway_root->scratchpad->length, - "Scratchpad is empty")) { + if (!sway_assert(root->scratchpad->length, "Scratchpad is empty")) { return; } - struct sway_container *con = root_container.sway_root->scratchpad->items[0]; - wlr_log(WLR_DEBUG, "Showing %s from list", con->name); + struct sway_container *con = root->scratchpad->items[0]; + wlr_log(WLR_DEBUG, "Showing %s from list", con->title); root_scratchpad_show(con); } @@ -74,7 +70,7 @@ static void scratchpad_toggle_container(struct sway_container *con) { } // Check if it matches a currently visible scratchpad window and hide it. - if (con->parent) { + if (con->workspace) { root_scratchpad_hide(con); return; } @@ -91,18 +87,18 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "scratchpad", "Expected 'scratchpad show'"); } - if (!root_container.sway_root->scratchpad->length) { + if (!root->scratchpad->length) { return cmd_results_new(CMD_INVALID, "scratchpad", "Scratchpad is empty"); } if (config->handler_context.using_criteria) { - struct sway_container *con = config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(con)) { - while (con->parent->type != C_WORKSPACE) { + while (con->parent) { con = con->parent; } } diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 4d0a22c78..cd6630e0d 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -42,8 +42,8 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); } // map absolute coords (0..1,0..1) to root container coords - float x = strtof(argv[1], NULL) / root_container.width; - float y = strtof(argv[2], NULL) / root_container.height; + float x = strtof(argv[1], NULL) / root->width; + float y = strtof(argv[2], NULL) / root->height; wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); cursor_send_pointer_motion(cursor, 0, true); } else { diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index 1844e9172..d501584a0 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c @@ -11,8 +11,8 @@ #include "util.h" static void rebuild_marks_iterator(struct sway_container *con, void *data) { - if (con->type == C_VIEW) { - view_update_marks_textures(con->sway_view); + if (con->view) { + view_update_marks_textures(con->view); } } @@ -28,9 +28,9 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { root_for_each_container(rebuild_marks_iterator, NULL); } - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *con = root_container.children->items[i]; - output_damage_whole(con->sway_output); + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c index 7d27e5719..273905df1 100644 --- a/sway/commands/smart_gaps.c +++ b/sway/commands/smart_gaps.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) { "Expected 'smart_gaps ' "); } - arrange_windows(&root_container); + arrange_root(); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/split.c b/sway/commands/split.c index a8eddf543..9a53f3d3d 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -4,15 +4,21 @@ #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "log.h" static struct cmd_results *do_split(int layout) { - struct sway_container *con = config->handler_context.current_container; - struct sway_container *parent = container_split(con, layout); - container_create_notify(parent); - arrange_windows(parent->parent); + struct sway_container *con = config->handler_context.container; + struct sway_workspace *ws = config->handler_context.workspace; + if (con) { + container_split(con, layout); + } else { + workspace_split(ws, layout); + } + + arrange_workspace(ws); return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -29,10 +35,9 @@ struct cmd_results *cmd_split(int argc, char **argv) { return do_split(L_HORIZ); } else if (strcasecmp(argv[0], "t") == 0 || strcasecmp(argv[0], "toggle") == 0) { - struct sway_container *focused = - config->handler_context.current_container; + struct sway_container *focused = config->handler_context.container; - if (focused->parent->layout == L_VERT) { + if (focused && container_parent_layout(focused) == L_VERT) { return do_split(L_HORIZ); } else { return do_split(L_VERT); @@ -66,9 +71,9 @@ struct cmd_results *cmd_splitt(int argc, char **argv) { return error; } - struct sway_container *con = config->handler_context.current_container; + struct sway_container *con = config->handler_context.container; - if (con->parent->layout == L_VERT) { + if (con && container_parent_layout(con) == L_VERT) { return do_split(L_HORIZ); } else { return do_split(L_VERT); diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 8692e08d8..7995cdd61 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -15,8 +15,7 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) { return error; } - struct sway_container *container = - config->handler_context.current_container; + struct sway_container *container = config->handler_context.container; if (!container_is_floating(container)) { return cmd_results_new(CMD_FAILURE, "sticky", "Can't set sticky on a tiled container"); @@ -37,20 +36,16 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { container->is_sticky = wants_sticky; if (wants_sticky) { - // move container to focused workspace - struct sway_container *output = container_parent(container, C_OUTPUT); - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus_inactive(seat, output); - struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); - struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); - if (current_workspace != focused_workspace) { - container_remove_child(container); - workspace_add_floating(focused_workspace, container); - container_handle_fullscreen_reparent(container, current_workspace); - arrange_windows(focused_workspace); - if (!container_reap_empty(current_workspace)) { - arrange_windows(current_workspace); - } + // move container to active workspace + struct sway_workspace *active_workspace = + output_get_active_workspace(container->workspace->output); + if (container->workspace != active_workspace) { + struct sway_workspace *old_workspace = container->workspace; + container_detach(container); + workspace_add_floating(active_workspace, container); + container_handle_fullscreen_reparent(container); + arrange_workspace(active_workspace); + workspace_consider_destroy(old_workspace); } } diff --git a/sway/commands/swap.c b/sway/commands/swap.c index f25c43a1e..a0ffbda82 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -4,6 +4,7 @@ #include "config.h" #include "log.h" #include "sway/commands.h" +#include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/root.h" #include "sway/tree/view.h" @@ -43,27 +44,28 @@ static void swap_focus(struct sway_container *con1, struct sway_container *con2, struct sway_seat *seat, struct sway_container *focus) { if (focus == con1 || focus == con2) { - struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); - struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); - if (focus == con1 && (con2->parent->layout == L_TABBED - || con2->parent->layout == L_STACKED)) { + struct sway_workspace *ws1 = con1->workspace; + struct sway_workspace *ws2 = con2->workspace; + enum sway_container_layout layout1 = container_parent_layout(con1); + enum sway_container_layout layout2 = container_parent_layout(con2); + if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { if (workspace_is_visible(ws2)) { - seat_set_focus_warp(seat, con2, false, true); + seat_set_focus_warp(seat, &con2->node, false, true); } - seat_set_focus(seat, ws1 != ws2 ? con2 : con1); - } else if (focus == con2 && (con1->parent->layout == L_TABBED - || con1->parent->layout == L_STACKED)) { + seat_set_focus(seat, ws1 != ws2 ? &con2->node : &con1->node); + } else if (focus == con2 && (layout1 == L_TABBED + || layout1 == L_STACKED)) { if (workspace_is_visible(ws1)) { - seat_set_focus_warp(seat, con1, false, true); + seat_set_focus_warp(seat, &con1->node, false, true); } - seat_set_focus(seat, ws1 != ws2 ? con1 : con2); + seat_set_focus(seat, ws1 != ws2 ? &con1->node : &con2->node); } else if (ws1 != ws2) { - seat_set_focus(seat, focus == con1 ? con2 : con1); + seat_set_focus(seat, focus == con1 ? &con2->node : &con1->node); } else { - seat_set_focus(seat, focus); + seat_set_focus(seat, &focus->node); } } else { - seat_set_focus(seat, focus); + seat_set_focus(seat, &focus->node); } } @@ -72,10 +74,6 @@ static void container_swap(struct sway_container *con1, if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { return; } - if (!sway_assert(con1->type >= C_CONTAINER && con2->type >= C_CONTAINER, - "Can only swap containers and views")) { - return; - } if (!sway_assert(!container_has_ancestor(con1, con2) && !container_has_ancestor(con2, con1), "Cannot swap ancestor and descendant")) { @@ -87,10 +85,11 @@ static void container_swap(struct sway_container *con1, return; } - wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); + wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", + con1->node.id, con2->node.id); - int fs1 = con1->is_fullscreen; - int fs2 = con2->is_fullscreen; + bool fs1 = con1->is_fullscreen; + bool fs2 = con2->is_fullscreen; if (fs1) { container_set_fullscreen(con1, false); } @@ -99,13 +98,11 @@ static void container_swap(struct sway_container *con1, } struct sway_seat *seat = input_manager_get_default_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *vis1 = container_parent( - seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), - C_WORKSPACE); - struct sway_container *vis2 = container_parent( - seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)), - C_WORKSPACE); + struct sway_container *focus = seat_get_focused_container(seat); + struct sway_workspace *vis1 = + output_get_active_workspace(con1->workspace->output); + struct sway_workspace *vis2 = + output_get_active_workspace(con2->workspace->output); char *stored_prev_name = NULL; if (prev_workspace_name) { @@ -115,10 +112,10 @@ static void container_swap(struct sway_container *con1, swap_places(con1, con2); if (!workspace_is_visible(vis1)) { - seat_set_focus(seat, seat_get_focus_inactive(seat, vis1)); + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); } if (!workspace_is_visible(vis2)) { - seat_set_focus(seat, seat_get_focus_inactive(seat, vis2)); + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); } swap_focus(con1, con2, seat, focus); @@ -137,23 +134,22 @@ static void container_swap(struct sway_container *con1, } static bool test_con_id(struct sway_container *container, void *con_id) { - return container->id == (size_t)con_id; + return container->node.id == (size_t)con_id; } static bool test_id(struct sway_container *container, void *id) { #ifdef HAVE_XWAYLAND xcb_window_t *wid = id; - return (container->type == C_VIEW - && container->sway_view->type == SWAY_VIEW_XWAYLAND - && container->sway_view->wlr_xwayland_surface->window_id == *wid); + return (container->view && container->view->type == SWAY_VIEW_XWAYLAND + && container->view->wlr_xwayland_surface->window_id == *wid); #else return false; #endif } static bool test_mark(struct sway_container *container, void *mark) { - if (container->type == C_VIEW && container->sway_view->marks->length) { - return !list_seq_find(container->sway_view->marks, + if (container->view && container->view->marks->length) { + return !list_seq_find(container->view->marks, (int (*)(const void *, const void *))strcmp, mark); } return false; @@ -169,7 +165,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); } - struct sway_container *current = config->handler_context.current_container; + struct sway_container *current = config->handler_context.container; struct sway_container *other; char *value = join_args(argv + 3, argc - 3); @@ -191,7 +187,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { if (!other) { error = cmd_results_new(CMD_FAILURE, "swap", "Failed to find %s '%s'", argv[2], value); - } else if (current->type < C_CONTAINER || other->type < C_CONTAINER) { + } else if (!current) { error = cmd_results_new(CMD_FAILURE, "swap", "Can only swap with containers and views"); } else if (container_has_ancestor(current, other) @@ -211,9 +207,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) { container_swap(current, other); - arrange_windows(current->parent); - if (other->parent != current->parent) { - arrange_windows(other->parent); + arrange_node(node_get_parent(¤t->node)); + if (node_get_parent(&other->node) != node_get_parent(¤t->node)) { + arrange_node(node_get_parent(&other->node)); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 3d1c578c6..c9ffe8fa2 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -11,13 +11,12 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { return error; } - struct sway_container *container = - config->handler_context.current_container; - if (container->type != C_VIEW) { + struct sway_container *container = config->handler_context.container; + if (!container->view) { return cmd_results_new(CMD_INVALID, "title_format", "Only views can have a title_format"); } - struct sway_view *view = container->sway_view; + struct sway_view *view = container->view; char *format = join_args(argv, argc); if (view->title_format) { free(view->title_format); diff --git a/sway/commands/unmark.c b/sway/commands/unmark.c index 62127c975..c6251dc8a 100644 --- a/sway/commands/unmark.c +++ b/sway/commands/unmark.c @@ -9,9 +9,9 @@ #include "stringop.h" static void remove_all_marks_iterator(struct sway_container *con, void *data) { - if (con->type == C_VIEW) { - view_clear_marks(con->sway_view); - view_update_marks_textures(con->sway_view); + if (con->view) { + view_clear_marks(con->view); + view_update_marks_textures(con->view); } } @@ -24,13 +24,12 @@ struct cmd_results *cmd_unmark(int argc, char **argv) { // Determine the view struct sway_view *view = NULL; if (config->handler_context.using_criteria) { - struct sway_container *container = - config->handler_context.current_container; - if (container->type != C_VIEW) { + struct sway_container *container = config->handler_context.container; + if (!container->view) { return cmd_results_new(CMD_INVALID, "unmark", "Only views can have marks"); } - view = container->sway_view; + view = container->view; } // Determine the mark diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c index bccb33fec..53c37d4d5 100644 --- a/sway/commands/urgent.c +++ b/sway/commands/urgent.c @@ -11,13 +11,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) { if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { return error; } - struct sway_container *container = - config->handler_context.current_container; - if (container->type != C_VIEW) { + struct sway_container *container = config->handler_context.container; + if (!container->view) { return cmd_results_new(CMD_INVALID, "urgent", "Only views can be urgent"); } - struct sway_view *view = container->sway_view; + struct sway_view *view = container->view; if (strcmp(argv[0], "allow") == 0) { view->allow_request_urgent = true; diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index ceb4cd6e0..f026a39d4 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -58,7 +58,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } - struct sway_container *ws = NULL; + struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { return cmd_results_new(CMD_INVALID, "workspace", diff --git a/sway/config.c b/sway/config.c index 8105722ad..89701640e 100644 --- a/sway/config.c +++ b/sway/config.c @@ -825,6 +825,6 @@ void config_update_font_height(bool recalculate) { root_for_each_container(find_font_height_iterator, &recalculate); if (config->font_height != prev_max_height) { - arrange_windows(&root_container); + arrange_root(); } } diff --git a/sway/config/bar.c b/sway/config/bar.c index ae9383d65..f83b37d1d 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -12,6 +12,7 @@ #include #include #include "sway/config.h" +#include "sway/output.h" #include "stringop.h" #include "list.h" #include "log.h" @@ -218,17 +219,6 @@ void invoke_swaybar(struct bar_config *bar) { close(filedes[1]); } -static bool active_output(const char *name) { - struct sway_container *cont = NULL; - for (int i = 0; i < root_container.children->length; ++i) { - cont = root_container.children->items[i]; - if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) { - return true; - } - } - return false; -} - void load_swaybars() { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; @@ -236,7 +226,7 @@ void load_swaybars() { if (bar->outputs) { for (int j = 0; j < bar->outputs->length; ++j) { char *o = bar->outputs->items[j]; - if (!strcmp(o, "*") || active_output(o)) { + if (!strcmp(o, "*") || output_by_name(o)) { apply = true; break; } diff --git a/sway/config/output.c b/sway/config/output.c index 65f09258e..aa53fc467 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -174,21 +174,16 @@ void terminate_swaybg(pid_t pid) { } } -void apply_output_config(struct output_config *oc, struct sway_container *output) { - assert(output->type == C_OUTPUT); - - struct wlr_output_layout *output_layout = - root_container.sway_root->output_layout; - struct wlr_output *wlr_output = output->sway_output->wlr_output; +void apply_output_config(struct output_config *oc, struct sway_output *output) { + struct wlr_output *wlr_output = output->wlr_output; if (oc && oc->enabled == 0) { - if (output->sway_output->bg_pid != 0) { - terminate_swaybg(output->sway_output->bg_pid); - output->sway_output->bg_pid = 0; + if (output->bg_pid != 0) { + terminate_swaybg(output->bg_pid); + output->bg_pid = 0; } - output_begin_destroy(output); - wlr_output_layout_remove(root_container.sway_root->output_layout, - wlr_output); + output_disable(output); + wlr_output_layout_remove(root->output_layout, wlr_output); return; } @@ -213,21 +208,21 @@ void apply_output_config(struct output_config *oc, struct sway_container *output // Find position for it if (oc && (oc->x != -1 || oc->y != -1)) { wlr_log(WLR_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); - wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); + wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y); } else { - wlr_output_layout_add_auto(output_layout, wlr_output); + wlr_output_layout_add_auto(root->output_layout, wlr_output); } int output_i; - for (output_i = 0; output_i < root_container.children->length; ++output_i) { - if (root_container.children->items[output_i] == output) { + for (output_i = 0; output_i < root->outputs->length; ++output_i) { + if (root->outputs->items[output_i] == output) { break; } } if (oc && oc->background) { - if (output->sway_output->bg_pid != 0) { - terminate_swaybg(output->sway_output->bg_pid); + if (output->bg_pid != 0) { + terminate_swaybg(output->bg_pid); } wlr_log(WLR_DEBUG, "Setting background for output %d to %s", @@ -249,8 +244,8 @@ void apply_output_config(struct output_config *oc, struct sway_container *output wlr_log(WLR_DEBUG, "-> %s", command); char *const cmd[] = { "sh", "-c", command, NULL }; - output->sway_output->bg_pid = fork(); - if (output->sway_output->bg_pid == 0) { + output->bg_pid = fork(); + if (output->bg_pid == 0) { execvp(cmd[0], cmd); } else { free(command); @@ -293,12 +288,11 @@ void apply_output_config_to_outputs(struct output_config *oc) { bool wildcard = strcmp(oc->name, "*") == 0; char id[128]; struct sway_output *sway_output; - wl_list_for_each(sway_output, - &root_container.sway_root->all_outputs, link) { + wl_list_for_each(sway_output, &root->all_outputs, link) { char *name = sway_output->wlr_output->name; output_get_identifier(id, sizeof(id), sway_output); if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { - if (!sway_output->swayc) { + if (!sway_output->enabled) { if (!oc->enabled) { if (!wildcard) { break; @@ -306,7 +300,7 @@ void apply_output_config_to_outputs(struct output_config *oc) { continue; } - output_enable(sway_output); + output_enable(sway_output, oc); } struct output_config *current = oc; @@ -316,7 +310,7 @@ void apply_output_config_to_outputs(struct output_config *oc) { current = tmp; } } - apply_output_config(current, sway_output->swayc); + apply_output_config(current, sway_output); if (!wildcard) { // Stop looking if the output config isn't applicable to all @@ -354,8 +348,7 @@ static void default_output_config(struct output_config *oc, void create_default_output_configs(void) { struct sway_output *sway_output; - wl_list_for_each(sway_output, - &root_container.sway_root->all_outputs, link) { + wl_list_for_each(sway_output, &root->all_outputs, link) { char *name = sway_output->wlr_output->name; struct output_config *oc = new_output_config(name); default_output_config(oc, sway_output->wlr_output); diff --git a/sway/criteria.c b/sway/criteria.c index feca904a9..e5b308e06 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -9,6 +9,7 @@ #include "sway/config.h" #include "sway/tree/root.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "stringop.h" #include "list.h" #include "log.h" @@ -87,12 +88,12 @@ static int cmp_urgent(const void *_a, const void *_b) { return 0; } -static void find_urgent_iterator(struct sway_container *swayc, void *data) { - if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { +static void find_urgent_iterator(struct sway_container *con, void *data) { + if (!con->view || !view_is_urgent(con->view)) { return; } list_t *urgent_views = data; - list_add(urgent_views, swayc->sway_view); + list_add(urgent_views, con->view); } static bool criteria_matches_view(struct criteria *criteria, @@ -132,7 +133,7 @@ static bool criteria_matches_view(struct criteria *criteria, } if (criteria->con_id) { // Internal ID - if (!view->swayc || view->swayc->id != criteria->con_id) { + if (!view->container || view->container->node.id != criteria->con_id) { return false; } } @@ -174,13 +175,13 @@ static bool criteria_matches_view(struct criteria *criteria, #endif if (criteria->floating) { - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return false; } } if (criteria->tiling) { - if (container_is_floating(view->swayc)) { + if (container_is_floating(view->container)) { return false; } } @@ -205,10 +206,7 @@ static bool criteria_matches_view(struct criteria *criteria, } if (criteria->workspace) { - if (!view->swayc) { - return false; - } - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_workspace *ws = view->container->workspace; if (!ws || strcmp(ws->name, criteria->workspace) != 0) { return false; } @@ -237,9 +235,9 @@ struct match_data { static void criteria_get_views_iterator(struct sway_container *container, void *data) { struct match_data *match_data = data; - if (container->type == C_VIEW) { - if (criteria_matches_view(match_data->criteria, container->sway_view)) { - list_add(match_data->matches, container->sway_view); + if (!container->view) { + if (criteria_matches_view(match_data->criteria, container->view)) { + list_add(match_data->matches, container->view); } } } @@ -355,12 +353,12 @@ static enum criteria_token token_from_name(char *name) { */ static char *get_focused_prop(enum criteria_token token) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); + struct sway_container *focus = seat_get_focused_container(seat); - if (!focus || focus->type != C_VIEW) { + if (!focus || !focus->view) { return NULL; } - struct sway_view *view = focus->sway_view; + struct sway_view *view = focus->view; const char *value = NULL; switch (token) { @@ -374,18 +372,15 @@ static char *get_focused_prop(enum criteria_token token) { value = view_get_title(view); break; case T_WORKSPACE: - { - struct sway_container *ws = container_parent(focus, C_WORKSPACE); - if (ws) { - value = ws->name; - } + if (focus->workspace) { + value = focus->workspace->name; } break; case T_CON_ID: - if (view->swayc == NULL) { + if (view->container == NULL) { return NULL; } - size_t id = view->swayc->id; + size_t id = view->container->node.id; size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; char *id_str = malloc(id_size); snprintf(id_str, id_size, "%zu", id); diff --git a/sway/debug-tree.c b/sway/debug-tree.c index 2768cf58b..2ed3c087d 100644 --- a/sway/debug-tree.c +++ b/sway/debug-tree.c @@ -10,6 +10,7 @@ #include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" +#include "sway/tree/workspace.h" #include "cairo.h" #include "config.h" #include "pango.h" @@ -32,28 +33,78 @@ static const char *layout_to_str(enum sway_container_layout layout) { return "L_NONE"; } -static int draw_container(cairo_t *cairo, struct sway_container *container, - struct sway_container *focus, int x, int y) { +static char *get_string(struct sway_node *node) { + char *buffer = malloc(512); + switch (node->type) { + case N_ROOT: + snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id, + root->width, root->height, root->x, root->y); + break; + case N_OUTPUT: + snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id, + node->sway_output->wlr_output->name, + node->sway_output->wlr_output->width, + node->sway_output->wlr_output->height, + node->sway_output->wlr_output->lx, + node->sway_output->wlr_output->ly); + break; + case N_WORKSPACE: + snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f", + node->id, node->sway_workspace->name, + layout_to_str(node->sway_workspace->layout), + node->sway_workspace->width, node->sway_workspace->height, + node->sway_workspace->x, node->sway_workspace->y); + break; + case N_CONTAINER: + snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f", + node->id, node->sway_container->title, + layout_to_str(node->sway_container->layout), + node->sway_container->width, node->sway_container->height, + node->sway_container->x, node->sway_container->y); + break; + } + return buffer; +} + +static list_t *get_children(struct sway_node *node) { + switch (node->type) { + case N_ROOT: + return root->outputs; + case N_OUTPUT: + return node->sway_output->workspaces; + case N_WORKSPACE: + return node->sway_workspace->tiling; + case N_CONTAINER: + return node->sway_container->children; + } + return NULL; +} + +static int draw_node(cairo_t *cairo, struct sway_node *node, + struct sway_node *focus, int x, int y) { int text_width, text_height; + char *buffer = get_string(node); get_text_size(cairo, "monospace", &text_width, &text_height, - 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", - container_type_to_str(container->type), container->id, container->name, - layout_to_str(container->layout), - container->width, container->height, container->x, container->y); + 1, false, buffer); cairo_save(cairo); cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); cairo_set_source_u32(cairo, 0xFFFFFFE0); cairo_fill(cairo); int height = text_height; - if (container->children) { - for (int i = 0; i < container->children->length; ++i) { - struct sway_container *child = container->children->items[i]; - if (child->parent == container) { + list_t *children = get_children(node); + if (children) { + for (int i = 0; i < children->length; ++i) { + // This is really dirty - the list contains specific structs but + // we're casting them as nodes. This works because node is the first + // item in each specific struct. This is acceptable because this is + // debug code. + struct sway_node *child = children->items[i]; + if (node_get_parent(child) == node) { cairo_set_source_u32(cairo, 0x000000FF); } else { cairo_set_source_u32(cairo, 0xFF0000FF); } - height += draw_container(cairo, child, focus, x + 10, y + height); + height += draw_node(cairo, child, focus, x + 10, y + height); } } cairo_set_source_u32(cairo, 0xFFFFFFE0); @@ -61,13 +112,11 @@ static int draw_container(cairo_t *cairo, struct sway_container *container, cairo_fill(cairo); cairo_restore(cairo); cairo_move_to(cairo, x, y); - if (focus == container) { + if (focus == node) { cairo_set_source_u32(cairo, 0x0000FFFF); } - pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", - container_type_to_str(container->type), container->id, container->name, - layout_to_str(container->layout), - container->width, container->height, container->x, container->y); + pango_printf(cairo, "monospace", 1, false, buffer); + free(buffer); return height; } @@ -77,13 +126,13 @@ void update_debug_tree() { } int width = 640, height = 480; - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *container = root_container.children->items[i]; - if (container->width > width) { - width = container->width; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + if (output->wlr_output->width > width) { + width = output->wlr_output->width; } - if (container->height > height) { - height = container->height; + if (output->wlr_output->height > height) { + height = output->wlr_output->height; } } cairo_surface_t *surface = @@ -91,28 +140,22 @@ void update_debug_tree() { cairo_t *cairo = cairo_create(surface); PangoContext *pango = pango_cairo_create_context(cairo); - struct sway_seat *seat = NULL; - wl_list_for_each(seat, &input_manager->seats, link) { - break; - } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_node *focus = seat_get_focus(seat); - struct sway_container *focus = NULL; - if (seat != NULL) { - focus = seat_get_focus(seat); - } cairo_set_source_u32(cairo, 0x000000FF); - draw_container(cairo, &root_container, focus, 0, 0); + draw_node(cairo, &root->node, focus, 0, 0); cairo_surface_flush(surface); struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); - if (root_container.sway_root->debug_tree) { - wlr_texture_destroy(root_container.sway_root->debug_tree); + if (root->debug_tree) { + wlr_texture_destroy(root->debug_tree); } unsigned char *data = cairo_image_surface_get_data(surface); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); struct wlr_texture *texture = wlr_texture_from_pixels(renderer, WL_SHM_FORMAT_ARGB8888, stride, width, height, data); - root_container.sway_root->debug_tree = texture; + root->debug_tree = texture; cairo_surface_destroy(surface); g_object_unref(pango); cairo_destroy(cairo); diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index 726503970..771b58fec 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -4,37 +4,32 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *cont = root_container.children->items[i]; - if (cont->type == C_OUTPUT) { - output_damage_surface(cont->sway_output, - lx - cont->current.swayc_x, ly - cont->current.swayc_y, - surface, whole); - } + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_surface(output, lx - output->wlr_output->lx, + ly - output->wlr_output->ly, surface, whole); } } void desktop_damage_whole_container(struct sway_container *con) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *cont = root_container.children->items[i]; - if (cont->type == C_OUTPUT) { - output_damage_whole_container(cont->sway_output, 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_container.children->length; ++i) { - struct sway_container *cont = root_container.children->items[i]; - output_damage_box(cont->sway_output, 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->swayc); + desktop_damage_whole_container(view->container); struct wlr_box box = { - .x = view->swayc->current.view_x - view->geometry.x, - .y = view->swayc->current.view_y - view->geometry.y, + .x = view->container->current.view_x - view->geometry.x, + .y = view->container->current.view_y - view->geometry.y, .width = view->surface->current.width, .height = view->surface->current.height, }; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index a4f7f928d..7d2541731 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -14,6 +14,7 @@ #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" +#include "sway/tree/workspace.h" #include "log.h" static void apply_exclusive(struct wlr_box *usable_area, @@ -176,7 +177,7 @@ void arrange_layers(struct sway_output *output) { sizeof(struct wlr_box)) != 0) { wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - arrange_output(output->swayc); + arrange_output(output); } // Arrange non-exlusive surfaces from top->bottom @@ -256,7 +257,7 @@ static void unmap(struct sway_layer_surface *sway_layer) { return; } struct sway_output *output = wlr_output->data; - if (output == NULL || output->swayc == NULL) { + if (output == NULL) { return; } output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, @@ -283,9 +284,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&sway_layer->surface_commit.link); if (sway_layer->layer_surface->output != NULL) { struct sway_output *output = sway_layer->layer_surface->output->data; - if (output != NULL && output->swayc != NULL) { + if (output != NULL) { arrange_layers(output); - arrange_windows(output->swayc); + arrange_output(output); transaction_commit_dirty(); } wl_list_remove(&sway_layer->output_destroy.link); @@ -332,23 +333,21 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { if (!layer_surface->output) { // Assign last active output - struct sway_container *output = NULL; + struct sway_output *output = NULL; struct sway_seat *seat = input_manager_get_default_seat(input_manager); if (seat) { - output = seat_get_focus_inactive(seat, &root_container); + struct sway_workspace *ws = seat_get_focused_workspace(seat); + output = ws->output; } if (!output) { - if (!sway_assert(root_container.children->length, + if (!sway_assert(root->outputs->length, "cannot auto-assign output for layer")) { wlr_layer_surface_close(layer_surface); return; } - output = root_container.children->items[0]; + output = root->outputs->items[0]; } - if (output->type != C_OUTPUT) { - output = container_parent(output, C_OUTPUT); - } - layer_surface->output = output->sway_output->wlr_output; + layer_surface->output = output->wlr_output; } struct sway_layer_surface *sway_layer = diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c30e52a1c..c182bad63 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -28,10 +28,10 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" -struct sway_container *output_by_name(const char *name) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - if (strcasecmp(output->name, name) == 0) { +struct sway_output *output_by_name(const char *name) { + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + if (strcasecmp(output->wlr_output->name, name) == 0) { return output; } } @@ -98,8 +98,8 @@ static bool get_surface_box(struct surface_iterator_data *data, wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); struct wlr_box output_box = { - .width = output->swayc->current.swayc_width, - .height = output->swayc->current.swayc_height, + .width = output->wlr_output->width, + .height = output->wlr_output->height, }; struct wlr_box intersection; @@ -145,12 +145,12 @@ void output_view_for_each_surface(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->swayc->current.view_x - output->swayc->current.swayc_x + .ox = view->container->current.view_x - output->wlr_output->lx - view->geometry.x, - .oy = view->swayc->current.view_y - output->swayc->current.swayc_y + .oy = view->container->current.view_y - output->wlr_output->ly - view->geometry.y, - .width = view->swayc->current.view_width, - .height = view->swayc->current.view_height, + .width = view->container->current.view_width, + .height = view->container->current.view_height, .rotation = 0, // TODO }; @@ -164,12 +164,12 @@ void output_view_for_each_popup(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->swayc->current.view_x - output->swayc->current.swayc_x + .ox = view->container->current.view_x - output->wlr_output->lx - view->geometry.x, - .oy = view->swayc->current.view_y - output->swayc->current.swayc_y + .oy = view->container->current.view_y - output->wlr_output->ly - view->geometry.y, - .width = view->swayc->current.view_width, - .height = view->swayc->current.view_height, + .width = view->container->current.view_width, + .height = view->container->current.view_height, .rotation = 0, // TODO }; @@ -197,8 +197,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output, wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; - double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; + double ox = unmanaged_surface->lx - output->wlr_output->lx; + double oy = unmanaged_surface->ly - output->wlr_output->ly; output_surface_for_each_surface(output, xsurface->surface, ox, oy, iterator, user_data); @@ -211,8 +211,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output, void *user_data) { struct sway_drag_icon *drag_icon; wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->swayc->x; - double oy = drag_icon->y - output->swayc->y; + double ox = drag_icon->x - output->wlr_output->lx; + double oy = drag_icon->y - output->wlr_output->ly; if (drag_icon->wlr_drag_icon->mapped) { output_surface_for_each_surface(output, @@ -229,19 +229,13 @@ static void scale_box(struct wlr_box *box, float scale) { box->height *= scale; } -struct sway_container *output_get_active_workspace(struct sway_output *output) { +struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = - seat_get_focus_inactive(seat, output->swayc); + struct sway_node *focus = seat_get_active_child(seat, &output->node); if (!focus) { - // We've never been to this output before - focus = output->swayc->current.children->items[0]; + return output->workspaces->items[0]; } - struct sway_container *workspace = focus; - if (workspace->type != C_WORKSPACE) { - workspace = container_parent(workspace, C_WORKSPACE); - } - return workspace; + return focus->sway_workspace; } bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { @@ -255,8 +249,8 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { struct sway_layer_surface *sway_layer_surface = layer_from_wlr_layer_surface(wlr_layer_surface); pixman_box32_t output_box = { - .x2 = output->swayc->current.swayc_width, - .y2 = output->swayc->current.swayc_height, + .x2 = output->wlr_output->width, + .y2 = output->wlr_output->height, }; pixman_region32_t surface_opaque_box; pixman_region32_init(&surface_opaque_box); @@ -307,15 +301,15 @@ struct send_frame_done_data { static void send_frame_done_container_iterator(struct sway_container *con, void *_data) { - if (con->type != C_VIEW) { + if (!con->view) { return; } - if (!view_is_visible(con->sway_view)) { + if (!view_is_visible(con->view)) { return; } struct send_frame_done_data *data = _data; - output_view_for_each_surface(data->output, con->sway_view, + output_view_for_each_surface(data->output, con->view, send_frame_done_iterator, data->when); } @@ -328,15 +322,14 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { .output = output, .when = when, }; - struct sway_container *workspace = output_get_active_workspace(output); - if (workspace->current.ws_fullscreen) { + struct sway_workspace *workspace = output_get_active_workspace(output); + if (workspace->current.fullscreen) { send_frame_done_container_iterator( - workspace->current.ws_fullscreen, &data); - container_for_each_child(workspace->current.ws_fullscreen, + workspace->current.fullscreen, &data); + container_for_each_child(workspace->current.fullscreen, send_frame_done_container_iterator, &data); #ifdef HAVE_XWAYLAND - send_frame_done_unmanaged(output, - &root_container.sway_root->xwayland_unmanaged, when); + send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when); #endif } else { send_frame_done_layer(output, @@ -348,8 +341,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_done_container_iterator, &data); #ifdef HAVE_XWAYLAND - send_frame_done_unmanaged(output, - &root_container.sway_root->xwayland_unmanaged, when); + send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when); #endif send_frame_done_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); @@ -358,8 +350,7 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) { send_frame_overlay: send_frame_done_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); - send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, - when); + send_frame_done_drag_icons(output, &root->drag_icons, when); } static void damage_handle_frame(struct wl_listener *listener, void *data) { @@ -391,7 +382,11 @@ static void damage_handle_frame(struct wl_listener *listener, void *data) { } void output_damage_whole(struct sway_output *output) { - wlr_output_damage_add_whole(output->damage); + // The output can exist with no wlr_output if it's just been disconnected + // and the transaction to evacuate it has't completed yet. + if (output && output->wlr_output) { + wlr_output_damage_add_whole(output->damage); + } } static void damage_surface_iterator(struct sway_output *output, @@ -446,14 +441,9 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, static void output_damage_view(struct sway_output *output, struct sway_view *view, bool whole) { - if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) { - return; - } - if (!view_is_visible(view)) { return; } - output_view_for_each_surface(output, view, damage_surface_iterator, &whole); } @@ -466,31 +456,29 @@ void output_damage_from_view(struct sway_output *output, 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->swayc->current.swayc_x; - box.y -= output->swayc->current.swayc_y; + box.x -= output->wlr_output->lx; + box.y -= output->wlr_output->ly; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } static void output_damage_whole_container_iterator(struct sway_container *con, void *data) { - struct sway_output *output = data; - - if (!sway_assert(con->type == C_VIEW, "expected a view")) { + if (!sway_assert(con->view, "expected a view")) { return; } - - output_damage_view(output, con->sway_view, true); + struct sway_output *output = data; + output_damage_view(output, con->view, true); } 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.swayc_x - output->wlr_output->lx - 1, - .y = con->current.swayc_y - output->wlr_output->ly - 1, - .width = con->current.swayc_width + 2, - .height = con->current.swayc_height + 2, + .x = con->current.con_x - output->wlr_output->lx - 1, + .y = con->current.con_y - output->wlr_output->ly - 1, + .width = con->current.con_width + 2, + .height = con->current.con_height + 2, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); @@ -499,44 +487,48 @@ void output_damage_whole_container(struct sway_output *output, static void damage_handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, damage_destroy); - output_begin_destroy(output->swayc); + output_disable(output); + transaction_commit_dirty(); } static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); wl_signal_emit(&output->events.destroy, output); - if (output->swayc) { - output_begin_destroy(output->swayc); + if (output->enabled) { + output_disable(output); } + output_begin_destroy(output); - wl_list_remove(&output->link); - wl_list_remove(&output->destroy.link); - output->wlr_output->data = NULL; - free(output); - - arrange_windows(&root_container); + transaction_commit_dirty(); } static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); arrange_layers(output); - arrange_windows(output->swayc); + arrange_output(output); transaction_commit_dirty(); } static void handle_transform(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, transform); arrange_layers(output); - arrange_windows(output->swayc); + arrange_output(output); transaction_commit_dirty(); } +static void update_textures(struct sway_container *con, void *data) { + container_update_title_textures(con); + if (con->view) { + view_update_marks_textures(con->view); + } +} + static void handle_scale(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, scale); arrange_layers(output); - container_update_textures_recursive(output->swayc); - arrange_windows(output->swayc); + output_for_each_container(output, update_textures, NULL); + arrange_output(output); transaction_commit_dirty(); } @@ -545,57 +537,27 @@ void handle_new_output(struct wl_listener *listener, void *data) { struct wlr_output *wlr_output = data; wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); - struct sway_output *output = calloc(1, sizeof(struct sway_output)); + struct sway_output *output = output_create(wlr_output); if (!output) { return; } - output->wlr_output = wlr_output; - wlr_output->data = output; output->server = server; output->damage = wlr_output_damage_create(wlr_output); - - wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; - wl_list_insert(&root_container.sway_root->all_outputs, &output->link); + struct output_config *oc = output_find_config(output); - output_enable(output); -} - -void output_enable(struct sway_output *output) { - struct wlr_output *wlr_output = output->wlr_output; - - if (!sway_assert(output->swayc == NULL, "output is already enabled")) { - return; + if (oc && oc->enabled) { + output_enable(output, oc); } - output->swayc = output_create(output); - if (!output->swayc) { - // Output is disabled - return; - } - - size_t len = sizeof(output->layers) / sizeof(output->layers[0]); - for (size_t i = 0; i < len; ++i) { - wl_list_init(&output->layers[i]); - } - wl_signal_init(&output->events.destroy); - - input_manager_configure_xcursor(input_manager); - - wl_signal_add(&wlr_output->events.mode, &output->mode); - output->mode.notify = handle_mode; - wl_signal_add(&wlr_output->events.transform, &output->transform); - output->transform.notify = handle_transform; - wl_signal_add(&wlr_output->events.scale, &output->scale); - output->scale.notify = handle_scale; - - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - output->damage_frame.notify = damage_handle_frame; - wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); - output->damage_destroy.notify = damage_handle_destroy; - - arrange_layers(output); - arrange_windows(&root_container); transaction_commit_dirty(); } + +void output_add_listeners(struct sway_output *output) { + output->mode.notify = handle_mode; + output->transform.notify = handle_transform; + output->scale.notify = handle_scale; + output->damage_frame.notify = damage_handle_frame; + output->damage_destroy.notify = damage_handle_destroy; +} diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 695213eb5..99b2cf3dc 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -193,10 +193,10 @@ static void render_view_toplevels(struct sway_view *view, .alpha = alpha, }; // Render all toplevels without descending into popups - double ox = - view->swayc->current.view_x - output->wlr_output->lx - view->geometry.x; - double oy = - view->swayc->current.view_y - output->wlr_output->ly - view->geometry.y; + double ox = view->container->current.view_x - + output->wlr_output->lx - view->geometry.x; + double oy = view->container->current.view_y - + output->wlr_output->ly - view->geometry.y; output_surface_for_each_surface(output, view->surface, ox, oy, render_surface_iterator, &data); } @@ -229,17 +229,17 @@ static void render_saved_view(struct sway_view *view, return; } struct wlr_box box = { - .x = view->swayc->current.view_x - output->swayc->current.swayc_x - + .x = view->container->current.view_x - output->wlr_output->lx - view->saved_geometry.x, - .y = view->swayc->current.view_y - output->swayc->current.swayc_y - + .y = view->container->current.view_y - output->wlr_output->ly - view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, }; struct wlr_box output_box = { - .width = output->swayc->current.swayc_width, - .height = output->swayc->current.swayc_height, + .width = output->wlr_output->width, + .height = output->wlr_output->height, }; struct wlr_box intersection; @@ -263,14 +263,14 @@ static void render_saved_view(struct sway_view *view, */ static void render_view(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, struct border_colors *colors) { - struct sway_view *view = con->sway_view; + struct sway_view *view = con->view; if (view->saved_buffer) { - render_saved_view(view, output, damage, view->swayc->alpha); + render_saved_view(view, output, damage, view->container->alpha); } else { - render_view_toplevels(view, output, damage, view->swayc->alpha); + render_view_toplevels(view, output, damage, view->container->alpha); } - if (view->swayc->current.using_csd) { + if (view->container->current.using_csd) { return; } @@ -283,7 +283,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (state->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; + box.x = state->con_x; box.y = state->view_y; box.width = state->border_thickness; box.height = state->view_height; @@ -291,9 +291,12 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, render_rect(output->wlr_output, damage, &box, color); } + list_t *siblings = container_get_current_siblings(con); + enum sway_container_layout layout = + container_current_parent_layout(con); + if (state->border_right) { - if (state->parent->current.children->length == 1 - && state->parent->current.layout == L_HORIZ) { + if (siblings->length == 1 && layout == L_HORIZ) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); @@ -308,16 +311,15 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, } if (state->border_bottom) { - if (state->parent->current.children->length == 1 - && con->current.parent->current.layout == L_VERT) { + if (siblings->length == 1 && layout == L_VERT) { memcpy(&color, colors->indicator, sizeof(float) * 4); } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; + box.x = state->con_x; box.y = state->view_y + state->view_height; - box.width = state->swayc_width; + box.width = state->con_width; box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); @@ -344,12 +346,12 @@ static void render_titlebar(struct sway_output *output, float color[4]; struct sway_container_state *state = &con->current; float output_scale = output->wlr_output->scale; - enum sway_container_layout layout = state->parent->current.layout; - list_t *children = state->parent->current.children; + enum sway_container_layout layout = container_current_parent_layout(con); + list_t *children = container_get_current_siblings(con); bool is_last_child = children->length == 0 || children->items[children->length - 1] == con; - double output_x = output->swayc->current.swayc_x; - double output_y = output->swayc->current.swayc_y; + double output_x = output->wlr_output->lx; + double output_y = output->wlr_output->ly; // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); @@ -366,7 +368,7 @@ static void render_titlebar(struct sway_output *output, bool connects_sides = false; if (layout == L_HORIZ || layout == L_VERT || (layout == L_STACKED && is_last_child)) { - if (con->type == C_VIEW) { + if (con->view) { left_offset = state->border_left * state->border_thickness; right_offset = state->border_right * state->border_thickness; connects_sides = true; @@ -542,14 +544,22 @@ static void render_top_border(struct sway_output *output, // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = state->swayc_x; - box.y = state->swayc_y; - box.width = state->swayc_width; + box.x = state->con_x; + box.y = state->con_y; + box.width = state->con_width; box.height = state->border_thickness; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } +struct parent_data { + enum sway_container_layout layout; + struct wlr_box box; + list_t *children; + bool focused; + struct sway_container *active_child; +}; + static void render_container(struct sway_output *output, pixman_region32_t *damage, struct sway_container *con, bool parent_focused); @@ -559,14 +569,13 @@ static void render_container(struct sway_output *output, * Wrap child views in borders and leave child containers borderless because * they'll apply their own borders to their children. */ -static void render_container_simple(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - for (int i = 0; i < con->current.children->length; ++i) { - struct sway_container *child = con->current.children->items[i]; +static void render_containers_linear(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; - if (child->type == C_VIEW) { - struct sway_view *view = child->sway_view; + if (child->view) { + struct sway_view *view = child->view; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; @@ -576,11 +585,11 @@ static void render_container_simple(struct sway_output *output, colors = &config->border_colors.urgent; title_texture = child->title_urgent; marks_texture = view->marks_urgent; - } else if (state->focused || parent_focused) { + } else if (state->focused || parent->focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view->marks_focused; - } else if (con->current.focused_inactive_child == child) { + } else if (child == parent->active_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = view->marks_focused_inactive; @@ -590,10 +599,10 @@ static void render_container_simple(struct sway_output *output, marks_texture = view->marks_unfocused; } - if (!view->swayc->current.using_csd) { + if (!view->container->current.using_csd) { if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, state->swayc_x, - state->swayc_y, state->swayc_width, colors, + render_titlebar(output, damage, child, state->con_x, + state->con_y, state->con_width, colors, title_texture, marks_texture); } else { render_top_border(output, damage, child, colors); @@ -602,7 +611,7 @@ static void render_container_simple(struct sway_output *output, render_view(output, damage, child, colors); } else { render_container(output, damage, child, - parent_focused || child->current.focused); + parent->focused || child->current.focused); } } } @@ -610,22 +619,19 @@ static void render_container_simple(struct sway_output *output, /** * Render a container's children using the L_TABBED layout. */ -static void render_container_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - if (!con->current.children->length) { +static void render_containers_tabbed(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + if (!parent->children->length) { return; } - struct sway_container_state *pstate = &con->current; - struct sway_container *current = pstate->focused_inactive_child; + struct sway_container *current = parent->active_child; struct border_colors *current_colors = &config->border_colors.unfocused; - - int tab_width = (pstate->swayc_width) / pstate->children->length; + int tab_width = parent->box.width / parent->children->length; // Render tabs - for (int i = 0; i < pstate->children->length; ++i) { - struct sway_container *child = pstate->children->items[i]; - struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + struct sway_view *view = child->view; struct sway_container_state *cstate = &child->current; struct border_colors *colors; struct wlr_texture *title_texture; @@ -637,11 +643,11 @@ static void render_container_tabbed(struct sway_output *output, colors = &config->border_colors.urgent; title_texture = child->title_urgent; marks_texture = view ? view->marks_urgent : NULL; - } else if (cstate->focused || parent_focused) { + } else if (cstate->focused || parent->focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; - } else if (child == pstate->focused_inactive_child) { + } else if (child == parent->active_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = view ? view->marks_focused_inactive : NULL; @@ -651,14 +657,14 @@ static void render_container_tabbed(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int x = cstate->swayc_x + tab_width * i; + int x = cstate->con_x + tab_width * i; // Make last tab use the remaining width of the parent - if (i == pstate->children->length - 1) { - tab_width = pstate->swayc_width - tab_width * i; + if (i == parent->children->length - 1) { + tab_width = parent->box.width - tab_width * i; } - render_titlebar(output, damage, child, x, pstate->swayc_y, tab_width, + render_titlebar(output, damage, child, x, parent->box.y, tab_width, colors, title_texture, marks_texture); if (child == current) { @@ -667,33 +673,30 @@ static void render_container_tabbed(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { + if (current->view) { render_view(output, damage, current, current_colors); } else { render_container(output, damage, current, - parent_focused || current->current.focused); + parent->focused || current->current.focused); } } /** * Render a container's children using the L_STACKED layout. */ -static void render_container_stacked(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - if (!con->current.children->length) { +static void render_containers_stacked(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + if (!parent->children->length) { return; } - struct sway_container_state *pstate = &con->current; - struct sway_container *current = pstate->focused_inactive_child; + struct sway_container *current = parent->active_child; struct border_colors *current_colors = &config->border_colors.unfocused; - size_t titlebar_height = container_titlebar_height(); // Render titles - for (int i = 0; i < pstate->children->length; ++i) { - struct sway_container *child = pstate->children->items[i]; - struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + struct sway_view *view = child->view; struct sway_container_state *cstate = &child->current; struct border_colors *colors; struct wlr_texture *title_texture; @@ -705,11 +708,11 @@ static void render_container_stacked(struct sway_output *output, colors = &config->border_colors.urgent; title_texture = child->title_urgent; marks_texture = view ? view->marks_urgent : NULL; - } else if (cstate->focused || parent_focused) { + } else if (cstate->focused || parent->focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; - } else if (child == pstate->focused_inactive_child) { + } else if (child == parent->active_child) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; marks_texture = view ? view->marks_focused_inactive : NULL; @@ -719,9 +722,9 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = pstate->swayc_y + titlebar_height * i; - render_titlebar(output, damage, child, pstate->swayc_x, y, - pstate->swayc_width, colors, title_texture, marks_texture); + int y = parent->box.y + titlebar_height * i; + render_titlebar(output, damage, child, parent->box.x, y, + parent->box.width, colors, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -729,36 +732,69 @@ static void render_container_stacked(struct sway_output *output, } // Render surface and left/right/bottom borders - if (current->type == C_VIEW) { + if (current->view) { render_view(output, damage, current, current_colors); } else { render_container(output, damage, current, - parent_focused || current->current.focused); + parent->focused || current->current.focused); + } +} + +static void render_containers(struct sway_output *output, + pixman_region32_t *damage, struct parent_data *parent) { + switch (parent->layout) { + case L_NONE: + case L_HORIZ: + case L_VERT: + render_containers_linear(output, damage, parent); + break; + case L_STACKED: + render_containers_stacked(output, damage, parent); + break; + case L_TABBED: + render_containers_tabbed(output, damage, parent); + break; } } static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - bool parent_focused) { - switch (con->current.layout) { - case L_NONE: - case L_HORIZ: - case L_VERT: - render_container_simple(output, damage, con, parent_focused); - break; - case L_STACKED: - render_container_stacked(output, damage, con, parent_focused); - break; - case L_TABBED: - render_container_tabbed(output, damage, con, parent_focused); - break; - } + pixman_region32_t *damage, struct sway_container *con, bool focused) { + struct parent_data data = { + .layout = con->current.layout, + .box = { + .x = con->current.con_x, + .y = con->current.con_y, + .width = con->current.con_width, + .height = con->current.con_height, + }, + .children = con->current.children, + .focused = focused, + .active_child = con->current.focused_inactive_child, + }; + render_containers(output, damage, &data); +} + +static void render_workspace(struct sway_output *output, + pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { + struct parent_data data = { + .layout = ws->current.layout, + .box = { + .x = ws->current.x, + .y = ws->current.y, + .width = ws->current.width, + .height = ws->current.height, + }, + .children = ws->current.tiling, + .focused = focused, + .active_child = ws->current.focused_inactive_child, + }; + render_containers(output, damage, &data); } static void render_floating_container(struct sway_output *soutput, pixman_region32_t *damage, struct sway_container *con) { - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; + if (con->view) { + struct sway_view *view = con->view; struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; @@ -777,10 +813,10 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (!view->swayc->current.using_csd) { + if (!view->container->current.using_csd) { if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->current.swayc_x, - con->current.swayc_y, con->current.swayc_width, colors, + render_titlebar(soutput, damage, con, con->current.con_x, + con->current.con_y, con->current.con_width, colors, title_texture, marks_texture); } else if (con->current.border != B_NONE) { render_top_border(soutput, damage, con, colors); @@ -794,17 +830,15 @@ static void render_floating_container(struct sway_output *soutput, static void render_floating(struct sway_output *soutput, pixman_region32_t *damage) { - for (int i = 0; i < root_container.current.children->length; ++i) { - struct sway_container *output = - root_container.current.children->items[i]; - for (int j = 0; j < output->current.children->length; ++j) { - struct sway_container *ws = output->current.children->items[j]; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + for (int j = 0; j < output->current.workspaces->length; ++j) { + struct sway_workspace *ws = output->current.workspaces->items[j]; if (!workspace_is_visible(ws)) { continue; } - list_t *floating = ws->current.ws_floating; - for (int k = 0; k < floating->length; ++k) { - struct sway_container *floater = floating->items[k]; + for (int k = 0; k < ws->current.floating->length; ++k) { + struct sway_container *floater = ws->current.floating->items[k]; render_floating_container(soutput, damage, floater); } } @@ -837,8 +871,8 @@ void output_render(struct sway_output *output, struct timespec *when, pixman_region32_union_rect(damage, damage, 0, 0, width, height); } - struct sway_container *workspace = output_get_active_workspace(output); - struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; + struct sway_workspace *workspace = output_get_active_workspace(output); + struct sway_container *fullscreen_con = workspace->current.fullscreen; if (output_has_opaque_overlay_layer_surface(output)) { goto render_overlay; @@ -855,12 +889,11 @@ void output_render(struct sway_output *output, struct timespec *when, } // TODO: handle views smaller than the output - if (fullscreen_con->type == C_VIEW) { - if (fullscreen_con->sway_view->saved_buffer) { - render_saved_view(fullscreen_con->sway_view, - output, damage, 1.0f); + if (fullscreen_con->view) { + if (fullscreen_con->view->saved_buffer) { + render_saved_view(fullscreen_con->view, output, damage, 1.0f); } else { - render_view_toplevels(fullscreen_con->sway_view, + render_view_toplevels(fullscreen_con->view, output, damage, 1.0f); } } else { @@ -868,8 +901,7 @@ void output_render(struct sway_output *output, struct timespec *when, fullscreen_con->current.focused); } #ifdef HAVE_XWAYLAND - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); + render_unmanaged(output, damage, &root->xwayland_unmanaged); #endif } else { float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; @@ -886,31 +918,30 @@ void output_render(struct sway_output *output, struct timespec *when, render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_container(output, damage, workspace, workspace->current.focused); + render_workspace(output, damage, workspace, workspace->current.focused); render_floating(output, damage); #ifdef HAVE_XWAYLAND - render_unmanaged(output, damage, - &root_container.sway_root->xwayland_unmanaged); + render_unmanaged(output, damage, &root->xwayland_unmanaged); #endif render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - if (focus && focus->type == C_VIEW) { - render_view_popups(focus->sway_view, output, damage, focus->alpha); + struct sway_container *focus = seat_get_focused_container(seat); + if (focus && focus->view) { + render_view_popups(focus->view, output, damage, focus->alpha); } render_overlay: render_layer(output, damage, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root_container.sway_root->drag_icons); + render_drag_icons(output, damage, &root->drag_icons); renderer_end: if (debug.render_tree) { wlr_renderer_scissor(renderer, NULL); - wlr_render_texture(renderer, root_container.sway_root->debug_tree, + wlr_render_texture(renderer, root->debug_tree, wlr_output->transform_matrix, 0, 40, 1); } if (debug.damage == DAMAGE_HIGHLIGHT) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 145c5f92f..b4eec933f 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -12,6 +12,7 @@ #include "sway/desktop/transaction.h" #include "sway/output.h" #include "sway/tree/container.h" +#include "sway/tree/node.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" @@ -27,8 +28,12 @@ struct sway_transaction { struct sway_transaction_instruction { struct sway_transaction *transaction; - struct sway_container *container; - struct sway_container_state state; + struct sway_node *node; + union { + struct sway_output_state *output_state; + struct sway_workspace_state *workspace_state; + struct sway_container_state *container_state; + }; uint32_t serial; }; @@ -47,26 +52,24 @@ static void transaction_destroy(struct sway_transaction *transaction) { for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - struct sway_container *con = instruction->container; - con->ntxnrefs--; - if (con->instruction == instruction) { - con->instruction = NULL; + struct sway_node *node = instruction->node; + node->ntxnrefs--; + if (node->instruction == instruction) { + node->instruction = NULL; } - if (con->destroying && con->ntxnrefs == 0) { - switch (con->type) { - case C_ROOT: + if (node->destroying && node->ntxnrefs == 0) { + switch (node->type) { + case N_ROOT: + sway_assert(false, "Never reached"); break; - case C_OUTPUT: - output_destroy(con); + case N_OUTPUT: + output_destroy(node->sway_output); break; - case C_WORKSPACE: - workspace_destroy(con); + case N_WORKSPACE: + workspace_destroy(node->sway_workspace); break; - case C_CONTAINER: - case C_VIEW: - container_destroy(con); - break; - case C_TYPES: + case N_CONTAINER: + container_destroy(node->sway_container); break; } } @@ -80,22 +83,79 @@ static void transaction_destroy(struct sway_transaction *transaction) { free(transaction); } -static void copy_pending_state(struct sway_container *container, - struct sway_container_state *state) { - state->layout = container->layout; - state->swayc_x = container->x; - state->swayc_y = container->y; - state->swayc_width = container->width; - state->swayc_height = container->height; - state->is_fullscreen = container->is_fullscreen; - state->has_gaps = container->has_gaps; - state->current_gaps = container->current_gaps; - state->gaps_inner = container->gaps_inner; - state->gaps_outer = container->gaps_outer; - state->parent = container->parent; +static void copy_output_state(struct sway_output *output, + struct sway_transaction_instruction *instruction) { + struct sway_output_state *state = + calloc(1, sizeof(struct sway_output_state)); + if (!state) { + wlr_log(WLR_ERROR, "Could not allocate output state"); + return; + } + instruction->output_state = state; - if (container->type == C_VIEW) { - struct sway_view *view = container->sway_view; + state->workspaces = create_list(); + list_cat(state->workspaces, output->workspaces); + + state->active_workspace = output_get_active_workspace(output); +} + +static void copy_workspace_state(struct sway_workspace *ws, + struct sway_transaction_instruction *instruction) { + struct sway_workspace_state *state = + calloc(1, sizeof(struct sway_workspace_state)); + if (!state) { + wlr_log(WLR_ERROR, "Could not allocate workspace state"); + return; + } + instruction->workspace_state = state; + + state->fullscreen = ws->fullscreen; + state->x = ws->x; + state->y = ws->y; + state->width = ws->width; + state->height = ws->height; + state->layout = ws->layout; + + state->output = ws->output; + state->floating = create_list(); + state->tiling = create_list(); + list_cat(state->floating, ws->floating); + list_cat(state->tiling, ws->tiling); + + struct sway_seat *seat = input_manager_current_seat(input_manager); + state->focused = seat_get_focus(seat) == &ws->node; + + // Set focused_inactive_child to the direct tiling child + struct sway_container *focus = seat_get_focus_inactive_tiling(seat, ws); + if (focus) { + while (focus->parent) { + focus = focus->parent; + } + } + state->focused_inactive_child = focus; +} + +static void copy_container_state(struct sway_container *container, + struct sway_transaction_instruction *instruction) { + struct sway_container_state *state = + calloc(1, sizeof(struct sway_container_state)); + if (!state) { + wlr_log(WLR_ERROR, "Could not allocate container state"); + return; + } + instruction->container_state = state; + + state->layout = container->layout; + state->con_x = container->x; + state->con_y = container->y; + state->con_width = container->width; + state->con_height = container->height; + state->is_fullscreen = container->is_fullscreen; + state->parent = container->parent; + state->workspace = container->workspace; + + if (container->view) { + struct sway_view *view = container->view; state->view_x = view->x; state->view_y = view->y; state->view_width = view->width; @@ -107,50 +167,111 @@ static void copy_pending_state(struct sway_container *container, state->border_right = view->border_right; state->border_bottom = view->border_bottom; state->using_csd = view->using_csd; - } else if (container->type == C_WORKSPACE) { - state->ws_fullscreen = container->sway_workspace->fullscreen; - state->ws_floating = create_list(); - state->children = create_list(); - list_cat(state->ws_floating, container->sway_workspace->floating); - list_cat(state->children, container->children); } else { state->children = create_list(); list_cat(state->children, container->children); } struct sway_seat *seat = input_manager_current_seat(input_manager); - state->focused = seat_get_focus(seat) == container; + state->focused = seat_get_focus(seat) == &container->node; - if (container->type == C_WORKSPACE) { - // Set focused_inactive_child to the direct tiling child - struct sway_container *focus = - seat_get_focus_inactive_tiling(seat, container); - if (focus && focus->type > C_WORKSPACE) { - while (focus->parent->type != C_WORKSPACE) { - focus = focus->parent; - } - } - state->focused_inactive_child = focus; - } else if (container->type != C_VIEW) { - state->focused_inactive_child = - seat_get_active_child(seat, container); + if (!container->view) { + struct sway_node *focus = seat_get_active_child(seat, &container->node); + state->focused_inactive_child = focus ? focus->sway_container : NULL; } } -static void transaction_add_container(struct sway_transaction *transaction, - struct sway_container *container) { +static void transaction_add_node(struct sway_transaction *transaction, + struct sway_node *node) { struct sway_transaction_instruction *instruction = calloc(1, sizeof(struct sway_transaction_instruction)); if (!sway_assert(instruction, "Unable to allocate instruction")) { return; } instruction->transaction = transaction; - instruction->container = container; + instruction->node = node; - copy_pending_state(container, &instruction->state); + switch (node->type) { + case N_ROOT: + break; + case N_OUTPUT: + copy_output_state(node->sway_output, instruction); + break; + case N_WORKSPACE: + copy_workspace_state(node->sway_workspace, instruction); + break; + case N_CONTAINER: + copy_container_state(node->sway_container, instruction); + break; + } list_add(transaction->instructions, instruction); - container->ntxnrefs++; + node->ntxnrefs++; +} + +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 && view->saved_buffer) { + struct wlr_box box = { + .x = container->current.view_x - view->saved_geometry.x, + .y = container->current.view_y - view->saved_geometry.y, + .width = view->saved_buffer_width, + .height = view->saved_buffer_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. + // Any child containers which are being deleted will be cleaned up in + // transaction_destroy(). + list_free(container->current.children); + + memcpy(&container->current, state, sizeof(struct sway_container_state)); + + if (view && view->saved_buffer) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { + view_remove_saved_buffer(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.view_x - view->geometry.x, + .y = container->current.view_y - view->geometry.y, + .width = surface->current.width, + .height = surface->current.height, + }; + desktop_damage_box(&box); + } + + if (!container->node.destroying) { + container_discover_outputs(container); + } } /** @@ -168,67 +289,36 @@ static void transaction_apply(struct sway_transaction *transaction) { "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60)); } - // Apply the instruction state to the container's current state + // Apply the instruction state to the node's current state for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - struct sway_container *container = instruction->container; + struct sway_node *node = instruction->node; - // Damage the old location - desktop_damage_whole_container(container); - if (container->type == C_VIEW && container->sway_view->saved_buffer) { - struct sway_view *view = container->sway_view; - struct wlr_box box = { - .x = container->current.view_x - view->saved_geometry.x, - .y = container->current.view_y - view->saved_geometry.y, - .width = view->saved_buffer_width, - .height = view->saved_buffer_height, - }; - desktop_damage_box(&box); + switch (node->type) { + case N_ROOT: + break; + case N_OUTPUT: + apply_output_state(node->sway_output, instruction->output_state); + break; + case N_WORKSPACE: + apply_workspace_state(node->sway_workspace, + instruction->workspace_state); + break; + case N_CONTAINER: + apply_container_state(node->sway_container, + instruction->container_state); + break; } - // 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. - // Any child containers which are being deleted will be cleaned up in - // transaction_destroy(). - list_free(container->current.children); - list_free(container->current.ws_floating); - - memcpy(&container->current, &instruction->state, - sizeof(struct sway_container_state)); - - if (container->type == C_VIEW && container->sway_view->saved_buffer) { - if (!container->destroying || container->ntxnrefs == 1) { - view_remove_saved_buffer(container->sway_view); - } - } - - // Damage the new location - desktop_damage_whole_container(container); - if (container->type == C_VIEW && container->sway_view->surface) { - struct sway_view *view = container->sway_view; - struct wlr_surface *surface = view->surface; - struct wlr_box box = { - .x = container->current.view_x - view->geometry.x, - .y = container->current.view_y - view->geometry.y, - .width = surface->current.width, - .height = surface->current.height, - }; - desktop_damage_box(&box); - } - - container->instruction = NULL; - if (container->type == C_CONTAINER || container->type == C_VIEW) { - container_discover_outputs(container); - } + node->instruction = NULL; } } static void transaction_commit(struct sway_transaction *transaction); -// Return true if both transactions operate on the same containers -static bool transaction_same_containers(struct sway_transaction *a, +// Return true if both transactions operate on the same nodes +static bool transaction_same_nodes(struct sway_transaction *a, struct sway_transaction *b) { if (a->instructions->length != b->instructions->length) { return false; @@ -236,7 +326,7 @@ static bool transaction_same_containers(struct sway_transaction *a, for (int i = 0; i < a->instructions->length; ++i) { struct sway_transaction_instruction *a_inst = a->instructions->items[i]; struct sway_transaction_instruction *b_inst = b->instructions->items[i]; - if (a_inst->container != b_inst->container) { + if (a_inst->node != b_inst->node) { return false; } } @@ -267,7 +357,7 @@ static void transaction_progress_queue() { while (server.transactions->length >= 2) { struct sway_transaction *a = server.transactions->items[0]; struct sway_transaction *b = server.transactions->items[1]; - if (transaction_same_containers(a, b)) { + if (transaction_same_nodes(a, b)) { list_del(server.transactions, 0); transaction_destroy(a); } else { @@ -289,16 +379,18 @@ static int handle_timeout(void *data) { return 0; } -static bool should_configure(struct sway_container *con, +static bool should_configure(struct sway_node *node, struct sway_transaction_instruction *instruction) { - if (con->type != C_VIEW) { + if (!node_is_view(node)) { return false; } - if (con->destroying) { + if (node->destroying) { return false; } - if (con->current.view_width == instruction->state.view_width && - con->current.view_height == instruction->state.view_height) { + struct sway_container_state *cstate = &node->sway_container->current; + struct sway_container_state *istate = instruction->container_state; + if (cstate->view_width == istate->view_width && + cstate->view_height == istate->view_height) { return false; } return true; @@ -311,13 +403,13 @@ static void transaction_commit(struct sway_transaction *transaction) { for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; - struct sway_container *con = instruction->container; - if (should_configure(con, instruction)) { - instruction->serial = view_configure(con->sway_view, - instruction->state.view_x, - instruction->state.view_y, - instruction->state.view_width, - instruction->state.view_height); + struct sway_node *node = instruction->node; + if (should_configure(node, instruction)) { + instruction->serial = view_configure(node->sway_container->view, + instruction->container_state->view_x, + instruction->container_state->view_y, + instruction->container_state->view_width, + instruction->container_state->view_height); ++transaction->num_waiting; // From here on we are rendering a saved buffer of the view, which @@ -325,14 +417,16 @@ static void transaction_commit(struct sway_transaction *transaction) { // as soon as possible. Additionally, this is required if a view is // mapping and its default geometry doesn't intersect an output. struct timespec when; - wlr_surface_send_frame_done(con->sway_view->surface, &when); + wlr_surface_send_frame_done( + node->sway_container->view->surface, &when); } - if (con->type == C_VIEW && !con->sway_view->saved_buffer) { - view_save_buffer(con->sway_view); - memcpy(&con->sway_view->saved_geometry, &con->sway_view->geometry, + if (node_is_view(node) && !node->sway_container->view->saved_buffer) { + view_save_buffer(node->sway_container->view); + memcpy(&node->sway_container->view->saved_geometry, + &node->sway_container->view->geometry, sizeof(struct wlr_box)); } - con->instruction = instruction; + node->instruction = instruction; } transaction->num_configures = transaction->num_waiting; if (debug.txn_timings) { @@ -381,7 +475,7 @@ static void set_instruction_ready( transaction, transaction->num_configures - transaction->num_waiting + 1, transaction->num_configures, ms, - instruction->container->name); + instruction->node->sway_container->title); } // If the transaction has timed out then its num_waiting will be 0 already. @@ -390,41 +484,43 @@ static void set_instruction_ready( wl_event_source_timer_update(transaction->timer, 0); } - instruction->container->instruction = NULL; + instruction->node->instruction = NULL; transaction_progress_queue(); } void transaction_notify_view_ready_by_serial(struct sway_view *view, uint32_t serial) { - struct sway_transaction_instruction *instruction = view->swayc->instruction; - if (view->swayc->instruction->serial == serial) { + struct sway_transaction_instruction *instruction = + view->container->node.instruction; + if (instruction->serial == serial) { set_instruction_ready(instruction); } } void transaction_notify_view_ready_by_size(struct sway_view *view, int width, int height) { - struct sway_transaction_instruction *instruction = view->swayc->instruction; - if (instruction->state.view_width == width && - instruction->state.view_height == height) { + struct sway_transaction_instruction *instruction = + view->container->node.instruction; + if (instruction->container_state->view_width == width && + instruction->container_state->view_height == height) { set_instruction_ready(instruction); } } void transaction_commit_dirty(void) { - if (!server.dirty_containers->length) { + if (!server.dirty_nodes->length) { return; } struct sway_transaction *transaction = transaction_create(); if (!transaction) { return; } - for (int i = 0; i < server.dirty_containers->length; ++i) { - struct sway_container *container = server.dirty_containers->items[i]; - transaction_add_container(transaction, container); - container->dirty = false; + for (int i = 0; i < server.dirty_nodes->length; ++i) { + struct sway_node *node = server.dirty_nodes->items[i]; + transaction_add_node(transaction, node); + node->dirty = false; } - server.dirty_containers->length = 0; + server.dirty_nodes->length = 0; list_add(server.transactions, transaction); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 587deb0fb..e19d8e185 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -11,10 +11,12 @@ #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" static const struct sway_view_child_impl popup_impl; @@ -52,15 +54,15 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) { struct sway_view *view = popup->child.view; struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + struct sway_output *output = view->container->workspace->output; // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = output->x - view->x, - .y = output->y - view->y, - .width = output->width, - .height = output->height, + .x = output->wlr_output->lx - view->x, + .y = output->wlr_output->ly - view->y, + .width = output->wlr_output->width, + .height = output->wlr_output->height, }; wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); @@ -252,11 +254,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; - if (!view->swayc) { - return; - } - - if (view->swayc->instruction) { + if (view->container->node.instruction) { wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); transaction_notify_view_ready_by_serial(view, xdg_surface->configure_serial); @@ -265,7 +263,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); if ((new_geo.width != view->width || new_geo.height != view->height) && - container_is_floating(view->swayc)) { + container_is_floating(view->container)) { // A floating view has unexpectedly sent a new size desktop_damage_view(view); view_update_size(view, new_geo.width, new_geo.height); @@ -319,10 +317,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - container_set_fullscreen(view->swayc, e->fullscreen); + container_set_fullscreen(view->container, e->fullscreen); - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - arrange_windows(output); + arrange_workspace(view->container->workspace); transaction_commit_dirty(); } @@ -330,13 +327,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_move); struct sway_view *view = &xdg_shell_view->view; - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return; } struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seat_begin_move(seat, view->swayc, seat->last_button); + seat_begin_move(seat, view->container, seat->last_button); } } @@ -344,13 +341,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_resize); struct sway_view *view = &xdg_shell_view->view; - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return; } struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seat_begin_resize_floating(seat, view->swayc, + seat_begin_resize_floating(seat, view->container, seat->last_button, e->edges); } } @@ -399,11 +396,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view_map(view, view->wlr_xdg_surface->surface); if (xdg_surface->toplevel->client_pending.fullscreen) { - container_set_fullscreen(view->swayc, true); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_windows(ws); + container_set_fullscreen(view->container, true); + arrange_workspace(view->container->workspace); } else { - arrange_windows(view->swayc->parent); + if (view->container->parent) { + arrange_container(view->container->parent); + } else { + arrange_workspace(view->container->workspace); + } } transaction_commit_dirty(); @@ -440,8 +440,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy); struct sway_view *view = &xdg_shell_view->view; - if (!sway_assert(view->swayc == NULL || view->swayc->destroying, - "Tried to destroy a mapped view")) { + if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { return; } wl_list_remove(&xdg_shell_view->destroy.link); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 175416f3c..b23d45777 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -10,10 +10,12 @@ #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" static const struct sway_view_child_impl popup_impl; @@ -51,15 +53,15 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) { struct sway_view *view = popup->child.view; struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + struct sway_output *output = view->container->workspace->output; // the output box expressed in the coordinate system of the toplevel parent // of the popup struct wlr_box output_toplevel_sx_box = { - .x = output->x - view->x, - .y = output->y - view->y, - .width = output->width, - .height = output->height, + .x = output->wlr_output->lx - view->x, + .y = output->wlr_output->ly - view->y, + .width = output->wlr_output->width, + .height = output->wlr_output->height, }; wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); @@ -249,11 +251,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_v6_view->view; struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; - if (!view->swayc) { - return; - } - - if (view->swayc->instruction) { + if (view->container->node.instruction) { wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); transaction_notify_view_ready_by_serial(view, xdg_surface_v6->configure_serial); @@ -262,7 +260,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); if ((new_geo.width != view->width || new_geo.height != view->height) && - container_is_floating(view->swayc)) { + container_is_floating(view->container)) { // A floating view has unexpectedly sent a new size desktop_damage_view(view); view_update_size(view, new_geo.width, new_geo.height); @@ -316,10 +314,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } - container_set_fullscreen(view->swayc, e->fullscreen); + container_set_fullscreen(view->container, e->fullscreen); - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - arrange_windows(output); + arrange_workspace(view->container->workspace); transaction_commit_dirty(); } @@ -327,13 +324,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, request_move); struct sway_view *view = &xdg_shell_v6_view->view; - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return; } struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seat_begin_move(seat, view->swayc, seat->last_button); + seat_begin_move(seat, view->container, seat->last_button); } } @@ -341,13 +338,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_v6_view *xdg_shell_v6_view = wl_container_of(listener, xdg_shell_v6_view, request_resize); struct sway_view *view = &xdg_shell_v6_view->view; - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return; } struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seat_begin_resize_floating(seat, view->swayc, + seat_begin_resize_floating(seat, view->container, seat->last_button, e->edges); } } @@ -396,11 +393,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view_map(view, view->wlr_xdg_surface_v6->surface); if (xdg_surface->toplevel->client_pending.fullscreen) { - container_set_fullscreen(view->swayc, true); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_windows(ws); + container_set_fullscreen(view->container, true); + arrange_workspace(view->container->workspace); } else { - arrange_windows(view->swayc->parent); + if (view->container->parent) { + arrange_container(view->container->parent); + } else { + arrange_workspace(view->container->workspace); + } } transaction_commit_dirty(); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 94a30239d..0d192b76e 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -59,8 +59,7 @@ 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_container.sway_root->xwayland_unmanaged.prev, - &surface->link); + wl_list_insert(root->xwayland_unmanaged.prev, &surface->link); wl_signal_add(&xsurface->surface->events.commit, &surface->commit); surface->commit.notify = unmanaged_handle_commit; @@ -90,11 +89,10 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { if (seat->wlr_seat->keyboard_state.focused_surface == xsurface->surface) { // Restore focus - struct sway_container *previous = - seat_get_focus_inactive(seat, &root_container); + struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); if (previous) { // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, previous->parent); + seat_set_focus(seat, NULL); seat_set_focus(seat, previous); } } @@ -299,7 +297,7 @@ 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; - if (view->swayc->instruction) { + if (view->container->node.instruction) { get_geometry(view, &view->geometry); transaction_notify_view_ready_by_size(view, state->width, state->height); @@ -308,7 +306,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { get_geometry(view, &new_geo); if ((new_geo.width != view->width || new_geo.height != view->height) && - container_is_floating(view->swayc)) { + container_is_floating(view->container)) { // A floating view has unexpectedly sent a new size // eg. The Firefox "Save As" dialog when downloading a file desktop_damage_view(view); @@ -391,11 +389,14 @@ static void handle_map(struct wl_listener *listener, void *data) { view_map(view, xsurface->surface); if (xsurface->fullscreen) { - container_set_fullscreen(view->swayc, true); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - arrange_windows(ws); + container_set_fullscreen(view->container, true); + arrange_workspace(view->container->workspace); } else { - arrange_windows(view->swayc->parent); + if (view->container->parent) { + arrange_container(view->container->parent); + } else { + arrange_workspace(view->container->workspace); + } } transaction_commit_dirty(); } @@ -411,13 +412,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { ev->width, ev->height); return; } - if (container_is_floating(view->swayc)) { - configure(view, view->swayc->current.view_x, - view->swayc->current.view_y, ev->width, ev->height); + if (container_is_floating(view->container)) { + configure(view, view->container->current.view_x, + view->container->current.view_y, ev->width, ev->height); } else { - configure(view, view->swayc->current.view_x, - view->swayc->current.view_y, view->swayc->current.view_width, - view->swayc->current.view_height); + configure(view, view->container->current.view_x, + view->container->current.view_y, + view->container->current.view_width, + view->container->current.view_height); } } @@ -429,10 +431,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) if (!xsurface->mapped) { return; } - container_set_fullscreen(view->swayc, xsurface->fullscreen); + container_set_fullscreen(view->container, xsurface->fullscreen); - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - arrange_windows(output); + arrange_workspace(view->container->workspace); transaction_commit_dirty(); } @@ -444,11 +445,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); - seat_begin_move(seat, view->swayc, seat->last_button); + seat_begin_move(seat, view->container, seat->last_button); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -459,12 +460,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!container_is_floating(view->swayc)) { + if (!container_is_floating(view->container)) { return; } struct wlr_xwayland_resize_event *e = data; struct sway_seat *seat = input_manager_current_seat(input_manager); - seat_begin_resize_floating(seat, view->swayc, seat->last_button, e->edges); + seat_begin_resize_floating(seat, view->container, + seat->last_button, e->edges); } static void handle_request_activate(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 00240e84a..159932651 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -20,6 +20,7 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" +#include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -50,15 +51,15 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output, } /** - * Returns the container at the cursor's position. If there is a surface at that + * 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). */ -static struct sway_container *container_at_coords( +static struct sway_node *node_at_coords( struct sway_seat *seat, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { // check for unmanaged views first #ifdef HAVE_XWAYLAND - struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; + 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 = @@ -75,67 +76,64 @@ static struct sway_container *container_at_coords( } #endif // find the output the cursor is on - struct wlr_output_layout *output_layout = - root_container.sway_root->output_layout; struct wlr_output *wlr_output = wlr_output_layout_output_at( - output_layout, lx, ly); + root->output_layout, lx, ly); if (wlr_output == NULL) { return NULL; } struct sway_output *output = wlr_output->data; double ox = lx, oy = ly; - wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); + wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); // find the focused workspace on the output for this seat - struct sway_container *ws = seat_get_focus_inactive(seat, output->swayc); - if (ws && ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } - if (!ws) { - return output->swayc; - } + struct sway_workspace *ws = output_get_active_workspace(output); if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], ox, oy, sx, sy))) { - return ws; + return &ws->node; } - if (ws->sway_workspace->fullscreen) { - return tiling_container_at(ws->sway_workspace->fullscreen, lx, ly, - surface, sx, sy); + if (ws->fullscreen) { + 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_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], ox, oy, sx, sy))) { - return ws; + return &ws->node; } struct sway_container *c; if ((c = container_at(ws, lx, ly, surface, sx, sy))) { - return c; + return &c->node; } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], ox, oy, sx, sy))) { - return ws; + return &ws->node; } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], ox, oy, sx, sy))) { - return ws; + return &ws->node; } - c = seat_get_active_child(seat, output->swayc); - if (c) { - return c; - } - if (!c && output->swayc->children->length) { - c = output->swayc->children->items[0]; - return c; - } + return &ws->node; +} - return output->swayc; +static struct sway_container *container_at_coords(struct sway_seat *seat, + double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + struct sway_node *node = node_at_coords(seat, lx, ly, surface, sx, sy); + if (node && node->type == N_CONTAINER) { + return node->sway_container; + } + return NULL; } /** @@ -160,13 +158,14 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { // Iterate the parents until we find one with the layout we want, // then check if the child has siblings between it and the edge. - while (cont->type != C_OUTPUT) { - if (cont->parent->layout == layout) { - int index = list_find(cont->parent->children, cont); + while (cont) { + if (container_parent_layout(cont) == layout) { + list_t *siblings = container_get_siblings(cont); + int index = list_find(siblings, cont); if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { return false; } - if (index < cont->parent->children->length - 1 && + if (index < siblings->length - 1 && (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { return false; } @@ -178,10 +177,10 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { static enum wlr_edges find_edge(struct sway_container *cont, struct sway_cursor *cursor) { - if (cont->type != C_VIEW) { + if (!cont->view) { return WLR_EDGE_NONE; } - struct sway_view *view = cont->sway_view; + struct sway_view *view = cont->view; if (view->border == B_NONE || !view->border_thickness || view->using_csd) { return WLR_EDGE_NONE; } @@ -219,7 +218,7 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont, static void handle_down_motion(struct sway_seat *seat, struct sway_cursor *cursor, uint32_t time_msec) { struct sway_container *con = seat->op_container; - if (seat_is_input_allowed(seat, con->sway_view->surface)) { + if (seat_is_input_allowed(seat, con->view->surface)) { double moved_x = cursor->cursor->x - seat->op_ref_lx; double moved_y = cursor->cursor->y - seat->op_ref_ly; double sx = seat->op_ref_con_lx + moved_x; @@ -260,8 +259,7 @@ static void calculate_floating_constraints(struct sway_container *con, if (config->floating_maximum_width == -1) { // no maximum *max_width = INT_MAX; } else if (config->floating_maximum_width == 0) { // automatic - struct sway_container *ws = container_parent(con, C_WORKSPACE); - *max_width = ws->width; + *max_width = con->workspace->width; } else { *max_width = config->floating_maximum_width; } @@ -269,8 +267,7 @@ static void calculate_floating_constraints(struct sway_container *con, if (config->floating_maximum_height == -1) { // no maximum *max_height = INT_MAX; } else if (config->floating_maximum_height == 0) { // automatic - struct sway_container *ws = container_parent(con, C_WORKSPACE); - *max_height = ws->height; + *max_height = con->workspace->height; } else { *max_height = config->floating_maximum_height; } @@ -314,9 +311,9 @@ static void handle_resize_floating_motion(struct sway_seat *seat, height = fmax(min_height, fmin(height, max_height)); // Apply the view's min/max size - if (con->type == C_VIEW) { + if (con->view) { double view_min_width, view_max_width, view_min_height, view_max_height; - view_get_constraints(con->sway_view, &view_min_width, &view_max_width, + view_get_constraints(con->view, &view_min_width, &view_max_width, &view_min_height, &view_max_height); width = fmax(view_min_width, fmin(width, view_max_width)); height = fmax(view_min_height, fmin(height, view_max_height)); @@ -357,15 +354,15 @@ static void handle_resize_floating_motion(struct sway_seat *seat, con->width += relative_grow_width; con->height += relative_grow_height; - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; + if (con->view) { + struct sway_view *view = con->view; view->x += relative_grow_x; view->y += relative_grow_y; view->width += relative_grow_width; view->height += relative_grow_height; } - arrange_windows(con); + arrange_container(con); } static void handle_resize_tiling_motion(struct sway_seat *seat, @@ -435,44 +432,40 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct wlr_surface *surface = NULL; double sx, sy; - // Find the container beneath the pointer's previous position - struct sway_container *prev_c = container_at_coords(seat, + // Find the node beneath the pointer's previous position + struct sway_node *prev_node = node_at_coords(seat, cursor->previous.x, cursor->previous.y, &surface, &sx, &sy); // Update the stored previous position cursor->previous.x = cursor->cursor->x; cursor->previous.y = cursor->cursor->y; - struct sway_container *c = container_at_coords(seat, + struct sway_node *node = node_at_coords(seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (c && config->focus_follows_mouse && allow_refocusing) { - struct sway_container *focus = seat_get_focus(seat); - if (focus && c->type == C_WORKSPACE) { + if (node && config->focus_follows_mouse && allow_refocusing) { + struct sway_node *focus = seat_get_focus(seat); + if (focus && node->type == N_WORKSPACE) { // Only follow the mouse if it would move to a new output // Otherwise we'll focus the workspace, which is probably wrong - if (focus->type != C_OUTPUT) { - focus = container_parent(focus, C_OUTPUT); + struct sway_output *focused_output = node_get_output(focus); + struct sway_output *output = node_get_output(node); + if (output != focused_output) { + seat_set_focus_warp(seat, node, false, true); } - struct sway_container *output = c; - if (output->type != C_OUTPUT) { - output = container_parent(c, C_OUTPUT); - } - if (output != focus) { - seat_set_focus_warp(seat, c, false, true); - } - } else if (c->type == C_VIEW) { - // Focus c if the following are true: + } else if (node->type == N_CONTAINER && node->sway_container->view) { + // Focus node if the following are true: // - cursor is over a new view, i.e. entered a new window; and // - the new view is visible, i.e. not hidden in a stack or tab; and // - the seat does not have a keyboard grab if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && - c != prev_c && - view_is_visible(c->sway_view)) { - seat_set_focus_warp(seat, c, false, true); + node != prev_node && + view_is_visible(node->sway_container->view)) { + seat_set_focus_warp(seat, node, false, true); } else { - struct sway_container *next_focus = - seat_get_focus_inactive(seat, &root_container); - if (next_focus && next_focus->type == C_VIEW && - view_is_visible(next_focus->sway_view)) { + struct sway_node *next_focus = + seat_get_focus_inactive(seat, &root->node); + if (next_focus && next_focus->type == N_CONTAINER && + node->sway_container->view && + view_is_visible(next_focus->sway_container->view)) { seat_set_focus_warp(seat, next_focus, false, true); } } @@ -486,12 +479,12 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, if (client != cursor->image_client) { cursor_set_image(cursor, "left_ptr", client); } - } else if (c) { - // Try a container's resize edge - enum wlr_edges edge = find_resize_edge(c, cursor); + } else if (node && node->type == N_CONTAINER) { + // Try a node's resize edge + enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); if (edge == WLR_EDGE_NONE) { cursor_set_image(cursor, "left_ptr", NULL); - } else if (container_is_floating(c)) { + } else if (container_is_floating(node->sway_container)) { cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); } else { if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { @@ -684,7 +677,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle tiling resize via border if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && !is_floating) { - seat_set_focus(seat, cont); + seat_set_focus(seat, &cont->node); seat_begin_resize_tiling(seat, cont, button, edge); return; } @@ -713,7 +706,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, image = "sw-resize"; } cursor_set_image(seat->cursor, image, NULL); - seat_set_focus(seat, cont); + seat_set_focus(seat, &cont->node); seat_begin_resize_tiling(seat, cont, button, edge); return; } @@ -725,7 +718,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; if (button == btn_move && state == WLR_BUTTON_PRESSED && (mod_pressed || on_titlebar)) { - while (cont->parent->type != C_WORKSPACE) { + while (cont->parent) { cont = cont->parent; } seat_begin_move(seat, cont, button); @@ -747,7 +740,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, BTN_LEFT : BTN_RIGHT; if (mod_pressed && button == btn_resize) { struct sway_container *floater = cont; - while (floater->parent->type != C_WORKSPACE) { + while (floater->parent) { floater = floater->parent; } edge = 0; @@ -762,7 +755,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle mousedown on a container surface if (surface && cont && state == WLR_BUTTON_PRESSED) { - seat_set_focus(seat, cont); + seat_set_focus(seat, &cont->node); seat_pointer_notify_button(seat, time_msec, button, state); seat_begin_down(seat, cont, button, sx, sy); return; @@ -770,7 +763,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle clicking a container surface if (cont) { - seat_set_focus(seat, cont); + seat_set_focus(seat, &cont->node); seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -1025,8 +1018,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { cursor->previous.y = wlr_cursor->y; cursor->seat = seat; - wlr_cursor_attach_output_layout(wlr_cursor, - root_container.sway_root->output_layout); + wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout); // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index c820e0321..b4352c6ae 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -293,12 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) struct sway_seat *seat; wl_list_for_each(seat, &input_manager->seats, link) { seat_set_exclusive_client(seat, NULL); - struct sway_container *previous = seat_get_focus(seat); + struct sway_node *previous = seat_get_focus(seat); if (previous) { - wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, - container_type_to_str(previous->type), previous->name); // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, previous->parent); + seat_set_focus(seat, NULL); seat_set_focus(seat, previous); } } @@ -369,10 +367,10 @@ struct sway_input_manager *input_manager_create( } bool input_manager_has_focus(struct sway_input_manager *input, - struct sway_container *container) { + struct sway_node *node) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &input->seats, link) { - if (seat_get_focus(seat) == container) { + if (seat_get_focus(seat) == node) { return true; } } @@ -381,10 +379,10 @@ bool input_manager_has_focus(struct sway_input_manager *input, } void input_manager_set_focus(struct sway_input_manager *input, - struct sway_container *container) { + struct sway_node *node) { struct sway_seat *seat; wl_list_for_each(seat, &input->seats, link) { - seat_set_focus(seat, container); + seat_set_focus(seat, node); } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 4b7c78934..2f7a33188 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -47,48 +47,36 @@ void seat_destroy(struct sway_seat *seat) { seat_device_destroy(seat_device); } sway_cursor_destroy(seat->cursor); - wl_list_remove(&seat->new_container.link); + wl_list_remove(&seat->new_node.link); wl_list_remove(&seat->new_drag_icon.link); wl_list_remove(&seat->link); wlr_seat_destroy(seat->wlr_seat); } -static struct sway_seat_container *seat_container_from_container( - struct sway_seat *seat, struct sway_container *con); +static struct sway_seat_node *seat_node_from_node( + struct sway_seat *seat, struct sway_node *node); -static void seat_container_destroy(struct sway_seat_container *seat_con) { - struct sway_container *con = seat_con->container; - struct sway_container *child = NULL; - - if (con->children != NULL) { - for (int i = 0; i < con->children->length; ++i) { - child = con->children->items[i]; - struct sway_seat_container *seat_child = - seat_container_from_container(seat_con->seat, child); - seat_container_destroy(seat_child); - } - } - - wl_list_remove(&seat_con->destroy.link); - wl_list_remove(&seat_con->link); - free(seat_con); +static void seat_node_destroy(struct sway_seat_node *seat_node) { + wl_list_remove(&seat_node->destroy.link); + wl_list_remove(&seat_node->link); + free(seat_node); } /** * Activate all views within this container recursively. */ -static void seat_send_activate(struct sway_container *con, - struct sway_seat *seat) { - if (con->type == C_VIEW) { - if (!seat_is_input_allowed(seat, con->sway_view->surface)) { +static void seat_send_activate(struct sway_node *node, struct sway_seat *seat) { + if (node_is_view(node)) { + if (!seat_is_input_allowed(seat, node->sway_container->view->surface)) { wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited"); return; } - view_set_activated(con->sway_view, true); + view_set_activated(node->sway_container->view, true); } else { - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; - seat_send_activate(child, seat); + list_t *children = node_get_children(node); + for (int i = 0; i < children->length; ++i) { + struct sway_container *child = children->items[i]; + seat_send_activate(&child->node, seat); } } } @@ -98,14 +86,15 @@ static void seat_send_activate(struct sway_container *con, * If con is a container, set all child views as active and don't enable * keyboard input on any. */ -static void seat_send_focus(struct sway_container *con, - struct sway_seat *seat) { - seat_send_activate(con, seat); +static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { + seat_send_activate(node, seat); - if (con->type == C_VIEW - && seat_is_input_allowed(seat, con->sway_view->surface)) { + struct sway_view *view = node->type == N_CONTAINER ? + node->sway_container->view : NULL; + + if (view && seat_is_input_allowed(seat, view->surface)) { #ifdef HAVE_XWAYLAND - if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { + if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = seat->input->server->xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); @@ -114,71 +103,67 @@ static void seat_send_focus(struct sway_container *con, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { wlr_seat_keyboard_notify_enter(seat->wlr_seat, - con->sway_view->surface, keyboard->keycodes, + view->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } else { wlr_seat_keyboard_notify_enter( - seat->wlr_seat, con->sway_view->surface, NULL, 0, NULL); + seat->wlr_seat, view->surface, NULL, 0, NULL); } } } -void seat_focus_inactive_children_for_each(struct sway_seat *seat, - struct sway_container *container, - void (*f)(struct sway_container *container, void *data), void *data) { - struct sway_seat_container *current = NULL; +void seat_for_each_node(struct sway_seat *seat, + void (*f)(struct sway_node *node, void *data), void *data) { + struct sway_seat_node *current = NULL; wl_list_for_each(current, &seat->focus_stack, link) { - if (current->container->parent == NULL) { - continue; - } - if (current->container->parent == container) { - f(current->container, data); - } + f(current->node, data); } } struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, - struct sway_container *ancestor) { - if (ancestor->type == C_VIEW) { - return ancestor; + struct sway_node *ancestor) { + if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) { + return ancestor->sway_container; } - struct sway_seat_container *current; + struct sway_seat_node *current; wl_list_for_each(current, &seat->focus_stack, link) { - struct sway_container *con = current->container; - if (con->type == C_VIEW && container_has_ancestor(con, ancestor)) { - return con; + struct sway_node *node = current->node; + if (node->type == N_CONTAINER && node->sway_container->view && + node_has_ancestor(node, ancestor)) { + return node->sway_container; } } return NULL; } -static void handle_seat_container_destroy(struct wl_listener *listener, - void *data) { - struct sway_seat_container *seat_con = - wl_container_of(listener, seat_con, destroy); - struct sway_seat *seat = seat_con->seat; - struct sway_container *con = seat_con->container; - struct sway_container *parent = con->parent; - struct sway_container *focus = seat_get_focus(seat); +static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { + struct sway_seat_node *seat_node = + wl_container_of(listener, seat_node, destroy); + struct sway_seat *seat = seat_node->seat; + struct sway_node *node = seat_node->node; + struct sway_node *parent = node_get_parent(node); + struct sway_node *focus = seat_get_focus(seat); bool set_focus = focus != NULL && - (focus == con || container_has_ancestor(focus, con)) && - con->type != C_WORKSPACE; + (focus == node || node_has_ancestor(focus, node)) && + node->type == N_CONTAINER; - seat_container_destroy(seat_con); + seat_node_destroy(seat_node); if (set_focus) { - struct sway_container *next_focus = NULL; + struct sway_node *next_focus = NULL; while (next_focus == NULL) { - next_focus = seat_get_focus_inactive_view(seat, parent); + struct sway_container *con = + seat_get_focus_inactive_view(seat, parent); + next_focus = con ? &con->node : NULL; - if (next_focus == NULL && parent->type == C_WORKSPACE) { + if (next_focus == NULL && parent->type == N_WORKSPACE) { next_focus = parent; break; } - parent = parent->parent; + parent = node_get_parent(parent); } // the structure change might have caused it to move up to the top of @@ -191,39 +176,39 @@ static void handle_seat_container_destroy(struct wl_listener *listener, } } -static struct sway_seat_container *seat_container_from_container( - struct sway_seat *seat, struct sway_container *con) { - if (con->type == C_ROOT || con->type == C_OUTPUT) { - // these don't get seat containers ever +static struct sway_seat_node *seat_node_from_node( + struct sway_seat *seat, struct sway_node *node) { + if (node->type == N_ROOT || node->type == N_OUTPUT) { + // these don't get seat nodes ever return NULL; } - struct sway_seat_container *seat_con = NULL; - wl_list_for_each(seat_con, &seat->focus_stack, link) { - if (seat_con->container == con) { - return seat_con; + struct sway_seat_node *seat_node = NULL; + wl_list_for_each(seat_node, &seat->focus_stack, link) { + if (seat_node->node == node) { + return seat_node; } } - seat_con = calloc(1, sizeof(struct sway_seat_container)); - if (seat_con == NULL) { - wlr_log(WLR_ERROR, "could not allocate seat container"); + seat_node = calloc(1, sizeof(struct sway_seat_node)); + if (seat_node == NULL) { + wlr_log(WLR_ERROR, "could not allocate seat node"); return NULL; } - seat_con->container = con; - seat_con->seat = seat; - wl_list_insert(seat->focus_stack.prev, &seat_con->link); - wl_signal_add(&con->events.destroy, &seat_con->destroy); - seat_con->destroy.notify = handle_seat_container_destroy; + seat_node->node = node; + seat_node->seat = seat; + wl_list_insert(seat->focus_stack.prev, &seat_node->link); + wl_signal_add(&node->events.destroy, &seat_node->destroy); + seat_node->destroy.notify = handle_seat_node_destroy; - return seat_con; + return seat_node; } -static void handle_new_container(struct wl_listener *listener, void *data) { - struct sway_seat *seat = wl_container_of(listener, seat, new_container); - struct sway_container *con = data; - seat_container_from_container(seat, con); +static void handle_new_node(struct wl_listener *listener, void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, new_node); + struct sway_node *node = data; + seat_node_from_node(seat, node); } static void drag_icon_damage_whole(struct sway_drag_icon *icon) { @@ -272,8 +257,7 @@ static void drag_icon_handle_unmap(struct wl_listener *listener, void *data) { drag_icon_damage_whole(icon); } -static void drag_icon_handle_destroy(struct wl_listener *listener, - void *data) { +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); @@ -305,20 +289,29 @@ static void handle_new_drag_icon(struct wl_listener *listener, void *data) { icon->destroy.notify = drag_icon_handle_destroy; wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); - wl_list_insert(&root_container.sway_root->drag_icons, &icon->link); + wl_list_insert(&root->drag_icons, &icon->link); drag_icon_update_position(icon); } -static void collect_focus_iter(struct sway_container *con, void *data) { +static void collect_focus_iter(struct sway_node *node, void *data) { struct sway_seat *seat = data; - struct sway_seat_container *seat_con = - seat_container_from_container(seat, con); - if (!seat_con) { + struct sway_seat_node *seat_node = seat_node_from_node(seat, node); + if (!seat_node) { return; } - wl_list_remove(&seat_con->link); - wl_list_insert(&seat->focus_stack, &seat_con->link); + wl_list_remove(&seat_node->link); + wl_list_insert(&seat->focus_stack, &seat_node->link); +} + +static void collect_focus_workspace_iter(struct sway_workspace *workspace, + void *data) { + collect_focus_iter(&workspace->node, data); +} + +static void collect_focus_container_iter(struct sway_container *container, + void *data) { + collect_focus_iter(&container->node, data); } struct sway_seat *seat_create(struct sway_input_manager *input, @@ -345,12 +338,11 @@ struct sway_seat *seat_create(struct sway_input_manager *input, // init the focus stack wl_list_init(&seat->focus_stack); - root_for_each_workspace(collect_focus_iter, seat); - root_for_each_container(collect_focus_iter, seat); + root_for_each_workspace(collect_focus_workspace_iter, seat); + root_for_each_container(collect_focus_container_iter, seat); - wl_signal_add(&root_container.sway_root->events.new_container, - &seat->new_container); - seat->new_container.notify = handle_new_container; + wl_signal_add(&root->events.new_node, &seat->new_node); + seat->new_node.notify = handle_new_node; wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); seat->new_drag_icon.notify = handle_new_drag_icon; @@ -388,19 +380,11 @@ static void seat_apply_input_config(struct sway_seat *seat, if (mapped_to_output != NULL) { wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", sway_device->input_device->identifier, mapped_to_output); - struct sway_container *output = NULL; - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *_output = root_container.children->items[i]; - if (strcasecmp(_output->name, mapped_to_output) == 0) { - output = _output; - break; - } - } + struct sway_output *output = output_by_name(mapped_to_output); if (output) { wlr_cursor_map_input_to_output(seat->cursor->cursor, - sway_device->input_device->wlr_device, - output->sway_output->wlr_output); - wlr_log(WLR_DEBUG, "Mapped to output %s", output->name); + sway_device->input_device->wlr_device, output->wlr_output); + wlr_log(WLR_DEBUG, "Mapped to output %s", output->wlr_output->name); } } } @@ -423,12 +407,12 @@ static void seat_configure_keyboard(struct sway_seat *seat, sway_keyboard_configure(seat_device->keyboard); wlr_seat_set_keyboard(seat->wlr_seat, seat_device->input_device->wlr_device); - struct sway_container *focus = seat_get_focus(seat); - if (focus && focus->type == C_VIEW) { + struct sway_node *focus = seat_get_focus(seat); + if (focus && node_is_view(focus)) { // force notify reenter to pick up the new configuration wlr_seat_keyboard_clear_focus(seat->wlr_seat); wlr_seat_keyboard_notify_enter(seat->wlr_seat, - focus->sway_view->surface, wlr_keyboard->keycodes, + focus->sway_container->view->surface, wlr_keyboard->keycodes, wlr_keyboard->num_keycodes, &wlr_keyboard->modifiers); } } @@ -461,8 +445,7 @@ static struct sway_seat_device *seat_get_device(struct sway_seat *seat, void seat_configure_device(struct sway_seat *seat, struct sway_input_device *input_device) { - struct sway_seat_device *seat_device = - seat_get_device(seat, input_device); + struct sway_seat_device *seat_device = seat_get_device(seat, input_device); if (!seat_device) { return; } @@ -512,8 +495,7 @@ void seat_add_device(struct sway_seat *seat, void seat_remove_device(struct sway_seat *seat, struct sway_input_device *input_device) { - struct sway_seat_device *seat_device = - seat_get_device(seat, input_device); + struct sway_seat_device *seat_device = seat_get_device(seat, input_device); if (!seat_device) { return; @@ -539,11 +521,9 @@ void seat_configure_xcursor(struct sway_seat *seat) { } } - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output_container = - root_container.children->items[i]; - struct wlr_output *output = - output_container->sway_output->wlr_output; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *sway_output = root->outputs->items[i]; + struct wlr_output *output = sway_output->wlr_output; bool result = wlr_xcursor_manager_load(seat->cursor->xcursor_manager, output->scale); @@ -566,17 +546,20 @@ bool seat_is_input_allowed(struct sway_seat *seat, return !seat->exclusive_client || seat->exclusive_client == client; } +static void send_unfocus(struct sway_container *con, void *data) { + if (con->view) { + view_set_activated(con->view, false); + } +} + // Unfocus the container and any children (eg. when leaving `focus parent`) -static void seat_send_unfocus(struct sway_container *container, - struct sway_seat *seat) { - if (container->type == C_VIEW) { - wlr_seat_keyboard_clear_focus(seat->wlr_seat); - view_set_activated(container->sway_view, false); +static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { + wlr_seat_keyboard_clear_focus(seat->wlr_seat); + if (node->type == N_WORKSPACE) { + workspace_for_each_container(node->sway_workspace, send_unfocus, seat); } else { - for (int i = 0; i < container->children->length; ++i) { - struct sway_container *child = container->children->items[i]; - seat_send_unfocus(child, seat); - } + send_unfocus(node->sway_container, seat); + container_for_each_child(node->sway_container, send_unfocus, seat); } } @@ -586,26 +569,23 @@ static int handle_urgent_timeout(void *data) { return 0; } -void seat_set_focus_warp(struct sway_seat *seat, - struct sway_container *container, bool warp, bool notify) { +void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, + bool warp, bool notify) { if (seat->focused_layer) { return; } - struct sway_container *last_focus = seat_get_focus(seat); - if (last_focus == container) { + struct sway_node *last_focus = seat_get_focus(seat); + if (last_focus == node) { return; } - struct sway_container *last_workspace = last_focus; - if (last_workspace && last_workspace->type != C_WORKSPACE) { - last_workspace = container_parent(last_workspace, C_WORKSPACE); - } + struct sway_workspace *last_workspace = seat_get_focused_workspace(seat); - if (container == NULL) { + if (node == NULL) { // Close any popups on the old focus - if (last_focus->type == C_VIEW) { - view_close_popups(last_focus->sway_view); + if (node_is_view(last_focus)) { + view_close_popups(last_focus->sway_container->view); } seat_send_unfocus(last_focus, seat); seat->has_focus = false; @@ -613,69 +593,70 @@ void seat_set_focus_warp(struct sway_seat *seat, return; } - struct sway_container *new_workspace = container; - if (new_workspace->type != C_WORKSPACE) { - new_workspace = container_parent(new_workspace, C_WORKSPACE); - } + struct sway_workspace *new_workspace = node->type == N_WORKSPACE ? + node->sway_workspace : node->sway_container->workspace; + struct sway_container *container = node->type == N_CONTAINER ? + node->sway_container : NULL; - if (last_workspace == new_workspace - && last_workspace->sway_workspace->fullscreen - && !container_is_fullscreen_or_child(container)) { + // Deny setting focus to a view which is hidden by a fullscreen container + if (new_workspace && new_workspace->fullscreen && container && + !container_is_fullscreen_or_child(container)) { return; } - struct sway_container *last_output = last_focus; - if (last_output && last_output->type != C_OUTPUT) { - last_output = container_parent(last_output, C_OUTPUT); - } - struct sway_container *new_output = container; - if (new_output->type != C_OUTPUT) { - new_output = container_parent(new_output, C_OUTPUT); - } + struct sway_output *last_output = last_workspace ? + last_workspace->output : NULL; + struct sway_output *new_output = new_workspace->output; // find new output's old workspace, which might have to be removed if empty - struct sway_container *new_output_last_ws = NULL; + struct sway_workspace *new_output_last_ws = NULL; if (new_output && last_output != new_output) { - new_output_last_ws = seat_get_active_child(seat, new_output); + new_output_last_ws = output_get_active_workspace(new_output); } - if (container->parent) { - struct sway_seat_container *seat_con = - seat_container_from_container(seat, container); - if (seat_con == NULL) { - return; - } - - // put all the ancestors of this container on top of the focus stack - struct sway_seat_container *parent = - seat_container_from_container(seat, container->parent); + // Put the container parents on the focus stack, then the workspace, then + // the focused container. + if (container) { + struct sway_container *parent = container->parent; while (parent) { - wl_list_remove(&parent->link); - wl_list_insert(&seat->focus_stack, &parent->link); - container_set_dirty(parent->container); - - parent = seat_container_from_container(seat, - parent->container->parent); + struct sway_seat_node *seat_node = + seat_node_from_node(seat, &parent->node); + wl_list_remove(&seat_node->link); + wl_list_insert(&seat->focus_stack, &seat_node->link); + node_set_dirty(&parent->node); + parent = parent->parent; } - - wl_list_remove(&seat_con->link); - wl_list_insert(&seat->focus_stack, &seat_con->link); + } + if (new_workspace) { + struct sway_seat_node *seat_node = + seat_node_from_node(seat, &new_workspace->node); + wl_list_remove(&seat_node->link); + wl_list_insert(&seat->focus_stack, &seat_node->link); + node_set_dirty(&new_workspace->node); + } + if (container) { + struct sway_seat_node *seat_node = + seat_node_from_node(seat, &container->node); + wl_list_remove(&seat_node->link); + wl_list_insert(&seat->focus_stack, &seat_node->link); + node_set_dirty(&container->node); if (last_focus) { seat_send_unfocus(last_focus, seat); - container_set_dirty(last_focus); + node_set_dirty(last_focus); + struct sway_node *last_parent = node_get_parent(last_focus); + if (last_parent) { + node_set_dirty(last_parent); + } } - seat_send_focus(container, seat); - - container_set_dirty(container); - container_set_dirty(container->parent); // for focused_inactive_child + seat_send_focus(&container->node, seat); } // emit ipc events if (notify && new_workspace && last_workspace != new_workspace) { ipc_event_workspace(last_workspace, new_workspace, "focus"); } - if (container->type == C_VIEW) { + if (container && container->view) { ipc_event_window(container, "focus"); } @@ -684,14 +665,14 @@ void seat_set_focus_warp(struct sway_seat *seat, } // Close any popups on the old focus - if (last_focus && last_focus->type == C_VIEW) { - view_close_popups(last_focus->sway_view); + if (last_focus && node_is_view(last_focus)) { + view_close_popups(last_focus->sway_container->view); } // If urgent, either unset the urgency or start a timer to unset it - if (container->type == C_VIEW && view_is_urgent(container->sway_view) && - !container->sway_view->urgent_timer) { - struct sway_view *view = container->sway_view; + if (container && container->view && view_is_urgent(container->view) && + !container->view->urgent_timer) { + struct sway_view *view = container->view; if (last_workspace && last_workspace != new_workspace && config->urgent_timeout > 0) { view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, @@ -711,12 +692,15 @@ void seat_set_focus_warp(struct sway_seat *seat, // If we've focused a floating container, bring it to the front. // We do this by putting it at the end of the floating list. - struct sway_container *floater = container; - while (floater->parent && floater->parent->type != C_WORKSPACE) { - floater = floater->parent; - } - if (container_is_floating(floater)) { - list_move_to_end(floater->parent->sway_workspace->floating, floater); + if (container) { + struct sway_container *floater = container; + while (floater->parent) { + floater = floater->parent; + } + if (container_is_floating(floater)) { + list_move_to_end(floater->workspace->floating, floater); + node_set_dirty(&floater->workspace->node); + } } if (last_focus) { @@ -727,11 +711,8 @@ void seat_set_focus_warp(struct sway_seat *seat, if (config->mouse_warping && warp && new_output != last_output) { double x = container->x + container->width / 2.0; double y = container->y + container->height / 2.0; - struct wlr_output *wlr_output = - new_output->sway_output->wlr_output; - if (!wlr_output_layout_contains_point( - root_container.sway_root->output_layout, - wlr_output, seat->cursor->cursor->x, + if (!wlr_output_layout_contains_point(root->output_layout, + new_output->wlr_output, seat->cursor->cursor->x, seat->cursor->cursor->y)) { wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); cursor_send_pointer_motion(seat->cursor, 0, true); @@ -744,9 +725,8 @@ void seat_set_focus_warp(struct sway_seat *seat, update_debug_tree(); } -void seat_set_focus(struct sway_seat *seat, - struct sway_container *container) { - seat_set_focus_warp(seat, container, true, true); +void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { + seat_set_focus_warp(seat, node, true, true); } void seat_set_focus_surface(struct sway_seat *seat, @@ -755,12 +735,11 @@ void seat_set_focus_surface(struct sway_seat *seat, return; } if (seat->has_focus && unfocus) { - struct sway_container *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus(seat); seat_send_unfocus(focus, seat); seat->has_focus = false; } - struct wlr_keyboard *keyboard = - wlr_seat_get_keyboard(seat->wlr_seat); + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); if (keyboard) { wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); @@ -773,11 +752,8 @@ void seat_set_focus_layer(struct sway_seat *seat, struct wlr_layer_surface *layer) { if (!layer && seat->focused_layer) { seat->focused_layer = NULL; - struct sway_container *previous = - seat_get_focus_inactive(seat, &root_container); + struct sway_node *previous = seat_get_focus_inactive(seat, &root->node); if (previous) { - wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, - container_type_to_str(previous->type), previous->name); // Hack to get seat to re-focus the return value of get_focus seat_set_focus(seat, NULL); seat_set_focus(seat, previous); @@ -798,13 +774,9 @@ void seat_set_exclusive_client(struct sway_seat *seat, seat->exclusive_client = client; // Triggers a refocus of the topmost surface layer if necessary // TODO: Make layer surface focus per-output based on cursor position - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - if (!sway_assert(output->type == C_OUTPUT, - "root container has non-output child")) { - continue; - } - arrange_layers(output->sway_output); + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + arrange_layers(output); } return; } @@ -814,9 +786,9 @@ void seat_set_exclusive_client(struct sway_seat *seat, } } if (seat->has_focus) { - struct sway_container *focus = seat_get_focus(seat); - if (focus->type == C_VIEW && wl_resource_get_client( - focus->sway_view->surface->resource) != client) { + struct sway_node *focus = seat_get_focus(seat); + if (node_is_view(focus) && wl_resource_get_client( + focus->sway_container->view->surface->resource) != client) { seat_set_focus(seat, NULL); } } @@ -837,79 +809,101 @@ void seat_set_exclusive_client(struct sway_seat *seat, seat->exclusive_client = client; } -struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, - struct sway_container *con) { - if (con->type == C_WORKSPACE && !con->children->length && - !con->sway_workspace->floating->length) { - return con; +struct sway_node *seat_get_focus_inactive(struct sway_seat *seat, + struct sway_node *node) { + if (node_is_view(node)) { + return node; } - if (con->type == C_VIEW) { - return con; - } - struct sway_seat_container *current; + struct sway_seat_node *current; wl_list_for_each(current, &seat->focus_stack, link) { - if (container_has_ancestor(current->container, con)) { - return current->container; + if (node_has_ancestor(current->node, node)) { + return current->node; } } + if (node->type == N_WORKSPACE) { + return node; + } return NULL; } struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, - struct sway_container *ancestor) { - if (ancestor->type == C_WORKSPACE && !ancestor->children->length) { - return ancestor; + struct sway_workspace *workspace) { + if (!workspace->tiling->length) { + return NULL; } - struct sway_seat_container *current; + struct sway_seat_node *current; wl_list_for_each(current, &seat->focus_stack, link) { - struct sway_container *con = current->container; - if (!container_is_floating_or_child(con) && - container_has_ancestor(current->container, ancestor)) { - return con; + struct sway_node *node = current->node; + if (node->type == N_CONTAINER && + !container_is_floating_or_child(node->sway_container) && + node->sway_container->workspace == workspace) { + return node->sway_container; } } return NULL; } struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, - struct sway_container *ancestor) { - if (ancestor->type == C_WORKSPACE && - !ancestor->sway_workspace->floating->length) { + struct sway_workspace *workspace) { + if (!workspace->floating->length) { return NULL; } - struct sway_seat_container *current; + struct sway_seat_node *current; wl_list_for_each(current, &seat->focus_stack, link) { - struct sway_container *con = current->container; - if (container_is_floating_or_child(con) && - container_has_ancestor(current->container, ancestor)) { - return con; + struct sway_node *node = current->node; + if (node->type == N_CONTAINER && + container_is_floating_or_child(node->sway_container) && + node->sway_container->workspace == workspace) { + return node->sway_container; } } return NULL; } -struct sway_container *seat_get_active_child(struct sway_seat *seat, - struct sway_container *parent) { - if (parent->type == C_VIEW) { +struct sway_node *seat_get_active_child(struct sway_seat *seat, + struct sway_node *parent) { + if (node_is_view(parent)) { return parent; } - struct sway_seat_container *current; + struct sway_seat_node *current; wl_list_for_each(current, &seat->focus_stack, link) { - struct sway_container *con = current->container; - if (con->parent == parent) { - return con; + struct sway_node *node = current->node; + if (node_get_parent(node) == parent) { + return node; } } return NULL; } -struct sway_container *seat_get_focus(struct sway_seat *seat) { +struct sway_node *seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; } - struct sway_seat_container *current = + struct sway_seat_node *current = wl_container_of(seat->focus_stack.next, current, link); - return current->container; + return current->node; +} + +struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { + struct sway_node *focus = seat_get_focus(seat); + if (!focus) { + return NULL; + } + if (focus->type == N_CONTAINER) { + return focus->sway_container->workspace; + } + if (focus->type == N_WORKSPACE) { + return focus->sway_workspace; + } + return NULL; // unreachable +} + +struct sway_container *seat_get_focused_container(struct sway_seat *seat) { + struct sway_node *focus = seat_get_focus(seat); + if (focus && focus->type == N_CONTAINER) { + return focus->sway_container; + } + return NULL; } void seat_apply_config(struct sway_seat *seat, diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 06cb7e11c..9a9559916 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -46,18 +46,18 @@ json_object *ipc_json_get_version() { return version; } -static json_object *ipc_json_create_rect(struct sway_container *c) { +static json_object *ipc_json_create_rect(struct wlr_box *box) { json_object *rect = json_object_new_object(); - json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); - json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); - json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width)); - json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height)); + json_object_object_add(rect, "x", json_object_new_int(box->x)); + json_object_object_add(rect, "y", json_object_new_int(box->y)); + json_object_object_add(rect, "width", json_object_new_int(box->width)); + json_object_object_add(rect, "height", json_object_new_int(box->height)); return rect; } -static void ipc_json_describe_root(struct sway_container *root, json_object *object) { +static void ipc_json_describe_root(struct sway_root *root, json_object *object) { json_object_object_add(object, "type", json_object_new_string("root")); json_object_object_add(object, "layout", json_object_new_string("splith")); } @@ -84,17 +84,13 @@ static const char *ipc_json_get_output_transform(enum wl_output_transform transf return NULL; } -static void ipc_json_describe_output(struct sway_container *container, +static void ipc_json_describe_output(struct sway_output *output, json_object *object) { - struct wlr_output *wlr_output = container->sway_output->wlr_output; - json_object_object_add(object, "type", - json_object_new_string("output")); - json_object_object_add(object, "active", - json_object_new_boolean(true)); - json_object_object_add(object, "primary", - json_object_new_boolean(false)); - json_object_object_add(object, "layout", - json_object_new_string("output")); + struct wlr_output *wlr_output = output->wlr_output; + json_object_object_add(object, "type", json_object_new_string("output")); + json_object_object_add(object, "active", json_object_new_boolean(true)); + json_object_object_add(object, "primary", json_object_new_boolean(false)); + json_object_object_add(object, "layout", json_object_new_string("output")); json_object_object_add(object, "make", json_object_new_string(wlr_output->make)); json_object_object_add(object, "model", @@ -109,20 +105,9 @@ static void ipc_json_describe_output(struct sway_container *container, json_object_new_string( ipc_json_get_output_transform(wlr_output->transform))); - struct sway_seat *seat = input_manager_get_default_seat(input_manager); - const char *ws = NULL; - if (seat) { - struct sway_container *focus = - seat_get_focus_inactive(seat, container); - if (focus && focus->type != C_WORKSPACE) { - focus = container_parent(focus, C_WORKSPACE); - } - if (focus) { - ws = focus->name; - } - } + struct sway_workspace *ws = output_get_active_workspace(output); json_object_object_add(object, "current_workspace", - json_object_new_string(ws)); + json_object_new_string(ws->name)); json_object *modes_array = json_object_new_array(); struct wlr_output_mode *mode; @@ -161,60 +146,57 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { return object; } -static void ipc_json_describe_workspace(struct sway_container *workspace, +static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object *object) { int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; json_object_object_add(object, "num", json_object_new_int(num)); - json_object_object_add(object, "output", workspace->parent ? - json_object_new_string(workspace->parent->name) : NULL); + json_object_object_add(object, "output", workspace->output ? + json_object_new_string(workspace->output->wlr_output->name) : NULL); json_object_object_add(object, "type", json_object_new_string("workspace")); json_object_object_add(object, "urgent", - json_object_new_boolean(workspace->sway_workspace->urgent)); - json_object_object_add(object, "representation", workspace->formatted_title ? - json_object_new_string(workspace->formatted_title) : NULL); + json_object_new_boolean(workspace->urgent)); + json_object_object_add(object, "representation", workspace->representation ? + json_object_new_string(workspace->representation) : NULL); const char *layout = ipc_json_layout_description(workspace->layout); json_object_object_add(object, "layout", json_object_new_string(layout)); // Floating json_object *floating_array = json_object_new_array(); - list_t *floating = workspace->sway_workspace->floating; - for (int i = 0; i < floating->length; ++i) { - struct sway_container *floater = floating->items[i]; + for (int i = 0; i < workspace->floating->length; ++i) { + struct sway_container *floater = workspace->floating->items[i]; json_object_array_add(floating_array, - ipc_json_describe_container_recursive(floater)); + ipc_json_describe_node_recursive(&floater->node)); } json_object_object_add(object, "floating_nodes", floating_array); } static void ipc_json_describe_view(struct sway_container *c, json_object *object) { json_object_object_add(object, "name", - c->name ? json_object_new_string(c->name) : NULL); + c->title ? json_object_new_string(c->title) : NULL); json_object_object_add(object, "type", json_object_new_string("con")); - if (c->type == C_VIEW) { - const char *app_id = view_get_app_id(c->sway_view); + if (c->view) { + const char *app_id = view_get_app_id(c->view); json_object_object_add(object, "app_id", app_id ? json_object_new_string(app_id) : NULL); - const char *class = view_get_class(c->sway_view); + const char *class = view_get_class(c->view); json_object_object_add(object, "class", class ? json_object_new_string(class) : NULL); } - if (c->parent) { - json_object_object_add(object, "layout", - json_object_new_string(ipc_json_layout_description(c->layout))); - } + json_object_object_add(object, "layout", + json_object_new_string(ipc_json_layout_description(c->layout))); - bool urgent = c->type == C_VIEW ? - view_is_urgent(c->sway_view) : container_has_urgent_child(c); + bool urgent = c->view ? + view_is_urgent(c->view) : container_has_urgent_child(c); json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); - if (c->type == C_VIEW) { + if (c->view) { json_object *marks = json_object_new_array(); - list_t *view_marks = c->sway_view->marks; + list_t *view_marks = c->view->marks; for (int i = 0; i < view_marks->length; ++i) { json_object_array_add(marks, json_object_new_string(view_marks->items[i])); } @@ -222,64 +204,97 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object } } -static void focus_inactive_children_iterator(struct sway_container *c, void *data) { - json_object *focus = data; - json_object_array_add(focus, json_object_new_int(c->id)); +struct focus_inactive_data { + struct sway_node *node; + json_object *object; +}; + +static void focus_inactive_children_iterator(struct sway_node *node, + void *_data) { + struct focus_inactive_data *data = _data; + if (node_get_parent(node) == data->node) { + json_object_array_add(data->object, json_object_new_int(node->id)); + } } -json_object *ipc_json_describe_container(struct sway_container *c) { - if (!(sway_assert(c, "Container must not be null."))) { - return NULL; - } - +json_object *ipc_json_describe_node(struct sway_node *node) { struct sway_seat *seat = input_manager_get_default_seat(input_manager); - bool focused = seat_get_focus(seat) == c; + bool focused = seat_get_focus(seat) == node; json_object *object = json_object_new_object(); + char *name = node_get_name(node); - json_object_object_add(object, "id", json_object_new_int((int)c->id)); + struct wlr_box box; + node_get_box(node, &box); + json_object_object_add(object, "id", json_object_new_int((int)node->id)); json_object_object_add(object, "name", - c->name ? json_object_new_string(c->name) : NULL); - json_object_object_add(object, "rect", ipc_json_create_rect(c)); - json_object_object_add(object, "focused", - json_object_new_boolean(focused)); + name ? json_object_new_string(name) : NULL); + json_object_object_add(object, "rect", ipc_json_create_rect(&box)); + json_object_object_add(object, "focused", json_object_new_boolean(focused)); json_object *focus = json_object_new_array(); - seat_focus_inactive_children_for_each(seat, c, - focus_inactive_children_iterator, focus); + struct focus_inactive_data data = { + .node = node, + .object = focus, + }; + seat_for_each_node(seat, focus_inactive_children_iterator, &data); json_object_object_add(object, "focus", focus); - switch (c->type) { - case C_ROOT: - ipc_json_describe_root(c, object); + switch (node->type) { + case N_ROOT: + ipc_json_describe_root(root, object); break; - case C_OUTPUT: - ipc_json_describe_output(c, object); + case N_OUTPUT: + ipc_json_describe_output(node->sway_output, object); break; - case C_CONTAINER: - case C_VIEW: - ipc_json_describe_view(c, object); + case N_CONTAINER: + ipc_json_describe_view(node->sway_container, object); break; - case C_WORKSPACE: - ipc_json_describe_workspace(c, object); - break; - case C_TYPES: - default: + case N_WORKSPACE: + ipc_json_describe_workspace(node->sway_workspace, object); break; } return object; } -json_object *ipc_json_describe_container_recursive(struct sway_container *c) { - json_object *object = ipc_json_describe_container(c); +json_object *ipc_json_describe_node_recursive(struct sway_node *node) { + json_object *object = ipc_json_describe_node(node); int i; json_object *children = json_object_new_array(); - if (c->type != C_VIEW && c->children) { - for (i = 0; i < c->children->length; ++i) { - json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); + switch (node->type) { + case N_ROOT: + for (i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + json_object_array_add(children, + ipc_json_describe_node_recursive(&output->node)); } + break; + case N_OUTPUT: + for (i = 0; i < node->sway_output->workspaces->length; ++i) { + struct sway_workspace *ws = node->sway_output->workspaces->items[i]; + json_object_array_add(children, + ipc_json_describe_node_recursive(&ws->node)); + } + break; + case N_WORKSPACE: + for (i = 0; i < node->sway_workspace->tiling->length; ++i) { + struct sway_container *con = node->sway_workspace->tiling->items[i]; + json_object_array_add(children, + ipc_json_describe_node_recursive(&con->node)); + } + break; + case N_CONTAINER: + if (node->sway_container->children) { + for (i = 0; i < node->sway_container->children->length; ++i) { + struct sway_container *child = + node->sway_container->children->items[i]; + json_object_array_add(children, + ipc_json_describe_node_recursive(&child->node)); + } + } + break; } json_object_object_add(object, "nodes", children); @@ -329,7 +344,7 @@ json_object *ipc_json_describe_seat(struct sway_seat *seat) { } json_object *object = json_object_new_object(); - struct sway_container *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus(seat); json_object_object_add(object, "name", json_object_new_string(seat->wlr_seat->name)); @@ -470,13 +485,13 @@ json_object *ipc_json_describe_bar_config(struct bar_config *bar) { json_object_object_add(json, "colors", colors); // Add outputs if defined + json_object *outputs = json_object_new_array(); if (bar->outputs && bar->outputs->length > 0) { - json_object *outputs = json_object_new_array(); for (int i = 0; i < bar->outputs->length; ++i) { const char *name = bar->outputs->items[i]; json_object_array_add(outputs, json_object_new_string(name)); } - json_object_object_add(json, "outputs", outputs); } + json_object_object_add(json, "outputs", outputs); return json; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index fb5be27bc..8ae265f6d 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -33,6 +33,7 @@ #include "sway/input/seat.h" #include "sway/tree/root.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" #include "list.h" #include "log.h" #include "util.h" @@ -291,8 +292,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) } } -void ipc_event_workspace(struct sway_container *old, - struct sway_container *new, const char *change) { +void ipc_event_workspace(struct sway_workspace *old, + struct sway_workspace *new, const char *change) { if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { return; } @@ -301,14 +302,14 @@ void ipc_event_workspace(struct sway_container *old, json_object_object_add(obj, "change", json_object_new_string(change)); if (old) { json_object_object_add(obj, "old", - ipc_json_describe_container_recursive(old)); + ipc_json_describe_node_recursive(&old->node)); } else { json_object_object_add(obj, "old", NULL); } if (new) { json_object_object_add(obj, "current", - ipc_json_describe_container_recursive(new)); + ipc_json_describe_node_recursive(&new->node)); } else { json_object_object_add(obj, "current", NULL); } @@ -325,7 +326,8 @@ void ipc_event_window(struct sway_container *window, const char *change) { wlr_log(WLR_DEBUG, "Sending window::%s event", change); json_object *obj = json_object_new_object(); json_object_object_add(obj, "change", json_object_new_string(change)); - json_object_object_add(obj, "container", ipc_json_describe_container_recursive(window)); + json_object_object_add(obj, "container", + ipc_json_describe_node_recursive(&window->node)); const char *json_string = json_object_to_json_string(obj); ipc_send_event(json_string, IPC_EVENT_WINDOW); @@ -521,30 +523,20 @@ void ipc_client_disconnect(struct ipc_client *client) { free(client); } -static void ipc_get_workspaces_callback(struct sway_container *workspace, +static void ipc_get_workspaces_callback(struct sway_workspace *workspace, void *data) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return; - } - json_object *workspace_json = ipc_json_describe_container(workspace); + json_object *workspace_json = ipc_json_describe_node(&workspace->node); // override the default focused indicator because // it's set differently for the get_workspaces reply - struct sway_seat *seat = - input_manager_get_default_seat(input_manager); - struct sway_container *focused_ws = seat_get_focus(seat); - if (focused_ws != NULL && focused_ws->type != C_WORKSPACE) { - focused_ws = container_parent(focused_ws, C_WORKSPACE); - } + struct sway_seat *seat = input_manager_get_default_seat(input_manager); + struct sway_workspace *focused_ws = seat_get_focused_workspace(seat); bool focused = workspace == focused_ws; json_object_object_del(workspace_json, "focused"); json_object_object_add(workspace_json, "focused", json_object_new_boolean(focused)); json_object_array_add((json_object *)data, workspace_json); - focused_ws = seat_get_focus_inactive(seat, workspace->parent); - if (focused_ws->type != C_WORKSPACE) { - focused_ws = container_parent(focused_ws, C_WORKSPACE); - } + focused_ws = output_get_active_workspace(workspace->output); bool visible = workspace == focused_ws; json_object_object_add(workspace_json, "visible", json_object_new_boolean(visible)); @@ -552,9 +544,9 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace, static void ipc_get_marks_callback(struct sway_container *con, void *data) { json_object *marks = (json_object *)data; - if (con->type == C_VIEW && con->sway_view->marks) { - for (int i = 0; i < con->sway_view->marks->length; ++i) { - char *mark = (char *)con->sway_view->marks->items[i]; + if (con->view && con->view->marks) { + for (int i = 0; i < con->view->marks->length; ++i) { + char *mark = (char *)con->view->marks->items[i]; json_object_array_add(marks, json_object_new_string(mark)); } } @@ -608,16 +600,14 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_OUTPUTS: { json_object *outputs = json_object_new_array(); - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *container = root_container.children->items[i]; - if (container->type == C_OUTPUT) { - json_object_array_add(outputs, - ipc_json_describe_container(container)); - } + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + json_object_array_add(outputs, + ipc_json_describe_node(&output->node)); } struct sway_output *output; - wl_list_for_each(output, &root_container.sway_root->all_outputs, link) { - if (!output->swayc) { + wl_list_for_each(output, &root->all_outputs, link) { + if (!output->enabled) { json_object_array_add(outputs, ipc_json_describe_disabled_output(output)); } @@ -717,8 +707,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_GET_TREE: { - json_object *tree = - ipc_json_describe_container_recursive(&root_container); + json_object *tree = ipc_json_describe_node_recursive(&root->node); const char *json_string = json_object_to_json_string(tree); client_valid = ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); diff --git a/sway/main.c b/sway/main.c index 2f05dc383..fb4f0d8c0 100644 --- a/sway/main.c +++ b/sway/main.c @@ -42,7 +42,6 @@ void sway_terminate(int exit_code) { } void sig_handler(int signal) { - //close_views(&root_container); sway_terminate(EXIT_SUCCESS); } @@ -395,7 +394,7 @@ int main(int argc, char **argv) { wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION); - root_create(); + root = root_create(); if (!server_init(&server)) { return 1; @@ -450,7 +449,8 @@ int main(int argc, char **argv) { wlr_log(WLR_INFO, "Shutting down sway"); server_fini(&server); - root_destroy(); + root_destroy(root); + root = NULL; if (config) { free_config(config); diff --git a/sway/meson.build b/sway/meson.build index c14e58dd0..8891ebc0b 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -151,6 +151,7 @@ sway_sources = files( 'tree/arrange.c', 'tree/container.c', + 'tree/node.c', 'tree/root.c', 'tree/view.c', 'tree/workspace.c', diff --git a/sway/server.c b/sway/server.c index 749365cb8..09ebe83ff 100644 --- a/sway/server.c +++ b/sway/server.c @@ -61,8 +61,7 @@ bool server_init(struct sway_server *server) { server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); - wlr_xdg_output_manager_create(server->wl_display, - root_container.sway_root->output_layout); + wlr_xdg_output_manager_create(server->wl_display, root->output_layout); server->idle = wlr_idle_create(server->wl_display); server->idle_inhibit_manager_v1 = @@ -131,7 +130,7 @@ bool server_init(struct sway_server *server) { server->txn_timeout_ms = 200; } - server->dirty_containers = create_list(); + server->dirty_nodes = create_list(); server->transactions = create_list(); input_manager = input_manager_create(server); @@ -145,7 +144,7 @@ void server_fini(struct sway_server *server) { #endif wl_display_destroy_clients(server->wl_display); wl_display_destroy(server->wl_display); - list_free(server->dirty_containers); + list_free(server->dirty_nodes); list_free(server->transactions); } diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 92f20fccc..f86d4a743 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -166,29 +166,23 @@ void arrange_container(struct sway_container *container) { if (config->reloading) { return; } - if (container->type == C_VIEW) { - view_autoconfigure(container->sway_view); - container_set_dirty(container); - return; - } - if (!sway_assert(container->type == C_CONTAINER, "Expected a container")) { + if (container->view) { + view_autoconfigure(container->view); + node_set_dirty(&container->node); return; } struct wlr_box box; container_get_box(container, &box); arrange_children(container->children, container->layout, &box); - container_set_dirty(container); + node_set_dirty(&container->node); } -void arrange_workspace(struct sway_container *workspace) { +void arrange_workspace(struct sway_workspace *workspace) { if (config->reloading) { return; } - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return; - } - struct sway_container *output = workspace->parent; - struct wlr_box *area = &output->sway_output->usable_area; + struct sway_output *output = workspace->output; + struct wlr_box *area = &output->usable_area; wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", area->width, area->height, area->x, area->y); workspace_remove_gaps(workspace); @@ -197,21 +191,20 @@ void arrange_workspace(struct sway_container *workspace) { double prev_y = workspace->y; workspace->width = area->width; workspace->height = area->height; - workspace->x = output->x + area->x; - workspace->y = output->y + area->y; + workspace->x = output->wlr_output->lx + area->x; + workspace->y = output->wlr_output->ly + area->y; // Adjust any floating containers double diff_x = workspace->x - prev_x; double diff_y = workspace->y - prev_y; if (diff_x != 0 || diff_y != 0) { - for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) { - struct sway_container *floater = - workspace->sway_workspace->floating->items[i]; + for (int i = 0; i < workspace->floating->length; ++i) { + struct sway_container *floater = workspace->floating->items[i]; container_floating_translate(floater, diff_x, diff_y); double center_x = floater->x + floater->width / 2; double center_y = floater->y + floater->height / 2; struct wlr_box workspace_box; - container_get_box(workspace, &workspace_box); + workspace_get_box(workspace, &workspace_box); if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { container_floating_move_to_center(floater); } @@ -219,43 +212,32 @@ void arrange_workspace(struct sway_container *workspace) { } workspace_add_gaps(workspace); - container_set_dirty(workspace); + node_set_dirty(&workspace->node); wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, workspace->x, workspace->y); - if (workspace->sway_workspace->fullscreen) { - struct sway_container *fs = workspace->sway_workspace->fullscreen; - fs->x = workspace->parent->x; - fs->y = workspace->parent->y; - fs->width = workspace->parent->width; - fs->height = workspace->parent->height; + if (workspace->fullscreen) { + struct sway_container *fs = workspace->fullscreen; + fs->x = output->wlr_output->lx; + fs->y = output->wlr_output->ly; + fs->width = output->wlr_output->width; + fs->height = output->wlr_output->height; arrange_container(fs); } else { struct wlr_box box; - container_get_box(workspace, &box); - arrange_children(workspace->children, workspace->layout, &box); - arrange_floating(workspace->sway_workspace->floating); + workspace_get_box(workspace, &box); + arrange_children(workspace->tiling, workspace->layout, &box); + arrange_floating(workspace->floating); } } -void arrange_output(struct sway_container *output) { +void arrange_output(struct sway_output *output) { if (config->reloading) { return; } - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return; - } - const struct wlr_box *output_box = wlr_output_layout_get_box( - root_container.sway_root->output_layout, - output->sway_output->wlr_output); - output->x = output_box->x; - output->y = output_box->y; - output->width = output_box->width; - output->height = output_box->height; - container_set_dirty(output); - wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", - output->name, output->x, output->y); - for (int i = 0; i < output->children->length; ++i) { - struct sway_container *workspace = output->children->items[i]; + // Outputs have no pending x/y/width/height, + // so all we do here is arrange the workspaces. + for (int i = 0; i < output->workspaces->length; ++i) { + struct sway_workspace *workspace = output->workspaces->items[i]; arrange_workspace(workspace); } } @@ -264,37 +246,31 @@ void arrange_root(void) { if (config->reloading) { return; } - struct wlr_output_layout *output_layout = - root_container.sway_root->output_layout; const struct wlr_box *layout_box = - wlr_output_layout_get_box(output_layout, NULL); - root_container.x = layout_box->x; - root_container.y = layout_box->y; - root_container.width = layout_box->width; - root_container.height = layout_box->height; - container_set_dirty(&root_container); - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; + wlr_output_layout_get_box(root->output_layout, NULL); + root->x = layout_box->x; + root->y = layout_box->y; + root->width = layout_box->width; + root->height = layout_box->height; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; arrange_output(output); } } -void arrange_windows(struct sway_container *container) { - switch (container->type) { - case C_ROOT: +void arrange_node(struct sway_node *node) { + switch (node->type) { + case N_ROOT: arrange_root(); break; - case C_OUTPUT: - arrange_output(container); + case N_OUTPUT: + arrange_output(node->sway_output); break; - case C_WORKSPACE: - arrange_workspace(container); + case N_WORKSPACE: + arrange_workspace(node->sway_workspace); break; - case C_CONTAINER: - case C_VIEW: - arrange_container(container); - break; - case C_TYPES: + case N_CONTAINER: + arrange_container(node->sway_container); break; } } diff --git a/sway/tree/container.c b/sway/tree/container.c index 520b45667..0cb8d0a51 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -24,97 +24,39 @@ #include "log.h" #include "stringop.h" -const char *container_type_to_str(enum sway_container_type type) { - switch (type) { - case C_ROOT: - return "C_ROOT"; - case C_OUTPUT: - return "C_OUTPUT"; - case C_WORKSPACE: - return "C_WORKSPACE"; - case C_CONTAINER: - return "C_CONTAINER"; - case C_VIEW: - return "C_VIEW"; - default: - return "C_UNKNOWN"; - } -} - -void container_create_notify(struct sway_container *container) { - if (container->type == C_VIEW) { - ipc_event_window(container, "new"); - } else if (container->type == C_WORKSPACE) { - ipc_event_workspace(NULL, container, "init"); - } - wl_signal_emit(&root_container.sway_root->events.new_container, container); -} - -void container_update_textures_recursive(struct sway_container *con) { - if (con->type == C_CONTAINER || con->type == C_VIEW) { - container_update_title_textures(con); - } - - if (con->type == C_VIEW) { - view_update_marks_textures(con->sway_view); - } else { - for (int i = 0; i < con->children->length; ++i) { - struct sway_container *child = con->children->items[i]; - container_update_textures_recursive(child); - } - - if (con->type == C_WORKSPACE) { - for (int i = 0; i < con->sway_workspace->floating->length; ++i) { - struct sway_container *floater = - con->sway_workspace->floating->items[i]; - container_update_textures_recursive(floater); - } - } - } -} - -struct sway_container *container_create(enum sway_container_type type) { - // next id starts at 1 because 0 is assigned to root_container in layout.c - static size_t next_id = 1; +struct sway_container *container_create(struct sway_view *view) { struct sway_container *c = calloc(1, sizeof(struct sway_container)); if (!c) { + wlr_log(WLR_ERROR, "Unable to allocate sway_container"); return NULL; } - c->id = next_id++; + node_init(&c->node, N_CONTAINER, c); c->layout = L_NONE; - c->type = type; + c->view = view; c->alpha = 1.0f; - if (type != C_VIEW) { + if (!view) { c->children = create_list(); c->current.children = create_list(); } c->outputs = create_list(); wl_signal_init(&c->events.destroy); - - c->has_gaps = false; - c->gaps_inner = 0; - c->gaps_outer = 0; - c->current_gaps = 0; + wl_signal_emit(&root->events.new_node, &c->node); return c; } void container_destroy(struct sway_container *con) { - if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, - "Expected a container or view")) { - return; - } - if (!sway_assert(con->destroying, + if (!sway_assert(con->node.destroying, "Tried to free container which wasn't marked as destroying")) { return; } - if (!sway_assert(con->ntxnrefs == 0, "Tried to free container " + if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " "which is still referenced by transactions")) { return; } - free(con->name); + free(con->title); free(con->formatted_title); wlr_texture_destroy(con->title_focused); wlr_texture_destroy(con->title_focused_inactive); @@ -124,14 +66,14 @@ void container_destroy(struct sway_container *con) { list_free(con->current.children); list_free(con->outputs); - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; - view->swayc = NULL; + if (con->view) { + struct sway_view *view = con->view; + view->container = NULL; free(view->title_format); view->title_format = NULL; if (view->destroying) { - view_destroy(view); + view_destroy(con->view); } } @@ -139,115 +81,57 @@ void container_destroy(struct sway_container *con) { } void container_begin_destroy(struct sway_container *con) { - if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, - "Expected a container or view")) { - return; - } - - if (con->type == C_VIEW) { + if (con->view) { ipc_event_window(con, "close"); } - wl_signal_emit(&con->events.destroy, con); + wl_signal_emit(&con->node.events.destroy, &con->node); container_end_mouse_operation(con); - con->destroying = true; - container_set_dirty(con); + con->node.destroying = true; + node_set_dirty(&con->node); if (con->scratchpad) { root_scratchpad_remove_container(con); } - if (con->parent) { - container_remove_child(con); + if (con->parent || con->workspace) { + container_detach(con); } } -struct sway_container *container_reap_empty(struct sway_container *con) { - while (con && con->type == C_CONTAINER) { - struct sway_container *next = con->parent; - if (con->children->length == 0) { - container_begin_destroy(con); - } - con = next; +void container_reap_empty(struct sway_container *con) { + if (con->view) { + return; } - if (con && con->type == C_WORKSPACE) { - workspace_consider_destroy(con); - if (con->destroying) { - con = con->parent; + struct sway_workspace *ws = con->workspace; + while (con) { + if (con->children->length) { + return; } + struct sway_container *parent = con->parent; + container_begin_destroy(con); + con = parent; } - return con; + workspace_consider_destroy(ws); } struct sway_container *container_flatten(struct sway_container *container) { - while (container->type == C_CONTAINER && container->children->length == 1) { + if (container->view) { + return NULL; + } + while (container && container->children->length == 1) { struct sway_container *child = container->children->items[0]; struct sway_container *parent = container->parent; - container_replace_child(container, child); + container_replace(container, child); container_begin_destroy(container); container = parent; } return container; } -static void container_close_func(struct sway_container *container, void *data) { - if (container->type == C_VIEW) { - view_close(container->sway_view); - } -} - -struct sway_container *container_close(struct sway_container *con) { - if (!sway_assert(con != NULL, - "container_close called with a NULL container")) { - return NULL; - } - - struct sway_container *parent = con->parent; - - if (con->type == C_VIEW) { - view_close(con->sway_view); - } else if (con->type == C_CONTAINER) { - container_for_each_child(con, container_close_func, NULL); - } else if (con->type == C_WORKSPACE) { - workspace_for_each_container(con, container_close_func, NULL); - } - - return parent; -} - -struct sway_container *container_view_create(struct sway_container *sibling, - struct sway_view *sway_view) { - if (!sway_assert(sibling, - "container_view_create called with NULL sibling/parent")) { - return NULL; - } - const char *title = view_get_title(sway_view); - struct sway_container *swayc = container_create(C_VIEW); - wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s", - swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); - // Setup values - swayc->sway_view = sway_view; - swayc->width = 0; - swayc->height = 0; - - if (sibling->type == C_WORKSPACE) { - // Case of focused workspace, just create as child of it - container_add_child(sibling, swayc); - } else { - // Regular case, create as sibling of current container - container_add_sibling(sibling, swayc); - } - container_create_notify(swayc); - return swayc; -} - struct sway_container *container_find_child(struct sway_container *container, - bool (*test)(struct sway_container *view, void *data), void *data) { - if (!sway_assert(container->type == C_CONTAINER || - container->type == C_VIEW, "Expected a container or view")) { - return NULL; - } + bool (*test)(struct sway_container *con, void *data), void *data) { if (!container->children) { return NULL; } @@ -264,46 +148,32 @@ struct sway_container *container_find_child(struct sway_container *container, return NULL; } -struct sway_container *container_parent(struct sway_container *container, - enum sway_container_type type) { - if (!sway_assert(container, "container is NULL")) { - return NULL; - } - if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) { - return NULL; - } - do { - container = container->parent; - } while (container && container->type != type); - return container; -} - -static void surface_at_view(struct sway_container *swayc, double lx, double ly, +static void surface_at_view(struct sway_container *con, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { + if (!sway_assert(con->view, "Expected a view")) { return; } - struct sway_view *sview = swayc->sway_view; - double view_sx = lx - sview->x + sview->geometry.x; - double view_sy = ly - sview->y + sview->geometry.y; + struct sway_view *view = con->view; + double view_sx = lx - view->x + view->geometry.x; + double view_sy = ly - view->y + view->geometry.y; double _sx, _sy; struct wlr_surface *_surface = NULL; - switch (sview->type) { + switch (view->type) { #ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: - _surface = wlr_surface_surface_at(sview->surface, + _surface = wlr_surface_surface_at(view->surface, view_sx, view_sy, &_sx, &_sy); break; #endif case SWAY_VIEW_XDG_SHELL_V6: _surface = wlr_xdg_surface_v6_surface_at( - sview->wlr_xdg_surface_v6, + view->wlr_xdg_surface_v6, view_sx, view_sy, &_sx, &_sy); break; case SWAY_VIEW_XDG_SHELL: _surface = wlr_xdg_surface_surface_at( - sview->wlr_xdg_surface, + view->wlr_xdg_surface, view_sx, view_sy, &_sx, &_sy); break; } @@ -317,65 +187,72 @@ static void surface_at_view(struct sway_container *swayc, double lx, double ly, /** * container_at for a container with layout L_TABBED. */ -static struct sway_container *container_at_tabbed(struct sway_container *parent, +static struct sway_container *container_at_tabbed(struct sway_node *parent, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - if (ly < parent->y || ly > parent->y + parent->height) { + struct wlr_box box; + node_get_box(parent, &box); + if (ly < box.y || ly > box.y + box.height) { return NULL; } struct sway_seat *seat = input_manager_current_seat(input_manager); + list_t *children = node_get_children(parent); // Tab titles int title_height = container_titlebar_height(); - if (ly < parent->y + title_height) { - int tab_width = parent->width / parent->children->length; - int child_index = (lx - parent->x) / tab_width; - if (child_index >= parent->children->length) { - child_index = parent->children->length - 1; + if (ly < box.y + title_height) { + int tab_width = box.width / children->length; + int child_index = (lx - box.x) / tab_width; + if (child_index >= children->length) { + child_index = children->length - 1; } - struct sway_container *child = parent->children->items[child_index]; - return seat_get_focus_inactive(seat, child); + struct sway_container *child = children->items[child_index]; + struct sway_node *node = seat_get_focus_inactive(seat, &child->node); + return node->sway_container; } // Surfaces - struct sway_container *current = seat_get_active_child(seat, parent); - + struct sway_node *current = seat_get_active_child(seat, parent); return tiling_container_at(current, lx, ly, surface, sx, sy); } /** * container_at for a container with layout L_STACKED. */ -static struct sway_container *container_at_stacked( - struct sway_container *parent, double lx, double ly, +static struct sway_container *container_at_stacked(struct sway_node *parent, + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - if (ly < parent->y || ly > parent->y + parent->height) { + struct wlr_box box; + node_get_box(parent, &box); + if (ly < box.y || ly > box.y + box.height) { return NULL; } struct sway_seat *seat = input_manager_current_seat(input_manager); + list_t *children = node_get_children(parent); // Title bars int title_height = container_titlebar_height(); - int child_index = (ly - parent->y) / title_height; - if (child_index < parent->children->length) { - struct sway_container *child = parent->children->items[child_index]; - return seat_get_focus_inactive(seat, child); + int child_index = (ly - box.y) / title_height; + if (child_index < children->length) { + struct sway_container *child = children->items[child_index]; + struct sway_node *node = seat_get_focus_inactive(seat, &child->node); + return node->sway_container; } // Surfaces - struct sway_container *current = seat_get_active_child(seat, parent); - + struct sway_node *current = seat_get_active_child(seat, parent); return tiling_container_at(current, lx, ly, surface, sx, sy); } /** * container_at for a container with layout L_HORIZ or L_VERT. */ -static struct sway_container *container_at_linear(struct sway_container *parent, +static struct sway_container *container_at_linear(struct sway_node *parent, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - for (int i = 0; i < parent->children->length; ++i) { - struct sway_container *child = parent->children->items[i]; + list_t *children = node_get_children(parent); + for (int i = 0; i < children->length; ++i) { + struct sway_container *child = children->items[i]; struct wlr_box box = { .x = child->x, .y = child->y, @@ -383,7 +260,7 @@ static struct sway_container *container_at_linear(struct sway_container *parent, .height = child->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return tiling_container_at(child, lx, ly, surface, sx, sy); + return tiling_container_at(&child->node, lx, ly, surface, sx, sy); } } return NULL; @@ -391,12 +268,11 @@ static struct sway_container *container_at_linear(struct sway_container *parent, static struct sway_container *floating_container_at(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - for (int j = 0; j < output->children->length; ++j) { - struct sway_container *workspace = output->children->items[j]; - struct sway_workspace *ws = workspace->sway_workspace; - if (!workspace_is_visible(workspace)) { + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + for (int j = 0; j < output->workspaces->length; ++j) { + struct sway_workspace *ws = output->workspaces->items[j]; + if (!workspace_is_visible(ws)) { continue; } // Items at the end of the list are on top, so iterate the list in @@ -410,7 +286,7 @@ static struct sway_container *floating_container_at(double lx, double ly, .height = floater->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return tiling_container_at(floater, lx, ly, + return tiling_container_at(&floater->node, lx, ly, surface, sx, sy); } } @@ -419,25 +295,24 @@ static struct sway_container *floating_container_at(double lx, double ly, return NULL; } -struct sway_container *tiling_container_at( - struct sway_container *con, double lx, double ly, +struct sway_container *tiling_container_at(struct sway_node *parent, + double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - if (con->type == C_VIEW) { - surface_at_view(con, lx, ly, surface, sx, sy); - return con; + if (node_is_view(parent)) { + surface_at_view(parent->sway_container, lx, ly, surface, sx, sy); + return parent->sway_container; } - if (!con->children->length) { + if (!node_get_children(parent)) { return NULL; } - - switch (con->layout) { + switch (node_get_layout(parent)) { case L_HORIZ: case L_VERT: - return container_at_linear(con, lx, ly, surface, sx, sy); + return container_at_linear(parent, lx, ly, surface, sx, sy); case L_TABBED: - return container_at_tabbed(con, lx, ly, surface, sx, sy); + return container_at_tabbed(parent, lx, ly, surface, sx, sy); case L_STACKED: - return container_at_stacked(con, lx, ly, surface, sx, sy); + return container_at_stacked(parent, lx, ly, surface, sx, sy); case L_NONE: return NULL; } @@ -472,19 +347,16 @@ static bool surface_is_popup(struct wlr_surface *surface) { return false; } -struct sway_container *container_at(struct sway_container *workspace, +struct sway_container *container_at(struct sway_workspace *workspace, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return NULL; - } struct sway_container *c; + // Focused view's popups struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = - seat_get_focus_inactive(seat, &root_container); + struct sway_container *focus = seat_get_focused_container(seat); bool is_floating = focus && container_is_floating_or_child(focus); // Focused view's popups - if (focus && focus->type == C_VIEW) { + if (focus && focus->view) { surface_at_view(focus, lx, ly, surface, sx, sy); if (*surface && surface_is_popup(*surface)) { return focus; @@ -492,7 +364,7 @@ struct sway_container *container_at(struct sway_container *workspace, *surface = NULL; } // If focused is floating, focused view's non-popups - if (focus && focus->type == C_VIEW && is_floating) { + if (focus && focus->view && is_floating) { surface_at_view(focus, lx, ly, surface, sx, sy); if (*surface) { return focus; @@ -504,7 +376,7 @@ struct sway_container *container_at(struct sway_container *workspace, return c; } // If focused is tiling, focused view's non-popups - if (focus && focus->type == C_VIEW && !is_floating) { + if (focus && focus->view && !is_floating) { surface_at_view(focus, lx, ly, surface, sx, sy); if (*surface) { return focus; @@ -512,7 +384,7 @@ struct sway_container *container_at(struct sway_container *workspace, *surface = NULL; } // Tiling (non-focused) - if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { + if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { return c; } return NULL; @@ -521,10 +393,6 @@ struct sway_container *container_at(struct sway_container *workspace, void container_for_each_child(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { - if (!sway_assert(container->type == C_CONTAINER || - container->type == C_VIEW, "Expected a container or view")) { - return; - } if (container->children) { for (int i = 0; i < container->children->length; ++i) { struct sway_container *child = container->children->items[i]; @@ -536,7 +404,7 @@ void container_for_each_child(struct sway_container *container, bool container_has_ancestor(struct sway_container *descendant, struct sway_container *ancestor) { - while (descendant && descendant->type != C_ROOT) { + while (descendant) { descendant = descendant->parent; if (descendant == ancestor) { return true; @@ -545,27 +413,10 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } -int container_count_descendants_of_type(struct sway_container *con, - enum sway_container_type type) { - int children = 0; - if (con->type == type) { - children++; - } - if (con->children) { - for (int i = 0; i < con->children->length; i++) { - struct sway_container *child = con->children->items[i]; - children += container_count_descendants_of_type(child, type); - } - } - return children; -} - void container_damage_whole(struct sway_container *container) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *cont = root_container.children->items[i]; - if (cont->type == C_OUTPUT) { - output_damage_whole_container(cont->sway_output, container); - } + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_whole_container(output, container); } } @@ -582,10 +433,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con) { static void update_title_texture(struct sway_container *con, struct wlr_texture **texture, struct border_colors *class) { - if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, - "Unexpected type %s", container_type_to_str(con->type))) { - return; - } struct sway_output *output = container_get_effective_output(con); if (!output) { return; @@ -664,9 +511,10 @@ void container_calculate_title_height(struct sway_container *container) { * An example tree representation is: V[Terminal, Firefox] * If buffer is not NULL, also populate the buffer with the representation. */ -static size_t get_tree_representation(struct sway_container *parent, char *buffer) { +size_t container_build_representation(enum sway_container_layout layout, + list_t *children, char *buffer) { size_t len = 2; - switch (parent->layout) { + switch (layout) { case L_VERT: lenient_strcat(buffer, "V["); break; @@ -683,17 +531,17 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe lenient_strcat(buffer, "D["); break; } - for (int i = 0; i < parent->children->length; ++i) { + for (int i = 0; i < children->length; ++i) { if (i != 0) { ++len; lenient_strcat(buffer, " "); } - struct sway_container *child = parent->children->items[i]; + struct sway_container *child = children->items[i]; const char *identifier = NULL; - if (child->type == C_VIEW) { - identifier = view_get_class(child->sway_view); + if (child->view) { + identifier = view_get_class(child->view); if (!identifier) { - identifier = view_get_app_id(child->sway_view); + identifier = view_get_app_id(child->view); } } else { identifier = child->formatted_title; @@ -711,25 +559,25 @@ static size_t get_tree_representation(struct sway_container *parent, char *buffe return len; } -void container_notify_subtree_changed(struct sway_container *container) { - if (!container || container->type < C_WORKSPACE) { - return; +void container_update_representation(struct sway_container *con) { + if (!con->view) { + size_t len = container_build_representation(con->layout, + con->children, NULL); + free(con->formatted_title); + con->formatted_title = calloc(len + 1, sizeof(char)); + if (!sway_assert(con->formatted_title, + "Unable to allocate title string")) { + return; + } + container_build_representation(con->layout, con->children, + con->formatted_title); + container_calculate_title_height(con); + container_update_title_textures(con); } - free(container->formatted_title); - container->formatted_title = NULL; - - size_t len = get_tree_representation(container, NULL); - char *buffer = calloc(len + 1, sizeof(char)); - if (!sway_assert(buffer, "Unable to allocate title string")) { - return; - } - get_tree_representation(container, buffer); - - container->formatted_title = buffer; - if (container->type != C_WORKSPACE) { - container_calculate_title_height(container); - container_update_title_textures(container); - container_notify_subtree_changed(container->parent); + if (con->parent) { + container_update_representation(con->parent); + } else if (con->workspace) { + workspace_update_representation(con->workspace); } } @@ -738,11 +586,7 @@ size_t container_titlebar_height() { } void container_init_floating(struct sway_container *con) { - if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, - "Expected a view or container")) { - return; - } - struct sway_container *ws = container_parent(con, C_WORKSPACE); + struct sway_workspace *ws = con->workspace; int min_width, min_height; int max_width, max_height; @@ -778,13 +622,13 @@ void container_init_floating(struct sway_container *con) { max_height = config->floating_maximum_height; } - if (con->type == C_CONTAINER) { + if (!con->view) { con->width = max_width; con->height = max_height; con->x = ws->x + (ws->width - con->width) / 2; con->y = ws->y + (ws->height - con->height) / 2; } else { - struct sway_view *view = con->sway_view; + struct sway_view *view = con->view; view->width = fmax(min_width, fmin(view->natural_width, max_width)); view->height = fmax(min_height, fmin(view->natural_height, max_height)); view->x = ws->x + (ws->width - view->width) / 2; @@ -794,7 +638,7 @@ void container_init_floating(struct sway_container *con) { view->border_top = view->border_bottom = true; view->border_left = view->border_right = true; - container_set_geometry_from_floating_view(view->swayc); + container_set_geometry_from_floating_view(con); } } @@ -804,32 +648,41 @@ void container_set_floating(struct sway_container *container, bool enable) { } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *workspace = container_parent(container, C_WORKSPACE); + struct sway_workspace *workspace = container->workspace; if (enable) { - struct sway_container *old_parent = container_remove_child(container); + struct sway_container *old_parent = container->parent; + container_detach(container); workspace_add_floating(workspace, container); container_init_floating(container); - if (container->type == C_VIEW) { - view_set_tiled(container->sway_view, false); + if (container->view) { + view_set_tiled(container->view, false); + } + if (old_parent) { + container_reap_empty(old_parent); } - container_reap_empty(old_parent); } else { // Returning to tiled if (container->scratchpad) { root_scratchpad_remove_container(container); } - container_remove_child(container); + container_detach(container); struct sway_container *reference = seat_get_focus_inactive_tiling(seat, workspace); - if (reference->type == C_VIEW) { + if (reference && reference->view) { reference = reference->parent; } - container_add_child(reference, container); - container->width = container->parent->width; - container->height = container->parent->height; - if (container->type == C_VIEW) { - view_set_tiled(container->sway_view, true); + if (reference) { + container_add_child(reference, container); + container->width = reference->width; + container->height = reference->height; + } else { + workspace_add_tiling(workspace, container); + container->width = workspace->width; + container->height = workspace->height; + } + if (container->view) { + view_set_tiled(container->view, true); } container->is_sticky = false; } @@ -840,14 +693,13 @@ void container_set_floating(struct sway_container *container, bool enable) { } void container_set_geometry_from_floating_view(struct sway_container *con) { - if (!sway_assert(con->type == C_VIEW, "Expected a view")) { + if (!sway_assert(con->view, "Expected a view")) { return; } - if (!sway_assert(container_is_floating(con), - "Expected a floating view")) { + if (!sway_assert(container_is_floating(con), "Expected a floating view")) { return; } - struct sway_view *view = con->sway_view; + struct sway_view *view = con->view; size_t border_width = 0; size_t top = 0; @@ -861,12 +713,12 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { con->y = view->y - top; con->width = view->width + border_width * 2; con->height = top + view->height + border_width; - container_set_dirty(con); + node_set_dirty(&con->node); } bool container_is_floating(struct sway_container *container) { - return container->parent && container->parent->type == C_WORKSPACE && - list_find(container->parent->sway_workspace->floating, container) != -1; + return !container->parent && container->workspace && + list_find(container->workspace->floating, container) != -1; } void container_get_box(struct sway_container *container, struct wlr_box *box) { @@ -883,16 +735,16 @@ void container_floating_translate(struct sway_container *con, double x_amount, double y_amount) { con->x += x_amount; con->y += y_amount; - if (con->type == C_VIEW) { - con->sway_view->x += x_amount; - con->sway_view->y += y_amount; + if (con->view) { + con->view->x += x_amount; + con->view->y += y_amount; } else { for (int i = 0; i < con->children->length; ++i) { struct sway_container *child = con->children->items[i]; container_floating_translate(child, x_amount, y_amount); } } - container_set_dirty(con); + node_set_dirty(&con->node); } /** @@ -902,17 +754,16 @@ void container_floating_translate(struct sway_container *con, * one, otherwise we'll choose whichever output is closest to the container's * center. */ -struct sway_container *container_floating_find_output( - struct sway_container *con) { +struct sway_output *container_floating_find_output(struct sway_container *con) { double center_x = con->x + con->width / 2; double center_y = con->y + con->height / 2; - struct sway_container *closest_output = NULL; + struct sway_output *closest_output = NULL; double closest_distance = DBL_MAX; - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; struct wlr_box output_box; double closest_x, closest_y; - container_get_box(output, &output_box); + output_get_box(output, &output_box); wlr_box_closest_point(&output_box, center_x, center_y, &closest_x, &closest_y); if (center_x == closest_x && center_y == closest_y) { @@ -937,18 +788,18 @@ void container_floating_move_to(struct sway_container *con, return; } container_floating_translate(con, lx - con->x, ly - con->y); - struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); - struct sway_container *new_output = container_floating_find_output(con); + struct sway_workspace *old_workspace = con->workspace; + struct sway_output *new_output = container_floating_find_output(con); if (!sway_assert(new_output, "Unable to find any output")) { return; } - struct sway_container *new_workspace = - output_get_active_workspace(new_output->sway_output); + struct sway_workspace *new_workspace = + output_get_active_workspace(new_output); if (old_workspace != new_workspace) { - container_remove_child(con); + container_detach(con); workspace_add_floating(new_workspace, con); - arrange_windows(old_workspace); - arrange_windows(new_workspace); + arrange_workspace(old_workspace); + arrange_workspace(new_workspace); workspace_detect_urgent(old_workspace); workspace_detect_urgent(new_workspace); } @@ -959,22 +810,14 @@ void container_floating_move_to_center(struct sway_container *con) { "Expected a floating container")) { return; } - struct sway_container *ws = container_parent(con, C_WORKSPACE); + struct sway_workspace *ws = con->workspace; double new_lx = ws->x + (ws->width - con->width) / 2; double new_ly = ws->y + (ws->height - con->height) / 2; container_floating_translate(con, new_lx - con->x, new_ly - con->y); } -void container_set_dirty(struct sway_container *container) { - if (container->dirty) { - return; - } - container->dirty = true; - list_add(server.dirty_containers, container); -} - static bool find_urgent_iterator(struct sway_container *con, void *data) { - return con->type == C_VIEW && view_is_urgent(con->sway_view); + return con->view && view_is_urgent(con->view); } bool container_has_urgent_child(struct sway_container *container) { @@ -991,12 +834,12 @@ void container_end_mouse_operation(struct sway_container *container) { } static void set_fullscreen_iterator(struct sway_container *con, void *data) { - if (con->type != C_VIEW) { + if (!con->view) { return; } - if (con->sway_view->impl->set_fullscreen) { + if (con->view->impl->set_fullscreen) { bool *enable = data; - con->sway_view->impl->set_fullscreen(con->sway_view, *enable); + con->view->impl->set_fullscreen(con->view, *enable); } } @@ -1005,9 +848,9 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { return; } - struct sway_container *workspace = container_parent(container, C_WORKSPACE); - if (enable && workspace->sway_workspace->fullscreen) { - container_set_fullscreen(workspace->sway_workspace->fullscreen, false); + struct sway_workspace *workspace = container->workspace; + if (enable && workspace->fullscreen) { + container_set_fullscreen(workspace->fullscreen, false); } set_fullscreen_iterator(container, &enable); @@ -1016,36 +859,32 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { container->is_fullscreen = enable; if (enable) { - workspace->sway_workspace->fullscreen = container; + workspace->fullscreen = container; container->saved_x = container->x; container->saved_y = container->y; container->saved_width = container->width; container->saved_height = container->height; struct sway_seat *seat; - struct sway_container *focus, *focus_ws; + struct sway_workspace *focus_ws; wl_list_for_each(seat, &input_manager->seats, link) { - focus = seat_get_focus(seat); - if (focus) { - focus_ws = focus; - if (focus_ws->type != C_WORKSPACE) { - focus_ws = container_parent(focus_ws, C_WORKSPACE); - } + focus_ws = seat_get_focused_workspace(seat); + if (focus_ws) { if (focus_ws == workspace) { - seat_set_focus(seat, container); + seat_set_focus(seat, &container->node); } } } } else { - workspace->sway_workspace->fullscreen = NULL; + workspace->fullscreen = NULL; if (container_is_floating(container)) { container->x = container->saved_x; container->y = container->saved_y; container->width = container->saved_width; container->height = container->saved_height; - struct sway_container *output = + struct sway_output *output = container_floating_find_output(container); - if (!container_has_ancestor(container, output)) { + if (workspace->output != output) { container_floating_move_to_center(container); } } else { @@ -1060,7 +899,7 @@ void container_set_fullscreen(struct sway_container *container, bool enable) { } bool container_is_floating_or_child(struct sway_container *container) { - while (container->parent && container->parent->type != C_WORKSPACE) { + while (container->parent) { container = container->parent; } return container_is_floating(container); @@ -1072,7 +911,7 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { return true; } container = container->parent; - } while (container && container->type != C_WORKSPACE); + } while (container); return false; } @@ -1090,42 +929,37 @@ static void surface_send_leave_iterator(struct wlr_surface *surface, } void container_discover_outputs(struct sway_container *con) { - if (!sway_assert(con->type == C_CONTAINER || con->type == C_VIEW, - "Expected a container or view")) { - return; - } struct wlr_box con_box = { - .x = con->current.swayc_x, - .y = con->current.swayc_y, - .width = con->current.swayc_width, - .height = con->current.swayc_height, + .x = con->current.con_x, + .y = con->current.con_y, + .width = con->current.con_width, + .height = con->current.con_height, }; struct sway_output *old_output = container_get_effective_output(con); - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; - struct sway_output *sway_output = output->sway_output; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; struct wlr_box output_box; - container_get_box(output, &output_box); + output_get_box(output, &output_box); struct wlr_box intersection; bool intersects = wlr_box_intersection(&con_box, &output_box, &intersection); - int index = list_find(con->outputs, sway_output); + int index = list_find(con->outputs, output); if (intersects && index == -1) { // Send enter - wlr_log(WLR_DEBUG, "Con %p entered output %p", con, sway_output); - if (con->type == C_VIEW) { - view_for_each_surface(con->sway_view, - surface_send_enter_iterator, sway_output->wlr_output); + wlr_log(WLR_DEBUG, "Container %p entered output %p", con, output); + if (con->view) { + view_for_each_surface(con->view, + surface_send_enter_iterator, output->wlr_output); } - list_add(con->outputs, sway_output); + list_add(con->outputs, output); } else if (!intersects && index != -1) { // Send leave - wlr_log(WLR_DEBUG, "Con %p left output %p", con, sway_output); - if (con->type == C_VIEW) { - view_for_each_surface(con->sway_view, - surface_send_leave_iterator, sway_output->wlr_output); + wlr_log(WLR_DEBUG, "Container %p left output %p", con, output); + if (con->view) { + view_for_each_surface(con->view, + surface_send_leave_iterator, output->wlr_output); } list_del(con->outputs, index); } @@ -1135,17 +969,13 @@ void container_discover_outputs(struct sway_container *con) { double new_scale = new_output ? new_output->wlr_output->scale : -1; if (old_scale != new_scale) { container_update_title_textures(con); - if (con->type == C_VIEW) { - view_update_marks_textures(con->sway_view); + if (con->view) { + view_update_marks_textures(con->view); } } } void container_remove_gaps(struct sway_container *c) { - if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW, - "Expected a container or view")) { - return; - } if (c->current_gaps == 0) { return; } @@ -1158,25 +988,20 @@ void container_remove_gaps(struct sway_container *c) { } void container_add_gaps(struct sway_container *c) { - if (!sway_assert(c->type == C_CONTAINER || c->type == C_VIEW, - "Expected a container or view")) { - return; - } if (c->current_gaps > 0) { return; } // Linear containers don't have gaps because it'd create double gaps - if (c->type == C_CONTAINER && - c->layout != L_TABBED && c->layout != L_STACKED) { + if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) { return; } // Children of tabbed/stacked containers re-use the gaps of the container - enum sway_container_layout layout = c->parent->layout; + enum sway_container_layout layout = container_parent_layout(c); if (layout == L_TABBED || layout == L_STACKED) { return; } - struct sway_container *ws = container_parent(c, C_WORKSPACE); + struct sway_workspace *ws = c->workspace; c->current_gaps = ws->has_gaps ? ws->gaps_inner : config->gaps_inner; c->x += c->current_gaps; @@ -1185,222 +1010,154 @@ void container_add_gaps(struct sway_container *c) { c->height -= 2 * c->current_gaps; } -int container_sibling_index(const struct sway_container *child) { - return list_find(child->parent->children, child); +enum sway_container_layout container_parent_layout(struct sway_container *con) { + if (con->parent) { + return con->parent->layout; + } + return con->workspace->layout; } -void container_handle_fullscreen_reparent(struct sway_container *con, - struct sway_container *old_parent) { - if (!con->is_fullscreen) { +enum sway_container_layout container_current_parent_layout( + struct sway_container *con) { + if (con->current.parent) { + return con->current.parent->current.layout; + } + return con->current.workspace->current.layout; +} + +list_t *container_get_siblings(const struct sway_container *container) { + if (container->parent) { + return container->parent->children; + } + if (!container->workspace) { + return NULL; + } + if (list_find(container->workspace->tiling, container) != -1) { + return container->workspace->tiling; + } + return container->workspace->floating; +} + +int container_sibling_index(const struct sway_container *child) { + return list_find(container_get_siblings(child), child); +} + +list_t *container_get_current_siblings(struct sway_container *container) { + if (container->current.parent) { + return container->current.parent->current.children; + } + return container->current.workspace->current.tiling; +} + +void container_handle_fullscreen_reparent(struct sway_container *con) { + if (!con->is_fullscreen || con->workspace->fullscreen == con) { return; } - struct sway_container *old_workspace = old_parent; - if (old_workspace && old_workspace->type != C_WORKSPACE) { - old_workspace = container_parent(old_workspace, C_WORKSPACE); - } - struct sway_container *new_workspace = container_parent(con, C_WORKSPACE); - if (old_workspace == new_workspace) { - return; - } - // Unmark the old workspace as fullscreen - if (old_workspace) { - old_workspace->sway_workspace->fullscreen = NULL; + if (con->workspace->fullscreen) { + container_set_fullscreen(con->workspace->fullscreen, false); } + con->workspace->fullscreen = con; - // Mark the new workspace as fullscreen - if (new_workspace->sway_workspace->fullscreen) { - container_set_fullscreen( - new_workspace->sway_workspace->fullscreen, false); - } - new_workspace->sway_workspace->fullscreen = con; + arrange_workspace(con->workspace); +} - // Resize container to new output dimensions - struct sway_container *output = new_workspace->parent; - con->x = output->x; - con->y = output->y; - con->width = output->width; - con->height = output->height; - - if (con->type == C_VIEW) { - struct sway_view *view = con->sway_view; - view->x = output->x; - view->y = output->y; - view->width = output->width; - view->height = output->height; - } else { - arrange_windows(new_workspace); - } +static void set_workspace(struct sway_container *container, void *data) { + container->workspace = container->parent->workspace; } void container_insert_child(struct sway_container *parent, struct sway_container *child, int i) { - struct sway_container *old_parent = child->parent; - if (old_parent) { - container_remove_child(child); + if (child->workspace) { + container_detach(child); } - wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i); list_insert(parent->children, i, child); child->parent = parent; - container_handle_fullscreen_reparent(child, old_parent); + child->workspace = parent->workspace; + container_for_each_child(child, set_workspace, NULL); + container_handle_fullscreen_reparent(child); + container_update_representation(parent); } -struct sway_container *container_add_sibling(struct sway_container *fixed, - struct sway_container *active) { - // TODO handle floating - struct sway_container *old_parent = NULL; - if (active->parent) { - old_parent = active->parent; - container_remove_child(active); +void container_add_sibling(struct sway_container *fixed, + struct sway_container *active, int offset) { + if (active->workspace) { + container_detach(active); } - struct sway_container *parent = fixed->parent; - int i = container_sibling_index(fixed); - list_insert(parent->children, i + 1, active); - active->parent = parent; - container_handle_fullscreen_reparent(active, old_parent); - return active->parent; + list_t *siblings = container_get_siblings(fixed); + int index = list_find(siblings, fixed); + list_insert(siblings, index + offset, active); + active->parent = fixed->parent; + active->workspace = fixed->workspace; + container_for_each_child(active, set_workspace, NULL); + container_handle_fullscreen_reparent(active); + container_update_representation(active); } void container_add_child(struct sway_container *parent, struct sway_container *child) { - wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", - child, child->type, child->width, child->height, - parent, parent->type, parent->width, parent->height); - struct sway_container *old_parent = child->parent; + if (child->workspace) { + container_detach(child); + } list_add(parent->children, child); child->parent = parent; - container_handle_fullscreen_reparent(child, old_parent); - if (old_parent) { - container_set_dirty(old_parent); - } - container_set_dirty(child); + child->workspace = parent->workspace; + container_for_each_child(child, set_workspace, NULL); + container_handle_fullscreen_reparent(child); + container_update_representation(parent); + node_set_dirty(&child->node); + node_set_dirty(&parent->node); } -struct sway_container *container_remove_child(struct sway_container *child) { +void container_detach(struct sway_container *child) { if (child->is_fullscreen) { - struct sway_container *workspace = container_parent(child, C_WORKSPACE); - workspace->sway_workspace->fullscreen = NULL; + child->workspace->fullscreen = NULL; } - struct sway_container *parent = child->parent; - list_t *list = container_is_floating(child) ? - parent->sway_workspace->floating : parent->children; - int index = list_find(list, child); + struct sway_container *old_parent = child->parent; + struct sway_workspace *old_workspace = child->workspace; + list_t *siblings = container_get_siblings(child); + int index = list_find(siblings, child); if (index != -1) { - list_del(list, index); + list_del(siblings, index); } child->parent = NULL; - container_notify_subtree_changed(parent); + child->workspace = NULL; + container_for_each_child(child, set_workspace, NULL); - container_set_dirty(parent); - container_set_dirty(child); - - return parent; -} - -enum sway_container_layout container_get_default_layout( - struct sway_container *con) { - if (con->type != C_OUTPUT) { - con = container_parent(con, C_OUTPUT); - } - - if (!sway_assert(con != NULL, - "container_get_default_layout must be called on an attached" - " container below the root container")) { - return 0; - } - - if (config->default_layout != L_NONE) { - return config->default_layout; - } else if (config->default_orientation != L_NONE) { - return config->default_orientation; - } else if (con->width >= con->height) { - return L_HORIZ; + if (old_parent) { + container_update_representation(old_parent); + node_set_dirty(&old_parent->node); } else { - return L_VERT; + workspace_update_representation(old_workspace); + node_set_dirty(&old_workspace->node); } + node_set_dirty(&child->node); } -struct sway_container *container_replace_child(struct sway_container *child, - struct sway_container *new_child) { - struct sway_container *parent = child->parent; - if (parent == NULL) { - return NULL; - } - - list_t *list = container_is_floating(child) ? - parent->sway_workspace->floating : parent->children; - int i = list_find(list, child); - - if (new_child->parent) { - container_remove_child(new_child); - } - list->items[i] = new_child; - new_child->parent = parent; - child->parent = NULL; - - // Set geometry for new child - new_child->x = child->x; - new_child->y = child->y; - new_child->width = child->width; - new_child->height = child->height; - - // reset geometry for child - child->width = 0; - child->height = 0; - - return parent; +void container_replace(struct sway_container *container, + struct sway_container *replacement) { + container_add_sibling(container, replacement, 1); + container_detach(container); } struct sway_container *container_split(struct sway_container *child, enum sway_container_layout layout) { - // TODO floating: cannot split a floating container - if (!sway_assert(child, "child cannot be null")) { - return NULL; - } - if (child->type == C_WORKSPACE && child->children->length == 0) { - // Special case: this just behaves like splitt - child->prev_split_layout = child->layout; - child->layout = layout; - return child; - } + struct sway_seat *seat = input_manager_get_default_seat(input_manager); + bool set_focus = (seat_get_focus(seat) == &child->node); - struct sway_container *cont = container_create(C_CONTAINER); - - wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child); - - cont->prev_split_layout = L_NONE; + struct sway_container *cont = container_create(NULL); cont->width = child->width; cont->height = child->height; - cont->x = child->x; - cont->y = child->y; cont->current_gaps = child->current_gaps; + cont->layout = layout; - struct sway_seat *seat = input_manager_get_default_seat(input_manager); - bool set_focus = (seat_get_focus(seat) == child); - - if (child->type == C_WORKSPACE) { - struct sway_container *workspace = child; - while (workspace->children->length) { - struct sway_container *ws_child = workspace->children->items[0]; - container_remove_child(ws_child); - container_add_child(cont, ws_child); - } - - container_add_child(workspace, cont); - enum sway_container_layout old_layout = workspace->layout; - workspace->layout = layout; - cont->layout = old_layout; - } else { - cont->layout = layout; - container_replace_child(child, cont); - container_add_child(cont, child); - } + container_replace(child, cont); + container_add_child(cont, child); if (set_focus) { - seat_set_focus(seat, cont); - seat_set_focus(seat, child); + seat_set_focus(seat, &cont->node); + seat_set_focus(seat, &child->node); } - container_notify_subtree_changed(cont); return cont; } diff --git a/sway/tree/node.c b/sway/tree/node.c new file mode 100644 index 000000000..74661c1ab --- /dev/null +++ b/sway/tree/node.c @@ -0,0 +1,151 @@ +#define _POSIX_C_SOURCE 200809L +#include "sway/output.h" +#include "sway/server.h" +#include "sway/tree/container.h" +#include "sway/tree/node.h" +#include "sway/tree/root.h" +#include "sway/tree/workspace.h" +#include "log.h" + +void node_init(struct sway_node *node, enum sway_node_type type, void *thing) { + static size_t next_id = 1; + node->id = next_id++; + node->type = type; + node->sway_root = thing; + wl_signal_init(&node->events.destroy); +} + +const char *node_type_to_str(enum sway_node_type type) { + switch (type) { + case N_ROOT: + return "N_ROOT"; + case N_OUTPUT: + return "N_OUTPUT"; + case N_WORKSPACE: + return "N_WORKSPACE"; + case N_CONTAINER: + return "N_CONTAINER"; + } + return ""; +} + +void node_set_dirty(struct sway_node *node) { + if (node->dirty) { + return; + } + node->dirty = true; + list_add(server.dirty_nodes, node); +} + +bool node_is_view(struct sway_node *node) { + return node->type == N_CONTAINER && node->sway_container->view; +} + +char *node_get_name(struct sway_node *node) { + switch (node->type) { + case N_ROOT: + return "root"; + case N_OUTPUT: + return node->sway_output->wlr_output->name; + case N_WORKSPACE: + return node->sway_workspace->name; + case N_CONTAINER: + return node->sway_container->title; + } + return NULL; +} + +void node_get_box(struct sway_node *node, struct wlr_box *box) { + switch (node->type) { + case N_ROOT: + root_get_box(root, box); + break; + case N_OUTPUT: + output_get_box(node->sway_output, box); + break; + case N_WORKSPACE: + workspace_get_box(node->sway_workspace, box); + break; + case N_CONTAINER: + container_get_box(node->sway_container, box); + break; + } +} + +struct sway_output *node_get_output(struct sway_node *node) { + switch (node->type) { + case N_CONTAINER: + return node->sway_container->workspace->output; + case N_WORKSPACE: + return node->sway_workspace->output; + case N_OUTPUT: + return node->sway_output; + case N_ROOT: + return NULL; + } + return NULL; +} + +enum sway_container_layout node_get_layout(struct sway_node *node) { + switch (node->type) { + case N_CONTAINER: + return node->sway_container->layout; + case N_WORKSPACE: + return node->sway_workspace->layout; + case N_OUTPUT: + case N_ROOT: + return L_NONE; + } + return L_NONE; +} + +struct sway_node *node_get_parent(struct sway_node *node) { + switch (node->type) { + case N_CONTAINER: { + struct sway_container *con = node->sway_container; + if (con->parent) { + return &con->parent->node; + } + if (con->workspace) { + return &con->workspace->node; + } + } + return NULL; + case N_WORKSPACE: { + struct sway_workspace *ws = node->sway_workspace; + if (ws->output) { + return &ws->output->node; + } + } + return NULL; + case N_OUTPUT: + return &root->node; + case N_ROOT: + return NULL; + } + return NULL; +} + +list_t *node_get_children(struct sway_node *node) { + switch (node->type) { + case N_CONTAINER: + return node->sway_container->children; + case N_WORKSPACE: + return node->sway_workspace->tiling; + case N_OUTPUT: + case N_ROOT: + return NULL; + } + return NULL; +} + +bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { + struct sway_node *parent = node_get_parent(node); + while (parent) { + if (parent == ancestor) { + return true; + } + parent = node_get_parent(parent); + } + return false; +} diff --git a/sway/tree/output.c b/sway/tree/output.c index 6601220bf..d72eb1a1d 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -2,28 +2,31 @@ #include #include #include +#include #include "sway/ipc-server.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/output.h" #include "sway/tree/workspace.h" #include "log.h" +#include "util.h" -static void restore_workspaces(struct sway_container *output) { +static void restore_workspaces(struct sway_output *output) { // Workspace output priority - for (int i = 0; i < root_container.children->length; i++) { - struct sway_container *other = root_container.children->items[i]; + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *other = root->outputs->items[i]; if (other == output) { continue; } - for (int j = 0; j < other->children->length; j++) { - struct sway_container *ws = other->children->items[j]; - struct sway_container *highest = + for (int j = 0; j < other->workspaces->length; j++) { + struct sway_workspace *ws = other->workspaces->items[j]; + struct sway_output *highest = workspace_output_get_highest_available(ws, NULL); if (highest == output) { - container_remove_child(ws); - container_add_child(output, ws); + workspace_detach(ws); + output_add_workspace(output, ws); ipc_event_workspace(NULL, ws, "move"); j--; } @@ -31,22 +34,197 @@ static void restore_workspaces(struct sway_container *output) { } // Saved workspaces - list_t *saved = root_container.sway_root->saved_workspaces; - for (int i = 0; i < saved->length; ++i) { - struct sway_container *ws = saved->items[i]; - container_add_child(output, ws); + for (int i = 0; i < root->saved_workspaces->length; ++i) { + struct sway_workspace *ws = root->saved_workspaces->items[i]; + output_add_workspace(output, ws); ipc_event_workspace(NULL, ws, "move"); } - saved->length = 0; + root->saved_workspaces->length = 0; output_sort_workspaces(output); } -struct sway_container *output_create( - struct sway_output *sway_output) { - const char *name = sway_output->wlr_output->name; +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); + output->wlr_output = wlr_output; + wlr_output->data = output; + + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + + wl_list_insert(&root->all_outputs, &output->link); + + if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + wlr_output_set_mode(wlr_output, mode); + } + + output->workspaces = create_list(); + output->current.workspaces = create_list(); + + return output; +} + +void output_enable(struct sway_output *output, struct output_config *oc) { + if (!sway_assert(!output->enabled, "output is already enabled")) { + return; + } + struct wlr_output *wlr_output = output->wlr_output; + output->enabled = true; + apply_output_config(oc, output); + list_add(root->outputs, output); + + restore_workspaces(output); + + if (!output->workspaces->length) { + // Create workspace + char *ws_name = workspace_next_name(wlr_output->name); + wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); + struct sway_workspace *ws = workspace_create(output, ws_name); + // Set each seat's focus if not already set + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &input_manager->seats, link) { + if (!seat->has_focus) { + seat_set_focus(seat, &ws->node); + } + } + free(ws_name); + } + + size_t len = sizeof(output->layers) / sizeof(output->layers[0]); + for (size_t i = 0; i < len; ++i) { + wl_list_init(&output->layers[i]); + } + wl_signal_init(&output->events.destroy); + + input_manager_configure_xcursor(input_manager); + + wl_signal_add(&wlr_output->events.mode, &output->mode); + wl_signal_add(&wlr_output->events.transform, &output->transform); + wl_signal_add(&wlr_output->events.scale, &output->scale); + wl_signal_add(&output->damage->events.frame, &output->damage_frame); + wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); + + output_add_listeners(output); + + wl_signal_emit(&root->events.new_node, &output->node); + + load_swaybars(); + + arrange_layers(output); + arrange_root(); +} + +static void output_evacuate(struct sway_output *output) { + if (!output->workspaces->length) { + return; + } + struct sway_output *fallback_output = NULL; + if (root->outputs->length > 1) { + fallback_output = root->outputs->items[0]; + if (fallback_output == output) { + fallback_output = root->outputs->items[1]; + } + } + + while (output->workspaces->length) { + struct sway_workspace *workspace = output->workspaces->items[0]; + + workspace_detach(workspace); + + if (workspace_is_empty(workspace)) { + workspace_begin_destroy(workspace); + continue; + } + + struct sway_output *new_output = + workspace_output_get_highest_available(workspace, output); + if (!new_output) { + new_output = fallback_output; + } + + if (new_output) { + workspace_output_add_priority(workspace, new_output); + output_add_workspace(new_output, workspace); + output_sort_workspaces(new_output); + ipc_event_workspace(NULL, workspace, "move"); + } else { + list_add(root->saved_workspaces, workspace); + } + } +} + +void output_destroy(struct sway_output *output) { + if (!sway_assert(output->node.destroying, + "Tried to free output which wasn't marked as destroying")) { + return; + } + if (!sway_assert(output->wlr_output == NULL, + "Tried to free output which still had a wlr_output")) { + return; + } + if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " + "which is still referenced by transactions")) { + return; + } + list_free(output->workspaces); + list_free(output->current.workspaces); + 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; + } + wlr_log(WLR_DEBUG, "Disabling output '%s'", output->wlr_output->name); + wl_signal_emit(&output->events.destroy, output); + + output_evacuate(output); + + root_for_each_container(untrack_output, output); + + int index = list_find(root->outputs, output); + list_del(root->outputs, index); + + wl_list_remove(&output->mode.link); + wl_list_remove(&output->transform.link); + wl_list_remove(&output->scale.link); + wl_list_remove(&output->damage_destroy.link); + wl_list_remove(&output->damage_frame.link); + + output->enabled = false; + + arrange_root(); +} + +void output_begin_destroy(struct sway_output *output) { + if (!sway_assert(!output->enabled, "Expected a disabled output")) { + return; + } + wlr_log(WLR_DEBUG, "Destroying output '%s'", output->wlr_output->name); + + output->node.destroying = true; + node_set_dirty(&output->node); + + wl_list_remove(&output->link); + wl_list_remove(&output->destroy.link); + output->wlr_output->data = NULL; + output->wlr_output = NULL; +} + +struct output_config *output_find_config(struct sway_output *output) { + const char *name = output->wlr_output->name; char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), sway_output); + output_get_identifier(identifier, sizeof(identifier), output); struct output_config *oc = NULL, *all = NULL; for (int i = 0; i < config->output_configs->length; ++i) { @@ -73,189 +251,61 @@ struct sway_container *output_create( if (oc && !oc->enabled) { return NULL; } + return oc; +} - struct sway_container *output = container_create(C_OUTPUT); - output->sway_output = sway_output; - output->name = strdup(name); - if (output->name == NULL) { - output_begin_destroy(output); +struct sway_output *output_from_wlr_output(struct wlr_output *output) { + return output->data; +} + +struct sway_output *output_get_in_direction(struct sway_output *reference, + enum movement_direction direction) { + enum wlr_direction wlr_dir = 0; + if (!sway_assert(sway_dir_to_wlr(direction, &wlr_dir), + "got invalid direction: %d", direction)) { return NULL; } - - apply_output_config(oc, output); - container_add_child(&root_container, output); - load_swaybars(); - - struct wlr_box size; - wlr_output_effective_resolution(sway_output->wlr_output, &size.width, - &size.height); - output->width = size.width; - output->height = size.height; - - restore_workspaces(output); - - if (!output->children->length) { - // Create workspace - char *ws_name = workspace_next_name(output->name); - wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); - struct sway_container *ws = workspace_create(output, ws_name); - // Set each seat's focus if not already set - struct sway_seat *seat = NULL; - wl_list_for_each(seat, &input_manager->seats, link) { - if (!seat->has_focus) { - seat_set_focus(seat, ws); - } - } - free(ws_name); - } - - container_create_notify(output); - return output; -} - -static void output_evacuate(struct sway_container *output) { - if (!output->children->length) { - return; - } - struct sway_container *fallback_output = NULL; - if (root_container.children->length > 1) { - fallback_output = root_container.children->items[0]; - if (fallback_output == output) { - fallback_output = root_container.children->items[1]; - } - } - - while (output->children->length) { - struct sway_container *workspace = output->children->items[0]; - - container_remove_child(workspace); - - if (workspace_is_empty(workspace)) { - workspace_begin_destroy(workspace); - continue; - } - - struct sway_container *new_output = - workspace_output_get_highest_available(workspace, output); - if (!new_output) { - new_output = fallback_output; - } - - if (new_output) { - workspace_output_add_priority(workspace, new_output); - container_add_child(new_output, workspace); - output_sort_workspaces(new_output); - ipc_event_workspace(NULL, workspace, "move"); - } else { - list_add(root_container.sway_root->saved_workspaces, workspace); - } - } -} - -void output_destroy(struct sway_container *output) { - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return; - } - if (!sway_assert(output->destroying, - "Tried to free output which wasn't marked as destroying")) { - return; - } - if (!sway_assert(output->ntxnrefs == 0, "Tried to free output " - "which is still referenced by transactions")) { - return; - } - free(output->name); - free(output->formatted_title); - wlr_texture_destroy(output->title_focused); - wlr_texture_destroy(output->title_focused_inactive); - wlr_texture_destroy(output->title_unfocused); - wlr_texture_destroy(output->title_urgent); - list_free(output->children); - list_free(output->current.children); - list_free(output->outputs); - free(output); - - // NOTE: We don't actually destroy the sway_output here -} - -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_begin_destroy(struct sway_container *output) { - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return; - } - wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); - wl_signal_emit(&output->events.destroy, output); - - output_evacuate(output); - - output->destroying = true; - container_set_dirty(output); - - root_for_each_container(untrack_output, output->sway_output); - - wl_list_remove(&output->sway_output->mode.link); - wl_list_remove(&output->sway_output->transform.link); - wl_list_remove(&output->sway_output->scale.link); - wl_list_remove(&output->sway_output->damage_destroy.link); - wl_list_remove(&output->sway_output->damage_frame.link); - - output->sway_output->swayc = NULL; - output->sway_output = NULL; - - if (output->parent) { - container_remove_child(output); - } -} - -struct sway_container *output_from_wlr_output(struct wlr_output *output) { - if (output == NULL) { + int lx = reference->wlr_output->lx + reference->wlr_output->width / 2; + int ly = reference->wlr_output->ly + reference->wlr_output->height / 2; + struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( + root->output_layout, wlr_dir, reference->wlr_output, lx, ly); + if (!wlr_adjacent) { return NULL; } - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *o = root_container.children->items[i]; - if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { - return o; - } - } - return NULL; + return output_from_wlr_output(wlr_adjacent); } -void output_for_each_workspace(struct sway_container *output, - void (*f)(struct sway_container *con, void *data), void *data) { - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return; +void output_add_workspace(struct sway_output *output, + struct sway_workspace *workspace) { + if (workspace->output) { + workspace_detach(workspace); } - for (int i = 0; i < output->children->length; ++i) { - struct sway_container *workspace = output->children->items[i]; + list_add(output->workspaces, workspace); + workspace->output = output; + node_set_dirty(&output->node); + node_set_dirty(&workspace->node); +} + +void output_for_each_workspace(struct sway_output *output, + void (*f)(struct sway_workspace *ws, void *data), void *data) { + for (int i = 0; i < output->workspaces->length; ++i) { + struct sway_workspace *workspace = output->workspaces->items[i]; f(workspace, data); } } -void output_for_each_container(struct sway_container *output, +void output_for_each_container(struct sway_output *output, void (*f)(struct sway_container *con, void *data), void *data) { - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return; - } - for (int i = 0; i < output->children->length; ++i) { - struct sway_container *workspace = output->children->items[i]; + for (int i = 0; i < output->workspaces->length; ++i) { + struct sway_workspace *workspace = output->workspaces->items[i]; workspace_for_each_container(workspace, f, data); } } -struct sway_container *output_find_workspace(struct sway_container *output, - bool (*test)(struct sway_container *con, void *data), void *data) { - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return NULL; - } - for (int i = 0; i < output->children->length; ++i) { - struct sway_container *workspace = output->children->items[i]; +struct sway_workspace *output_find_workspace(struct sway_output *output, + bool (*test)(struct sway_workspace *ws, void *data), void *data) { + for (int i = 0; i < output->workspaces->length; ++i) { + struct sway_workspace *workspace = output->workspaces->items[i]; if (test(workspace, data)) { return workspace; } @@ -263,14 +313,11 @@ struct sway_container *output_find_workspace(struct sway_container *output, return NULL; } -struct sway_container *output_find_container(struct sway_container *output, +struct sway_container *output_find_container(struct sway_output *output, bool (*test)(struct sway_container *con, void *data), void *data) { - if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { - return NULL; - } struct sway_container *result = NULL; - for (int i = 0; i < output->children->length; ++i) { - struct sway_container *workspace = output->children->items[i]; + for (int i = 0; i < output->workspaces->length; ++i) { + struct sway_workspace *workspace = output->workspaces->items[i]; if ((result = workspace_find_container(workspace, test, data))) { return result; } @@ -279,8 +326,8 @@ struct sway_container *output_find_container(struct sway_container *output, } static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { - struct sway_container *a = *(void **)_a; - struct sway_container *b = *(void **)_b; + struct sway_workspace *a = *(void **)_a; + struct sway_workspace *b = *(void **)_b; if (isdigit(a->name[0]) && isdigit(b->name[0])) { int a_num = strtol(a->name, NULL, 10); @@ -294,6 +341,27 @@ static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { return 0; } -void output_sort_workspaces(struct sway_container *output) { - list_stable_sort(output->children, sort_workspace_cmp_qsort); +void output_sort_workspaces(struct sway_output *output) { + list_stable_sort(output->workspaces, sort_workspace_cmp_qsort); +} + +void output_get_box(struct sway_output *output, struct wlr_box *box) { + box->x = output->wlr_output->lx; + box->y = output->wlr_output->ly; + box->width = output->wlr_output->width; + box->height = output->wlr_output->height; +} + +enum sway_container_layout output_get_default_layout( + struct sway_output *output) { + if (config->default_layout != L_NONE) { + return config->default_layout; + } + if (config->default_orientation != L_NONE) { + return config->default_orientation; + } + if (output->wlr_output->height > output->wlr_output->width) { + return L_VERT; + } + return L_HORIZ; } diff --git a/sway/tree/root.c b/sway/tree/root.c index b42371dec..ecc04ddbb 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -14,54 +14,45 @@ #include "log.h" #include "util.h" -struct sway_container root_container; +struct sway_root *root; static void output_layout_handle_change(struct wl_listener *listener, void *data) { - arrange_windows(&root_container); + arrange_root(); transaction_commit_dirty(); } -void root_create(void) { - root_container.id = 0; // normally assigned in new_swayc() - root_container.type = C_ROOT; - root_container.layout = L_NONE; - root_container.name = strdup("root"); - root_container.children = create_list(); - root_container.current.children = create_list(); - wl_signal_init(&root_container.events.destroy); - - root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); - root_container.sway_root->output_layout = wlr_output_layout_create(); - wl_list_init(&root_container.sway_root->all_outputs); +struct sway_root *root_create(void) { + struct sway_root *root = calloc(1, sizeof(struct sway_root)); + if (!root) { + wlr_log(WLR_ERROR, "Unable to allocate sway_root"); + return NULL; + } + node_init(&root->node, N_ROOT, root); + root->output_layout = wlr_output_layout_create(); + wl_list_init(&root->all_outputs); #ifdef HAVE_XWAYLAND - wl_list_init(&root_container.sway_root->xwayland_unmanaged); + wl_list_init(&root->xwayland_unmanaged); #endif - wl_list_init(&root_container.sway_root->drag_icons); - wl_signal_init(&root_container.sway_root->events.new_container); - root_container.sway_root->scratchpad = create_list(); - root_container.sway_root->saved_workspaces = create_list(); + wl_list_init(&root->drag_icons); + wl_signal_init(&root->events.new_node); + root->outputs = create_list(); + root->scratchpad = create_list(); + root->saved_workspaces = create_list(); - root_container.sway_root->output_layout_change.notify = - output_layout_handle_change; - wl_signal_add(&root_container.sway_root->output_layout->events.change, - &root_container.sway_root->output_layout_change); + root->output_layout_change.notify = output_layout_handle_change; + wl_signal_add(&root->output_layout->events.change, + &root->output_layout_change); + return root; } -void root_destroy(void) { - // sway_root - wl_list_remove(&root_container.sway_root->output_layout_change.link); - list_free(root_container.sway_root->scratchpad); - list_free(root_container.sway_root->saved_workspaces); - wlr_output_layout_destroy(root_container.sway_root->output_layout); - free(root_container.sway_root); - - // root_container - list_free(root_container.children); - list_free(root_container.current.children); - free(root_container.name); - - memset(&root_container, 0, sizeof(root_container)); +void root_destroy(struct sway_root *root) { + wl_list_remove(&root->output_layout_change.link); + list_free(root->scratchpad); + list_free(root->saved_workspaces); + list_free(root->outputs); + wlr_output_layout_destroy(root->output_layout); + free(root); } void root_scratchpad_add_container(struct sway_container *con) { @@ -69,15 +60,21 @@ void root_scratchpad_add_container(struct sway_container *con) { return; } con->scratchpad = true; - list_add(root_container.sway_root->scratchpad, con); + list_add(root->scratchpad, con); struct sway_container *parent = con->parent; + struct sway_workspace *workspace = con->workspace; container_set_floating(con, true); - container_remove_child(con); - arrange_windows(parent); + container_detach(con); struct sway_seat *seat = input_manager_current_seat(input_manager); - seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); + if (parent) { + arrange_container(parent); + seat_set_focus(seat, seat_get_focus_inactive(seat, &parent->node)); + } else { + arrange_workspace(workspace); + seat_set_focus(seat, seat_get_focus_inactive(seat, &workspace->node)); + } } void root_scratchpad_remove_container(struct sway_container *con) { @@ -85,28 +82,25 @@ void root_scratchpad_remove_container(struct sway_container *con) { return; } con->scratchpad = false; - int index = list_find(root_container.sway_root->scratchpad, con); + int index = list_find(root->scratchpad, con); if (index != -1) { - list_del(root_container.sway_root->scratchpad, index); + list_del(root->scratchpad, index); } } void root_scratchpad_show(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *ws = seat_get_focus(seat); - if (ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } + struct sway_workspace *ws = seat_get_focused_workspace(seat); // If the current con or any of its parents are in fullscreen mode, we // first need to disable it before showing the scratchpad con. - if (ws->sway_workspace->fullscreen) { - container_set_fullscreen(ws->sway_workspace->fullscreen, false); + if (ws->fullscreen) { + container_set_fullscreen(ws->fullscreen, false); } // Show the container - if (con->parent) { - container_remove_child(con); + if (con->workspace) { + container_detach(con); } workspace_add_floating(ws, con); @@ -115,7 +109,7 @@ void root_scratchpad_show(struct sway_container *con) { double center_ly = con->y + con->height / 2; struct wlr_box workspace_box; - container_get_box(ws, &workspace_box); + workspace_get_box(ws, &workspace_box); if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { // Maybe resize it if (con->width > ws->width || con->height > ws->height) { @@ -128,23 +122,21 @@ void root_scratchpad_show(struct sway_container *con) { container_floating_move_to(con, new_lx, new_ly); } - arrange_windows(ws); - seat_set_focus(seat, seat_get_focus_inactive(seat, con)); - - container_set_dirty(con->parent); + arrange_workspace(ws); + seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); } void root_scratchpad_hide(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *ws = container_parent(con, C_WORKSPACE); + struct sway_node *focus = seat_get_focus(seat); + struct sway_workspace *ws = con->workspace; - container_remove_child(con); - arrange_windows(ws); - if (con == focus) { - seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); + container_detach(con); + arrange_workspace(ws); + if (&con->node == focus) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &ws->node)); } - list_move_to_end(root_container.sway_root->scratchpad, con); + list_move_to_end(root->scratchpad, con); } struct pid_workspace { @@ -152,7 +144,7 @@ struct pid_workspace { char *workspace; struct timespec time_added; - struct sway_container *output; + struct sway_output *output; struct wl_listener output_destroy; struct wl_list link; @@ -160,13 +152,13 @@ struct pid_workspace { static struct wl_list pid_workspaces; -struct sway_container *root_workspace_for_pid(pid_t pid) { +struct sway_workspace *root_workspace_for_pid(pid_t pid) { if (!pid_workspaces.prev && !pid_workspaces.next) { wl_list_init(&pid_workspaces); return NULL; } - struct sway_container *ws = NULL; + struct sway_workspace *ws = NULL; struct pid_workspace *pw = NULL; wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); @@ -219,16 +211,12 @@ void root_record_workspace_pid(pid_t pid) { } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *ws = - seat_get_focus_inactive(seat, &root_container); - if (ws && ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } + struct sway_workspace *ws = seat_get_focused_workspace(seat); if (!ws) { wlr_log(WLR_DEBUG, "Bailing out, no workspace"); return; } - struct sway_container *output = ws->parent; + struct sway_output *output = ws->output; if (!output) { wlr_log(WLR_DEBUG, "Bailing out, no output"); return; @@ -255,30 +243,28 @@ void root_record_workspace_pid(pid_t pid) { pw->pid = pid; memcpy(&pw->time_added, &now, sizeof(struct timespec)); pw->output_destroy.notify = pw_handle_output_destroy; - wl_signal_add(&output->sway_output->wlr_output->events.destroy, - &pw->output_destroy); + wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); wl_list_insert(&pid_workspaces, &pw->link); } -void root_for_each_workspace(void (*f)(struct sway_container *con, void *data), +void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), void *data) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; output_for_each_workspace(output, f, data); } } void root_for_each_container(void (*f)(struct sway_container *con, void *data), void *data) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; output_for_each_container(output, f, data); } // Scratchpad - for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { - struct sway_container *container = - root_container.sway_root->scratchpad->items[i]; + for (int i = 0; i < root->scratchpad->length; ++i) { + struct sway_container *container = root->scratchpad->items[i]; // If the container has a parent then it's visible on a workspace // and will have been iterated in the previous for loop. So we only // iterate the hidden scratchpad containers here. @@ -289,10 +275,10 @@ void root_for_each_container(void (*f)(struct sway_container *con, void *data), } } -struct sway_container *root_find_output( - bool (*test)(struct sway_container *con, void *data), void *data) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; +struct sway_output *root_find_output( + bool (*test)(struct sway_output *output, void *data), void *data) { + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; if (test(output, data)) { return output; } @@ -300,11 +286,11 @@ struct sway_container *root_find_output( return NULL; } -struct sway_container *root_find_workspace( - bool (*test)(struct sway_container *con, void *data), void *data) { - struct sway_container *result = NULL; - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; +struct sway_workspace *root_find_workspace( + bool (*test)(struct sway_workspace *ws, void *data), void *data) { + struct sway_workspace *result = NULL; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; if ((result = output_find_workspace(output, test, data))) { return result; } @@ -315,17 +301,16 @@ struct sway_container *root_find_workspace( struct sway_container *root_find_container( bool (*test)(struct sway_container *con, void *data), void *data) { struct sway_container *result = NULL; - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *output = root_container.children->items[i]; + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; if ((result = output_find_container(output, test, data))) { return result; } } // Scratchpad - for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { - struct sway_container *container = - root_container.sway_root->scratchpad->items[i]; + for (int i = 0; i < root->scratchpad->length; ++i) { + struct sway_container *container = root->scratchpad->items[i]; if (!container->parent) { if (test(container, data)) { return container; @@ -337,3 +322,10 @@ struct sway_container *root_find_container( } return NULL; } + +void root_get_box(struct sway_root *root, struct wlr_box *box) { + box->x = root->x; + box->y = root->y; + box->width = root->width; + box->height = root->height; +} diff --git a/sway/tree/view.c b/sway/tree/view.c index 6bd0ef670..452c2bd1b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->marks = create_list(); view->allow_request_urgent = true; wl_signal_init(&view->events.unmap); + + view->container = container_create(view); } void view_destroy(struct sway_view *view) { @@ -43,8 +45,8 @@ void view_destroy(struct sway_view *view) { "Tried to free view which wasn't marked as destroying")) { return; } - if (!sway_assert(view->swayc == NULL, - "Tried to free view which still has a swayc " + if (!sway_assert(view->container == NULL, + "Tried to free view which still has a container " "(might have a pending transaction?)")) { return; } @@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) { wlr_texture_destroy(view->marks_focused_inactive); wlr_texture_destroy(view->marks_unfocused); wlr_texture_destroy(view->marks_urgent); + free(view->title_format); if (view->impl->destroy) { view->impl->destroy(view); @@ -65,23 +68,13 @@ void view_destroy(struct sway_view *view) { } } -/** - * The view may or may not be involved in a transaction. For example, a view may - * unmap then attempt to destroy itself before we've applied the new layout. If - * an unmapping view is still involved in a transaction then it'll still have a - * swayc. - * - * If there's no transaction we can simply free the view. Otherwise the - * destroying flag will make the view get freed when the transaction is - * finished. - */ void view_begin_destroy(struct sway_view *view) { if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { return; } view->destroying = true; - if (!view->swayc) { + if (!view->container) { view_destroy(view); } } @@ -171,30 +164,27 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, } void view_autoconfigure(struct sway_view *view) { - if (!sway_assert(view->swayc, - "Called view_autoconfigure() on a view without a swayc")) { + struct sway_output *output = view->container->workspace->output; + + if (view->container->is_fullscreen) { + view->x = output->wlr_output->lx; + view->y = output->wlr_output->ly; + view->width = output->wlr_output->width; + view->height = output->wlr_output->height; return; } - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + struct sway_workspace *ws = view->container->workspace; - if (view->swayc->is_fullscreen) { - view->x = output->x; - view->y = output->y; - view->width = output->width; - view->height = output->height; - return; - } - - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - - int other_views = 0; + bool other_views = false; if (config->hide_edge_borders == E_SMART) { - struct sway_container *con = view->swayc; - while (con != output) { - if (con->layout != L_TABBED && con->layout != L_STACKED) { - other_views += con->children ? con->children->length - 1 : 0; - if (other_views > 0) { + struct sway_container *con = view->container; + while (con) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout != L_TABBED && layout != L_STACKED) { + list_t *siblings = container_get_siblings(con); + if (siblings && siblings->length > 1) { + other_views = true; break; } } @@ -202,7 +192,7 @@ void view_autoconfigure(struct sway_view *view) { } } - struct sway_container *con = view->swayc; + struct sway_container *con = view->container; view->border_top = view->border_bottom = true; view->border_left = view->border_right = true; @@ -228,7 +218,8 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the swayc's y is the bottom of the // title area. We have to disable any top border because the title bar is // rendered by the parent. - if (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout == L_TABBED || layout == L_STACKED) { view->border_top = false; } else { y_offset = container_titlebar_height(); @@ -281,13 +272,16 @@ void view_set_activated(struct sway_view *view, bool activated) { } void view_request_activate(struct sway_view *view) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_workspace *ws = view->container->workspace; + if (!ws) { // hidden scratchpad container + return; + } struct sway_seat *seat = input_manager_current_seat(input_manager); switch (config->focus_on_window_activation) { case FOWA_SMART: if (workspace_is_visible(ws)) { - seat_set_focus(seat, view->swayc); + seat_set_focus(seat, &view->container->node); } else { view_set_urgent(view, true); } @@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) { view_set_urgent(view, true); break; case FOWA_FOCUS: - seat_set_focus(seat, view->swayc); + seat_set_focus(seat, &view->container->node); break; case FOWA_NONE: break; @@ -331,23 +325,12 @@ void view_close_popups(struct sway_view *view) { } void view_damage_from(struct sway_view *view) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *cont = root_container.children->items[i]; - if (cont->type == C_OUTPUT) { - output_damage_from_view(cont->sway_output, view); - } + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_from_view(output, view); } } -static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) { - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - - box->x = output->x + view->swayc->x; - box->y = output->y + view->swayc->y; - box->width = view->width; - box->height = view->height; -} - void view_for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (!view->surface) { @@ -396,11 +379,8 @@ static bool view_has_executed_criteria(struct sway_view *view, } void view_execute_criteria(struct sway_view *view) { - if (!view->swayc) { - return; - } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *prior_focus = seat_get_focus(seat); + struct sway_node *prior_focus = seat_get_focus(seat); list_t *criterias = criteria_for_view(view, CT_COMMAND); for (int i = 0; i < criterias->length; i++) { struct criteria *criteria = criterias->items[i]; @@ -411,7 +391,7 @@ void view_execute_criteria(struct sway_view *view) { } wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", criteria->raw, view, criteria->cmdlist); - seat_set_focus(seat, view->swayc); + seat_set_focus(seat, &view->container->node); list_add(view->executed_criteria, criteria); struct cmd_results *res = execute_command(criteria->cmdlist, NULL); if (res->status != CMD_SUCCESS) { @@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) { seat_set_focus(seat, prior_focus); } -static struct sway_container *select_workspace(struct sway_view *view) { +static struct sway_workspace *select_workspace(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(input_manager); // Check if there's any `assign` criteria for the view list_t *criterias = criteria_for_view(view, CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); - struct sway_container *ws = NULL; + struct sway_workspace *ws = NULL; for (int i = 0; i < criterias->length; ++i) { struct criteria *criteria = criterias->items[i]; if (criteria->type == CT_ASSIGN_OUTPUT) { - struct sway_container *output = output_by_name(criteria->target); + struct sway_output *output = output_by_name(criteria->target); if (output) { - ws = seat_get_active_child(seat, output); + ws = output_get_active_workspace(output); break; } } else { @@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) { } // Use the focused workspace - ws = seat_get_focus_inactive(seat, &root_container); - if (ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } - return ws; + return seat_get_focused_workspace(seat); } static bool should_focus(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *prev_focus = - seat_get_focus_inactive(seat, &root_container); - struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? - prev_focus : container_parent(prev_focus, C_WORKSPACE); - struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_container *prev_con = seat_get_focused_container(seat); + struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); + struct sway_workspace *map_ws = view->container->workspace; // Views can only take focus if they are mapped into the active workspace if (prev_ws != map_ws) { @@ -506,10 +480,9 @@ static bool should_focus(struct sway_view *view) { // If the view is the only one in the focused workspace, it'll get focus // regardless of any no_focus criteria. - struct sway_container *parent = view->swayc->parent; - if (parent->type == C_WORKSPACE && prev_focus == parent) { - size_t num_children = parent->children->length + - parent->sway_workspace->floating->length; + if (!view->container->parent && !prev_con) { + size_t num_children = view->container->workspace->tiling->length + + view->container->workspace->floating->length; if (num_children == 1) { return true; } @@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->surface = wlr_surface; struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *ws = select_workspace(view); - struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); + struct sway_workspace *ws = select_workspace(view); + struct sway_node *node = seat_get_focus_inactive(seat, &ws->node); + struct sway_container *target_sibling = node->type == N_CONTAINER ? + node->sway_container : NULL; // If we're about to launch the view into the floating container, then // launch it as a tiled view in the root of the workspace instead. - if (container_is_floating(target_sibling)) { - target_sibling = target_sibling->parent; + if (target_sibling && container_is_floating(target_sibling)) { + target_sibling = NULL; } - view->swayc = container_view_create(target_sibling, view); + view->container = container_create(view); + if (target_sibling) { + container_add_sibling(target_sibling, view->container, 1); + } else { + workspace_add_tiling(ws, view->container); + } + ipc_event_window(view->container, "new"); view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -548,7 +529,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (view->impl->wants_floating && view->impl->wants_floating(view)) { view->border = config->floating_border; view->border_thickness = config->floating_border_thickness; - container_set_floating(view->swayc, true); + container_set_floating(view->container, true); } else { view->border = config->border; view->border_thickness = config->border_thickness; @@ -556,11 +537,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { } if (should_focus(view)) { - input_manager_set_focus(input_manager, view->swayc); + input_manager_set_focus(input_manager, &view->container->node); } view_update_title(view, false); - container_notify_subtree_changed(view->swayc->parent); + container_update_representation(view->container); view_execute_criteria(view); } @@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) { view->urgent_timer = NULL; } - bool was_fullscreen = view->swayc->is_fullscreen; - struct sway_container *parent = view->swayc->parent; - container_begin_destroy(view->swayc); - struct sway_container *surviving_ancestor = container_reap_empty(parent); + struct sway_container *parent = view->container->parent; + struct sway_workspace *ws = view->container->workspace; + container_begin_destroy(view->container); + if (parent) { + container_reap_empty(parent); + } else { + workspace_consider_destroy(ws); + } - // If the workspace wasn't reaped - if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { - struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ? - surviving_ancestor : - container_parent(surviving_ancestor, C_WORKSPACE); - arrange_windows(was_fullscreen ? ws : surviving_ancestor); + if (!ws->node.destroying) { + arrange_workspace(ws); workspace_detect_urgent(ws); } @@ -593,15 +574,15 @@ void view_unmap(struct sway_view *view) { } void view_update_size(struct sway_view *view, int width, int height) { - if (!sway_assert(container_is_floating(view->swayc), + if (!sway_assert(container_is_floating(view->container), "Expected a floating container")) { return; } view->width = width; view->height = height; - view->swayc->current.view_width = width; - view->swayc->current.view_height = height; - container_set_geometry_from_floating_view(view->swayc); + view->container->current.view_width = width; + view->container->current.view_height = height; + container_set_geometry_from_floating_view(view->container); } static void view_subsurface_create(struct sway_view *view, @@ -670,27 +651,18 @@ void view_child_init(struct sway_view_child *child, wl_signal_add(&view->events.unmap, &child->view_unmap); child->view_unmap.notify = view_child_handle_view_unmap; - struct sway_container *output = child->view->swayc->parent; - if (output != NULL) { - if (output->type != C_OUTPUT) { - output = container_parent(output, C_OUTPUT); - } - wlr_surface_send_enter(child->surface, output->sway_output->wlr_output); - } + struct sway_output *output = child->view->container->workspace->output; + wlr_surface_send_enter(child->surface, output->wlr_output); view_init_subsurfaces(child->view, surface); // TODO: only damage the whole child - if (child->view->swayc) { - container_damage_whole(child->view->swayc); - } + container_damage_whole(child->view->container); } void view_child_destroy(struct sway_view_child *child) { // TODO: only damage the whole child - if (child->view->swayc) { - container_damage_whole(child->view->swayc); - } + container_damage_whole(child->view->container); wl_list_remove(&child->surface_commit.link); wl_list_remove(&child->surface_destroy.link); @@ -808,22 +780,20 @@ static char *escape_title(char *buffer) { } void view_update_title(struct sway_view *view, bool force) { - if (!view->swayc) { - return; - } const char *title = view_get_title(view); if (!force) { - if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { + if (title && view->container->title && + strcmp(title, view->container->title) == 0) { return; } - if (!title && !view->swayc->name) { + if (!title && !view->container->title) { return; } } - free(view->swayc->name); - free(view->swayc->formatted_title); + free(view->container->title); + free(view->container->formatted_title); if (title) { size_t len = parse_title_format(view, NULL); char *buffer = calloc(len + 1, sizeof(char)); @@ -836,25 +806,25 @@ void view_update_title(struct sway_view *view, bool force) { buffer = escape_title(buffer); } - view->swayc->name = strdup(title); - view->swayc->formatted_title = buffer; + view->container->title = strdup(title); + view->container->formatted_title = buffer; } else { - view->swayc->name = NULL; - view->swayc->formatted_title = NULL; + view->container->title = NULL; + view->container->formatted_title = NULL; } - container_calculate_title_height(view->swayc); + container_calculate_title_height(view->container); config_update_font_height(false); // Update title after the global font height is updated - container_update_title_textures(view->swayc); + container_update_title_textures(view->container); - ipc_event_window(view->swayc, "title"); + ipc_event_window(view->container, "title"); } static bool find_by_mark_iterator(struct sway_container *con, void *data) { char *mark = data; - return con->type == C_VIEW && view_has_mark(con->sway_view, mark); + return con->view && view_has_mark(con->view, mark); } struct sway_view *view_find_mark(char *mark) { @@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) { if (!container) { return NULL; } - return container->sway_view; + return container->view; } bool view_find_and_unmark(char *mark) { @@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) { if (!container) { return false; } - struct sway_view *view = container->sway_view; + struct sway_view *view = container->view; for (int i = 0; i < view->marks->length; ++i) { char *view_mark = view->marks->items[i]; @@ -888,10 +858,9 @@ bool view_find_and_unmark(char *mark) { } void view_clear_marks(struct sway_view *view) { - while (view->marks->length) { - list_del(view->marks, 0); - ipc_event_window(view->swayc, "mark"); - } + list_foreach(view->marks, free); + view->marks->length = 0; + ipc_event_window(view->container, "mark"); } bool view_has_mark(struct sway_view *view, char *mark) { @@ -906,12 +875,13 @@ bool view_has_mark(struct sway_view *view, char *mark) { void view_add_mark(struct sway_view *view, char *mark) { list_add(view->marks, strdup(mark)); - ipc_event_window(view->swayc, "mark"); + ipc_event_window(view->container, "mark"); } static void update_marks_texture(struct sway_view *view, struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(view->swayc); + struct sway_output *output = + container_get_effective_output(view->container); if (!output) { return; } @@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view, double scale = output->wlr_output->scale; int width = 0; - int height = view->swayc->title_height * scale; + int height = view->container->title_height * scale; cairo_t *c = cairo_create(NULL); get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); @@ -994,44 +964,40 @@ void view_update_marks_textures(struct sway_view *view) { &config->border_colors.unfocused); update_marks_texture(view, &view->marks_urgent, &config->border_colors.urgent); - container_damage_whole(view->swayc); + container_damage_whole(view->container); } bool view_is_visible(struct sway_view *view) { - if (!view->swayc || view->swayc->destroying) { + if (view->container->node.destroying) { return false; } - struct sway_container *workspace = - container_parent(view->swayc, C_WORKSPACE); + struct sway_workspace *workspace = view->container->workspace; if (!workspace) { return false; } - // Determine if view is nested inside a floating container which is sticky. - // A simple floating view will have this ancestry: - // C_VIEW -> floating -> workspace - // A more complex ancestry could be: - // C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace - struct sway_container *floater = view->swayc; - while (floater->parent->type != C_WORKSPACE - && floater->parent->parent->type != C_WORKSPACE) { + // Determine if view is nested inside a floating container which is sticky + struct sway_container *floater = view->container; + while (floater->parent) { floater = floater->parent; } bool is_sticky = container_is_floating(floater) && floater->is_sticky; // Check view isn't in a tabbed or stacked container on an inactive tab struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *container = view->swayc; - while (container->type != C_WORKSPACE) { - if (container->parent->layout == L_TABBED || - container->parent->layout == L_STACKED) { - if (seat_get_active_child(seat, container->parent) != container) { + struct sway_container *container = view->container; + while (container) { + enum sway_container_layout layout = container_parent_layout(container); + if (layout == L_TABBED || layout == L_STACKED) { + struct sway_node *parent = container->parent ? + &container->parent->node : &container->workspace->node; + if (seat_get_active_child(seat, parent) != &container->node) { return false; } } container = container->parent; } // Check view isn't hidden by another fullscreen view - if (workspace->sway_workspace->fullscreen && - !container_is_fullscreen_or_child(view->swayc)) { + if (workspace->fullscreen && + !container_is_fullscreen_or_child(view->container)) { return false; } // Check the workspace is visible @@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { } if (enable) { struct sway_seat *seat = input_manager_current_seat(input_manager); - if (seat_get_focus(seat) == view->swayc) { + if (seat_get_focused_container(seat) == view->container) { return; } clock_gettime(CLOCK_MONOTONIC, &view->urgent); @@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) { view->urgent_timer = NULL; } } - container_damage_whole(view->swayc); + container_damage_whole(view->container); - ipc_event_window(view->swayc, "urgent"); + ipc_event_window(view->container, "urgent"); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - workspace_detect_urgent(ws); + workspace_detect_urgent(view->container->workspace); } bool view_is_urgent(struct sway_view *view) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 1957d94fc..38ee478e9 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -12,128 +12,105 @@ #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/tree/node.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" #include "log.h" #include "util.h" -struct sway_container *workspace_get_initial_output(const char *name) { - struct sway_container *parent; +struct sway_output *workspace_get_initial_output(const char *name) { // Search for workspace<->output pair - int e = config->workspace_outputs->length; for (int i = 0; i < config->workspace_outputs->length; ++i) { struct workspace_output *wso = config->workspace_outputs->items[i]; if (strcasecmp(wso->workspace, name) == 0) { // Find output to use if it exists - e = root_container.children->length; - for (i = 0; i < e; ++i) { - parent = root_container.children->items[i]; - if (strcmp(parent->name, wso->output) == 0) { - return parent; - } + struct sway_output *output = output_by_name(wso->output); + if (output) { + return output; } break; } } // Otherwise put it on the focused output struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = - seat_get_focus_inactive(seat, &root_container); - parent = focus; - parent = container_parent(parent, C_OUTPUT); - return parent; + struct sway_workspace *focus = seat_get_focused_workspace(seat); + return focus->output; } -struct sway_container *workspace_create(struct sway_container *output, +struct sway_workspace *workspace_create(struct sway_output *output, const char *name) { if (output == NULL) { output = workspace_get_initial_output(name); } - wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); - struct sway_container *workspace = container_create(C_WORKSPACE); + wlr_log(WLR_DEBUG, "Adding workspace %s for output %s", name, + output->wlr_output->name); - workspace->x = output->x; - workspace->y = output->y; - workspace->width = output->width; - workspace->height = output->height; - workspace->name = !name ? NULL : strdup(name); - workspace->prev_split_layout = L_NONE; - workspace->layout = container_get_default_layout(output); - - struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace)); - if (!swayws) { + struct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace)); + if (!ws) { + wlr_log(WLR_ERROR, "Unable to allocate sway_workspace"); return NULL; } - swayws->swayc = workspace; - swayws->floating = create_list(); - swayws->output_priority = create_list(); - workspace->sway_workspace = swayws; - workspace_output_add_priority(workspace, output); + node_init(&ws->node, N_WORKSPACE, ws); + ws->x = output->wlr_output->lx; + ws->y = output->wlr_output->ly; + ws->width = output->wlr_output->width; + ws->height = output->wlr_output->height; + ws->name = name ? strdup(name) : NULL; + ws->prev_split_layout = L_NONE; + ws->layout = output_get_default_layout(output); + ws->floating = create_list(); + ws->tiling = create_list(); + ws->output_priority = create_list(); + workspace_output_add_priority(ws, output); - container_add_child(output, workspace); + output_add_workspace(output, ws); output_sort_workspaces(output); - container_create_notify(workspace); - return workspace; + ipc_event_workspace(NULL, ws, "init"); + wl_signal_emit(&root->events.new_node, &ws->node); + + return ws; } -void workspace_destroy(struct sway_container *workspace) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return; - } - if (!sway_assert(workspace->destroying, +void workspace_destroy(struct sway_workspace *workspace) { + if (!sway_assert(workspace->node.destroying, "Tried to free workspace which wasn't marked as destroying")) { return; } - if (!sway_assert(workspace->ntxnrefs == 0, "Tried to free workspace " + if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace " "which is still referenced by transactions")) { return; } - // sway_workspace - struct sway_workspace *ws = workspace->sway_workspace; - list_foreach(ws->output_priority, free); - list_free(ws->output_priority); - list_free(ws->floating); - free(ws); - // swayc free(workspace->name); - free(workspace->formatted_title); - wlr_texture_destroy(workspace->title_focused); - wlr_texture_destroy(workspace->title_focused_inactive); - wlr_texture_destroy(workspace->title_unfocused); - wlr_texture_destroy(workspace->title_urgent); - list_free(workspace->children); - list_free(workspace->current.children); - list_free(workspace->outputs); + free(workspace->representation); + list_foreach(workspace->output_priority, free); + list_free(workspace->output_priority); + list_free(workspace->floating); + list_free(workspace->tiling); + list_free(workspace->current.floating); + list_free(workspace->current.tiling); free(workspace); } -void workspace_begin_destroy(struct sway_container *workspace) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return; - } +void workspace_begin_destroy(struct sway_workspace *workspace) { wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); - wl_signal_emit(&workspace->events.destroy, workspace); ipc_event_workspace(NULL, workspace, "empty"); // intentional + wl_signal_emit(&workspace->node.events.destroy, &workspace->node); - workspace->destroying = true; - container_set_dirty(workspace); - - if (workspace->parent) { - container_remove_child(workspace); + if (workspace->output) { + workspace_detach(workspace); } + + workspace->node.destroying = true; + node_set_dirty(&workspace->node); } -void workspace_consider_destroy(struct sway_container *ws) { - if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { - return; - } - struct sway_seat *seat = input_manager_current_seat(input_manager); - if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0 - && seat_get_active_child(seat, ws->parent) != ws) { +void workspace_consider_destroy(struct sway_workspace *ws) { + if (ws->tiling->length == 0 && ws->floating->length == 0 + && output_get_active_workspace(ws->output) != ws) { workspace_begin_destroy(ws); } } @@ -272,59 +249,49 @@ char *workspace_next_name(const char *output_name) { } // As a fall back, get the current number of active workspaces // and return that + 1 for the next workspace's name - int ws_num = root_container.children->length; + int ws_num = root->outputs->length; int l = snprintf(NULL, 0, "%d", ws_num); char *name = malloc(l + 1); - if (!sway_assert(name, "Cloud not allocate workspace name")) { + if (!sway_assert(name, "Could not allocate workspace name")) { return NULL; } sprintf(name, "%d", ws_num++); return name; } -static bool _workspace_by_number(struct sway_container *view, void *data) { - if (view->type != C_WORKSPACE) { - return false; - } +static bool _workspace_by_number(struct sway_workspace *ws, void *data) { char *name = data; - char *view_name = view->name; + char *ws_name = ws->name; while (isdigit(*name)) { - if (*name++ != *view_name++) { + if (*name++ != *ws_name++) { return false; } } - return !isdigit(*view_name); + return !isdigit(*ws_name); } -struct sway_container *workspace_by_number(const char* name) { +struct sway_workspace *workspace_by_number(const char* name) { return root_find_workspace(_workspace_by_number, (void *) name); } -static bool _workspace_by_name(struct sway_container *view, void *data) { - return (view->type == C_WORKSPACE) && - (strcasecmp(view->name, (char *) data) == 0); +static bool _workspace_by_name(struct sway_workspace *ws, void *data) { + return strcasecmp(ws->name, data) == 0; } -struct sway_container *workspace_by_name(const char *name) { +struct sway_workspace *workspace_by_name(const char *name) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *current_workspace = NULL, *current_output = NULL; - struct sway_container *focus = seat_get_focus(seat); - if (focus) { - current_workspace = focus->type == C_WORKSPACE ? - focus : container_parent(focus, C_WORKSPACE); - current_output = container_parent(focus, C_OUTPUT); - } + struct sway_workspace *current = seat_get_focused_workspace(seat); if (strcmp(name, "prev") == 0) { - return workspace_prev(current_workspace); + return workspace_prev(current); } else if (strcmp(name, "prev_on_output") == 0) { - return workspace_output_prev(current_output); + return workspace_output_prev(current); } else if (strcmp(name, "next") == 0) { - return workspace_next(current_workspace); + return workspace_next(current); } else if (strcmp(name, "next_on_output") == 0) { - return workspace_output_next(current_output); + return workspace_output_next(current); } else if (strcmp(name, "current") == 0) { - return current_workspace; + return current; } else if (strcasecmp(name, "back_and_forth") == 0) { return prev_workspace_name ? root_find_workspace(_workspace_by_name, (void*)prev_workspace_name) @@ -339,97 +306,68 @@ struct sway_container *workspace_by_name(const char *name) { * the end and beginning. If next is false, the previous workspace is returned, * otherwise the next one is returned. */ -static struct sway_container *workspace_output_prev_next_impl( - struct sway_container *output, int dir) { - if (!output) { - return NULL; - } - if (!sway_assert(output->type == C_OUTPUT, - "Argument must be an output, is %d", output->type)) { - return NULL; - } - +static struct sway_workspace *workspace_output_prev_next_impl( + struct sway_output *output, int dir) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus_inactive(seat, output); - struct sway_container *workspace = (focus->type == C_WORKSPACE ? - focus : - container_parent(focus, C_WORKSPACE)); + struct sway_workspace *workspace = seat_get_focused_workspace(seat); - int index = list_find(output->children, workspace); - size_t new_index = wrap(index + dir, output->children->length); - return output->children->items[new_index]; + int index = list_find(output->workspaces, workspace); + size_t new_index = wrap(index + dir, output->workspaces->length); + return output->workspaces->items[new_index]; } /** * Get the previous or next workspace. If the first/last workspace on an output * is active, proceed to the previous/next output's previous/next workspace. */ -static struct sway_container *workspace_prev_next_impl( - struct sway_container *workspace, int dir) { - if (!workspace) { - return NULL; - } - if (!sway_assert(workspace->type == C_WORKSPACE, - "Argument must be a workspace, is %d", workspace->type)) { - return NULL; - } - - struct sway_container *output = workspace->parent; - int index = list_find(output->children, workspace); +static struct sway_workspace *workspace_prev_next_impl( + struct sway_workspace *workspace, int dir) { + struct sway_output *output = workspace->output; + int index = list_find(output->workspaces, workspace); int new_index = index + dir; - if (new_index >= 0 && new_index < output->children->length) { - return output->children->items[index + dir]; + if (new_index >= 0 && new_index < output->workspaces->length) { + return output->workspaces->items[new_index]; } // Look on a different output - int output_index = list_find(root_container.children, output); - new_index = wrap(output_index + dir, root_container.children->length); - output = root_container.children->items[new_index]; + int output_index = list_find(root->outputs, output); + new_index = wrap(output_index + dir, root->outputs->length); + output = root->outputs->items[new_index]; if (dir == 1) { - return output->children->items[0]; + return output->workspaces->items[0]; } else { - return output->children->items[output->children->length - 1]; + return output->workspaces->items[output->workspaces->length - 1]; } } -struct sway_container *workspace_output_next(struct sway_container *current) { - return workspace_output_prev_next_impl(current, 1); +struct sway_workspace *workspace_output_next(struct sway_workspace *current) { + return workspace_output_prev_next_impl(current->output, 1); } -struct sway_container *workspace_next(struct sway_container *current) { +struct sway_workspace *workspace_next(struct sway_workspace *current) { return workspace_prev_next_impl(current, 1); } -struct sway_container *workspace_output_prev(struct sway_container *current) { - return workspace_output_prev_next_impl(current, -1); +struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { + return workspace_output_prev_next_impl(current->output, -1); } -struct sway_container *workspace_prev(struct sway_container *current) { +struct sway_workspace *workspace_prev(struct sway_workspace *current) { return workspace_prev_next_impl(current, -1); } -bool workspace_switch(struct sway_container *workspace, +bool workspace_switch(struct sway_workspace *workspace, bool no_auto_back_and_forth) { - if (!workspace) { - return false; - } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = - seat_get_focus_inactive(seat, &root_container); - if (!seat || !focus) { - return false; - } - struct sway_container *active_ws = focus; - if (active_ws->type != C_WORKSPACE) { - active_ws = container_parent(focus, C_WORKSPACE); - } + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + struct sway_workspace *active_ws = seat_get_focused_workspace(seat); if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) { - struct sway_container *new_ws = workspace_by_name(prev_workspace_name); + struct sway_workspace *new_ws = workspace_by_name(prev_workspace_name); workspace = new_ws ? new_ws : workspace_create(NULL, prev_workspace_name); @@ -447,21 +385,21 @@ bool workspace_switch(struct sway_container *workspace, } // Move sticky containers to new workspace - struct sway_container *next_output = workspace->parent; - struct sway_container *next_output_prev_ws = - seat_get_active_child(seat, next_output); - list_t *floating = next_output_prev_ws->sway_workspace->floating; + struct sway_output *next_output = workspace->output; + struct sway_workspace *next_output_prev_ws = + output_get_active_workspace(next_output); bool has_sticky = false; if (workspace != next_output_prev_ws) { - for (int i = 0; i < floating->length; ++i) { - struct sway_container *floater = floating->items[i]; + for (int i = 0; i < next_output_prev_ws->floating->length; ++i) { + struct sway_container *floater = + next_output_prev_ws->floating->items[i]; if (floater->is_sticky) { has_sticky = true; - container_remove_child(floater); + container_detach(floater); workspace_add_floating(workspace, floater); - if (floater == focus) { + if (&floater->node == focus) { seat_set_focus(seat, NULL); - seat_set_focus(seat, floater); + seat_set_focus(seat, &floater->node); } --i; } @@ -470,9 +408,9 @@ bool workspace_switch(struct sway_container *workspace, wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); - struct sway_container *next = seat_get_focus_inactive(seat, workspace); + struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node); if (next == NULL) { - next = workspace; + next = &workspace->node; } if (has_sticky) { // If there's a sticky container, we might be setting focus to the same @@ -483,35 +421,24 @@ bool workspace_switch(struct sway_container *workspace, workspace_consider_destroy(active_ws); } seat_set_focus(seat, next); - struct sway_container *output = container_parent(workspace, C_OUTPUT); - arrange_windows(output); + arrange_workspace(workspace); return true; } -bool workspace_is_visible(struct sway_container *ws) { - if (ws->destroying) { +bool workspace_is_visible(struct sway_workspace *ws) { + if (ws->node.destroying) { return false; } - struct sway_container *output = container_parent(ws, C_OUTPUT); - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus_inactive(seat, output); - if (focus->type != C_WORKSPACE) { - focus = container_parent(focus, C_WORKSPACE); - } - return focus == ws; + return output_get_active_workspace(ws->output) == ws; } -bool workspace_is_empty(struct sway_container *ws) { - if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { - return false; - } - if (ws->children->length) { +bool workspace_is_empty(struct sway_workspace *ws) { + if (ws->tiling->length) { return false; } // Sticky views are not considered to be part of this workspace - list_t *floating = ws->sway_workspace->floating; - for (int i = 0; i < floating->length; ++i) { - struct sway_container *floater = floating->items[i]; + for (int i = 0; i < ws->floating->length; ++i) { + struct sway_container *floater = ws->floating->items[i]; if (!floater->is_sticky) { return false; } @@ -523,20 +450,19 @@ static int find_output(const void *id1, const void *id2) { return strcmp(id1, id2) ? 0 : 1; } -void workspace_output_raise_priority(struct sway_container *workspace, - struct sway_container *old_output, struct sway_container *output) { - struct sway_workspace *ws = workspace->sway_workspace; - +void workspace_output_raise_priority(struct sway_workspace *ws, + struct sway_output *old_output, struct sway_output *output) { int old_index = list_seq_find(ws->output_priority, find_output, - old_output->name); + old_output->wlr_output->name); if (old_index < 0) { return; } int new_index = list_seq_find(ws->output_priority, find_output, - output->name); + output->wlr_output->name); if (new_index < 0) { - list_insert(ws->output_priority, old_index, strdup(output->name)); + list_insert(ws->output_priority, old_index, + strdup(output->wlr_output->name)); } else if (new_index > old_index) { char *name = ws->output_priority->items[new_index]; list_del(ws->output_priority, new_index); @@ -544,29 +470,24 @@ void workspace_output_raise_priority(struct sway_container *workspace, } } -void workspace_output_add_priority(struct sway_container *workspace, - struct sway_container *output) { - int index = list_seq_find(workspace->sway_workspace->output_priority, - find_output, output->name); +void workspace_output_add_priority(struct sway_workspace *workspace, + struct sway_output *output) { + int index = list_seq_find(workspace->output_priority, + find_output, output->wlr_output->name); if (index < 0) { - list_add(workspace->sway_workspace->output_priority, - strdup(output->name)); + list_add(workspace->output_priority, strdup(output->wlr_output->name)); } } -static bool _output_by_name(struct sway_container *output, void *data) { - return output->type == C_OUTPUT && strcasecmp(output->name, data) == 0; -} - -struct sway_container *workspace_output_get_highest_available( - struct sway_container *ws, struct sway_container *exclude) { - for (int i = 0; i < ws->sway_workspace->output_priority->length; i++) { - char *name = ws->sway_workspace->output_priority->items[i]; - if (exclude && strcasecmp(name, exclude->name) == 0) { +struct sway_output *workspace_output_get_highest_available( + struct sway_workspace *ws, struct sway_output *exclude) { + for (int i = 0; i < ws->output_priority->length; i++) { + char *name = ws->output_priority->items[i]; + if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) { continue; } - struct sway_container *output = root_find_output(_output_by_name, name); + struct sway_output *output = output_by_name(name); if (output) { return output; } @@ -576,49 +497,42 @@ struct sway_container *workspace_output_get_highest_available( } static bool find_urgent_iterator(struct sway_container *con, void *data) { - return con->type == C_VIEW && view_is_urgent(con->sway_view); + return con->view && view_is_urgent(con->view); } -void workspace_detect_urgent(struct sway_container *workspace) { +void workspace_detect_urgent(struct sway_workspace *workspace) { bool new_urgent = (bool)workspace_find_container(workspace, find_urgent_iterator, NULL); - if (workspace->sway_workspace->urgent != new_urgent) { - workspace->sway_workspace->urgent = new_urgent; + if (workspace->urgent != new_urgent) { + workspace->urgent = new_urgent; ipc_event_workspace(NULL, workspace, "urgent"); - container_damage_whole(workspace); + output_damage_whole(workspace->output); } } -void workspace_for_each_container(struct sway_container *ws, +void workspace_for_each_container(struct sway_workspace *ws, void (*f)(struct sway_container *con, void *data), void *data) { - if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { - return; - } // Tiling - for (int i = 0; i < ws->children->length; ++i) { - struct sway_container *container = ws->children->items[i]; + for (int i = 0; i < ws->tiling->length; ++i) { + struct sway_container *container = ws->tiling->items[i]; f(container, data); container_for_each_child(container, f, data); } // Floating - for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { - struct sway_container *container = - ws->sway_workspace->floating->items[i]; + for (int i = 0; i < ws->floating->length; ++i) { + struct sway_container *container = ws->floating->items[i]; f(container, data); container_for_each_child(container, f, data); } } -struct sway_container *workspace_find_container(struct sway_container *ws, +struct sway_container *workspace_find_container(struct sway_workspace *ws, bool (*test)(struct sway_container *con, void *data), void *data) { - if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { - return NULL; - } struct sway_container *result = NULL; // Tiling - for (int i = 0; i < ws->children->length; ++i) { - struct sway_container *child = ws->children->items[i]; + for (int i = 0; i < ws->tiling->length; ++i) { + struct sway_container *child = ws->tiling->items[i]; if (test(child, data)) { return child; } @@ -627,8 +541,8 @@ struct sway_container *workspace_find_container(struct sway_container *ws, } } // Floating - for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { - struct sway_container *child = ws->sway_workspace->floating->items[i]; + for (int i = 0; i < ws->floating->length; ++i) { + struct sway_container *child = ws->floating->items[i]; if (test(child, data)) { return child; } @@ -639,37 +553,76 @@ struct sway_container *workspace_find_container(struct sway_container *ws, return NULL; } -struct sway_container *workspace_wrap_children(struct sway_container *ws) { - struct sway_container *middle = container_create(C_CONTAINER); +struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { + struct sway_container *middle = container_create(NULL); middle->layout = ws->layout; - while (ws->children->length) { - struct sway_container *child = ws->children->items[0]; - container_remove_child(child); + while (ws->tiling->length) { + struct sway_container *child = ws->tiling->items[0]; + container_detach(child); container_add_child(middle, child); } - container_add_child(ws, middle); + workspace_add_tiling(ws, middle); return middle; } -void workspace_add_floating(struct sway_container *workspace, - struct sway_container *con) { - if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { - return; - } - if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { - return; +void workspace_detach(struct sway_workspace *workspace) { + struct sway_output *output = workspace->output; + int index = list_find(output->workspaces, workspace); + if (index != -1) { + list_del(output->workspaces, index); } + workspace->output = NULL; - list_add(workspace->sway_workspace->floating, con); - con->parent = workspace; - container_set_dirty(workspace); - container_set_dirty(con); + node_set_dirty(&workspace->node); + node_set_dirty(&output->node); } -void workspace_remove_gaps(struct sway_container *ws) { - if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { - return; +static void set_workspace(struct sway_container *container, void *data) { + container->workspace = container->parent->workspace; +} + +void workspace_add_tiling(struct sway_workspace *workspace, + struct sway_container *con) { + if (con->workspace) { + container_detach(con); } + list_add(workspace->tiling, con); + con->workspace = workspace; + container_for_each_child(con, set_workspace, NULL); + container_handle_fullscreen_reparent(con); + workspace_update_representation(workspace); + node_set_dirty(&workspace->node); + node_set_dirty(&con->node); +} + +void workspace_add_floating(struct sway_workspace *workspace, + struct sway_container *con) { + if (con->workspace) { + container_detach(con); + } + list_add(workspace->floating, con); + con->workspace = workspace; + container_for_each_child(con, set_workspace, NULL); + container_handle_fullscreen_reparent(con); + node_set_dirty(&workspace->node); + node_set_dirty(&con->node); +} + +void workspace_insert_tiling(struct sway_workspace *workspace, + struct sway_container *con, int index) { + if (con->workspace) { + container_detach(con); + } + list_insert(workspace->tiling, index, con); + con->workspace = workspace; + container_for_each_child(con, set_workspace, NULL); + container_handle_fullscreen_reparent(con); + workspace_update_representation(workspace); + node_set_dirty(&workspace->node); + node_set_dirty(&con->node); +} + +void workspace_remove_gaps(struct sway_workspace *ws) { if (ws->current_gaps == 0) { return; } @@ -681,15 +634,12 @@ void workspace_remove_gaps(struct sway_container *ws) { ws->current_gaps = 0; } -void workspace_add_gaps(struct sway_container *ws) { - if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { - return; - } +void workspace_add_gaps(struct sway_workspace *ws) { if (ws->current_gaps > 0) { return; } bool should_apply = - config->edge_gaps || (config->smart_gaps && ws->children->length > 1); + config->edge_gaps || (config->smart_gaps && ws->tiling->length > 1); if (!should_apply) { return; } @@ -708,3 +658,36 @@ void workspace_add_gaps(struct sway_container *ws) { ws->width -= 2 * ws->current_gaps; ws->height -= 2 * ws->current_gaps; } + +struct sway_container *workspace_split(struct sway_workspace *workspace, + enum sway_container_layout layout) { + if (workspace->tiling->length == 0) { + workspace->prev_split_layout = workspace->layout; + workspace->layout = layout; + return NULL; + } + + enum sway_container_layout old_layout = workspace->layout; + struct sway_container *middle = workspace_wrap_children(workspace); + workspace->layout = layout; + middle->layout = old_layout; + + return middle; +} + +void workspace_update_representation(struct sway_workspace *ws) { + size_t len = container_build_representation(ws->layout, ws->tiling, NULL); + free(ws->representation); + ws->representation = calloc(len + 1, sizeof(char)); + if (!sway_assert(ws->representation, "Unable to allocate title string")) { + return; + } + container_build_representation(ws->layout, ws->tiling, ws->representation); +} + +void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box) { + box->x = workspace->x; + box->y = workspace->y; + box->width = workspace->width; + box->height = workspace->height; +}