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.
master
Ryan Dwyer 6 years ago
parent 309fcf2300
commit 7586f150c0

@ -407,7 +407,9 @@ struct sway_config {
struct output_config *output_config; struct output_config *output_config;
struct seat_config *seat_config; struct seat_config *seat_config;
struct sway_seat *seat; struct sway_seat *seat;
struct sway_container *current_container; struct sway_node *node;
struct sway_container *container;
struct sway_workspace *workspace;
bool using_criteria; bool using_criteria;
struct { struct {
int argc; 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 merge_output_config(struct output_config *dst, struct output_config *src);
void apply_output_config(struct output_config *oc, void apply_output_config(struct output_config *oc, struct sway_output *output);
struct sway_container *output);
struct output_config *store_output_config(struct output_config *oc); struct output_config *store_output_config(struct output_config *oc);

@ -1,7 +1,6 @@
#ifndef _SWAY_TRANSACTION_H #ifndef _SWAY_TRANSACTION_H
#define _SWAY_TRANSACTION_H #define _SWAY_TRANSACTION_H
#include <wlr/render/wlr_texture.h> #include <stdint.h>
#include "sway/tree/container.h"
/** /**
* Transactions enable us to perform atomic layout updates. * Transactions enable us to perform atomic layout updates.
@ -21,6 +20,7 @@
*/ */
struct sway_transaction_instruction; struct sway_transaction_instruction;
struct sway_view;
/** /**
* Find all dirty containers, create and commit a transaction containing them, * Find all dirty containers, create and commit a transaction containing them,

@ -37,10 +37,10 @@ struct sway_input_manager {
struct sway_input_manager *input_manager_create(struct sway_server *server); struct sway_input_manager *input_manager_create(struct sway_server *server);
bool input_manager_has_focus(struct sway_input_manager *input, 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, 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); void input_manager_configure_xcursor(struct sway_input_manager *input);

@ -13,9 +13,9 @@ struct sway_seat_device {
struct wl_list link; // sway_seat::devices struct wl_list link; // sway_seat::devices
}; };
struct sway_seat_container { struct sway_seat_node {
struct sway_seat *seat; struct sway_seat *seat;
struct sway_container *container; struct sway_node *node;
struct wl_list link; // sway_seat::focus_stack struct wl_list link; // sway_seat::focus_stack
@ -76,7 +76,7 @@ struct sway_seat {
uint32_t last_button_serial; uint32_t last_button_serial;
struct wl_listener focus_destroy; struct wl_listener focus_destroy;
struct wl_listener new_container; struct wl_listener new_node;
struct wl_listener new_drag_icon; struct wl_listener new_drag_icon;
struct wl_list devices; // sway_seat_device::link 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_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, 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, void seat_set_focus_surface(struct sway_seat *seat,
struct wlr_surface *surface, bool unfocus); 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, void seat_set_exclusive_client(struct sway_seat *seat,
struct wl_client *client); 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 * 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 * is destroyed, or focus moves to a container with children and we need to
* descend into the next leaf in focus order. * descend into the next leaf in focus order.
*/ */
struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_container *container); struct sway_node *node);
struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, 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 * Descend into the focus stack to find the focus-inactive view. Useful for
* container placement when they change position in the tree. * container placement when they change position in the tree.
*/ */
struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, 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. * Return the immediate child of container which was most recently focused.
*/ */
struct sway_container *seat_get_active_child(struct sway_seat *seat, struct sway_node *seat_get_active_child(struct sway_seat *seat,
struct sway_container *container); struct sway_node *parent);
/** /**
* Iterate over the focus-inactive children of the container calling the * Iterate over the focus-inactive children of the container calling the
* function on each. * function on each.
*/ */
void seat_focus_inactive_children_for_each(struct sway_seat *seat, void seat_for_each_node(struct sway_seat *seat,
struct sway_container *container, void (*f)(struct sway_node *node, void *data), void *data);
void (*f)(struct sway_container *container, void *data), void *data);
void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); 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 *con, uint32_t button, enum wlr_edges edge);
struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, 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); void seat_end_mouse_operation(struct sway_seat *seat);

@ -7,8 +7,8 @@
json_object *ipc_json_get_version(); json_object *ipc_json_get_version();
json_object *ipc_json_describe_disabled_output(struct sway_output *o); 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_node(struct sway_node *node);
json_object *ipc_json_describe_container_recursive(struct sway_container *c); 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_input(struct sway_input_device *device);
json_object *ipc_json_describe_seat(struct sway_seat *seat); json_object *ipc_json_describe_seat(struct sway_seat *seat);
json_object *ipc_json_describe_bar_config(struct bar_config *bar); json_object *ipc_json_describe_bar_config(struct bar_config *bar);

@ -11,8 +11,8 @@ void ipc_init(struct sway_server *server);
struct sockaddr_un *ipc_user_sockaddr(void); struct sockaddr_un *ipc_user_sockaddr(void);
void ipc_event_workspace(struct sway_container *old, void ipc_event_workspace(struct sway_workspace *old,
struct sway_container *new, const char *change); struct sway_workspace *new, const char *change);
void ipc_event_window(struct sway_container *window, 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_barconfig_update(struct bar_config *bar);
void ipc_event_mode(const char *mode, bool pango); void ipc_event_mode(const char *mode, bool pango);

@ -6,14 +6,20 @@
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include "config.h" #include "config.h"
#include "sway/tree/node.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
struct sway_server; struct sway_server;
struct sway_container; struct sway_container;
struct sway_output_state {
list_t *workspaces;
struct sway_workspace *active_workspace;
};
struct sway_output { struct sway_output {
struct sway_node node;
struct wlr_output *wlr_output; struct wlr_output *wlr_output;
struct sway_container *swayc;
struct sway_server *server; struct sway_server *server;
struct wl_list layers[4]; // sway_layer_surface::link struct wl_list layers[4]; // sway_layer_surface::link
@ -22,11 +28,15 @@ struct sway_output {
struct timespec last_frame; struct timespec last_frame;
struct wlr_output_damage *damage; struct wlr_output_damage *damage;
bool enabled;
list_t *workspaces;
struct sway_output_state current;
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener mode; struct wl_listener mode;
struct wl_listener transform; struct wl_listener transform;
struct wl_listener scale; struct wl_listener scale;
struct wl_listener damage_destroy; struct wl_listener damage_destroy;
struct wl_listener damage_frame; struct wl_listener damage_frame;
@ -39,13 +49,19 @@ struct sway_output {
} events; } 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_output *output);
void output_begin_destroy(struct sway_output *output);
void output_destroy(struct sway_container *output); struct sway_output *output_from_wlr_output(struct wlr_output *output);
void output_begin_destroy(struct sway_container *output); struct sway_output *output_get_in_direction(struct sway_output *reference,
enum movement_direction direction);
struct sway_container *output_from_wlr_output(struct wlr_output *output); void output_add_workspace(struct sway_output *output,
struct sway_workspace *workspace);
typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, typedef void (*sway_surface_iterator_func_t)(struct sway_output *output,
struct wlr_surface *surface, struct wlr_box *box, float rotation, 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, void output_damage_whole_container(struct sway_output *output,
struct sway_container *con); 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); 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, void output_render(struct sway_output *output, struct timespec *when,
pixman_region32_t *damage); 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, struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
void *user_data); 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_container *con, void *data), void *data); void (*f)(struct sway_workspace *ws, void *data), void *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); void (*f)(struct sway_container *con, void *data), void *data);
struct sway_container *output_find_workspace(struct sway_container *output, struct sway_workspace *output_find_workspace(struct sway_output *output,
bool (*test)(struct sway_container *con, void *data), void *data); bool (*test)(struct sway_workspace *ws, void *data), void *data);
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); 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 #endif

@ -56,7 +56,7 @@ struct sway_server {
size_t txn_timeout_ms; size_t txn_timeout_ms;
list_t *transactions; list_t *transactions;
list_t *dirty_containers; list_t *dirty_nodes;
}; };
struct sway_server server; struct sway_server server;

@ -1,16 +1,19 @@
#ifndef _SWAY_ARRANGE_H #ifndef _SWAY_ARRANGE_H
#define _SWAY_ARRANGE_H #define _SWAY_ARRANGE_H
struct sway_output;
struct sway_workspace;
struct sway_container; struct sway_container;
struct sway_node;
void arrange_container(struct sway_container *container); 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_root(void);
void arrange_windows(struct sway_container *container); void arrange_node(struct sway_node *node);
#endif #endif

@ -5,8 +5,7 @@
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_surface.h>
#include "list.h" #include "list.h"
#include "sway/tree/node.h"
extern struct sway_container root_container;
struct sway_view; struct sway_view;
struct sway_seat; struct sway_seat;
@ -17,23 +16,6 @@ struct sway_seat;
#define TITLEBAR_H_PADDING 3 #define TITLEBAR_H_PADDING 3
#define TITLEBAR_V_PADDING 4 #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 { enum sway_container_layout {
L_NONE, L_NONE,
L_HORIZ, L_HORIZ,
@ -57,18 +39,14 @@ enum movement_direction;
enum wlr_direction; enum wlr_direction;
struct sway_container_state { struct sway_container_state {
// Container/swayc properties // Container properties
enum sway_container_layout layout; enum sway_container_layout layout;
double swayc_x, swayc_y; double con_x, con_y;
double swayc_width, swayc_height; double con_width, con_height;
bool is_fullscreen; bool is_fullscreen;
bool has_gaps; struct sway_workspace *workspace;
double current_gaps;
double gaps_inner;
double gaps_outer;
struct sway_container *parent; struct sway_container *parent;
list_t *children; list_t *children;
@ -86,35 +64,19 @@ struct sway_container_state {
bool border_left; bool border_left;
bool border_right; bool border_right;
bool using_csd; bool using_csd;
// Workspace properties
struct sway_container *ws_fullscreen;
list_t *ws_floating;
}; };
struct sway_container { struct sway_container {
union { struct sway_node node;
// TODO: Encapsulate state for other node types as well like C_CONTAINER struct sway_view *view;
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;
// The pending state is the main container properties, and the current state is in the below struct. // 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. // 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; 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 char *formatted_title; // The title displayed in the title bar
enum sway_container_type type;
enum sway_container_layout layout; enum sway_container_layout layout;
enum sway_container_layout prev_split_layout; enum sway_container_layout prev_split_layout;
@ -132,14 +94,13 @@ struct sway_container {
// The gaps currently applied to the container. // The gaps currently applied to the container.
double current_gaps; double current_gaps;
bool has_gaps; bool has_gaps;
double gaps_inner; double gaps_inner;
double gaps_outer; double gaps_outer;
list_t *children; struct sway_workspace *workspace; // NULL when hidden in the scratchpad
struct sway_container *parent; // NULL if container in root of workspace
struct sway_container *parent; list_t *children; // struct sway_container
// Outputs currently being intersected // Outputs currently being intersected
list_t *outputs; // struct sway_output list_t *outputs; // struct sway_output
@ -157,42 +118,17 @@ struct sway_container {
struct wlr_texture *title_urgent; struct wlr_texture *title_urgent;
size_t title_height; 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 {
struct wl_signal destroy; struct wl_signal destroy;
} events; } events;
}; };
struct sway_container *container_create(enum sway_container_type type); struct sway_container *container_create(struct sway_view *view);
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);
void container_destroy(struct sway_container *con); void container_destroy(struct sway_container *con);
void container_begin_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 * Search a container's descendants a container based on test criteria. Returns
* the first container that passes the test. * 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, struct sway_container *container_find_child(struct sway_container *container,
bool (*test)(struct sway_container *view, void *data), void *data); 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 * Find a container at the given coordinates. Returns the the surface and
* surface-local coordinates of the given layout coordinates if the container * surface-local coordinates of the given layout coordinates if the container
* is a view and the view contains a surface at those coordinates. * 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 lx, double ly, struct wlr_surface **surface,
double *sx, double *sy); double *sx, double *sy);
struct sway_container *tiling_container_at( 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); struct wlr_surface **surface, double *sx, double *sy);
void container_for_each_child(struct sway_container *container, 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, bool container_has_ancestor(struct sway_container *container,
struct sway_container *ancestor); 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_update_textures_recursive(struct sway_container *con);
void container_damage_whole(struct sway_container *container); 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); 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); void container_calculate_title_height(struct sway_container *container);
/** size_t container_build_representation(enum sway_container_layout layout,
* Notify a container that a tree modification has changed in its children, list_t *children, char *buffer);
* so the container can update its tree representation.
*/ void container_update_representation(struct sway_container *container);
void container_notify_subtree_changed(struct sway_container *container);
/** /**
* Return the height of a regular title bar. * 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. * Choose an output for the floating container's new position.
*/ */
struct sway_container *container_floating_find_output( struct sway_output *container_floating_find_output(struct sway_container *con);
struct sway_container *con);
/** /**
* Move a floating container to a new layout-local position. * 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); 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); 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); 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); int container_sibling_index(const struct sway_container *child);
void container_handle_fullscreen_reparent(struct sway_container *con, list_t *container_get_current_siblings(struct sway_container *container);
struct sway_container *old_parent);
void container_handle_fullscreen_reparent(struct sway_container *con);
void container_add_child(struct sway_container *parent, void container_add_child(struct sway_container *parent,
struct sway_container *child); struct sway_container *child);
@ -353,19 +278,16 @@ void container_add_child(struct sway_container *parent,
void container_insert_child(struct sway_container *parent, void container_insert_child(struct sway_container *parent,
struct sway_container *child, int i); struct sway_container *child, int i);
struct sway_container *container_add_sibling(struct sway_container *parent, void container_add_sibling(struct sway_container *parent,
struct sway_container *child); 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, void container_replace(struct sway_container *container,
struct sway_container *new_child); struct sway_container *replacement);
bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out); 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, struct sway_container *container_split(struct sway_container *child,
enum sway_container_layout layout); enum sway_container_layout layout);

@ -0,0 +1,74 @@
#ifndef _SWAY_NODE_H
#define _SWAY_NODE_H
#include <stdbool.h>
#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

@ -5,12 +5,14 @@
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/render/wlr_texture.h> #include <wlr/render/wlr_texture.h>
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/node.h"
#include "config.h" #include "config.h"
#include "list.h" #include "list.h"
extern struct sway_container root_container; extern struct sway_root *root;
struct sway_root { struct sway_root {
struct sway_node node;
struct wlr_output_layout *output_layout; struct wlr_output_layout *output_layout;
struct wl_listener output_layout_change; struct wl_listener output_layout_change;
@ -24,17 +26,21 @@ struct sway_root {
// Includes disabled outputs // Includes disabled outputs
struct wl_list all_outputs; // sway_output::link 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 *scratchpad; // struct sway_container
list_t *saved_workspaces; // For when there's no connected outputs list_t *saved_workspaces; // For when there's no connected outputs
struct { struct {
struct wl_signal new_container; struct wl_signal new_node;
} events; } 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. * 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); 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_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 *data);
void root_for_each_container(void (*f)(struct sway_container *con, void *data), void root_for_each_container(void (*f)(struct sway_container *con, void *data),
void *data); void *data);
struct sway_container *root_find_output( struct sway_output *root_find_output(
bool (*test)(struct sway_container *con, void *data), void *data); bool (*test)(struct sway_output *output, void *data), void *data);
struct sway_container *root_find_workspace( struct sway_workspace *root_find_workspace(
bool (*test)(struct sway_container *con, void *data), void *data); bool (*test)(struct sway_workspace *ws, void *data), void *data);
struct sway_container *root_find_container( struct sway_container *root_find_container(
bool (*test)(struct sway_container *con, void *data), void *data); bool (*test)(struct sway_container *con, void *data), void *data);
void root_get_box(struct sway_root *root, struct wlr_box *box);
#endif #endif

@ -58,7 +58,7 @@ struct sway_view {
enum sway_view_type type; enum sway_view_type type;
const struct sway_view_impl *impl; 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 struct wlr_surface *surface; // NULL for unmapped views
// Geometry of the view itself (excludes borders) in layout coordinates // 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); 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. * size, taking borders into consideration.
*/ */
void view_autoconfigure(struct sway_view *view); void view_autoconfigure(struct sway_view *view);

@ -3,66 +3,98 @@
#include <stdbool.h> #include <stdbool.h>
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/node.h"
struct sway_view; struct sway_view;
struct sway_workspace_state {
struct sway_container *fullscreen;
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_workspace {
struct sway_container *swayc; struct sway_node node;
struct sway_container *fullscreen; struct sway_container *fullscreen;
list_t *floating; // struct sway_container
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; list_t *output_priority;
bool urgent; bool urgent;
struct sway_workspace_state current;
}; };
extern char *prev_workspace_name; 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); 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); 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); 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, void workspace_output_raise_priority(struct sway_workspace *workspace,
struct sway_container *old_output, struct sway_container *new_output); struct sway_output *old_output, struct sway_output *new_output);
void workspace_output_add_priority(struct sway_container *workspace, void workspace_output_add_priority(struct sway_workspace *workspace,
struct sway_container *output); struct sway_output *output);
struct sway_container *workspace_output_get_highest_available( struct sway_output *workspace_output_get_highest_available(
struct sway_container *ws, struct sway_container *exclude); 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); 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); 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 will be the only direct tiling child of the workspace.
* The new container is returned. * 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_add_floating(struct sway_workspace *workspace,
struct sway_container *con); struct sway_container *con);
void workspace_remove_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_add_gaps(struct sway_container *ws); void workspace_get_box(struct sway_workspace *workspace, struct wlr_box *box);
#endif #endif

@ -212,6 +212,24 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
return res; 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) { struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
// Even though this function will process multiple commands we will only // 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 // 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) { if (!config->handler_context.using_criteria) {
// without criteria, the command acts upon the focused // without criteria, the command acts upon the focused
// container // container
config->handler_context.current_container = set_config_node(seat_get_focus_inactive(seat, &root->node));
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;
}
struct cmd_results *res = handler->handle(argc-1, argv+1); struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) { if (res->status != CMD_SUCCESS) {
free_argv(argc, argv); free_argv(argc, argv);
@ -314,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
} else { } else {
for (int i = 0; i < views->length; ++i) { for (int i = 0; i < views->length; ++i) {
struct sway_view *view = views->items[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); struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) { if (res->status != CMD_SUCCESS) {
free_argv(argc, argv); free_argv(argc, argv);

@ -13,13 +13,12 @@ struct cmd_results *cmd_border(int argc, char **argv) {
return error; return error;
} }
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container; if (!container->view) {
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "border", return cmd_results_new(CMD_INVALID, "border",
"Only views can have borders"); "Only views can have borders");
} }
struct sway_view *view = container->sway_view; struct sway_view *view = container->view;
if (strcmp(argv[0], "none") == 0) { if (strcmp(argv[0], "none") == 0) {
view->border = B_NONE; view->border = B_NONE;
@ -38,11 +37,11 @@ struct cmd_results *cmd_border(int argc, char **argv) {
view->border_thickness = atoi(argv[1]); view->border_thickness = atoi(argv[1]);
} }
if (container_is_floating(view->swayc)) { if (container_is_floating(view->container)) {
container_set_geometry_from_floating_view(view->swayc); 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); struct sway_seat *seat = input_manager_current_seat(input_manager);
if (seat->cursor) { if (seat->cursor) {

@ -15,24 +15,23 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) { if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
return error; return error;
} }
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container; struct sway_workspace *workspace = config->handler_context.workspace;
if (container->type == C_WORKSPACE && container->children->length == 0) { if (!container && workspace->tiling->length == 0) {
return cmd_results_new(CMD_INVALID, "floating", return cmd_results_new(CMD_INVALID, "floating",
"Can't float an empty workspace"); "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 // Wrap the workspace's children in a container so we can float it
struct sway_container *workspace = container; container = workspace_wrap_children(workspace);
container = workspace_wrap_children(container);
workspace->layout = L_HORIZ; 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, // If the container is in a floating split container,
// operate on the split container instead of the child. // operate on the split container instead of the child.
if (container_is_floating_or_child(container)) { if (container_is_floating_or_child(container)) {
while (container->parent->type != C_WORKSPACE) { while (container->parent) {
container = container->parent; container = container->parent;
} }
} }
@ -51,8 +50,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
container_set_floating(container, wants_floating); container_set_floating(container, wants_floating);
struct sway_container *workspace = container_parent(container, C_WORKSPACE); arrange_workspace(container->workspace);
arrange_windows(workspace);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

@ -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( static struct sway_node *get_node_in_output_direction(
struct sway_container *output, enum movement_direction dir, struct sway_output *output, enum movement_direction dir) {
struct sway_seat *seat) { struct sway_seat *seat = config->handler_context.seat;
if (!output) { struct sway_workspace *ws = output_get_active_workspace(output);
return NULL; if (ws->fullscreen) {
} return seat_get_focus_inactive(seat, &ws->fullscreen->node);
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;
} }
struct sway_container *container = NULL;
if (ws->children->length > 0) { if (ws->tiling->length > 0) {
switch (dir) { switch (dir) {
case MOVE_LEFT: case MOVE_LEFT:
if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
// get most right child of new output // get most right child of new output
return ws->children->items[ws->children->length-1]; container = ws->tiling->items[ws->tiling->length-1];
} else { } else {
return seat_get_focus_inactive(seat, ws); container = seat_get_focus_inactive_tiling(seat, ws);
} }
return &container->node;
case MOVE_RIGHT: case MOVE_RIGHT:
if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
// get most left child of new output // get most left child of new output
return ws->children->items[0]; container = ws->tiling->items[0];
} else { } else {
return seat_get_focus_inactive(seat, ws); container = seat_get_focus_inactive_tiling(seat, ws);
} }
return &container->node;
case MOVE_UP: case MOVE_UP:
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);
}
return &container->node;
case MOVE_DOWN: { case MOVE_DOWN: {
struct sway_container *focused = if (ws->layout == L_VERT || ws->layout == L_STACKED) {
seat_get_focus_inactive(seat, ws); // get most top child of new output
if (focused && focused->parent) { container = ws->tiling->items[0];
struct sway_container *parent = focused->parent; } else {
if (parent->layout == L_VERT) { container = seat_get_focus_inactive_tiling(seat, ws);
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;
} }
break; return &container->node;
} }
default: default:
break; break;
} }
} }
return ws; return &ws->node;
} }
static struct sway_container *container_get_in_direction( static struct sway_node *node_get_in_direction(struct sway_container *container,
struct sway_container *container, struct sway_seat *seat, struct sway_seat *seat, enum movement_direction dir) {
enum movement_direction dir) {
struct sway_container *parent = container->parent;
if (dir == MOVE_CHILD) { if (dir == MOVE_CHILD) {
return seat_get_focus_inactive(seat, container); return seat_get_active_child(seat, &container->node);
} }
if (container->is_fullscreen) { if (container->is_fullscreen) {
if (dir == MOVE_PARENT) { if (dir == MOVE_PARENT) {
return NULL; return NULL;
} }
container = container_parent(container, C_OUTPUT); // Fullscreen container with a direction - go straight to outputs
parent = container->parent; struct sway_output *output = container->workspace->output;
} else { struct sway_output *new_output = output_get_in_direction(output, dir);
if (dir == MOVE_PARENT) { return get_node_in_output_direction(new_output, dir);
if (parent->type == C_OUTPUT || container_is_floating(container)) { }
return NULL; if (dir == MOVE_PARENT) {
} else { return node_get_parent(&container->node);
return parent;
}
}
} }
struct sway_container *wrap_candidate = NULL; struct sway_container *wrap_candidate = NULL;
while (true) { struct sway_container *current = container;
while (current) {
bool can_move = false; bool can_move = false;
int desired; int desired;
int idx = list_find(container->parent->children, container); int idx = container_sibling_index(current);
if (idx == -1) { enum sway_container_layout parent_layout =
return NULL; container_parent_layout(current);
} list_t *siblings = container_get_siblings(current);
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);
if (!adjacent || adjacent == container) { if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
if (!wrap_candidate) { if (parent_layout == L_HORIZ || parent_layout == L_TABBED) {
return NULL; can_move = true;
} desired = idx + (dir == MOVE_LEFT ? -1 : 1);
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;
} }
} else { } else {
if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { if (parent_layout == L_VERT || parent_layout == L_STACKED) {
if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { can_move = true;
can_move = true; desired = idx + (dir == MOVE_UP ? -1 : 1);
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 (can_move) { if (can_move) {
// TODO handle floating if (desired < 0 || desired >= siblings->length) {
if (desired < 0 || desired >= parent->children->length) {
can_move = false; can_move = false;
int len = parent->children->length; int len = siblings->length;
if (config->focus_wrapping != WRAP_NO && !wrap_candidate if (config->focus_wrapping != WRAP_NO && !wrap_candidate
&& len > 1) { && len > 1) {
if (desired < 0) { if (desired < 0) {
wrap_candidate = parent->children->items[len-1]; wrap_candidate = siblings->items[len-1];
} else { } else {
wrap_candidate = parent->children->items[0]; wrap_candidate = siblings->items[0];
} }
if (config->focus_wrapping == WRAP_FORCE) { if (config->focus_wrapping == WRAP_FORCE) {
return seat_get_focus_inactive_view(seat, struct sway_container *c = seat_get_focus_inactive_view(
wrap_candidate); seat, &wrap_candidate->node);
return &c->node;
} }
} }
} else { } else {
struct sway_container *desired_con = struct sway_container *desired_con = siblings->items[desired];
parent->children->items[desired]; struct sway_container *c = seat_get_focus_inactive_view(
wlr_log(WLR_DEBUG, seat, &desired_con->node);
"cont %d-%p dir %i sibling %d: %p", idx, return &c->node;
container, dir, desired, desired_con);
return seat_get_focus_inactive_view(seat, desired_con);
} }
} }
if (!can_move) { current = current->parent;
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);
}
}
} }
}
static struct cmd_results *focus_mode(struct sway_container *con, // Check a different output
struct sway_seat *seat, bool floating) { struct sway_output *output = container->workspace->output;
struct sway_container *ws = con->type == C_WORKSPACE ? struct sway_output *new_output = output_get_in_direction(output, dir);
con : container_parent(con, C_WORKSPACE); if (new_output) {
return get_node_in_output_direction(new_output, dir);
// 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;
}
} }
return NULL;
}
static struct cmd_results *focus_mode(struct sway_workspace *ws,
struct sway_seat *seat, bool floating) {
struct sway_container *new_focus = NULL; struct sway_container *new_focus = NULL;
if (floating) { if (floating) {
new_focus = seat_get_focus_inactive_floating(seat, ws); 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); new_focus = seat_get_focus_inactive_tiling(seat, ws);
} }
if (new_focus) { if (new_focus) {
seat_set_focus(seat, new_focus); seat_set_focus(seat, &new_focus->node);
} else { } else {
return cmd_results_new(CMD_FAILURE, "focus", return cmd_results_new(CMD_FAILURE, "focus",
"Failed to find a %s container in workspace", "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); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static struct cmd_results *focus_output(struct sway_container *con, static struct cmd_results *focus_output(struct sway_seat *seat,
struct sway_seat *seat, int argc, char **argv) { int argc, char **argv) {
if (!argc) { if (!argc) {
return cmd_results_new(CMD_INVALID, "focus", return cmd_results_new(CMD_INVALID, "focus",
"Expected 'focus output <direction|name>'"); "Expected 'focus output <direction|name>'");
} }
char *identifier = join_args(argv, argc); char *identifier = join_args(argv, argc);
struct sway_container *output = output_by_name(identifier); struct sway_output *output = output_by_name(identifier);
if (!output) { if (!output) {
enum movement_direction direction; 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", return cmd_results_new(CMD_INVALID, "focus",
"There is no output with that name"); "There is no output with that name");
} }
struct sway_container *focus = seat_get_focus(seat); struct sway_workspace *ws = seat_get_focused_workspace(seat);
focus = container_parent(focus, C_OUTPUT); output = output_get_in_direction(ws->output, direction);
output = container_get_in_direction(focus, seat, direction);
} }
free(identifier); free(identifier);
if (output) { 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); 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) { if (config->reading || !config->active) {
return cmd_results_new(CMD_DEFER, NULL, NULL); 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; 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", return cmd_results_new(CMD_FAILURE, "focus",
"Command 'focus' cannot be used above the workspace level"); "Command 'focus' cannot be used above the workspace level");
} }
if (argc == 0) { if (argc == 0) {
seat_set_focus(seat, con); seat_set_focus(seat, node);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
if (strcmp(argv[0], "floating") == 0) { 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) { } 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) { } 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) { if (strcmp(argv[0], "output") == 0) {
argc--; argv++; argc--; argv++;
return focus_output(con, seat, argc, argv); return focus_output(seat, argc, argv);
} }
enum movement_direction direction = 0; enum movement_direction direction = 0;
@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
"or 'focus output <direction|name>'"); "or 'focus output <direction|name>'");
} }
struct sway_container *next_focus = container_get_in_direction( if (node->type == N_WORKSPACE) {
con, seat, direction); // 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) { if (next_focus) {
seat_set_focus(seat, next_focus); seat_set_focus(seat, next_focus);
} }

@ -12,18 +12,18 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) { if ((error = checkarg(argc, "fullscreen", EXPECTED_LESS_THAN, 2))) {
return error; return error;
} }
struct sway_container *container = struct sway_node *node = config->handler_context.node;
config->handler_context.current_container; struct sway_container *container = config->handler_context.container;
if (container->type == C_WORKSPACE && container->children->length == 0) { struct sway_workspace *workspace = config->handler_context.workspace;
if (node->type == N_WORKSPACE && workspace->tiling->length == 0) {
return cmd_results_new(CMD_INVALID, "fullscreen", return cmd_results_new(CMD_INVALID, "fullscreen",
"Can't fullscreen an empty workspace"); "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 // Wrap the workspace's children in a container so we can fullscreen it
struct sway_container *workspace = container; container = workspace_wrap_children(workspace);
container = workspace_wrap_children(container);
workspace->layout = L_HORIZ; 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; bool enable = !container->is_fullscreen;
@ -32,9 +32,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
} }
container_set_fullscreen(container, enable); container_set_fullscreen(container, enable);
arrange_workspace(workspace);
struct sway_container *workspace = container_parent(container, C_WORKSPACE);
arrange_windows(workspace->parent);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

@ -2,6 +2,7 @@
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/workspace.h"
#include "log.h" #include "log.h"
#include "stringop.h" #include "stringop.h"
#include <math.h> #include <math.h>
@ -43,7 +44,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "gaps", return cmd_results_new(CMD_INVALID, "gaps",
"gaps edge_gaps on|off|toggle"); "gaps edge_gaps on|off|toggle");
} }
arrange_windows(&root_container); arrange_root();
} else { } else {
int amount_idx = 0; // the current index in argv int amount_idx = 0; // the current index in argv
enum gaps_op op = GAPS_OP_SET; 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 <amount> if (amount_idx == 0) { // gaps <amount>
config->gaps_inner = val; config->gaps_inner = val;
config->gaps_outer = val; config->gaps_outer = val;
arrange_windows(&root_container); arrange_root();
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
// Other variants. The middle-length variant (gaps inner|outer <amount>) // Other variants. The middle-length variant (gaps inner|outer <amount>)
@ -155,21 +156,27 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
} else { } else {
config->gaps_outer = total; config->gaps_outer = total;
} }
arrange_windows(&root_container); arrange_root();
} else { } else {
struct sway_container *c = if (scope == GAPS_SCOPE_WORKSPACE) {
config->handler_context.current_container; struct sway_workspace *ws = config->handler_context.workspace;
if (scope == GAPS_SCOPE_WORKSPACE && c->type != C_WORKSPACE) { ws->has_gaps = true;
c = container_parent(c, C_WORKSPACE); if (inner) {
} ws->gaps_inner = total;
c->has_gaps = true; } else {
if (inner) { ws->gaps_outer = total;
c->gaps_inner = total; }
arrange_workspace(ws);
} else { } 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);
} }
} }

@ -5,8 +5,8 @@
#include "sway/tree/view.h" #include "sway/tree/view.h"
static void _configure_view(struct sway_container *con, void *data) { static void _configure_view(struct sway_container *con, void *data) {
if (con->type == C_VIEW) { if (con->view) {
view_autoconfigure(con->sway_view); view_autoconfigure(con->view);
} }
} }

@ -2,15 +2,27 @@
#include "log.h" #include "log.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/tree/view.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "sway/commands.h" #include "sway/commands.h"
static void close_container_iterator(struct sway_container *con, void *data) {
if (con->view) {
view_close(con->view);
}
}
struct cmd_results *cmd_kill(int argc, char **argv) { struct cmd_results *cmd_kill(int argc, char **argv) {
struct sway_container *con = struct sway_container *con = config->handler_context.container;
config->handler_context.current_container; struct sway_workspace *ws = config->handler_context.workspace;
container_close(con); 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); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

@ -4,21 +4,20 @@
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/workspace.h"
#include "log.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) { if (strcasecmp(s, "splith") == 0) {
*ptr = L_HORIZ; return L_HORIZ;
} else if (strcasecmp(s, "splitv") == 0) { } else if (strcasecmp(s, "splitv") == 0) {
*ptr = L_VERT; return L_VERT;
} else if (strcasecmp(s, "tabbed") == 0) { } else if (strcasecmp(s, "tabbed") == 0) {
*ptr = L_TABBED; return L_TABBED;
} else if (strcasecmp(s, "stacking") == 0) { } else if (strcasecmp(s, "stacking") == 0) {
*ptr = L_STACKED; return L_STACKED;
} else {
return false;
} }
return true; return L_NONE;
} }
static const char* expected_syntax = static const char* expected_syntax =
@ -26,84 +25,129 @@ static const char* expected_syntax =
"'layout toggle [split|all]' or " "'layout toggle [split|all]' or "
"'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'"; "'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 *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error; 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", return cmd_results_new(CMD_FAILURE, "layout",
"Unable to change layout of floating windows"); "Unable to change layout of floating windows");
} }
while (parent->type == C_VIEW) { // Typically we change the layout of the current container, but if the
parent = parent->parent; // 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; // We could be working with a container OR a workspace. These are different
bool assigned_directly = parse_layout_string(argv[0], &parent->layout); // structures, so we set up pointers to they layouts so we can refer them in
if (!assigned_directly) { // an abstract way.
if (strcasecmp(argv[0], "default") == 0) { enum sway_container_layout new_layout = L_NONE;
parent->layout = parent->prev_split_layout; enum sway_container_layout old_layout = L_NONE;
} else if (strcasecmp(argv[0], "toggle") == 0) { if (container) {
if (argc == 1) { old_layout = container->layout;
parent->layout = new_layout = get_layout(argc, argv,
parent->layout == L_STACKED ? L_TABBED : container->layout, container->prev_split_layout);
parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED; } else {
} else if (argc == 2) { old_layout = workspace->layout;
if (strcasecmp(argv[1], "all") == 0) { new_layout = get_layout(argc, argv,
parent->layout = workspace->layout, workspace->prev_split_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
}
}
} else {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
}
} }
if (parent->layout == L_NONE) { if (new_layout == L_NONE) {
parent->layout = container_get_default_layout(parent); return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
} }
if (prev != parent->layout) { if (new_layout != old_layout) {
if (prev != L_TABBED && prev != L_STACKED) { if (container) {
parent->prev_split_layout = prev; 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 {
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);
} }
container_notify_subtree_changed(parent);
arrange_windows(parent->parent);
} }
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);

@ -18,13 +18,12 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) { if ((error = checkarg(argc, "mark", EXPECTED_AT_LEAST, 1))) {
return error; return error;
} }
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container; if (!container->view) {
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "mark", return cmd_results_new(CMD_INVALID, "mark",
"Only views can have marks"); "Only views can have marks");
} }
struct sway_view *view = container->sway_view; struct sway_view *view = container->view;
bool add = false, toggle = false; bool add = false, toggle = false;
while (argc > 0 && strncmp(*argv, "--", 2) == 0) { while (argc > 0 && strncmp(*argv, "--", 2) == 0) {

File diff suppressed because it is too large Load Diff

@ -19,8 +19,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) {
return error; return error;
} }
struct sway_container *con = struct sway_container *con = config->handler_context.container;
config->handler_context.current_container;
float opacity = 0.0f; float opacity = 0.0f;

@ -42,7 +42,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
} }
list_free(bar_ids); list_free(bar_ids);
arrange_windows(&root_container); arrange_root();
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

@ -25,14 +25,11 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
} }
int argn = 1; int argn = 1;
struct sway_container *workspace; struct sway_workspace *workspace = NULL;
if (strcasecmp(argv[1], "to") == 0) { if (strcasecmp(argv[1], "to") == 0) {
// 'rename workspace to new_name' // 'rename workspace to new_name'
workspace = config->handler_context.current_container; workspace = config->handler_context.workspace;
if (workspace->type != C_WORKSPACE) {
workspace = container_parent(workspace, C_WORKSPACE);
}
} else if (strcasecmp(argv[1], "number") == 0) { } else if (strcasecmp(argv[1], "number") == 0) {
// 'rename workspace number x to new_name' // 'rename workspace number x to new_name'
if (!isdigit(argv[2][0])) { 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", return cmd_results_new(CMD_INVALID, "rename",
"Cannot use special workspace name '%s'", argv[argn]); "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) { if (tmp_workspace) {
free(new_name); free(new_name);
return cmd_results_new(CMD_INVALID, "rename", return cmd_results_new(CMD_INVALID, "rename",
@ -89,7 +86,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
free(workspace->name); free(workspace->name);
workspace->name = new_name; workspace->name = new_name;
output_sort_workspaces(workspace->parent); output_sort_workspaces(workspace->output);
ipc_event_workspace(NULL, workspace, "rename"); ipc_event_workspace(NULL, workspace, "rename");
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);

@ -10,6 +10,7 @@
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "log.h" #include "log.h"
static const int MIN_SANE_W = 100, MIN_SANE_H = 60; 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, static void calculate_constraints(int *min_width, int *max_width,
int *min_height, int *max_height) { 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 if (config->floating_minimum_width == -1) { // no minimum
*min_width = 0; *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 if (config->floating_maximum_width == -1) { // no maximum
*max_width = INT_MAX; *max_width = INT_MAX;
} else if (config->floating_maximum_width == 0) { // automatic } else if (config->floating_maximum_width == 0) { // automatic
struct sway_container *ws = container_parent(con, C_WORKSPACE); *max_width = con->workspace->width;
*max_width = ws->width;
} else { } else {
*max_width = config->floating_maximum_width; *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 if (config->floating_maximum_height == -1) { // no maximum
*max_height = INT_MAX; *max_height = INT_MAX;
} else if (config->floating_maximum_height == 0) { // automatic } else if (config->floating_maximum_height == 0) { // automatic
struct sway_container *ws = container_parent(con, C_WORKSPACE); *max_height = con->workspace->height;
*max_height = ws->height;
} else { } else {
*max_height = config->floating_maximum_height; *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; normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
int minor_weight = 0; int minor_weight = 0;
int major_weight = 0; int major_weight = 0;
while (parent->parent) { while (parent) {
struct sway_container *next = parent->parent; list_t *siblings = container_get_siblings(parent);
if (next->layout == parallel_layout) { if (container_parent_layout(parent) == parallel_layout) {
for (int i = 0; i < next->children->length; i++) { for (int i = 0; i < siblings->length; i++) {
struct sway_container *sibling = next->children->items[i]; struct sway_container *sibling = siblings->items[i];
int sibling_pos = parallel_coord(sibling, axis); int sibling_pos = parallel_coord(sibling, axis);
int focused_pos = parallel_coord(focused, axis); int focused_pos = parallel_coord(focused, axis);
@ -213,17 +212,13 @@ static void resize_tiled(struct sway_container *parent, int amount,
break; break;
} }
} }
parent = next; parent = parent->parent;
} }
if (!parent) {
if (parent->type == C_ROOT) { // Can't resize in this direction
return; 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, // Implement up/down/left/right direction by zeroing one of the weights,
// then setting the axis to be horizontal or vertical // then setting the axis to be horizontal or vertical
if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) { 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 //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++) { for (int i = 0; i < siblings->length; i++) {
struct sway_container *sibling = parent->parent->children->items[i]; struct sway_container *sibling = siblings->items[i];
int sibling_pos = parallel_coord(sibling, axis); int sibling_pos = parallel_coord(sibling, axis);
int focused_pos = parallel_coord(focused, 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 ? enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM; WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM;
for (int i = 0; i < parent->parent->children->length; i++) { for (int i = 0; i < siblings->length; i++) {
struct sway_container *sibling = parent->parent->children->items[i]; struct sway_container *sibling = siblings->items[i];
int sibling_pos = parallel_coord(sibling, axis); int sibling_pos = parallel_coord(sibling, axis);
int focused_pos = parallel_coord(focused, 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, 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, static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
struct resize_amount *amount) { 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; int grow_width = 0, grow_height = 0;
switch (axis) { switch (axis) {
case RESIZE_AXIS_HORIZONTAL: case RESIZE_AXIS_HORIZONTAL:
@ -400,15 +400,15 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
con->width += grow_width; con->width += grow_width;
con->height += grow_height; con->height += grow_height;
if (con->type == C_VIEW) { if (con->view) {
struct sway_view *view = con->sway_view; struct sway_view *view = con->view;
view->x += grow_x; view->x += grow_x;
view->y += grow_y; view->y += grow_y;
view->width += grow_width; view->width += grow_width;
view->height += grow_height; view->height += grow_height;
} }
arrange_windows(con); arrange_container(con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); 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, static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
struct resize_amount *amount) { 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) { if (amount->unit == RESIZE_UNIT_DEFAULT) {
amount->unit = RESIZE_UNIT_PPT; 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) { width->unit == RESIZE_UNIT_DEFAULT) {
// Convert to px // Convert to px
struct sway_container *parent = con->parent; struct sway_container *parent = con->parent;
while (parent->type >= C_WORKSPACE && parent->layout != L_HORIZ) { while (parent && parent->layout != L_HORIZ) {
parent = parent->parent; parent = parent->parent;
} }
if (parent->type >= C_WORKSPACE) { if (parent) {
width->amount = parent->width * width->amount / 100; 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) { if (width->unit == RESIZE_UNIT_PX) {
resize_tiled(con, width->amount - con->width, 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) { height->unit == RESIZE_UNIT_DEFAULT) {
// Convert to px // Convert to px
struct sway_container *parent = con->parent; struct sway_container *parent = con->parent;
while (parent->type >= C_WORKSPACE && parent->layout != L_VERT) { while (parent && parent->layout != L_VERT) {
parent = parent->parent; parent = parent->parent;
} }
if (parent->type >= C_WORKSPACE) { if (parent) {
height->amount = parent->height * height->amount / 100; 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) { if (height->unit == RESIZE_UNIT_PX) {
resize_tiled(con, height->amount - con->height, 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->width = width->amount;
con->height = height->amount; con->height = height->amount;
if (con->type == C_VIEW) { if (con->view) {
struct sway_view *view = con->sway_view; struct sway_view *view = con->view;
view->x -= grow_width / 2; view->x -= grow_width / 2;
view->y -= grow_height / 2; view->y -= grow_height / 2;
view->width += grow_width; view->width += grow_width;
view->height += grow_height; view->height += grow_height;
} }
arrange_windows(con); arrange_container(con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); 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 // 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) { if (width.amount <= 0) {
width.amount = con->width; width.amount = con->width;
} }
@ -624,7 +628,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
first_amount.amount *= multiplier; first_amount.amount *= multiplier;
second_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)) { if (container_is_floating(con)) {
// Floating containers can only resize in px. Choose an amount which // Floating containers can only resize in px. Choose an amount which
// uses px, with fallback to an amount that specified no unit. // 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 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) { if (!current) {
return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing"); 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; struct cmd_results *error;
if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {

@ -9,36 +9,34 @@
static void scratchpad_toggle_auto(void) { static void scratchpad_toggle_auto(void) {
struct sway_seat *seat = input_manager_current_seat(input_manager); 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);
struct sway_container *ws = focus->type == C_WORKSPACE ? struct sway_workspace *ws = seat_get_focused_workspace(seat);
focus : container_parent(focus, C_WORKSPACE);
// If the focus is in a floating split container, // If the focus is in a floating split container,
// operate on the split container instead of the child. // operate on the split container instead of the child.
if (container_is_floating_or_child(focus)) { if (focus && container_is_floating_or_child(focus)) {
while (focus->parent->type != C_WORKSPACE) { while (focus->parent) {
focus = focus->parent; focus = focus->parent;
} }
} }
// Check if the currently focused window is a scratchpad window and should // Check if the currently focused window is a scratchpad window and should
// be hidden again. // be hidden again.
if (focus->scratchpad) { if (focus && focus->scratchpad) {
wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s",
focus->name); focus->title);
root_scratchpad_hide(focus); root_scratchpad_hide(focus);
return; return;
} }
// Check if there is an unfocused scratchpad window on the current workspace // Check if there is an unfocused scratchpad window on the current workspace
// and focus it. // and focus it.
for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { for (int i = 0; i < ws->floating->length; ++i) {
struct sway_container *floater = ws->sway_workspace->floating->items[i]; struct sway_container *floater = ws->floating->items[i];
if (floater->scratchpad && focus != floater) { if (floater->scratchpad && focus != floater) {
wlr_log(WLR_DEBUG, wlr_log(WLR_DEBUG,
"Focusing other scratchpad window (%s) in this workspace", "Focusing other scratchpad window (%s) in this workspace",
floater->name); floater->title);
root_scratchpad_show(floater); root_scratchpad_show(floater);
return; return;
} }
@ -46,25 +44,23 @@ static void scratchpad_toggle_auto(void) {
// Check if there is a visible scratchpad window on another workspace. // Check if there is a visible scratchpad window on another workspace.
// In this case we move it to the current workspace. // In this case we move it to the current workspace.
for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { for (int i = 0; i < root->scratchpad->length; ++i) {
struct sway_container *con = struct sway_container *con = root->scratchpad->items[i];
root_container.sway_root->scratchpad->items[i];
if (con->parent) { if (con->parent) {
wlr_log(WLR_DEBUG, wlr_log(WLR_DEBUG,
"Moving a visible scratchpad window (%s) to this workspace", "Moving a visible scratchpad window (%s) to this workspace",
con->name); con->title);
root_scratchpad_show(con); root_scratchpad_show(con);
return; return;
} }
} }
// Take the container at the bottom of the scratchpad list // Take the container at the bottom of the scratchpad list
if (!sway_assert(root_container.sway_root->scratchpad->length, if (!sway_assert(root->scratchpad->length, "Scratchpad is empty")) {
"Scratchpad is empty")) {
return; return;
} }
struct sway_container *con = root_container.sway_root->scratchpad->items[0]; struct sway_container *con = root->scratchpad->items[0];
wlr_log(WLR_DEBUG, "Showing %s from list", con->name); wlr_log(WLR_DEBUG, "Showing %s from list", con->title);
root_scratchpad_show(con); 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. // Check if it matches a currently visible scratchpad window and hide it.
if (con->parent) { if (con->workspace) {
root_scratchpad_hide(con); root_scratchpad_hide(con);
return; return;
} }
@ -91,18 +87,18 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "scratchpad", return cmd_results_new(CMD_INVALID, "scratchpad",
"Expected 'scratchpad show'"); "Expected 'scratchpad show'");
} }
if (!root_container.sway_root->scratchpad->length) { if (!root->scratchpad->length) {
return cmd_results_new(CMD_INVALID, "scratchpad", return cmd_results_new(CMD_INVALID, "scratchpad",
"Scratchpad is empty"); "Scratchpad is empty");
} }
if (config->handler_context.using_criteria) { 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, // If the container is in a floating split container,
// operate on the split container instead of the child. // operate on the split container instead of the child.
if (container_is_floating_or_child(con)) { if (container_is_floating_or_child(con)) {
while (con->parent->type != C_WORKSPACE) { while (con->parent) {
con = con->parent; con = con->parent;
} }
} }

@ -42,8 +42,8 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
} }
// map absolute coords (0..1,0..1) to root container coords // map absolute coords (0..1,0..1) to root container coords
float x = strtof(argv[1], NULL) / root_container.width; float x = strtof(argv[1], NULL) / root->width;
float y = strtof(argv[2], NULL) / root_container.height; float y = strtof(argv[2], NULL) / root->height;
wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
cursor_send_pointer_motion(cursor, 0, true); cursor_send_pointer_motion(cursor, 0, true);
} else { } else {

@ -11,8 +11,8 @@
#include "util.h" #include "util.h"
static void rebuild_marks_iterator(struct sway_container *con, void *data) { static void rebuild_marks_iterator(struct sway_container *con, void *data) {
if (con->type == C_VIEW) { if (con->view) {
view_update_marks_textures(con->sway_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); root_for_each_container(rebuild_marks_iterator, NULL);
} }
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *con = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
output_damage_whole(con->sway_output); output_damage_whole(output);
} }
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);

@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
"Expected 'smart_gaps <on|off>' "); "Expected 'smart_gaps <on|off>' ");
} }
arrange_windows(&root_container); arrange_root();
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

@ -4,15 +4,21 @@
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "log.h" #include "log.h"
static struct cmd_results *do_split(int layout) { static struct cmd_results *do_split(int layout) {
struct sway_container *con = config->handler_context.current_container; struct sway_container *con = config->handler_context.container;
struct sway_container *parent = container_split(con, layout); struct sway_workspace *ws = config->handler_context.workspace;
container_create_notify(parent); if (con) {
arrange_windows(parent->parent); container_split(con, layout);
} else {
workspace_split(ws, layout);
}
arrange_workspace(ws);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); 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); return do_split(L_HORIZ);
} else if (strcasecmp(argv[0], "t") == 0 || } else if (strcasecmp(argv[0], "t") == 0 ||
strcasecmp(argv[0], "toggle") == 0) { strcasecmp(argv[0], "toggle") == 0) {
struct sway_container *focused = struct sway_container *focused = config->handler_context.container;
config->handler_context.current_container;
if (focused->parent->layout == L_VERT) { if (focused && container_parent_layout(focused) == L_VERT) {
return do_split(L_HORIZ); return do_split(L_HORIZ);
} else { } else {
return do_split(L_VERT); return do_split(L_VERT);
@ -66,9 +71,9 @@ struct cmd_results *cmd_splitt(int argc, char **argv) {
return error; 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); return do_split(L_HORIZ);
} else { } else {
return do_split(L_VERT); return do_split(L_VERT);

@ -15,8 +15,7 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) { if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
return error; return error;
} }
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container;
if (!container_is_floating(container)) { if (!container_is_floating(container)) {
return cmd_results_new(CMD_FAILURE, "sticky", return cmd_results_new(CMD_FAILURE, "sticky",
"Can't set sticky on a tiled container"); "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; container->is_sticky = wants_sticky;
if (wants_sticky) { if (wants_sticky) {
// move container to focused workspace // move container to active workspace
struct sway_container *output = container_parent(container, C_OUTPUT); struct sway_workspace *active_workspace =
struct sway_seat *seat = input_manager_current_seat(input_manager); output_get_active_workspace(container->workspace->output);
struct sway_container *focus = seat_get_focus_inactive(seat, output); if (container->workspace != active_workspace) {
struct sway_container *focused_workspace = container_parent(focus, C_WORKSPACE); struct sway_workspace *old_workspace = container->workspace;
struct sway_container *current_workspace = container_parent(container, C_WORKSPACE); container_detach(container);
if (current_workspace != focused_workspace) { workspace_add_floating(active_workspace, container);
container_remove_child(container); container_handle_fullscreen_reparent(container);
workspace_add_floating(focused_workspace, container); arrange_workspace(active_workspace);
container_handle_fullscreen_reparent(container, current_workspace); workspace_consider_destroy(old_workspace);
arrange_windows(focused_workspace);
if (!container_reap_empty(current_workspace)) {
arrange_windows(current_workspace);
}
} }
} }

@ -4,6 +4,7 @@
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/output.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/view.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 *con2, struct sway_seat *seat,
struct sway_container *focus) { struct sway_container *focus) {
if (focus == con1 || focus == con2) { if (focus == con1 || focus == con2) {
struct sway_container *ws1 = container_parent(con1, C_WORKSPACE); struct sway_workspace *ws1 = con1->workspace;
struct sway_container *ws2 = container_parent(con2, C_WORKSPACE); struct sway_workspace *ws2 = con2->workspace;
if (focus == con1 && (con2->parent->layout == L_TABBED enum sway_container_layout layout1 = container_parent_layout(con1);
|| con2->parent->layout == L_STACKED)) { enum sway_container_layout layout2 = container_parent_layout(con2);
if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) {
if (workspace_is_visible(ws2)) { 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); seat_set_focus(seat, ws1 != ws2 ? &con2->node : &con1->node);
} else if (focus == con2 && (con1->parent->layout == L_TABBED } else if (focus == con2 && (layout1 == L_TABBED
|| con1->parent->layout == L_STACKED)) { || layout1 == L_STACKED)) {
if (workspace_is_visible(ws1)) { 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) { } else if (ws1 != ws2) {
seat_set_focus(seat, focus == con1 ? con2 : con1); seat_set_focus(seat, focus == con1 ? &con2->node : &con1->node);
} else { } else {
seat_set_focus(seat, focus); seat_set_focus(seat, &focus->node);
} }
} else { } 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")) { if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
return; 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) if (!sway_assert(!container_has_ancestor(con1, con2)
&& !container_has_ancestor(con2, con1), && !container_has_ancestor(con2, con1),
"Cannot swap ancestor and descendant")) { "Cannot swap ancestor and descendant")) {
@ -87,10 +85,11 @@ static void container_swap(struct sway_container *con1,
return; 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; bool fs1 = con1->is_fullscreen;
int fs2 = con2->is_fullscreen; bool fs2 = con2->is_fullscreen;
if (fs1) { if (fs1) {
container_set_fullscreen(con1, false); 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_seat *seat = input_manager_get_default_seat(input_manager);
struct sway_container *focus = seat_get_focus(seat); struct sway_container *focus = seat_get_focused_container(seat);
struct sway_container *vis1 = container_parent( struct sway_workspace *vis1 =
seat_get_focus_inactive(seat, container_parent(con1, C_OUTPUT)), output_get_active_workspace(con1->workspace->output);
C_WORKSPACE); struct sway_workspace *vis2 =
struct sway_container *vis2 = container_parent( output_get_active_workspace(con2->workspace->output);
seat_get_focus_inactive(seat, container_parent(con2, C_OUTPUT)),
C_WORKSPACE);
char *stored_prev_name = NULL; char *stored_prev_name = NULL;
if (prev_workspace_name) { if (prev_workspace_name) {
@ -115,10 +112,10 @@ static void container_swap(struct sway_container *con1,
swap_places(con1, con2); swap_places(con1, con2);
if (!workspace_is_visible(vis1)) { 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)) { 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); 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) { 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) { static bool test_id(struct sway_container *container, void *id) {
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
xcb_window_t *wid = id; xcb_window_t *wid = id;
return (container->type == C_VIEW return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
&& container->sway_view->type == SWAY_VIEW_XWAYLAND && container->view->wlr_xwayland_surface->window_id == *wid);
&& container->sway_view->wlr_xwayland_surface->window_id == *wid);
#else #else
return false; return false;
#endif #endif
} }
static bool test_mark(struct sway_container *container, void *mark) { static bool test_mark(struct sway_container *container, void *mark) {
if (container->type == C_VIEW && container->sway_view->marks->length) { if (container->view && container->view->marks->length) {
return !list_seq_find(container->sway_view->marks, return !list_seq_find(container->view->marks,
(int (*)(const void *, const void *))strcmp, mark); (int (*)(const void *, const void *))strcmp, mark);
} }
return false; return false;
@ -169,7 +165,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); 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; struct sway_container *other;
char *value = join_args(argv + 3, argc - 3); char *value = join_args(argv + 3, argc - 3);
@ -191,7 +187,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
if (!other) { if (!other) {
error = cmd_results_new(CMD_FAILURE, "swap", error = cmd_results_new(CMD_FAILURE, "swap",
"Failed to find %s '%s'", argv[2], value); "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", error = cmd_results_new(CMD_FAILURE, "swap",
"Can only swap with containers and views"); "Can only swap with containers and views");
} else if (container_has_ancestor(current, other) } else if (container_has_ancestor(current, other)
@ -211,9 +207,9 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
container_swap(current, other); container_swap(current, other);
arrange_windows(current->parent); arrange_node(node_get_parent(&current->node));
if (other->parent != current->parent) { if (node_get_parent(&other->node) != node_get_parent(&current->node)) {
arrange_windows(other->parent); arrange_node(node_get_parent(&other->node));
} }
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);

@ -11,13 +11,12 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) { if ((error = checkarg(argc, "title_format", EXPECTED_AT_LEAST, 1))) {
return error; return error;
} }
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container; if (!container->view) {
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "title_format", return cmd_results_new(CMD_INVALID, "title_format",
"Only views can have a 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); char *format = join_args(argv, argc);
if (view->title_format) { if (view->title_format) {
free(view->title_format); free(view->title_format);

@ -9,9 +9,9 @@
#include "stringop.h" #include "stringop.h"
static void remove_all_marks_iterator(struct sway_container *con, void *data) { static void remove_all_marks_iterator(struct sway_container *con, void *data) {
if (con->type == C_VIEW) { if (con->view) {
view_clear_marks(con->sway_view); view_clear_marks(con->view);
view_update_marks_textures(con->sway_view); view_update_marks_textures(con->view);
} }
} }
@ -24,13 +24,12 @@ struct cmd_results *cmd_unmark(int argc, char **argv) {
// Determine the view // Determine the view
struct sway_view *view = NULL; struct sway_view *view = NULL;
if (config->handler_context.using_criteria) { if (config->handler_context.using_criteria) {
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container; if (!container->view) {
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "unmark", return cmd_results_new(CMD_INVALID, "unmark",
"Only views can have marks"); "Only views can have marks");
} }
view = container->sway_view; view = container->view;
} }
// Determine the mark // Determine the mark

@ -11,13 +11,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) {
if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) {
return error; return error;
} }
struct sway_container *container = struct sway_container *container = config->handler_context.container;
config->handler_context.current_container; if (!container->view) {
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "urgent", return cmd_results_new(CMD_INVALID, "urgent",
"Only views can be 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) { if (strcmp(argv[0], "allow") == 0) {
view->allow_request_urgent = true; view->allow_request_urgent = true;

@ -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 (strcasecmp(argv[0], "number") == 0) {
if (argc < 2) { if (argc < 2) {
return cmd_results_new(CMD_INVALID, "workspace", return cmd_results_new(CMD_INVALID, "workspace",

@ -825,6 +825,6 @@ void config_update_font_height(bool recalculate) {
root_for_each_container(find_font_height_iterator, &recalculate); root_for_each_container(find_font_height_iterator, &recalculate);
if (config->font_height != prev_max_height) { if (config->font_height != prev_max_height) {
arrange_windows(&root_container); arrange_root();
} }
} }

@ -12,6 +12,7 @@
#include <strings.h> #include <strings.h>
#include <signal.h> #include <signal.h>
#include "sway/config.h" #include "sway/config.h"
#include "sway/output.h"
#include "stringop.h" #include "stringop.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
@ -218,17 +219,6 @@ void invoke_swaybar(struct bar_config *bar) {
close(filedes[1]); 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() { void load_swaybars() {
for (int i = 0; i < config->bars->length; ++i) { for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *bar = config->bars->items[i]; struct bar_config *bar = config->bars->items[i];
@ -236,7 +226,7 @@ void load_swaybars() {
if (bar->outputs) { if (bar->outputs) {
for (int j = 0; j < bar->outputs->length; ++j) { for (int j = 0; j < bar->outputs->length; ++j) {
char *o = bar->outputs->items[j]; char *o = bar->outputs->items[j];
if (!strcmp(o, "*") || active_output(o)) { if (!strcmp(o, "*") || output_by_name(o)) {
apply = true; apply = true;
break; break;
} }

@ -174,21 +174,16 @@ void terminate_swaybg(pid_t pid) {
} }
} }
void apply_output_config(struct output_config *oc, struct sway_container *output) { void apply_output_config(struct output_config *oc, struct sway_output *output) {
assert(output->type == C_OUTPUT); struct wlr_output *wlr_output = output->wlr_output;
struct wlr_output_layout *output_layout =
root_container.sway_root->output_layout;
struct wlr_output *wlr_output = output->sway_output->wlr_output;
if (oc && oc->enabled == 0) { if (oc && oc->enabled == 0) {
if (output->sway_output->bg_pid != 0) { if (output->bg_pid != 0) {
terminate_swaybg(output->sway_output->bg_pid); terminate_swaybg(output->bg_pid);
output->sway_output->bg_pid = 0; output->bg_pid = 0;
} }
output_begin_destroy(output); output_disable(output);
wlr_output_layout_remove(root_container.sway_root->output_layout, wlr_output_layout_remove(root->output_layout, wlr_output);
wlr_output);
return; return;
} }
@ -213,21 +208,21 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
// Find position for it // Find position for it
if (oc && (oc->x != -1 || oc->y != -1)) { 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_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 { } else {
wlr_output_layout_add_auto(output_layout, wlr_output); wlr_output_layout_add_auto(root->output_layout, wlr_output);
} }
int output_i; int output_i;
for (output_i = 0; output_i < root_container.children->length; ++output_i) { for (output_i = 0; output_i < root->outputs->length; ++output_i) {
if (root_container.children->items[output_i] == output) { if (root->outputs->items[output_i] == output) {
break; break;
} }
} }
if (oc && oc->background) { if (oc && oc->background) {
if (output->sway_output->bg_pid != 0) { if (output->bg_pid != 0) {
terminate_swaybg(output->sway_output->bg_pid); terminate_swaybg(output->bg_pid);
} }
wlr_log(WLR_DEBUG, "Setting background for output %d to %s", 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); wlr_log(WLR_DEBUG, "-> %s", command);
char *const cmd[] = { "sh", "-c", command, NULL }; char *const cmd[] = { "sh", "-c", command, NULL };
output->sway_output->bg_pid = fork(); output->bg_pid = fork();
if (output->sway_output->bg_pid == 0) { if (output->bg_pid == 0) {
execvp(cmd[0], cmd); execvp(cmd[0], cmd);
} else { } else {
free(command); free(command);
@ -293,12 +288,11 @@ void apply_output_config_to_outputs(struct output_config *oc) {
bool wildcard = strcmp(oc->name, "*") == 0; bool wildcard = strcmp(oc->name, "*") == 0;
char id[128]; char id[128];
struct sway_output *sway_output; struct sway_output *sway_output;
wl_list_for_each(sway_output, wl_list_for_each(sway_output, &root->all_outputs, link) {
&root_container.sway_root->all_outputs, link) {
char *name = sway_output->wlr_output->name; char *name = sway_output->wlr_output->name;
output_get_identifier(id, sizeof(id), sway_output); output_get_identifier(id, sizeof(id), sway_output);
if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) {
if (!sway_output->swayc) { if (!sway_output->enabled) {
if (!oc->enabled) { if (!oc->enabled) {
if (!wildcard) { if (!wildcard) {
break; break;
@ -306,7 +300,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
continue; continue;
} }
output_enable(sway_output); output_enable(sway_output, oc);
} }
struct output_config *current = oc; struct output_config *current = oc;
@ -316,7 +310,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
current = tmp; current = tmp;
} }
} }
apply_output_config(current, sway_output->swayc); apply_output_config(current, sway_output);
if (!wildcard) { if (!wildcard) {
// Stop looking if the output config isn't applicable to all // 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) { void create_default_output_configs(void) {
struct sway_output *sway_output; struct sway_output *sway_output;
wl_list_for_each(sway_output, wl_list_for_each(sway_output, &root->all_outputs, link) {
&root_container.sway_root->all_outputs, link) {
char *name = sway_output->wlr_output->name; char *name = sway_output->wlr_output->name;
struct output_config *oc = new_output_config(name); struct output_config *oc = new_output_config(name);
default_output_config(oc, sway_output->wlr_output); default_output_config(oc, sway_output->wlr_output);

@ -9,6 +9,7 @@
#include "sway/config.h" #include "sway/config.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "stringop.h" #include "stringop.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
@ -87,12 +88,12 @@ static int cmp_urgent(const void *_a, const void *_b) {
return 0; return 0;
} }
static void find_urgent_iterator(struct sway_container *swayc, void *data) { static void find_urgent_iterator(struct sway_container *con, void *data) {
if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { if (!con->view || !view_is_urgent(con->view)) {
return; return;
} }
list_t *urgent_views = data; 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, 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 (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; return false;
} }
} }
@ -174,13 +175,13 @@ static bool criteria_matches_view(struct criteria *criteria,
#endif #endif
if (criteria->floating) { if (criteria->floating) {
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return false; return false;
} }
} }
if (criteria->tiling) { if (criteria->tiling) {
if (container_is_floating(view->swayc)) { if (container_is_floating(view->container)) {
return false; return false;
} }
} }
@ -205,10 +206,7 @@ static bool criteria_matches_view(struct criteria *criteria,
} }
if (criteria->workspace) { if (criteria->workspace) {
if (!view->swayc) { struct sway_workspace *ws = view->container->workspace;
return false;
}
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
if (!ws || strcmp(ws->name, criteria->workspace) != 0) { if (!ws || strcmp(ws->name, criteria->workspace) != 0) {
return false; return false;
} }
@ -237,9 +235,9 @@ struct match_data {
static void criteria_get_views_iterator(struct sway_container *container, static void criteria_get_views_iterator(struct sway_container *container,
void *data) { void *data) {
struct match_data *match_data = data; struct match_data *match_data = data;
if (container->type == C_VIEW) { if (!container->view) {
if (criteria_matches_view(match_data->criteria, container->sway_view)) { if (criteria_matches_view(match_data->criteria, container->view)) {
list_add(match_data->matches, container->sway_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) { static char *get_focused_prop(enum criteria_token token) {
struct sway_seat *seat = input_manager_current_seat(input_manager); 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; return NULL;
} }
struct sway_view *view = focus->sway_view; struct sway_view *view = focus->view;
const char *value = NULL; const char *value = NULL;
switch (token) { switch (token) {
@ -374,18 +372,15 @@ static char *get_focused_prop(enum criteria_token token) {
value = view_get_title(view); value = view_get_title(view);
break; break;
case T_WORKSPACE: case T_WORKSPACE:
{ if (focus->workspace) {
struct sway_container *ws = container_parent(focus, C_WORKSPACE); value = focus->workspace->name;
if (ws) {
value = ws->name;
}
} }
break; break;
case T_CON_ID: case T_CON_ID:
if (view->swayc == NULL) { if (view->container == NULL) {
return 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; size_t id_size = snprintf(NULL, 0, "%zu", id) + 1;
char *id_str = malloc(id_size); char *id_str = malloc(id_size);
snprintf(id_str, id_size, "%zu", id); snprintf(id_str, id_size, "%zu", id);

@ -10,6 +10,7 @@
#include "sway/server.h" #include "sway/server.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/workspace.h"
#include "cairo.h" #include "cairo.h"
#include "config.h" #include "config.h"
#include "pango.h" #include "pango.h"
@ -32,28 +33,78 @@ static const char *layout_to_str(enum sway_container_layout layout) {
return "L_NONE"; return "L_NONE";
} }
static int draw_container(cairo_t *cairo, struct sway_container *container, static char *get_string(struct sway_node *node) {
struct sway_container *focus, int x, int y) { 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; int text_width, text_height;
char *buffer = get_string(node);
get_text_size(cairo, "monospace", &text_width, &text_height, get_text_size(cairo, "monospace", &text_width, &text_height,
1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", 1, false, buffer);
container_type_to_str(container->type), container->id, container->name,
layout_to_str(container->layout),
container->width, container->height, container->x, container->y);
cairo_save(cairo); cairo_save(cairo);
cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
cairo_set_source_u32(cairo, 0xFFFFFFE0); cairo_set_source_u32(cairo, 0xFFFFFFE0);
cairo_fill(cairo); cairo_fill(cairo);
int height = text_height; int height = text_height;
if (container->children) { list_t *children = get_children(node);
for (int i = 0; i < container->children->length; ++i) { if (children) {
struct sway_container *child = container->children->items[i]; for (int i = 0; i < children->length; ++i) {
if (child->parent == container) { // 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); cairo_set_source_u32(cairo, 0x000000FF);
} else { } else {
cairo_set_source_u32(cairo, 0xFF0000FF); 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); 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_fill(cairo);
cairo_restore(cairo); cairo_restore(cairo);
cairo_move_to(cairo, x, y); cairo_move_to(cairo, x, y);
if (focus == container) { if (focus == node) {
cairo_set_source_u32(cairo, 0x0000FFFF); cairo_set_source_u32(cairo, 0x0000FFFF);
} }
pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f", pango_printf(cairo, "monospace", 1, false, buffer);
container_type_to_str(container->type), container->id, container->name, free(buffer);
layout_to_str(container->layout),
container->width, container->height, container->x, container->y);
return height; return height;
} }
@ -77,13 +126,13 @@ void update_debug_tree() {
} }
int width = 640, height = 480; int width = 640, height = 480;
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *container = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (container->width > width) { if (output->wlr_output->width > width) {
width = container->width; width = output->wlr_output->width;
} }
if (container->height > height) { if (output->wlr_output->height > height) {
height = container->height; height = output->wlr_output->height;
} }
} }
cairo_surface_t *surface = cairo_surface_t *surface =
@ -91,28 +140,22 @@ void update_debug_tree() {
cairo_t *cairo = cairo_create(surface); cairo_t *cairo = cairo_create(surface);
PangoContext *pango = pango_cairo_create_context(cairo); PangoContext *pango = pango_cairo_create_context(cairo);
struct sway_seat *seat = NULL; struct sway_seat *seat = input_manager_current_seat(input_manager);
wl_list_for_each(seat, &input_manager->seats, link) { struct sway_node *focus = seat_get_focus(seat);
break;
}
struct sway_container *focus = NULL;
if (seat != NULL) {
focus = seat_get_focus(seat);
}
cairo_set_source_u32(cairo, 0x000000FF); 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); cairo_surface_flush(surface);
struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
if (root_container.sway_root->debug_tree) { if (root->debug_tree) {
wlr_texture_destroy(root_container.sway_root->debug_tree); wlr_texture_destroy(root->debug_tree);
} }
unsigned char *data = cairo_image_surface_get_data(surface); unsigned char *data = cairo_image_surface_get_data(surface);
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
struct wlr_texture *texture = wlr_texture_from_pixels(renderer, struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
WL_SHM_FORMAT_ARGB8888, stride, width, height, data); WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
root_container.sway_root->debug_tree = texture; root->debug_tree = texture;
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
g_object_unref(pango); g_object_unref(pango);
cairo_destroy(cairo); cairo_destroy(cairo);

@ -4,37 +4,32 @@
void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
bool whole) { bool whole) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *cont = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (cont->type == C_OUTPUT) { output_damage_surface(output, lx - output->wlr_output->lx,
output_damage_surface(cont->sway_output, ly - output->wlr_output->ly, surface, whole);
lx - cont->current.swayc_x, ly - cont->current.swayc_y,
surface, whole);
}
} }
} }
void desktop_damage_whole_container(struct sway_container *con) { void desktop_damage_whole_container(struct sway_container *con) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *cont = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (cont->type == C_OUTPUT) { output_damage_whole_container(output, con);
output_damage_whole_container(cont->sway_output, con);
}
} }
} }
void desktop_damage_box(struct wlr_box *box) { void desktop_damage_box(struct wlr_box *box) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *cont = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
output_damage_box(cont->sway_output, box); output_damage_box(output, box);
} }
} }
void desktop_damage_view(struct sway_view *view) { void desktop_damage_view(struct sway_view *view) {
desktop_damage_whole_container(view->swayc); desktop_damage_whole_container(view->container);
struct wlr_box box = { struct wlr_box box = {
.x = view->swayc->current.view_x - view->geometry.x, .x = view->container->current.view_x - view->geometry.x,
.y = view->swayc->current.view_y - view->geometry.y, .y = view->container->current.view_y - view->geometry.y,
.width = view->surface->current.width, .width = view->surface->current.width,
.height = view->surface->current.height, .height = view->surface->current.height,
}; };

@ -14,6 +14,7 @@
#include "sway/output.h" #include "sway/output.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/workspace.h"
#include "log.h" #include "log.h"
static void apply_exclusive(struct wlr_box *usable_area, 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) { sizeof(struct wlr_box)) != 0) {
wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
arrange_output(output->swayc); arrange_output(output);
} }
// Arrange non-exlusive surfaces from top->bottom // Arrange non-exlusive surfaces from top->bottom
@ -256,7 +257,7 @@ static void unmap(struct sway_layer_surface *sway_layer) {
return; return;
} }
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
if (output == NULL || output->swayc == NULL) { if (output == NULL) {
return; return;
} }
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 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); wl_list_remove(&sway_layer->surface_commit.link);
if (sway_layer->layer_surface->output != NULL) { if (sway_layer->layer_surface->output != NULL) {
struct sway_output *output = sway_layer->layer_surface->output->data; struct sway_output *output = sway_layer->layer_surface->output->data;
if (output != NULL && output->swayc != NULL) { if (output != NULL) {
arrange_layers(output); arrange_layers(output);
arrange_windows(output->swayc); arrange_output(output);
transaction_commit_dirty(); transaction_commit_dirty();
} }
wl_list_remove(&sway_layer->output_destroy.link); 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) { if (!layer_surface->output) {
// Assign last active 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); struct sway_seat *seat = input_manager_get_default_seat(input_manager);
if (seat) { 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 (!output) {
if (!sway_assert(root_container.children->length, if (!sway_assert(root->outputs->length,
"cannot auto-assign output for layer")) { "cannot auto-assign output for layer")) {
wlr_layer_surface_close(layer_surface); wlr_layer_surface_close(layer_surface);
return; return;
} }
output = root_container.children->items[0]; output = root->outputs->items[0];
} }
if (output->type != C_OUTPUT) { layer_surface->output = output->wlr_output;
output = container_parent(output, C_OUTPUT);
}
layer_surface->output = output->sway_output->wlr_output;
} }
struct sway_layer_surface *sway_layer = struct sway_layer_surface *sway_layer =

@ -28,10 +28,10 @@
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
struct sway_container *output_by_name(const char *name) { struct sway_output *output_by_name(const char *name) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (strcasecmp(output->name, name) == 0) { if (strcasecmp(output->wlr_output->name, name) == 0) {
return output; 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); wlr_box_rotated_bounds(&box, data->rotation, &rotated_box);
struct wlr_box output_box = { struct wlr_box output_box = {
.width = output->swayc->current.swayc_width, .width = output->wlr_output->width,
.height = output->swayc->current.swayc_height, .height = output->wlr_output->height,
}; };
struct wlr_box intersection; struct wlr_box intersection;
@ -145,12 +145,12 @@ void output_view_for_each_surface(struct sway_output *output,
.user_iterator = iterator, .user_iterator = iterator,
.user_data = user_data, .user_data = user_data,
.output = output, .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, - 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, - view->geometry.y,
.width = view->swayc->current.view_width, .width = view->container->current.view_width,
.height = view->swayc->current.view_height, .height = view->container->current.view_height,
.rotation = 0, // TODO .rotation = 0, // TODO
}; };
@ -164,12 +164,12 @@ void output_view_for_each_popup(struct sway_output *output,
.user_iterator = iterator, .user_iterator = iterator,
.user_data = user_data, .user_data = user_data,
.output = output, .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, - 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, - view->geometry.y,
.width = view->swayc->current.view_width, .width = view->container->current.view_width,
.height = view->swayc->current.view_height, .height = view->container->current.view_height,
.rotation = 0, // TODO .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) { wl_list_for_each(unmanaged_surface, unmanaged, link) {
struct wlr_xwayland_surface *xsurface = struct wlr_xwayland_surface *xsurface =
unmanaged_surface->wlr_xwayland_surface; unmanaged_surface->wlr_xwayland_surface;
double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; double ox = unmanaged_surface->lx - output->wlr_output->lx;
double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; double oy = unmanaged_surface->ly - output->wlr_output->ly;
output_surface_for_each_surface(output, xsurface->surface, ox, oy, output_surface_for_each_surface(output, xsurface->surface, ox, oy,
iterator, user_data); iterator, user_data);
@ -211,8 +211,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
void *user_data) { void *user_data) {
struct sway_drag_icon *drag_icon; struct sway_drag_icon *drag_icon;
wl_list_for_each(drag_icon, drag_icons, link) { wl_list_for_each(drag_icon, drag_icons, link) {
double ox = drag_icon->x - output->swayc->x; double ox = drag_icon->x - output->wlr_output->lx;
double oy = drag_icon->y - output->swayc->y; double oy = drag_icon->y - output->wlr_output->ly;
if (drag_icon->wlr_drag_icon->mapped) { if (drag_icon->wlr_drag_icon->mapped) {
output_surface_for_each_surface(output, output_surface_for_each_surface(output,
@ -229,19 +229,13 @@ static void scale_box(struct wlr_box *box, float scale) {
box->height *= 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_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = struct sway_node *focus = seat_get_active_child(seat, &output->node);
seat_get_focus_inactive(seat, output->swayc);
if (!focus) { if (!focus) {
// We've never been to this output before return output->workspaces->items[0];
focus = output->swayc->current.children->items[0];
} }
struct sway_container *workspace = focus; return focus->sway_workspace;
if (workspace->type != C_WORKSPACE) {
workspace = container_parent(workspace, C_WORKSPACE);
}
return workspace;
} }
bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { 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 = struct sway_layer_surface *sway_layer_surface =
layer_from_wlr_layer_surface(wlr_layer_surface); layer_from_wlr_layer_surface(wlr_layer_surface);
pixman_box32_t output_box = { pixman_box32_t output_box = {
.x2 = output->swayc->current.swayc_width, .x2 = output->wlr_output->width,
.y2 = output->swayc->current.swayc_height, .y2 = output->wlr_output->height,
}; };
pixman_region32_t surface_opaque_box; pixman_region32_t surface_opaque_box;
pixman_region32_init(&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, static void send_frame_done_container_iterator(struct sway_container *con,
void *_data) { void *_data) {
if (con->type != C_VIEW) { if (!con->view) {
return; return;
} }
if (!view_is_visible(con->sway_view)) { if (!view_is_visible(con->view)) {
return; return;
} }
struct send_frame_done_data *data = _data; 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); send_frame_done_iterator, data->when);
} }
@ -328,15 +322,14 @@ static void send_frame_done(struct sway_output *output, struct timespec *when) {
.output = output, .output = output,
.when = when, .when = when,
}; };
struct sway_container *workspace = output_get_active_workspace(output); struct sway_workspace *workspace = output_get_active_workspace(output);
if (workspace->current.ws_fullscreen) { if (workspace->current.fullscreen) {
send_frame_done_container_iterator( send_frame_done_container_iterator(
workspace->current.ws_fullscreen, &data); workspace->current.fullscreen, &data);
container_for_each_child(workspace->current.ws_fullscreen, container_for_each_child(workspace->current.fullscreen,
send_frame_done_container_iterator, &data); send_frame_done_container_iterator, &data);
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
send_frame_done_unmanaged(output, send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
&root_container.sway_root->xwayland_unmanaged, when);
#endif #endif
} else { } else {
send_frame_done_layer(output, 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); send_frame_done_container_iterator, &data);
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
send_frame_done_unmanaged(output, send_frame_done_unmanaged(output, &root->xwayland_unmanaged, when);
&root_container.sway_root->xwayland_unmanaged, when);
#endif #endif
send_frame_done_layer(output, send_frame_done_layer(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); &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_overlay:
send_frame_done_layer(output, send_frame_done_layer(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when);
send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, send_frame_done_drag_icons(output, &root->drag_icons, when);
when);
} }
static void damage_handle_frame(struct wl_listener *listener, void *data) { 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) { 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, 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, static void output_damage_view(struct sway_output *output,
struct sway_view *view, bool whole) { struct sway_view *view, bool whole) {
if (!sway_assert(view->swayc != NULL, "expected a view in the tree")) {
return;
}
if (!view_is_visible(view)) { if (!view_is_visible(view)) {
return; return;
} }
output_view_for_each_surface(output, view, damage_surface_iterator, &whole); 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) { void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
struct wlr_box box; struct wlr_box box;
memcpy(&box, _box, sizeof(struct wlr_box)); memcpy(&box, _box, sizeof(struct wlr_box));
box.x -= output->swayc->current.swayc_x; box.x -= output->wlr_output->lx;
box.y -= output->swayc->current.swayc_y; box.y -= output->wlr_output->ly;
scale_box(&box, output->wlr_output->scale); scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box); wlr_output_damage_add_box(output->damage, &box);
} }
static void output_damage_whole_container_iterator(struct sway_container *con, static void output_damage_whole_container_iterator(struct sway_container *con,
void *data) { void *data) {
struct sway_output *output = data; if (!sway_assert(con->view, "expected a view")) {
if (!sway_assert(con->type == C_VIEW, "expected a view")) {
return; return;
} }
struct sway_output *output = data;
output_damage_view(output, con->sway_view, true); output_damage_view(output, con->view, true);
} }
void output_damage_whole_container(struct sway_output *output, void output_damage_whole_container(struct sway_output *output,
struct sway_container *con) { struct sway_container *con) {
// Pad the box by 1px, because the width is a double and might be a fraction // Pad the box by 1px, because the width is a double and might be a fraction
struct wlr_box box = { struct wlr_box box = {
.x = con->current.swayc_x - output->wlr_output->lx - 1, .x = con->current.con_x - output->wlr_output->lx - 1,
.y = con->current.swayc_y - output->wlr_output->ly - 1, .y = con->current.con_y - output->wlr_output->ly - 1,
.width = con->current.swayc_width + 2, .width = con->current.con_width + 2,
.height = con->current.swayc_height + 2, .height = con->current.con_height + 2,
}; };
scale_box(&box, output->wlr_output->scale); scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box); 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) { static void damage_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output = struct sway_output *output =
wl_container_of(listener, output, damage_destroy); 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) { static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_output *output = wl_container_of(listener, output, destroy);
wl_signal_emit(&output->events.destroy, output); wl_signal_emit(&output->events.destroy, output);
if (output->swayc) { if (output->enabled) {
output_begin_destroy(output->swayc); output_disable(output);
} }
output_begin_destroy(output);
wl_list_remove(&output->link); transaction_commit_dirty();
wl_list_remove(&output->destroy.link);
output->wlr_output->data = NULL;
free(output);
arrange_windows(&root_container);
} }
static void handle_mode(struct wl_listener *listener, void *data) { static void handle_mode(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, mode); struct sway_output *output = wl_container_of(listener, output, mode);
arrange_layers(output); arrange_layers(output);
arrange_windows(output->swayc); arrange_output(output);
transaction_commit_dirty(); transaction_commit_dirty();
} }
static void handle_transform(struct wl_listener *listener, void *data) { static void handle_transform(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, transform); struct sway_output *output = wl_container_of(listener, output, transform);
arrange_layers(output); arrange_layers(output);
arrange_windows(output->swayc); arrange_output(output);
transaction_commit_dirty(); 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) { static void handle_scale(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, scale); struct sway_output *output = wl_container_of(listener, output, scale);
arrange_layers(output); arrange_layers(output);
container_update_textures_recursive(output->swayc); output_for_each_container(output, update_textures, NULL);
arrange_windows(output->swayc); arrange_output(output);
transaction_commit_dirty(); transaction_commit_dirty();
} }
@ -545,57 +537,27 @@ void handle_new_output(struct wl_listener *listener, void *data) {
struct wlr_output *wlr_output = data; struct wlr_output *wlr_output = data;
wlr_log(WLR_DEBUG, "New output %p: %s", wlr_output, wlr_output->name); 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) { if (!output) {
return; return;
} }
output->wlr_output = wlr_output;
wlr_output->data = output;
output->server = server; output->server = server;
output->damage = wlr_output_damage_create(wlr_output); output->damage = wlr_output_damage_create(wlr_output);
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->destroy.notify = handle_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;
}
output->swayc = output_create(output);
if (!output->swayc) {
// Output is disabled
return;
}
size_t len = sizeof(output->layers) / sizeof(output->layers[0]); if (oc && oc->enabled) {
for (size_t i = 0; i < len; ++i) { output_enable(output, oc);
wl_list_init(&output->layers[i]);
} }
wl_signal_init(&output->events.destroy);
input_manager_configure_xcursor(input_manager); transaction_commit_dirty();
}
wl_signal_add(&wlr_output->events.mode, &output->mode); void output_add_listeners(struct sway_output *output) {
output->mode.notify = handle_mode; output->mode.notify = handle_mode;
wl_signal_add(&wlr_output->events.transform, &output->transform);
output->transform.notify = handle_transform; output->transform.notify = handle_transform;
wl_signal_add(&wlr_output->events.scale, &output->scale);
output->scale.notify = handle_scale; output->scale.notify = handle_scale;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
output->damage_frame.notify = damage_handle_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; output->damage_destroy.notify = damage_handle_destroy;
arrange_layers(output);
arrange_windows(&root_container);
transaction_commit_dirty();
} }

@ -193,10 +193,10 @@ static void render_view_toplevels(struct sway_view *view,
.alpha = alpha, .alpha = alpha,
}; };
// Render all toplevels without descending into popups // Render all toplevels without descending into popups
double ox = double ox = view->container->current.view_x -
view->swayc->current.view_x - output->wlr_output->lx - view->geometry.x; output->wlr_output->lx - view->geometry.x;
double oy = double oy = view->container->current.view_y -
view->swayc->current.view_y - output->wlr_output->ly - view->geometry.y; output->wlr_output->ly - view->geometry.y;
output_surface_for_each_surface(output, view->surface, ox, oy, output_surface_for_each_surface(output, view->surface, ox, oy,
render_surface_iterator, &data); render_surface_iterator, &data);
} }
@ -229,17 +229,17 @@ static void render_saved_view(struct sway_view *view,
return; return;
} }
struct wlr_box box = { 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, 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, view->saved_geometry.y,
.width = view->saved_buffer_width, .width = view->saved_buffer_width,
.height = view->saved_buffer_height, .height = view->saved_buffer_height,
}; };
struct wlr_box output_box = { struct wlr_box output_box = {
.width = output->swayc->current.swayc_width, .width = output->wlr_output->width,
.height = output->swayc->current.swayc_height, .height = output->wlr_output->height,
}; };
struct wlr_box intersection; 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, static void render_view(struct sway_output *output, pixman_region32_t *damage,
struct sway_container *con, struct border_colors *colors) { 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) { if (view->saved_buffer) {
render_saved_view(view, output, damage, view->swayc->alpha); render_saved_view(view, output, damage, view->container->alpha);
} else { } 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; return;
} }
@ -283,7 +283,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
if (state->border_left) { if (state->border_left) {
memcpy(&color, colors->child_border, sizeof(float) * 4); memcpy(&color, colors->child_border, sizeof(float) * 4);
premultiply_alpha(color, con->alpha); premultiply_alpha(color, con->alpha);
box.x = state->swayc_x; box.x = state->con_x;
box.y = state->view_y; box.y = state->view_y;
box.width = state->border_thickness; box.width = state->border_thickness;
box.height = state->view_height; 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); 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->border_right) {
if (state->parent->current.children->length == 1 if (siblings->length == 1 && layout == L_HORIZ) {
&& state->parent->current.layout == L_HORIZ) {
memcpy(&color, colors->indicator, sizeof(float) * 4); memcpy(&color, colors->indicator, sizeof(float) * 4);
} else { } else {
memcpy(&color, colors->child_border, sizeof(float) * 4); 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->border_bottom) {
if (state->parent->current.children->length == 1 if (siblings->length == 1 && layout == L_VERT) {
&& con->current.parent->current.layout == L_VERT) {
memcpy(&color, colors->indicator, sizeof(float) * 4); memcpy(&color, colors->indicator, sizeof(float) * 4);
} else { } else {
memcpy(&color, colors->child_border, sizeof(float) * 4); memcpy(&color, colors->child_border, sizeof(float) * 4);
} }
premultiply_alpha(color, con->alpha); premultiply_alpha(color, con->alpha);
box.x = state->swayc_x; box.x = state->con_x;
box.y = state->view_y + state->view_height; box.y = state->view_y + state->view_height;
box.width = state->swayc_width; box.width = state->con_width;
box.height = state->border_thickness; box.height = state->border_thickness;
scale_box(&box, output_scale); scale_box(&box, output_scale);
render_rect(output->wlr_output, damage, &box, color); render_rect(output->wlr_output, damage, &box, color);
@ -344,12 +346,12 @@ static void render_titlebar(struct sway_output *output,
float color[4]; float color[4];
struct sway_container_state *state = &con->current; struct sway_container_state *state = &con->current;
float output_scale = output->wlr_output->scale; float output_scale = output->wlr_output->scale;
enum sway_container_layout layout = state->parent->current.layout; enum sway_container_layout layout = container_current_parent_layout(con);
list_t *children = state->parent->current.children; list_t *children = container_get_current_siblings(con);
bool is_last_child = children->length == 0 || bool is_last_child = children->length == 0 ||
children->items[children->length - 1] == con; children->items[children->length - 1] == con;
double output_x = output->swayc->current.swayc_x; double output_x = output->wlr_output->lx;
double output_y = output->swayc->current.swayc_y; double output_y = output->wlr_output->ly;
// Single pixel bar above title // Single pixel bar above title
memcpy(&color, colors->border, sizeof(float) * 4); memcpy(&color, colors->border, sizeof(float) * 4);
@ -366,7 +368,7 @@ static void render_titlebar(struct sway_output *output,
bool connects_sides = false; bool connects_sides = false;
if (layout == L_HORIZ || layout == L_VERT || if (layout == L_HORIZ || layout == L_VERT ||
(layout == L_STACKED && is_last_child)) { (layout == L_STACKED && is_last_child)) {
if (con->type == C_VIEW) { if (con->view) {
left_offset = state->border_left * state->border_thickness; left_offset = state->border_left * state->border_thickness;
right_offset = state->border_right * state->border_thickness; right_offset = state->border_right * state->border_thickness;
connects_sides = true; connects_sides = true;
@ -542,14 +544,22 @@ static void render_top_border(struct sway_output *output,
// Child border - top edge // Child border - top edge
memcpy(&color, colors->child_border, sizeof(float) * 4); memcpy(&color, colors->child_border, sizeof(float) * 4);
premultiply_alpha(color, con->alpha); premultiply_alpha(color, con->alpha);
box.x = state->swayc_x; box.x = state->con_x;
box.y = state->swayc_y; box.y = state->con_y;
box.width = state->swayc_width; box.width = state->con_width;
box.height = state->border_thickness; box.height = state->border_thickness;
scale_box(&box, output_scale); scale_box(&box, output_scale);
render_rect(output->wlr_output, output_damage, &box, color); 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, static void render_container(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con, bool parent_focused); 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 * Wrap child views in borders and leave child containers borderless because
* they'll apply their own borders to their children. * they'll apply their own borders to their children.
*/ */
static void render_container_simple(struct sway_output *output, static void render_containers_linear(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con, pixman_region32_t *damage, struct parent_data *parent) {
bool parent_focused) { for (int i = 0; i < parent->children->length; ++i) {
for (int i = 0; i < con->current.children->length; ++i) { struct sway_container *child = parent->children->items[i];
struct sway_container *child = con->current.children->items[i];
if (child->view) {
if (child->type == C_VIEW) { struct sway_view *view = child->view;
struct sway_view *view = child->sway_view;
struct border_colors *colors; struct border_colors *colors;
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
struct wlr_texture *marks_texture; struct wlr_texture *marks_texture;
@ -576,11 +585,11 @@ static void render_container_simple(struct sway_output *output,
colors = &config->border_colors.urgent; colors = &config->border_colors.urgent;
title_texture = child->title_urgent; title_texture = child->title_urgent;
marks_texture = view->marks_urgent; marks_texture = view->marks_urgent;
} else if (state->focused || parent_focused) { } else if (state->focused || parent->focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = child->title_focused; title_texture = child->title_focused;
marks_texture = view->marks_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; colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive; title_texture = child->title_focused_inactive;
marks_texture = view->marks_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; marks_texture = view->marks_unfocused;
} }
if (!view->swayc->current.using_csd) { if (!view->container->current.using_csd) {
if (state->border == B_NORMAL) { if (state->border == B_NORMAL) {
render_titlebar(output, damage, child, state->swayc_x, render_titlebar(output, damage, child, state->con_x,
state->swayc_y, state->swayc_width, colors, state->con_y, state->con_width, colors,
title_texture, marks_texture); title_texture, marks_texture);
} else { } else {
render_top_border(output, damage, child, colors); 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); render_view(output, damage, child, colors);
} else { } else {
render_container(output, damage, child, 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. * Render a container's children using the L_TABBED layout.
*/ */
static void render_container_tabbed(struct sway_output *output, static void render_containers_tabbed(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con, pixman_region32_t *damage, struct parent_data *parent) {
bool parent_focused) { if (!parent->children->length) {
if (!con->current.children->length) {
return; return;
} }
struct sway_container_state *pstate = &con->current; struct sway_container *current = parent->active_child;
struct sway_container *current = pstate->focused_inactive_child;
struct border_colors *current_colors = &config->border_colors.unfocused; struct border_colors *current_colors = &config->border_colors.unfocused;
int tab_width = parent->box.width / parent->children->length;
int tab_width = (pstate->swayc_width) / pstate->children->length;
// Render tabs // Render tabs
for (int i = 0; i < pstate->children->length; ++i) { for (int i = 0; i < parent->children->length; ++i) {
struct sway_container *child = pstate->children->items[i]; struct sway_container *child = parent->children->items[i];
struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; struct sway_view *view = child->view;
struct sway_container_state *cstate = &child->current; struct sway_container_state *cstate = &child->current;
struct border_colors *colors; struct border_colors *colors;
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
@ -637,11 +643,11 @@ static void render_container_tabbed(struct sway_output *output,
colors = &config->border_colors.urgent; colors = &config->border_colors.urgent;
title_texture = child->title_urgent; title_texture = child->title_urgent;
marks_texture = view ? view->marks_urgent : NULL; marks_texture = view ? view->marks_urgent : NULL;
} else if (cstate->focused || parent_focused) { } else if (cstate->focused || parent->focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = child->title_focused; title_texture = child->title_focused;
marks_texture = view ? view->marks_focused : NULL; 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; colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive; title_texture = child->title_focused_inactive;
marks_texture = view ? view->marks_focused_inactive : NULL; 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; 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 // Make last tab use the remaining width of the parent
if (i == pstate->children->length - 1) { if (i == parent->children->length - 1) {
tab_width = pstate->swayc_width - tab_width * i; 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); colors, title_texture, marks_texture);
if (child == current) { if (child == current) {
@ -667,33 +673,30 @@ static void render_container_tabbed(struct sway_output *output,
} }
// Render surface and left/right/bottom borders // Render surface and left/right/bottom borders
if (current->type == C_VIEW) { if (current->view) {
render_view(output, damage, current, current_colors); render_view(output, damage, current, current_colors);
} else { } else {
render_container(output, damage, current, 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. * Render a container's children using the L_STACKED layout.
*/ */
static void render_container_stacked(struct sway_output *output, static void render_containers_stacked(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con, pixman_region32_t *damage, struct parent_data *parent) {
bool parent_focused) { if (!parent->children->length) {
if (!con->current.children->length) {
return; return;
} }
struct sway_container_state *pstate = &con->current; struct sway_container *current = parent->active_child;
struct sway_container *current = pstate->focused_inactive_child;
struct border_colors *current_colors = &config->border_colors.unfocused; struct border_colors *current_colors = &config->border_colors.unfocused;
size_t titlebar_height = container_titlebar_height(); size_t titlebar_height = container_titlebar_height();
// Render titles // Render titles
for (int i = 0; i < pstate->children->length; ++i) { for (int i = 0; i < parent->children->length; ++i) {
struct sway_container *child = pstate->children->items[i]; struct sway_container *child = parent->children->items[i];
struct sway_view *view = child->type == C_VIEW ? child->sway_view : NULL; struct sway_view *view = child->view;
struct sway_container_state *cstate = &child->current; struct sway_container_state *cstate = &child->current;
struct border_colors *colors; struct border_colors *colors;
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
@ -705,11 +708,11 @@ static void render_container_stacked(struct sway_output *output,
colors = &config->border_colors.urgent; colors = &config->border_colors.urgent;
title_texture = child->title_urgent; title_texture = child->title_urgent;
marks_texture = view ? view->marks_urgent : NULL; marks_texture = view ? view->marks_urgent : NULL;
} else if (cstate->focused || parent_focused) { } else if (cstate->focused || parent->focused) {
colors = &config->border_colors.focused; colors = &config->border_colors.focused;
title_texture = child->title_focused; title_texture = child->title_focused;
marks_texture = view ? view->marks_focused : NULL; 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; colors = &config->border_colors.focused_inactive;
title_texture = child->title_focused_inactive; title_texture = child->title_focused_inactive;
marks_texture = view ? view->marks_focused_inactive : NULL; 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; marks_texture = view ? view->marks_unfocused : NULL;
} }
int y = pstate->swayc_y + titlebar_height * i; int y = parent->box.y + titlebar_height * i;
render_titlebar(output, damage, child, pstate->swayc_x, y, render_titlebar(output, damage, child, parent->box.x, y,
pstate->swayc_width, colors, title_texture, marks_texture); parent->box.width, colors, title_texture, marks_texture);
if (child == current) { if (child == current) {
current_colors = colors; current_colors = colors;
@ -729,36 +732,69 @@ static void render_container_stacked(struct sway_output *output,
} }
// Render surface and left/right/bottom borders // Render surface and left/right/bottom borders
if (current->type == C_VIEW) { if (current->view) {
render_view(output, damage, current, current_colors); render_view(output, damage, current, current_colors);
} else { } else {
render_container(output, damage, current, render_container(output, damage, current,
parent_focused || current->current.focused); parent->focused || current->current.focused);
} }
} }
static void render_container(struct sway_output *output, static void render_containers(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con, pixman_region32_t *damage, struct parent_data *parent) {
bool parent_focused) { switch (parent->layout) {
switch (con->current.layout) {
case L_NONE: case L_NONE:
case L_HORIZ: case L_HORIZ:
case L_VERT: case L_VERT:
render_container_simple(output, damage, con, parent_focused); render_containers_linear(output, damage, parent);
break; break;
case L_STACKED: case L_STACKED:
render_container_stacked(output, damage, con, parent_focused); render_containers_stacked(output, damage, parent);
break; break;
case L_TABBED: case L_TABBED:
render_container_tabbed(output, damage, con, parent_focused); render_containers_tabbed(output, damage, parent);
break; break;
} }
} }
static void render_container(struct sway_output *output,
pixman_region32_t *damage, struct sway_container *con, bool focused) {
struct parent_data data = {
.layout = con->current.layout,
.box = {
.x = 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, static void render_floating_container(struct sway_output *soutput,
pixman_region32_t *damage, struct sway_container *con) { pixman_region32_t *damage, struct sway_container *con) {
if (con->type == C_VIEW) { if (con->view) {
struct sway_view *view = con->sway_view; struct sway_view *view = con->view;
struct border_colors *colors; struct border_colors *colors;
struct wlr_texture *title_texture; struct wlr_texture *title_texture;
struct wlr_texture *marks_texture; struct wlr_texture *marks_texture;
@ -777,10 +813,10 @@ static void render_floating_container(struct sway_output *soutput,
marks_texture = view->marks_unfocused; marks_texture = view->marks_unfocused;
} }
if (!view->swayc->current.using_csd) { if (!view->container->current.using_csd) {
if (con->current.border == B_NORMAL) { if (con->current.border == B_NORMAL) {
render_titlebar(soutput, damage, con, con->current.swayc_x, render_titlebar(soutput, damage, con, con->current.con_x,
con->current.swayc_y, con->current.swayc_width, colors, con->current.con_y, con->current.con_width, colors,
title_texture, marks_texture); title_texture, marks_texture);
} else if (con->current.border != B_NONE) { } else if (con->current.border != B_NONE) {
render_top_border(soutput, damage, con, colors); 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, static void render_floating(struct sway_output *soutput,
pixman_region32_t *damage) { pixman_region32_t *damage) {
for (int i = 0; i < root_container.current.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = struct sway_output *output = root->outputs->items[i];
root_container.current.children->items[i]; for (int j = 0; j < output->current.workspaces->length; ++j) {
for (int j = 0; j < output->current.children->length; ++j) { struct sway_workspace *ws = output->current.workspaces->items[j];
struct sway_container *ws = output->current.children->items[j];
if (!workspace_is_visible(ws)) { if (!workspace_is_visible(ws)) {
continue; continue;
} }
list_t *floating = ws->current.ws_floating; for (int k = 0; k < ws->current.floating->length; ++k) {
for (int k = 0; k < floating->length; ++k) { struct sway_container *floater = ws->current.floating->items[k];
struct sway_container *floater = floating->items[k];
render_floating_container(soutput, damage, floater); 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); pixman_region32_union_rect(damage, damage, 0, 0, width, height);
} }
struct sway_container *workspace = output_get_active_workspace(output); struct sway_workspace *workspace = output_get_active_workspace(output);
struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; struct sway_container *fullscreen_con = workspace->current.fullscreen;
if (output_has_opaque_overlay_layer_surface(output)) { if (output_has_opaque_overlay_layer_surface(output)) {
goto render_overlay; goto render_overlay;
@ -855,12 +889,11 @@ void output_render(struct sway_output *output, struct timespec *when,
} }
// TODO: handle views smaller than the output // TODO: handle views smaller than the output
if (fullscreen_con->type == C_VIEW) { if (fullscreen_con->view) {
if (fullscreen_con->sway_view->saved_buffer) { if (fullscreen_con->view->saved_buffer) {
render_saved_view(fullscreen_con->sway_view, render_saved_view(fullscreen_con->view, output, damage, 1.0f);
output, damage, 1.0f);
} else { } else {
render_view_toplevels(fullscreen_con->sway_view, render_view_toplevels(fullscreen_con->view,
output, damage, 1.0f); output, damage, 1.0f);
} }
} else { } else {
@ -868,8 +901,7 @@ void output_render(struct sway_output *output, struct timespec *when,
fullscreen_con->current.focused); fullscreen_con->current.focused);
} }
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
render_unmanaged(output, damage, render_unmanaged(output, damage, &root->xwayland_unmanaged);
&root_container.sway_root->xwayland_unmanaged);
#endif #endif
} else { } else {
float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; 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, render_layer(output, damage,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); &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); render_floating(output, damage);
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
render_unmanaged(output, damage, render_unmanaged(output, damage, &root->xwayland_unmanaged);
&root_container.sway_root->xwayland_unmanaged);
#endif #endif
render_layer(output, damage, render_layer(output, damage,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
} }
struct sway_seat *seat = input_manager_current_seat(input_manager); 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) {
render_view_popups(focus->sway_view, output, damage, focus->alpha); render_view_popups(focus->view, output, damage, focus->alpha);
} }
render_overlay: render_overlay:
render_layer(output, damage, render_layer(output, damage,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); &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: renderer_end:
if (debug.render_tree) { if (debug.render_tree) {
wlr_renderer_scissor(renderer, NULL); 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); wlr_output->transform_matrix, 0, 40, 1);
} }
if (debug.damage == DAMAGE_HIGHLIGHT) { if (debug.damage == DAMAGE_HIGHLIGHT) {

@ -12,6 +12,7 @@
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/node.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "list.h" #include "list.h"
@ -27,8 +28,12 @@ struct sway_transaction {
struct sway_transaction_instruction { struct sway_transaction_instruction {
struct sway_transaction *transaction; struct sway_transaction *transaction;
struct sway_container *container; struct sway_node *node;
struct sway_container_state state; union {
struct sway_output_state *output_state;
struct sway_workspace_state *workspace_state;
struct sway_container_state *container_state;
};
uint32_t serial; uint32_t serial;
}; };
@ -47,26 +52,24 @@ static void transaction_destroy(struct sway_transaction *transaction) {
for (int i = 0; i < transaction->instructions->length; ++i) { for (int i = 0; i < transaction->instructions->length; ++i) {
struct sway_transaction_instruction *instruction = struct sway_transaction_instruction *instruction =
transaction->instructions->items[i]; transaction->instructions->items[i];
struct sway_container *con = instruction->container; struct sway_node *node = instruction->node;
con->ntxnrefs--; node->ntxnrefs--;
if (con->instruction == instruction) { if (node->instruction == instruction) {
con->instruction = NULL; node->instruction = NULL;
} }
if (con->destroying && con->ntxnrefs == 0) { if (node->destroying && node->ntxnrefs == 0) {
switch (con->type) { switch (node->type) {
case C_ROOT: case N_ROOT:
sway_assert(false, "Never reached");
break; break;
case C_OUTPUT: case N_OUTPUT:
output_destroy(con); output_destroy(node->sway_output);
break; break;
case C_WORKSPACE: case N_WORKSPACE:
workspace_destroy(con); workspace_destroy(node->sway_workspace);
break; break;
case C_CONTAINER: case N_CONTAINER:
case C_VIEW: container_destroy(node->sway_container);
container_destroy(con);
break;
case C_TYPES:
break; break;
} }
} }
@ -80,22 +83,79 @@ static void transaction_destroy(struct sway_transaction *transaction) {
free(transaction); free(transaction);
} }
static void copy_pending_state(struct sway_container *container, static void copy_output_state(struct sway_output *output,
struct sway_container_state *state) { 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;
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->layout = container->layout;
state->swayc_x = container->x; state->con_x = container->x;
state->swayc_y = container->y; state->con_y = container->y;
state->swayc_width = container->width; state->con_width = container->width;
state->swayc_height = container->height; state->con_height = container->height;
state->is_fullscreen = container->is_fullscreen; 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; state->parent = container->parent;
state->workspace = container->workspace;
if (container->type == C_VIEW) { if (container->view) {
struct sway_view *view = container->sway_view; struct sway_view *view = container->view;
state->view_x = view->x; state->view_x = view->x;
state->view_y = view->y; state->view_y = view->y;
state->view_width = view->width; 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_right = view->border_right;
state->border_bottom = view->border_bottom; state->border_bottom = view->border_bottom;
state->using_csd = view->using_csd; 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 { } else {
state->children = create_list(); state->children = create_list();
list_cat(state->children, container->children); list_cat(state->children, container->children);
} }
struct sway_seat *seat = input_manager_current_seat(input_manager); 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) { if (!container->view) {
// Set focused_inactive_child to the direct tiling child struct sway_node *focus = seat_get_active_child(seat, &container->node);
struct sway_container *focus = state->focused_inactive_child = focus ? focus->sway_container : NULL;
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);
} }
} }
static void transaction_add_container(struct sway_transaction *transaction, static void transaction_add_node(struct sway_transaction *transaction,
struct sway_container *container) { struct sway_node *node) {
struct sway_transaction_instruction *instruction = struct sway_transaction_instruction *instruction =
calloc(1, sizeof(struct sway_transaction_instruction)); calloc(1, sizeof(struct sway_transaction_instruction));
if (!sway_assert(instruction, "Unable to allocate instruction")) { if (!sway_assert(instruction, "Unable to allocate instruction")) {
return; return;
} }
instruction->transaction = transaction; 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); 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)); "(%.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) { for (int i = 0; i < transaction->instructions->length; ++i) {
struct sway_transaction_instruction *instruction = struct sway_transaction_instruction *instruction =
transaction->instructions->items[i]; 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);
}
// 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 switch (node->type) {
desktop_damage_whole_container(container); case N_ROOT:
if (container->type == C_VIEW && container->sway_view->surface) { break;
struct sway_view *view = container->sway_view; case N_OUTPUT:
struct wlr_surface *surface = view->surface; apply_output_state(node->sway_output, instruction->output_state);
struct wlr_box box = { break;
.x = container->current.view_x - view->geometry.x, case N_WORKSPACE:
.y = container->current.view_y - view->geometry.y, apply_workspace_state(node->sway_workspace,
.width = surface->current.width, instruction->workspace_state);
.height = surface->current.height, break;
}; case N_CONTAINER:
desktop_damage_box(&box); apply_container_state(node->sway_container,
instruction->container_state);
break;
} }
container->instruction = NULL; node->instruction = NULL;
if (container->type == C_CONTAINER || container->type == C_VIEW) {
container_discover_outputs(container);
}
} }
} }
static void transaction_commit(struct sway_transaction *transaction); static void transaction_commit(struct sway_transaction *transaction);
// Return true if both transactions operate on the same containers // Return true if both transactions operate on the same nodes
static bool transaction_same_containers(struct sway_transaction *a, static bool transaction_same_nodes(struct sway_transaction *a,
struct sway_transaction *b) { struct sway_transaction *b) {
if (a->instructions->length != b->instructions->length) { if (a->instructions->length != b->instructions->length) {
return false; return false;
@ -236,7 +326,7 @@ static bool transaction_same_containers(struct sway_transaction *a,
for (int i = 0; i < a->instructions->length; ++i) { for (int i = 0; i < a->instructions->length; ++i) {
struct sway_transaction_instruction *a_inst = a->instructions->items[i]; struct sway_transaction_instruction *a_inst = a->instructions->items[i];
struct sway_transaction_instruction *b_inst = b->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; return false;
} }
} }
@ -267,7 +357,7 @@ static void transaction_progress_queue() {
while (server.transactions->length >= 2) { while (server.transactions->length >= 2) {
struct sway_transaction *a = server.transactions->items[0]; struct sway_transaction *a = server.transactions->items[0];
struct sway_transaction *b = server.transactions->items[1]; 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); list_del(server.transactions, 0);
transaction_destroy(a); transaction_destroy(a);
} else { } else {
@ -289,16 +379,18 @@ static int handle_timeout(void *data) {
return 0; return 0;
} }
static bool should_configure(struct sway_container *con, static bool should_configure(struct sway_node *node,
struct sway_transaction_instruction *instruction) { struct sway_transaction_instruction *instruction) {
if (con->type != C_VIEW) { if (!node_is_view(node)) {
return false; return false;
} }
if (con->destroying) { if (node->destroying) {
return false; return false;
} }
if (con->current.view_width == instruction->state.view_width && struct sway_container_state *cstate = &node->sway_container->current;
con->current.view_height == instruction->state.view_height) { struct sway_container_state *istate = instruction->container_state;
if (cstate->view_width == istate->view_width &&
cstate->view_height == istate->view_height) {
return false; return false;
} }
return true; return true;
@ -311,13 +403,13 @@ static void transaction_commit(struct sway_transaction *transaction) {
for (int i = 0; i < transaction->instructions->length; ++i) { for (int i = 0; i < transaction->instructions->length; ++i) {
struct sway_transaction_instruction *instruction = struct sway_transaction_instruction *instruction =
transaction->instructions->items[i]; transaction->instructions->items[i];
struct sway_container *con = instruction->container; struct sway_node *node = instruction->node;
if (should_configure(con, instruction)) { if (should_configure(node, instruction)) {
instruction->serial = view_configure(con->sway_view, instruction->serial = view_configure(node->sway_container->view,
instruction->state.view_x, instruction->container_state->view_x,
instruction->state.view_y, instruction->container_state->view_y,
instruction->state.view_width, instruction->container_state->view_width,
instruction->state.view_height); instruction->container_state->view_height);
++transaction->num_waiting; ++transaction->num_waiting;
// From here on we are rendering a saved buffer of the view, which // 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 // as soon as possible. Additionally, this is required if a view is
// mapping and its default geometry doesn't intersect an output. // mapping and its default geometry doesn't intersect an output.
struct timespec when; 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) { if (node_is_view(node) && !node->sway_container->view->saved_buffer) {
view_save_buffer(con->sway_view); view_save_buffer(node->sway_container->view);
memcpy(&con->sway_view->saved_geometry, &con->sway_view->geometry, memcpy(&node->sway_container->view->saved_geometry,
&node->sway_container->view->geometry,
sizeof(struct wlr_box)); sizeof(struct wlr_box));
} }
con->instruction = instruction; node->instruction = instruction;
} }
transaction->num_configures = transaction->num_waiting; transaction->num_configures = transaction->num_waiting;
if (debug.txn_timings) { if (debug.txn_timings) {
@ -381,7 +475,7 @@ static void set_instruction_ready(
transaction, transaction,
transaction->num_configures - transaction->num_waiting + 1, transaction->num_configures - transaction->num_waiting + 1,
transaction->num_configures, ms, 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. // 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); wl_event_source_timer_update(transaction->timer, 0);
} }
instruction->container->instruction = NULL; instruction->node->instruction = NULL;
transaction_progress_queue(); transaction_progress_queue();
} }
void transaction_notify_view_ready_by_serial(struct sway_view *view, void transaction_notify_view_ready_by_serial(struct sway_view *view,
uint32_t serial) { uint32_t serial) {
struct sway_transaction_instruction *instruction = view->swayc->instruction; struct sway_transaction_instruction *instruction =
if (view->swayc->instruction->serial == serial) { view->container->node.instruction;
if (instruction->serial == serial) {
set_instruction_ready(instruction); set_instruction_ready(instruction);
} }
} }
void transaction_notify_view_ready_by_size(struct sway_view *view, void transaction_notify_view_ready_by_size(struct sway_view *view,
int width, int height) { int width, int height) {
struct sway_transaction_instruction *instruction = view->swayc->instruction; struct sway_transaction_instruction *instruction =
if (instruction->state.view_width == width && view->container->node.instruction;
instruction->state.view_height == height) { if (instruction->container_state->view_width == width &&
instruction->container_state->view_height == height) {
set_instruction_ready(instruction); set_instruction_ready(instruction);
} }
} }
void transaction_commit_dirty(void) { void transaction_commit_dirty(void) {
if (!server.dirty_containers->length) { if (!server.dirty_nodes->length) {
return; return;
} }
struct sway_transaction *transaction = transaction_create(); struct sway_transaction *transaction = transaction_create();
if (!transaction) { if (!transaction) {
return; return;
} }
for (int i = 0; i < server.dirty_containers->length; ++i) { for (int i = 0; i < server.dirty_nodes->length; ++i) {
struct sway_container *container = server.dirty_containers->items[i]; struct sway_node *node = server.dirty_nodes->items[i];
transaction_add_container(transaction, container); transaction_add_node(transaction, node);
container->dirty = false; node->dirty = false;
} }
server.dirty_containers->length = 0; server.dirty_nodes->length = 0;
list_add(server.transactions, transaction); list_add(server.transactions, transaction);

@ -11,10 +11,12 @@
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h"
static const struct sway_view_child_impl popup_impl; 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 sway_view *view = popup->child.view;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; 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 // the output box expressed in the coordinate system of the toplevel parent
// of the popup // of the popup
struct wlr_box output_toplevel_sx_box = { struct wlr_box output_toplevel_sx_box = {
.x = output->x - view->x, .x = output->wlr_output->lx - view->x,
.y = output->y - view->y, .y = output->wlr_output->ly - view->y,
.width = output->width, .width = output->wlr_output->width,
.height = output->height, .height = output->wlr_output->height,
}; };
wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 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 sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
if (!view->swayc) { if (view->container->node.instruction) {
return;
}
if (view->swayc->instruction) {
wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry); wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry);
transaction_notify_view_ready_by_serial(view, transaction_notify_view_ready_by_serial(view,
xdg_surface->configure_serial); 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); wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
if ((new_geo.width != view->width || new_geo.height != view->height) && 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 // A floating view has unexpectedly sent a new size
desktop_damage_view(view); desktop_damage_view(view);
view_update_size(view, new_geo.width, new_geo.height); 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; 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_workspace(view->container->workspace);
arrange_windows(output);
transaction_commit_dirty(); 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 = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_move); wl_container_of(listener, xdg_shell_view, request_move);
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return; return;
} }
struct wlr_xdg_toplevel_move_event *e = data; struct wlr_xdg_toplevel_move_event *e = data;
struct sway_seat *seat = e->seat->seat->data; struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) { 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 = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_resize); wl_container_of(listener, xdg_shell_view, request_resize);
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return; return;
} }
struct wlr_xdg_toplevel_resize_event *e = data; struct wlr_xdg_toplevel_resize_event *e = data;
struct sway_seat *seat = e->seat->seat->data; struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) { 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); 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); view_map(view, view->wlr_xdg_surface->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) { if (xdg_surface->toplevel->client_pending.fullscreen) {
container_set_fullscreen(view->swayc, true); container_set_fullscreen(view->container, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); arrange_workspace(view->container->workspace);
arrange_windows(ws);
} else { } else {
arrange_windows(view->swayc->parent); if (view->container->parent) {
arrange_container(view->container->parent);
} else {
arrange_workspace(view->container->workspace);
}
} }
transaction_commit_dirty(); 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 = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, destroy); wl_container_of(listener, xdg_shell_view, destroy);
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
if (!sway_assert(view->swayc == NULL || view->swayc->destroying, if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
"Tried to destroy a mapped view")) {
return; return;
} }
wl_list_remove(&xdg_shell_view->destroy.link); wl_list_remove(&xdg_shell_view->destroy.link);

@ -10,10 +10,12 @@
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h"
static const struct sway_view_child_impl popup_impl; 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 sway_view *view = popup->child.view;
struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup; 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 // the output box expressed in the coordinate system of the toplevel parent
// of the popup // of the popup
struct wlr_box output_toplevel_sx_box = { struct wlr_box output_toplevel_sx_box = {
.x = output->x - view->x, .x = output->wlr_output->lx - view->x,
.y = output->y - view->y, .y = output->wlr_output->ly - view->y,
.width = output->width, .width = output->wlr_output->width,
.height = output->height, .height = output->wlr_output->height,
}; };
wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); 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 sway_view *view = &xdg_shell_v6_view->view;
struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6; struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
if (!view->swayc) { if (view->container->node.instruction) {
return;
}
if (view->swayc->instruction) {
wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry); wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry);
transaction_notify_view_ready_by_serial(view, transaction_notify_view_ready_by_serial(view,
xdg_surface_v6->configure_serial); 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); wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo);
if ((new_geo.width != view->width || new_geo.height != view->height) && 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 // A floating view has unexpectedly sent a new size
desktop_damage_view(view); desktop_damage_view(view);
view_update_size(view, new_geo.width, new_geo.height); 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; 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_workspace(view->container->workspace);
arrange_windows(output);
transaction_commit_dirty(); 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 = struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, request_move); wl_container_of(listener, xdg_shell_v6_view, request_move);
struct sway_view *view = &xdg_shell_v6_view->view; struct sway_view *view = &xdg_shell_v6_view->view;
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return; return;
} }
struct wlr_xdg_toplevel_v6_move_event *e = data; struct wlr_xdg_toplevel_v6_move_event *e = data;
struct sway_seat *seat = e->seat->seat->data; struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) { 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 = struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, request_resize); wl_container_of(listener, xdg_shell_v6_view, request_resize);
struct sway_view *view = &xdg_shell_v6_view->view; struct sway_view *view = &xdg_shell_v6_view->view;
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return; return;
} }
struct wlr_xdg_toplevel_v6_resize_event *e = data; struct wlr_xdg_toplevel_v6_resize_event *e = data;
struct sway_seat *seat = e->seat->seat->data; struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) { 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); 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); view_map(view, view->wlr_xdg_surface_v6->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) { if (xdg_surface->toplevel->client_pending.fullscreen) {
container_set_fullscreen(view->swayc, true); container_set_fullscreen(view->container, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); arrange_workspace(view->container->workspace);
arrange_windows(ws);
} else { } else {
arrange_windows(view->swayc->parent); if (view->container->parent) {
arrange_container(view->container->parent);
} else {
arrange_workspace(view->container->workspace);
}
} }
transaction_commit_dirty(); transaction_commit_dirty();

@ -59,8 +59,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
wl_container_of(listener, surface, map); wl_container_of(listener, surface, map);
struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface;
wl_list_insert(root_container.sway_root->xwayland_unmanaged.prev, wl_list_insert(root->xwayland_unmanaged.prev, &surface->link);
&surface->link);
wl_signal_add(&xsurface->surface->events.commit, &surface->commit); wl_signal_add(&xsurface->surface->events.commit, &surface->commit);
surface->commit.notify = unmanaged_handle_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 == if (seat->wlr_seat->keyboard_state.focused_surface ==
xsurface->surface) { xsurface->surface) {
// Restore focus // Restore focus
struct sway_container *previous = struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
seat_get_focus_inactive(seat, &root_container);
if (previous) { if (previous) {
// Hack to get seat to re-focus the return value of get_focus // 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); 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_xwayland_surface *xsurface = view->wlr_xwayland_surface;
struct wlr_surface_state *state = &xsurface->surface->current; struct wlr_surface_state *state = &xsurface->surface->current;
if (view->swayc->instruction) { if (view->container->node.instruction) {
get_geometry(view, &view->geometry); get_geometry(view, &view->geometry);
transaction_notify_view_ready_by_size(view, transaction_notify_view_ready_by_size(view,
state->width, state->height); state->width, state->height);
@ -308,7 +306,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
get_geometry(view, &new_geo); get_geometry(view, &new_geo);
if ((new_geo.width != view->width || new_geo.height != view->height) && 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 // A floating view has unexpectedly sent a new size
// eg. The Firefox "Save As" dialog when downloading a file // eg. The Firefox "Save As" dialog when downloading a file
desktop_damage_view(view); desktop_damage_view(view);
@ -391,11 +389,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
view_map(view, xsurface->surface); view_map(view, xsurface->surface);
if (xsurface->fullscreen) { if (xsurface->fullscreen) {
container_set_fullscreen(view->swayc, true); container_set_fullscreen(view->container, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); arrange_workspace(view->container->workspace);
arrange_windows(ws);
} else { } else {
arrange_windows(view->swayc->parent); if (view->container->parent) {
arrange_container(view->container->parent);
} else {
arrange_workspace(view->container->workspace);
}
} }
transaction_commit_dirty(); transaction_commit_dirty();
} }
@ -411,13 +412,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
ev->width, ev->height); ev->width, ev->height);
return; return;
} }
if (container_is_floating(view->swayc)) { if (container_is_floating(view->container)) {
configure(view, view->swayc->current.view_x, configure(view, view->container->current.view_x,
view->swayc->current.view_y, ev->width, ev->height); view->container->current.view_y, ev->width, ev->height);
} else { } else {
configure(view, view->swayc->current.view_x, configure(view, view->container->current.view_x,
view->swayc->current.view_y, view->swayc->current.view_width, view->container->current.view_y,
view->swayc->current.view_height); 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) { if (!xsurface->mapped) {
return; 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_workspace(view->container->workspace);
arrange_windows(output);
transaction_commit_dirty(); transaction_commit_dirty();
} }
@ -444,11 +445,11 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
if (!xsurface->mapped) { if (!xsurface->mapped) {
return; return;
} }
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return; return;
} }
struct sway_seat *seat = input_manager_current_seat(input_manager); 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) { 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) { if (!xsurface->mapped) {
return; return;
} }
if (!container_is_floating(view->swayc)) { if (!container_is_floating(view->container)) {
return; return;
} }
struct wlr_xwayland_resize_event *e = data; struct wlr_xwayland_resize_event *e = data;
struct sway_seat *seat = input_manager_current_seat(input_manager); 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) { static void handle_request_activate(struct wl_listener *listener, void *data) {

@ -20,6 +20,7 @@
#include "sway/layers.h" #include "sway/layers.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.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). * 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 sway_seat *seat, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy) { struct wlr_surface **surface, double *sx, double *sy) {
// check for unmanaged views first // check for unmanaged views first
#ifdef HAVE_XWAYLAND #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; struct sway_xwayland_unmanaged *unmanaged_surface;
wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
struct wlr_xwayland_surface *xsurface = struct wlr_xwayland_surface *xsurface =
@ -75,67 +76,64 @@ static struct sway_container *container_at_coords(
} }
#endif #endif
// find the output the cursor is on // 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( struct wlr_output *wlr_output = wlr_output_layout_output_at(
output_layout, lx, ly); root->output_layout, lx, ly);
if (wlr_output == NULL) { if (wlr_output == NULL) {
return NULL; return NULL;
} }
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
double ox = lx, oy = ly; 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 // find the focused workspace on the output for this seat
struct sway_container *ws = seat_get_focus_inactive(seat, output->swayc); struct sway_workspace *ws = output_get_active_workspace(output);
if (ws && ws->type != C_WORKSPACE) {
ws = container_parent(ws, C_WORKSPACE);
}
if (!ws) {
return output->swayc;
}
if ((*surface = layer_surface_at(output, if ((*surface = layer_surface_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
ox, oy, sx, sy))) { ox, oy, sx, sy))) {
return ws; return &ws->node;
} }
if (ws->sway_workspace->fullscreen) { if (ws->fullscreen) {
return tiling_container_at(ws->sway_workspace->fullscreen, lx, ly, struct sway_container *con =
surface, sx, sy); tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
if (con) {
return &con->node;
}
return NULL;
} }
if ((*surface = layer_surface_at(output, if ((*surface = layer_surface_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
ox, oy, sx, sy))) { ox, oy, sx, sy))) {
return ws; return &ws->node;
} }
struct sway_container *c; struct sway_container *c;
if ((c = container_at(ws, lx, ly, surface, sx, sy))) { if ((c = container_at(ws, lx, ly, surface, sx, sy))) {
return c; return &c->node;
} }
if ((*surface = layer_surface_at(output, if ((*surface = layer_surface_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
ox, oy, sx, sy))) { ox, oy, sx, sy))) {
return ws; return &ws->node;
} }
if ((*surface = layer_surface_at(output, if ((*surface = layer_surface_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
ox, oy, sx, sy))) { ox, oy, sx, sy))) {
return ws; return &ws->node;
} }
c = seat_get_active_child(seat, output->swayc); return &ws->node;
if (c) { }
return c;
}
if (!c && output->swayc->children->length) {
c = output->swayc->children->items[0];
return c;
}
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, // Iterate the parents until we find one with the layout we want,
// then check if the child has siblings between it and the edge. // then check if the child has siblings between it and the edge.
while (cont->type != C_OUTPUT) { while (cont) {
if (cont->parent->layout == layout) { if (container_parent_layout(cont) == layout) {
int index = list_find(cont->parent->children, cont); list_t *siblings = container_get_siblings(cont);
int index = list_find(siblings, cont);
if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) {
return false; return false;
} }
if (index < cont->parent->children->length - 1 && if (index < siblings->length - 1 &&
(edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) {
return false; 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, static enum wlr_edges find_edge(struct sway_container *cont,
struct sway_cursor *cursor) { struct sway_cursor *cursor) {
if (cont->type != C_VIEW) { if (!cont->view) {
return WLR_EDGE_NONE; 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) { if (view->border == B_NONE || !view->border_thickness || view->using_csd) {
return WLR_EDGE_NONE; 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, static void handle_down_motion(struct sway_seat *seat,
struct sway_cursor *cursor, uint32_t time_msec) { struct sway_cursor *cursor, uint32_t time_msec) {
struct sway_container *con = seat->op_container; 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_x = cursor->cursor->x - seat->op_ref_lx;
double moved_y = cursor->cursor->y - seat->op_ref_ly; double moved_y = cursor->cursor->y - seat->op_ref_ly;
double sx = seat->op_ref_con_lx + moved_x; 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 if (config->floating_maximum_width == -1) { // no maximum
*max_width = INT_MAX; *max_width = INT_MAX;
} else if (config->floating_maximum_width == 0) { // automatic } else if (config->floating_maximum_width == 0) { // automatic
struct sway_container *ws = container_parent(con, C_WORKSPACE); *max_width = con->workspace->width;
*max_width = ws->width;
} else { } else {
*max_width = config->floating_maximum_width; *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 if (config->floating_maximum_height == -1) { // no maximum
*max_height = INT_MAX; *max_height = INT_MAX;
} else if (config->floating_maximum_height == 0) { // automatic } else if (config->floating_maximum_height == 0) { // automatic
struct sway_container *ws = container_parent(con, C_WORKSPACE); *max_height = con->workspace->height;
*max_height = ws->height;
} else { } else {
*max_height = config->floating_maximum_height; *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)); height = fmax(min_height, fmin(height, max_height));
// Apply the view's min/max size // 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; 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); &view_min_height, &view_max_height);
width = fmax(view_min_width, fmin(width, view_max_width)); width = fmax(view_min_width, fmin(width, view_max_width));
height = fmax(view_min_height, fmin(height, view_max_height)); 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->width += relative_grow_width;
con->height += relative_grow_height; con->height += relative_grow_height;
if (con->type == C_VIEW) { if (con->view) {
struct sway_view *view = con->sway_view; struct sway_view *view = con->view;
view->x += relative_grow_x; view->x += relative_grow_x;
view->y += relative_grow_y; view->y += relative_grow_y;
view->width += relative_grow_width; view->width += relative_grow_width;
view->height += relative_grow_height; view->height += relative_grow_height;
} }
arrange_windows(con); arrange_container(con);
} }
static void handle_resize_tiling_motion(struct sway_seat *seat, 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; struct wlr_surface *surface = NULL;
double sx, sy; double sx, sy;
// Find the container beneath the pointer's previous position // Find the node beneath the pointer's previous position
struct sway_container *prev_c = container_at_coords(seat, struct sway_node *prev_node = node_at_coords(seat,
cursor->previous.x, cursor->previous.y, &surface, &sx, &sy); cursor->previous.x, cursor->previous.y, &surface, &sx, &sy);
// Update the stored previous position // Update the stored previous position
cursor->previous.x = cursor->cursor->x; cursor->previous.x = cursor->cursor->x;
cursor->previous.y = cursor->cursor->y; 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); cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
if (c && config->focus_follows_mouse && allow_refocusing) { if (node && config->focus_follows_mouse && allow_refocusing) {
struct sway_container *focus = seat_get_focus(seat); struct sway_node *focus = seat_get_focus(seat);
if (focus && c->type == C_WORKSPACE) { if (focus && node->type == N_WORKSPACE) {
// Only follow the mouse if it would move to a new output // Only follow the mouse if it would move to a new output
// Otherwise we'll focus the workspace, which is probably wrong // Otherwise we'll focus the workspace, which is probably wrong
if (focus->type != C_OUTPUT) { struct sway_output *focused_output = node_get_output(focus);
focus = container_parent(focus, C_OUTPUT); struct sway_output *output = node_get_output(node);
} if (output != focused_output) {
struct sway_container *output = c; seat_set_focus_warp(seat, node, false, true);
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) { } else if (node->type == N_CONTAINER && node->sway_container->view) {
// Focus c if the following are true: // Focus node if the following are true:
// - cursor is over a new view, i.e. entered a new window; and // - 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 new view is visible, i.e. not hidden in a stack or tab; and
// - the seat does not have a keyboard grab // - the seat does not have a keyboard grab
if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
c != prev_c && node != prev_node &&
view_is_visible(c->sway_view)) { view_is_visible(node->sway_container->view)) {
seat_set_focus_warp(seat, c, false, true); seat_set_focus_warp(seat, node, false, true);
} else { } else {
struct sway_container *next_focus = struct sway_node *next_focus =
seat_get_focus_inactive(seat, &root_container); seat_get_focus_inactive(seat, &root->node);
if (next_focus && next_focus->type == C_VIEW && if (next_focus && next_focus->type == N_CONTAINER &&
view_is_visible(next_focus->sway_view)) { node->sway_container->view &&
view_is_visible(next_focus->sway_container->view)) {
seat_set_focus_warp(seat, next_focus, false, true); 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) { if (client != cursor->image_client) {
cursor_set_image(cursor, "left_ptr", client); cursor_set_image(cursor, "left_ptr", client);
} }
} else if (c) { } else if (node && node->type == N_CONTAINER) {
// Try a container's resize edge // Try a node's resize edge
enum wlr_edges edge = find_resize_edge(c, cursor); enum wlr_edges edge = find_resize_edge(node->sway_container, cursor);
if (edge == WLR_EDGE_NONE) { if (edge == WLR_EDGE_NONE) {
cursor_set_image(cursor, "left_ptr", NULL); 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); cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL);
} else { } else {
if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { 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 // Handle tiling resize via border
if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED &&
!is_floating) { !is_floating) {
seat_set_focus(seat, cont); seat_set_focus(seat, &cont->node);
seat_begin_resize_tiling(seat, cont, button, edge); seat_begin_resize_tiling(seat, cont, button, edge);
return; return;
} }
@ -713,7 +706,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
image = "sw-resize"; image = "sw-resize";
} }
cursor_set_image(seat->cursor, image, NULL); 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); seat_begin_resize_tiling(seat, cont, button, edge);
return; 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; uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
if (button == btn_move && state == WLR_BUTTON_PRESSED && if (button == btn_move && state == WLR_BUTTON_PRESSED &&
(mod_pressed || on_titlebar)) { (mod_pressed || on_titlebar)) {
while (cont->parent->type != C_WORKSPACE) { while (cont->parent) {
cont = cont->parent; cont = cont->parent;
} }
seat_begin_move(seat, cont, button); seat_begin_move(seat, cont, button);
@ -747,7 +740,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
BTN_LEFT : BTN_RIGHT; BTN_LEFT : BTN_RIGHT;
if (mod_pressed && button == btn_resize) { if (mod_pressed && button == btn_resize) {
struct sway_container *floater = cont; struct sway_container *floater = cont;
while (floater->parent->type != C_WORKSPACE) { while (floater->parent) {
floater = floater->parent; floater = floater->parent;
} }
edge = 0; edge = 0;
@ -762,7 +755,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
// Handle mousedown on a container surface // Handle mousedown on a container surface
if (surface && cont && state == WLR_BUTTON_PRESSED) { 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_pointer_notify_button(seat, time_msec, button, state);
seat_begin_down(seat, cont, button, sx, sy); seat_begin_down(seat, cont, button, sx, sy);
return; return;
@ -770,7 +763,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
// Handle clicking a container surface // Handle clicking a container surface
if (cont) { if (cont) {
seat_set_focus(seat, cont); seat_set_focus(seat, &cont->node);
seat_pointer_notify_button(seat, time_msec, button, state); seat_pointer_notify_button(seat, time_msec, button, state);
return; return;
} }
@ -1025,8 +1018,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
cursor->previous.y = wlr_cursor->y; cursor->previous.y = wlr_cursor->y;
cursor->seat = seat; cursor->seat = seat;
wlr_cursor_attach_output_layout(wlr_cursor, wlr_cursor_attach_output_layout(wlr_cursor, root->output_layout);
root_container.sway_root->output_layout);
// input events // input events
wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);

@ -293,12 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
struct sway_seat *seat; struct sway_seat *seat;
wl_list_for_each(seat, &input_manager->seats, link) { wl_list_for_each(seat, &input_manager->seats, link) {
seat_set_exclusive_client(seat, NULL); seat_set_exclusive_client(seat, NULL);
struct sway_container *previous = seat_get_focus(seat); struct sway_node *previous = seat_get_focus(seat);
if (previous) { 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 // 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); 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, bool input_manager_has_focus(struct sway_input_manager *input,
struct sway_container *container) { struct sway_node *node) {
struct sway_seat *seat = NULL; struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input->seats, link) { wl_list_for_each(seat, &input->seats, link) {
if (seat_get_focus(seat) == container) { if (seat_get_focus(seat) == node) {
return true; 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, void input_manager_set_focus(struct sway_input_manager *input,
struct sway_container *container) { struct sway_node *node) {
struct sway_seat *seat; struct sway_seat *seat;
wl_list_for_each(seat, &input->seats, link) { wl_list_for_each(seat, &input->seats, link) {
seat_set_focus(seat, container); seat_set_focus(seat, node);
} }
} }

@ -47,48 +47,36 @@ void seat_destroy(struct sway_seat *seat) {
seat_device_destroy(seat_device); seat_device_destroy(seat_device);
} }
sway_cursor_destroy(seat->cursor); 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->new_drag_icon.link);
wl_list_remove(&seat->link); wl_list_remove(&seat->link);
wlr_seat_destroy(seat->wlr_seat); wlr_seat_destroy(seat->wlr_seat);
} }
static struct sway_seat_container *seat_container_from_container( static struct sway_seat_node *seat_node_from_node(
struct sway_seat *seat, struct sway_container *con); struct sway_seat *seat, struct sway_node *node);
static void seat_container_destroy(struct sway_seat_container *seat_con) { static void seat_node_destroy(struct sway_seat_node *seat_node) {
struct sway_container *con = seat_con->container; wl_list_remove(&seat_node->destroy.link);
struct sway_container *child = NULL; wl_list_remove(&seat_node->link);
free(seat_node);
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);
} }
/** /**
* Activate all views within this container recursively. * Activate all views within this container recursively.
*/ */
static void seat_send_activate(struct sway_container *con, static void seat_send_activate(struct sway_node *node, struct sway_seat *seat) {
struct sway_seat *seat) { if (node_is_view(node)) {
if (con->type == C_VIEW) { if (!seat_is_input_allowed(seat, node->sway_container->view->surface)) {
if (!seat_is_input_allowed(seat, con->sway_view->surface)) {
wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited"); wlr_log(WLR_DEBUG, "Refusing to set focus, input is inhibited");
return; return;
} }
view_set_activated(con->sway_view, true); view_set_activated(node->sway_container->view, true);
} else { } else {
for (int i = 0; i < con->children->length; ++i) { list_t *children = node_get_children(node);
struct sway_container *child = con->children->items[i]; for (int i = 0; i < children->length; ++i) {
seat_send_activate(child, seat); 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 * If con is a container, set all child views as active and don't enable
* keyboard input on any. * keyboard input on any.
*/ */
static void seat_send_focus(struct sway_container *con, static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
struct sway_seat *seat) { seat_send_activate(node, seat);
seat_send_activate(con, seat);
if (con->type == C_VIEW struct sway_view *view = node->type == N_CONTAINER ?
&& seat_is_input_allowed(seat, con->sway_view->surface)) { node->sway_container->view : NULL;
if (view && seat_is_input_allowed(seat, view->surface)) {
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { if (view->type == SWAY_VIEW_XWAYLAND) {
struct wlr_xwayland *xwayland = struct wlr_xwayland *xwayland =
seat->input->server->xwayland.wlr_xwayland; seat->input->server->xwayland.wlr_xwayland;
wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 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); struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
if (keyboard) { if (keyboard) {
wlr_seat_keyboard_notify_enter(seat->wlr_seat, wlr_seat_keyboard_notify_enter(seat->wlr_seat,
con->sway_view->surface, keyboard->keycodes, view->surface, keyboard->keycodes,
keyboard->num_keycodes, &keyboard->modifiers); keyboard->num_keycodes, &keyboard->modifiers);
} else { } else {
wlr_seat_keyboard_notify_enter( 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, void seat_for_each_node(struct sway_seat *seat,
struct sway_container *container, void (*f)(struct sway_node *node, void *data), void *data) {
void (*f)(struct sway_container *container, void *data), void *data) { struct sway_seat_node *current = NULL;
struct sway_seat_container *current = NULL;
wl_list_for_each(current, &seat->focus_stack, link) { wl_list_for_each(current, &seat->focus_stack, link) {
if (current->container->parent == NULL) { f(current->node, data);
continue;
}
if (current->container->parent == container) {
f(current->container, data);
}
} }
} }
struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
struct sway_container *ancestor) { struct sway_node *ancestor) {
if (ancestor->type == C_VIEW) { if (ancestor->type == N_CONTAINER && ancestor->sway_container->view) {
return ancestor; return ancestor->sway_container;
} }
struct sway_seat_container *current; struct sway_seat_node *current;
wl_list_for_each(current, &seat->focus_stack, link) { wl_list_for_each(current, &seat->focus_stack, link) {
struct sway_container *con = current->container; struct sway_node *node = current->node;
if (con->type == C_VIEW && container_has_ancestor(con, ancestor)) { if (node->type == N_CONTAINER && node->sway_container->view &&
return con; node_has_ancestor(node, ancestor)) {
return node->sway_container;
} }
} }
return NULL; return NULL;
} }
static void handle_seat_container_destroy(struct wl_listener *listener, static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
void *data) { struct sway_seat_node *seat_node =
struct sway_seat_container *seat_con = wl_container_of(listener, seat_node, destroy);
wl_container_of(listener, seat_con, destroy); struct sway_seat *seat = seat_node->seat;
struct sway_seat *seat = seat_con->seat; struct sway_node *node = seat_node->node;
struct sway_container *con = seat_con->container; struct sway_node *parent = node_get_parent(node);
struct sway_container *parent = con->parent; struct sway_node *focus = seat_get_focus(seat);
struct sway_container *focus = seat_get_focus(seat);
bool set_focus = bool set_focus =
focus != NULL && focus != NULL &&
(focus == con || container_has_ancestor(focus, con)) && (focus == node || node_has_ancestor(focus, node)) &&
con->type != C_WORKSPACE; node->type == N_CONTAINER;
seat_container_destroy(seat_con); seat_node_destroy(seat_node);
if (set_focus) { if (set_focus) {
struct sway_container *next_focus = NULL; struct sway_node *next_focus = NULL;
while (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; next_focus = parent;
break; break;
} }
parent = parent->parent; parent = node_get_parent(parent);
} }
// the structure change might have caused it to move up to the top of // 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( static struct sway_seat_node *seat_node_from_node(
struct sway_seat *seat, struct sway_container *con) { struct sway_seat *seat, struct sway_node *node) {
if (con->type == C_ROOT || con->type == C_OUTPUT) { if (node->type == N_ROOT || node->type == N_OUTPUT) {
// these don't get seat containers ever // these don't get seat nodes ever
return NULL; return NULL;
} }
struct sway_seat_container *seat_con = NULL; struct sway_seat_node *seat_node = NULL;
wl_list_for_each(seat_con, &seat->focus_stack, link) { wl_list_for_each(seat_node, &seat->focus_stack, link) {
if (seat_con->container == con) { if (seat_node->node == node) {
return seat_con; return seat_node;
} }
} }
seat_con = calloc(1, sizeof(struct sway_seat_container)); seat_node = calloc(1, sizeof(struct sway_seat_node));
if (seat_con == NULL) { if (seat_node == NULL) {
wlr_log(WLR_ERROR, "could not allocate seat container"); wlr_log(WLR_ERROR, "could not allocate seat node");
return NULL; return NULL;
} }
seat_con->container = con; seat_node->node = node;
seat_con->seat = seat; seat_node->seat = seat;
wl_list_insert(seat->focus_stack.prev, &seat_con->link); wl_list_insert(seat->focus_stack.prev, &seat_node->link);
wl_signal_add(&con->events.destroy, &seat_con->destroy); wl_signal_add(&node->events.destroy, &seat_node->destroy);
seat_con->destroy.notify = handle_seat_container_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) { static void handle_new_node(struct wl_listener *listener, void *data) {
struct sway_seat *seat = wl_container_of(listener, seat, new_container); struct sway_seat *seat = wl_container_of(listener, seat, new_node);
struct sway_container *con = data; struct sway_node *node = data;
seat_container_from_container(seat, con); seat_node_from_node(seat, node);
} }
static void drag_icon_damage_whole(struct sway_drag_icon *icon) { 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); drag_icon_damage_whole(icon);
} }
static void drag_icon_handle_destroy(struct wl_listener *listener, static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) {
void *data) {
struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy); struct sway_drag_icon *icon = wl_container_of(listener, icon, destroy);
icon->wlr_drag_icon->data = NULL; icon->wlr_drag_icon->data = NULL;
wl_list_remove(&icon->link); 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; icon->destroy.notify = drag_icon_handle_destroy;
wl_signal_add(&wlr_drag_icon->events.destroy, &icon->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); 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 *seat = data;
struct sway_seat_container *seat_con = struct sway_seat_node *seat_node = seat_node_from_node(seat, node);
seat_container_from_container(seat, con); if (!seat_node) {
if (!seat_con) {
return; return;
} }
wl_list_remove(&seat_con->link); wl_list_remove(&seat_node->link);
wl_list_insert(&seat->focus_stack, &seat_con->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, 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 // init the focus stack
wl_list_init(&seat->focus_stack); wl_list_init(&seat->focus_stack);
root_for_each_workspace(collect_focus_iter, seat); root_for_each_workspace(collect_focus_workspace_iter, seat);
root_for_each_container(collect_focus_iter, seat); root_for_each_container(collect_focus_container_iter, seat);
wl_signal_add(&root_container.sway_root->events.new_container, wl_signal_add(&root->events.new_node, &seat->new_node);
&seat->new_container); seat->new_node.notify = handle_new_node;
seat->new_container.notify = handle_new_container;
wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon);
seat->new_drag_icon.notify = handle_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) { if (mapped_to_output != NULL) {
wlr_log(WLR_DEBUG, "Mapping input device %s to output %s", wlr_log(WLR_DEBUG, "Mapping input device %s to output %s",
sway_device->input_device->identifier, mapped_to_output); sway_device->input_device->identifier, mapped_to_output);
struct sway_container *output = NULL; struct sway_output *output = output_by_name(mapped_to_output);
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;
}
}
if (output) { if (output) {
wlr_cursor_map_input_to_output(seat->cursor->cursor, wlr_cursor_map_input_to_output(seat->cursor->cursor,
sway_device->input_device->wlr_device, sway_device->input_device->wlr_device, output->wlr_output);
output->sway_output->wlr_output); wlr_log(WLR_DEBUG, "Mapped to output %s", output->wlr_output->name);
wlr_log(WLR_DEBUG, "Mapped to output %s", output->name);
} }
} }
} }
@ -423,12 +407,12 @@ static void seat_configure_keyboard(struct sway_seat *seat,
sway_keyboard_configure(seat_device->keyboard); sway_keyboard_configure(seat_device->keyboard);
wlr_seat_set_keyboard(seat->wlr_seat, wlr_seat_set_keyboard(seat->wlr_seat,
seat_device->input_device->wlr_device); seat_device->input_device->wlr_device);
struct sway_container *focus = seat_get_focus(seat); struct sway_node *focus = seat_get_focus(seat);
if (focus && focus->type == C_VIEW) { if (focus && node_is_view(focus)) {
// force notify reenter to pick up the new configuration // force notify reenter to pick up the new configuration
wlr_seat_keyboard_clear_focus(seat->wlr_seat); wlr_seat_keyboard_clear_focus(seat->wlr_seat);
wlr_seat_keyboard_notify_enter(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); 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, void seat_configure_device(struct sway_seat *seat,
struct sway_input_device *input_device) { struct sway_input_device *input_device) {
struct sway_seat_device *seat_device = struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
seat_get_device(seat, input_device);
if (!seat_device) { if (!seat_device) {
return; return;
} }
@ -512,8 +495,7 @@ void seat_add_device(struct sway_seat *seat,
void seat_remove_device(struct sway_seat *seat, void seat_remove_device(struct sway_seat *seat,
struct sway_input_device *input_device) { struct sway_input_device *input_device) {
struct sway_seat_device *seat_device = struct sway_seat_device *seat_device = seat_get_device(seat, input_device);
seat_get_device(seat, input_device);
if (!seat_device) { if (!seat_device) {
return; return;
@ -539,11 +521,9 @@ void seat_configure_xcursor(struct sway_seat *seat) {
} }
} }
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output_container = struct sway_output *sway_output = root->outputs->items[i];
root_container.children->items[i]; struct wlr_output *output = sway_output->wlr_output;
struct wlr_output *output =
output_container->sway_output->wlr_output;
bool result = bool result =
wlr_xcursor_manager_load(seat->cursor->xcursor_manager, wlr_xcursor_manager_load(seat->cursor->xcursor_manager,
output->scale); output->scale);
@ -566,17 +546,20 @@ bool seat_is_input_allowed(struct sway_seat *seat,
return !seat->exclusive_client || seat->exclusive_client == client; 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`) // Unfocus the container and any children (eg. when leaving `focus parent`)
static void seat_send_unfocus(struct sway_container *container, static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) {
struct sway_seat *seat) { wlr_seat_keyboard_clear_focus(seat->wlr_seat);
if (container->type == C_VIEW) { if (node->type == N_WORKSPACE) {
wlr_seat_keyboard_clear_focus(seat->wlr_seat); workspace_for_each_container(node->sway_workspace, send_unfocus, seat);
view_set_activated(container->sway_view, false);
} else { } else {
for (int i = 0; i < container->children->length; ++i) { send_unfocus(node->sway_container, seat);
struct sway_container *child = container->children->items[i]; container_for_each_child(node->sway_container, send_unfocus, seat);
seat_send_unfocus(child, seat);
}
} }
} }
@ -586,26 +569,23 @@ static int handle_urgent_timeout(void *data) {
return 0; return 0;
} }
void seat_set_focus_warp(struct sway_seat *seat, void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node,
struct sway_container *container, bool warp, bool notify) { bool warp, bool notify) {
if (seat->focused_layer) { if (seat->focused_layer) {
return; return;
} }
struct sway_container *last_focus = seat_get_focus(seat); struct sway_node *last_focus = seat_get_focus(seat);
if (last_focus == container) { if (last_focus == node) {
return; return;
} }
struct sway_container *last_workspace = last_focus; struct sway_workspace *last_workspace = seat_get_focused_workspace(seat);
if (last_workspace && last_workspace->type != C_WORKSPACE) {
last_workspace = container_parent(last_workspace, C_WORKSPACE);
}
if (container == NULL) { if (node == NULL) {
// Close any popups on the old focus // Close any popups on the old focus
if (last_focus->type == C_VIEW) { if (node_is_view(last_focus)) {
view_close_popups(last_focus->sway_view); view_close_popups(last_focus->sway_container->view);
} }
seat_send_unfocus(last_focus, seat); seat_send_unfocus(last_focus, seat);
seat->has_focus = false; seat->has_focus = false;
@ -613,69 +593,70 @@ void seat_set_focus_warp(struct sway_seat *seat,
return; return;
} }
struct sway_container *new_workspace = container; struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
if (new_workspace->type != C_WORKSPACE) { node->sway_workspace : node->sway_container->workspace;
new_workspace = container_parent(new_workspace, C_WORKSPACE); struct sway_container *container = node->type == N_CONTAINER ?
} node->sway_container : NULL;
if (last_workspace == new_workspace // Deny setting focus to a view which is hidden by a fullscreen container
&& last_workspace->sway_workspace->fullscreen if (new_workspace && new_workspace->fullscreen && container &&
&& !container_is_fullscreen_or_child(container)) { !container_is_fullscreen_or_child(container)) {
return; return;
} }
struct sway_container *last_output = last_focus; struct sway_output *last_output = last_workspace ?
if (last_output && last_output->type != C_OUTPUT) { last_workspace->output : NULL;
last_output = container_parent(last_output, C_OUTPUT); struct sway_output *new_output = new_workspace->output;
}
struct sway_container *new_output = container;
if (new_output->type != C_OUTPUT) {
new_output = container_parent(new_output, C_OUTPUT);
}
// find new output's old workspace, which might have to be removed if empty // 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) { 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) { // Put the container parents on the focus stack, then the workspace, then
struct sway_seat_container *seat_con = // the focused container.
seat_container_from_container(seat, container); if (container) {
if (seat_con == NULL) { struct sway_container *parent = container->parent;
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);
while (parent) { while (parent) {
wl_list_remove(&parent->link); struct sway_seat_node *seat_node =
wl_list_insert(&seat->focus_stack, &parent->link); seat_node_from_node(seat, &parent->node);
container_set_dirty(parent->container); wl_list_remove(&seat_node->link);
wl_list_insert(&seat->focus_stack, &seat_node->link);
parent = seat_container_from_container(seat, node_set_dirty(&parent->node);
parent->container->parent); parent = parent->parent;
} }
}
wl_list_remove(&seat_con->link); if (new_workspace) {
wl_list_insert(&seat->focus_stack, &seat_con->link); 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) { if (last_focus) {
seat_send_unfocus(last_focus, seat); 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); seat_send_focus(&container->node, seat);
container_set_dirty(container);
container_set_dirty(container->parent); // for focused_inactive_child
} }
// emit ipc events // emit ipc events
if (notify && new_workspace && last_workspace != new_workspace) { if (notify && new_workspace && last_workspace != new_workspace) {
ipc_event_workspace(last_workspace, new_workspace, "focus"); ipc_event_workspace(last_workspace, new_workspace, "focus");
} }
if (container->type == C_VIEW) { if (container && container->view) {
ipc_event_window(container, "focus"); 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 // Close any popups on the old focus
if (last_focus && last_focus->type == C_VIEW) { if (last_focus && node_is_view(last_focus)) {
view_close_popups(last_focus->sway_view); view_close_popups(last_focus->sway_container->view);
} }
// If urgent, either unset the urgency or start a timer to unset it // If urgent, either unset the urgency or start a timer to unset it
if (container->type == C_VIEW && view_is_urgent(container->sway_view) && if (container && container->view && view_is_urgent(container->view) &&
!container->sway_view->urgent_timer) { !container->view->urgent_timer) {
struct sway_view *view = container->sway_view; struct sway_view *view = container->view;
if (last_workspace && last_workspace != new_workspace && if (last_workspace && last_workspace != new_workspace &&
config->urgent_timeout > 0) { config->urgent_timeout > 0) {
view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, 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. // 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. // We do this by putting it at the end of the floating list.
struct sway_container *floater = container; if (container) {
while (floater->parent && floater->parent->type != C_WORKSPACE) { struct sway_container *floater = container;
floater = floater->parent; while (floater->parent) {
} floater = floater->parent;
if (container_is_floating(floater)) { }
list_move_to_end(floater->parent->sway_workspace->floating, floater); if (container_is_floating(floater)) {
list_move_to_end(floater->workspace->floating, floater);
node_set_dirty(&floater->workspace->node);
}
} }
if (last_focus) { 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) { if (config->mouse_warping && warp && new_output != last_output) {
double x = container->x + container->width / 2.0; double x = container->x + container->width / 2.0;
double y = container->y + container->height / 2.0; double y = container->y + container->height / 2.0;
struct wlr_output *wlr_output = if (!wlr_output_layout_contains_point(root->output_layout,
new_output->sway_output->wlr_output; new_output->wlr_output, seat->cursor->cursor->x,
if (!wlr_output_layout_contains_point(
root_container.sway_root->output_layout,
wlr_output, seat->cursor->cursor->x,
seat->cursor->cursor->y)) { seat->cursor->cursor->y)) {
wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
cursor_send_pointer_motion(seat->cursor, 0, true); cursor_send_pointer_motion(seat->cursor, 0, true);
@ -744,9 +725,8 @@ void seat_set_focus_warp(struct sway_seat *seat,
update_debug_tree(); update_debug_tree();
} }
void seat_set_focus(struct sway_seat *seat, void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
struct sway_container *container) { seat_set_focus_warp(seat, node, true, true);
seat_set_focus_warp(seat, container, true, true);
} }
void seat_set_focus_surface(struct sway_seat *seat, void seat_set_focus_surface(struct sway_seat *seat,
@ -755,12 +735,11 @@ void seat_set_focus_surface(struct sway_seat *seat,
return; return;
} }
if (seat->has_focus && unfocus) { 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_send_unfocus(focus, seat);
seat->has_focus = false; seat->has_focus = false;
} }
struct wlr_keyboard *keyboard = struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
wlr_seat_get_keyboard(seat->wlr_seat);
if (keyboard) { if (keyboard) {
wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface, wlr_seat_keyboard_notify_enter(seat->wlr_seat, surface,
keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); 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) { struct wlr_layer_surface *layer) {
if (!layer && seat->focused_layer) { if (!layer && seat->focused_layer) {
seat->focused_layer = NULL; seat->focused_layer = NULL;
struct sway_container *previous = struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
seat_get_focus_inactive(seat, &root_container);
if (previous) { 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 // Hack to get seat to re-focus the return value of get_focus
seat_set_focus(seat, NULL); seat_set_focus(seat, NULL);
seat_set_focus(seat, previous); seat_set_focus(seat, previous);
@ -798,13 +774,9 @@ void seat_set_exclusive_client(struct sway_seat *seat,
seat->exclusive_client = client; seat->exclusive_client = client;
// Triggers a refocus of the topmost surface layer if necessary // Triggers a refocus of the topmost surface layer if necessary
// TODO: Make layer surface focus per-output based on cursor position // TODO: Make layer surface focus per-output based on cursor position
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (!sway_assert(output->type == C_OUTPUT, arrange_layers(output);
"root container has non-output child")) {
continue;
}
arrange_layers(output->sway_output);
} }
return; return;
} }
@ -814,9 +786,9 @@ void seat_set_exclusive_client(struct sway_seat *seat,
} }
} }
if (seat->has_focus) { if (seat->has_focus) {
struct sway_container *focus = seat_get_focus(seat); struct sway_node *focus = seat_get_focus(seat);
if (focus->type == C_VIEW && wl_resource_get_client( if (node_is_view(focus) && wl_resource_get_client(
focus->sway_view->surface->resource) != client) { focus->sway_container->view->surface->resource) != client) {
seat_set_focus(seat, NULL); seat_set_focus(seat, NULL);
} }
} }
@ -837,79 +809,101 @@ void seat_set_exclusive_client(struct sway_seat *seat,
seat->exclusive_client = client; seat->exclusive_client = client;
} }
struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_container *con) { struct sway_node *node) {
if (con->type == C_WORKSPACE && !con->children->length && if (node_is_view(node)) {
!con->sway_workspace->floating->length) { return node;
return con;
}
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) { wl_list_for_each(current, &seat->focus_stack, link) {
if (container_has_ancestor(current->container, con)) { if (node_has_ancestor(current->node, node)) {
return current->container; return current->node;
} }
} }
if (node->type == N_WORKSPACE) {
return node;
}
return NULL; return NULL;
} }
struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
struct sway_container *ancestor) { struct sway_workspace *workspace) {
if (ancestor->type == C_WORKSPACE && !ancestor->children->length) { if (!workspace->tiling->length) {
return ancestor; return NULL;
} }
struct sway_seat_container *current; struct sway_seat_node *current;
wl_list_for_each(current, &seat->focus_stack, link) { wl_list_for_each(current, &seat->focus_stack, link) {
struct sway_container *con = current->container; struct sway_node *node = current->node;
if (!container_is_floating_or_child(con) && if (node->type == N_CONTAINER &&
container_has_ancestor(current->container, ancestor)) { !container_is_floating_or_child(node->sway_container) &&
return con; node->sway_container->workspace == workspace) {
return node->sway_container;
} }
} }
return NULL; return NULL;
} }
struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
struct sway_container *ancestor) { struct sway_workspace *workspace) {
if (ancestor->type == C_WORKSPACE && if (!workspace->floating->length) {
!ancestor->sway_workspace->floating->length) {
return NULL; return NULL;
} }
struct sway_seat_container *current; struct sway_seat_node *current;
wl_list_for_each(current, &seat->focus_stack, link) { wl_list_for_each(current, &seat->focus_stack, link) {
struct sway_container *con = current->container; struct sway_node *node = current->node;
if (container_is_floating_or_child(con) && if (node->type == N_CONTAINER &&
container_has_ancestor(current->container, ancestor)) { container_is_floating_or_child(node->sway_container) &&
return con; node->sway_container->workspace == workspace) {
return node->sway_container;
} }
} }
return NULL; return NULL;
} }
struct sway_container *seat_get_active_child(struct sway_seat *seat, struct sway_node *seat_get_active_child(struct sway_seat *seat,
struct sway_container *parent) { struct sway_node *parent) {
if (parent->type == C_VIEW) { if (node_is_view(parent)) {
return parent; return parent;
} }
struct sway_seat_container *current; struct sway_seat_node *current;
wl_list_for_each(current, &seat->focus_stack, link) { wl_list_for_each(current, &seat->focus_stack, link) {
struct sway_container *con = current->container; struct sway_node *node = current->node;
if (con->parent == parent) { if (node_get_parent(node) == parent) {
return con; return node;
} }
} }
return NULL; 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) { if (!seat->has_focus) {
return NULL; return NULL;
} }
struct sway_seat_container *current = struct sway_seat_node *current =
wl_container_of(seat->focus_stack.next, current, link); 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, void seat_apply_config(struct sway_seat *seat,

@ -46,18 +46,18 @@ json_object *ipc_json_get_version() {
return 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 *rect = json_object_new_object();
json_object_object_add(rect, "x", json_object_new_int((int32_t)c->x)); json_object_object_add(rect, "x", json_object_new_int(box->x));
json_object_object_add(rect, "y", json_object_new_int((int32_t)c->y)); json_object_object_add(rect, "y", json_object_new_int(box->y));
json_object_object_add(rect, "width", json_object_new_int((int32_t)c->width)); json_object_object_add(rect, "width", json_object_new_int(box->width));
json_object_object_add(rect, "height", json_object_new_int((int32_t)c->height)); json_object_object_add(rect, "height", json_object_new_int(box->height));
return rect; 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, "type", json_object_new_string("root"));
json_object_object_add(object, "layout", json_object_new_string("splith")); 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; 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) { json_object *object) {
struct wlr_output *wlr_output = container->sway_output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
json_object_object_add(object, "type", json_object_object_add(object, "type", json_object_new_string("output"));
json_object_new_string("output")); json_object_object_add(object, "active", json_object_new_boolean(true));
json_object_object_add(object, "active", json_object_object_add(object, "primary", json_object_new_boolean(false));
json_object_new_boolean(true)); json_object_object_add(object, "layout", json_object_new_string("output"));
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_object_add(object, "make",
json_object_new_string(wlr_output->make)); json_object_new_string(wlr_output->make));
json_object_object_add(object, "model", json_object_object_add(object, "model",
@ -109,20 +105,9 @@ static void ipc_json_describe_output(struct sway_container *container,
json_object_new_string( json_object_new_string(
ipc_json_get_output_transform(wlr_output->transform))); ipc_json_get_output_transform(wlr_output->transform)));
struct sway_seat *seat = input_manager_get_default_seat(input_manager); struct sway_workspace *ws = output_get_active_workspace(output);
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;
}
}
json_object_object_add(object, "current_workspace", 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(); json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode; struct wlr_output_mode *mode;
@ -161,60 +146,57 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
return object; 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) { json_object *object) {
int num = isdigit(workspace->name[0]) ? atoi(workspace->name) : -1; 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, "num", json_object_new_int(num));
json_object_object_add(object, "output", workspace->parent ? json_object_object_add(object, "output", workspace->output ?
json_object_new_string(workspace->parent->name) : NULL); 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, "type", json_object_new_string("workspace"));
json_object_object_add(object, "urgent", json_object_object_add(object, "urgent",
json_object_new_boolean(workspace->sway_workspace->urgent)); json_object_new_boolean(workspace->urgent));
json_object_object_add(object, "representation", workspace->formatted_title ? json_object_object_add(object, "representation", workspace->representation ?
json_object_new_string(workspace->formatted_title) : NULL); json_object_new_string(workspace->representation) : NULL);
const char *layout = ipc_json_layout_description(workspace->layout); const char *layout = ipc_json_layout_description(workspace->layout);
json_object_object_add(object, "layout", json_object_new_string(layout)); json_object_object_add(object, "layout", json_object_new_string(layout));
// Floating // Floating
json_object *floating_array = json_object_new_array(); json_object *floating_array = json_object_new_array();
list_t *floating = workspace->sway_workspace->floating; for (int i = 0; i < workspace->floating->length; ++i) {
for (int i = 0; i < floating->length; ++i) { struct sway_container *floater = workspace->floating->items[i];
struct sway_container *floater = floating->items[i];
json_object_array_add(floating_array, 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); json_object_object_add(object, "floating_nodes", floating_array);
} }
static void ipc_json_describe_view(struct sway_container *c, json_object *object) { static void ipc_json_describe_view(struct sway_container *c, json_object *object) {
json_object_object_add(object, "name", 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")); json_object_object_add(object, "type", json_object_new_string("con"));
if (c->type == C_VIEW) { if (c->view) {
const char *app_id = view_get_app_id(c->sway_view); const char *app_id = view_get_app_id(c->view);
json_object_object_add(object, "app_id", json_object_object_add(object, "app_id",
app_id ? json_object_new_string(app_id) : NULL); 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", json_object_object_add(object, "class",
class ? json_object_new_string(class) : NULL); class ? json_object_new_string(class) : NULL);
} }
if (c->parent) { json_object_object_add(object, "layout",
json_object_object_add(object, "layout", json_object_new_string(ipc_json_layout_description(c->layout)));
json_object_new_string(ipc_json_layout_description(c->layout)));
}
bool urgent = c->type == C_VIEW ? bool urgent = c->view ?
view_is_urgent(c->sway_view) : container_has_urgent_child(c); view_is_urgent(c->view) : container_has_urgent_child(c);
json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); 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(); 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) { for (int i = 0; i < view_marks->length; ++i) {
json_object_array_add(marks, json_object_new_string(view_marks->items[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) { struct focus_inactive_data {
json_object *focus = data; struct sway_node *node;
json_object_array_add(focus, json_object_new_int(c->id)); json_object *object;
} };
json_object *ipc_json_describe_container(struct sway_container *c) { static void focus_inactive_children_iterator(struct sway_node *node,
if (!(sway_assert(c, "Container must not be null."))) { void *_data) {
return NULL; 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_node(struct sway_node *node) {
struct sway_seat *seat = input_manager_get_default_seat(input_manager); 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(); 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", json_object_object_add(object, "name",
c->name ? json_object_new_string(c->name) : NULL); name ? json_object_new_string(name) : NULL);
json_object_object_add(object, "rect", ipc_json_create_rect(c)); json_object_object_add(object, "rect", ipc_json_create_rect(&box));
json_object_object_add(object, "focused", json_object_object_add(object, "focused", json_object_new_boolean(focused));
json_object_new_boolean(focused));
json_object *focus = json_object_new_array(); json_object *focus = json_object_new_array();
seat_focus_inactive_children_for_each(seat, c, struct focus_inactive_data data = {
focus_inactive_children_iterator, focus); .node = node,
.object = focus,
};
seat_for_each_node(seat, focus_inactive_children_iterator, &data);
json_object_object_add(object, "focus", focus); json_object_object_add(object, "focus", focus);
switch (c->type) { switch (node->type) {
case C_ROOT: case N_ROOT:
ipc_json_describe_root(c, object); ipc_json_describe_root(root, object);
break; break;
case C_OUTPUT: case N_OUTPUT:
ipc_json_describe_output(c, object); ipc_json_describe_output(node->sway_output, object);
break; break;
case C_CONTAINER: case N_CONTAINER:
case C_VIEW: ipc_json_describe_view(node->sway_container, object);
ipc_json_describe_view(c, object);
break; break;
case C_WORKSPACE: case N_WORKSPACE:
ipc_json_describe_workspace(c, object); ipc_json_describe_workspace(node->sway_workspace, object);
break;
case C_TYPES:
default:
break; break;
} }
return object; return object;
} }
json_object *ipc_json_describe_container_recursive(struct sway_container *c) { json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
json_object *object = ipc_json_describe_container(c); json_object *object = ipc_json_describe_node(node);
int i; int i;
json_object *children = json_object_new_array(); json_object *children = json_object_new_array();
if (c->type != C_VIEW && c->children) { switch (node->type) {
for (i = 0; i < c->children->length; ++i) { case N_ROOT:
json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i])); 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); 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(); 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_object_add(object, "name",
json_object_new_string(seat->wlr_seat->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); json_object_object_add(json, "colors", colors);
// Add outputs if defined // Add outputs if defined
json_object *outputs = json_object_new_array();
if (bar->outputs && bar->outputs->length > 0) { if (bar->outputs && bar->outputs->length > 0) {
json_object *outputs = json_object_new_array();
for (int i = 0; i < bar->outputs->length; ++i) { for (int i = 0; i < bar->outputs->length; ++i) {
const char *name = bar->outputs->items[i]; const char *name = bar->outputs->items[i];
json_object_array_add(outputs, json_object_new_string(name)); 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; return json;
} }

@ -33,6 +33,7 @@
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
#include "util.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, void ipc_event_workspace(struct sway_workspace *old,
struct sway_container *new, const char *change) { struct sway_workspace *new, const char *change) {
if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) { if (!ipc_has_event_listeners(IPC_EVENT_WORKSPACE)) {
return; return;
} }
@ -301,14 +302,14 @@ void ipc_event_workspace(struct sway_container *old,
json_object_object_add(obj, "change", json_object_new_string(change)); json_object_object_add(obj, "change", json_object_new_string(change));
if (old) { if (old) {
json_object_object_add(obj, "old", json_object_object_add(obj, "old",
ipc_json_describe_container_recursive(old)); ipc_json_describe_node_recursive(&old->node));
} else { } else {
json_object_object_add(obj, "old", NULL); json_object_object_add(obj, "old", NULL);
} }
if (new) { if (new) {
json_object_object_add(obj, "current", json_object_object_add(obj, "current",
ipc_json_describe_container_recursive(new)); ipc_json_describe_node_recursive(&new->node));
} else { } else {
json_object_object_add(obj, "current", NULL); 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); wlr_log(WLR_DEBUG, "Sending window::%s event", change);
json_object *obj = json_object_new_object(); json_object *obj = json_object_new_object();
json_object_object_add(obj, "change", json_object_new_string(change)); 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); const char *json_string = json_object_to_json_string(obj);
ipc_send_event(json_string, IPC_EVENT_WINDOW); ipc_send_event(json_string, IPC_EVENT_WINDOW);
@ -521,30 +523,20 @@ void ipc_client_disconnect(struct ipc_client *client) {
free(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) { void *data) {
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { json_object *workspace_json = ipc_json_describe_node(&workspace->node);
return;
}
json_object *workspace_json = ipc_json_describe_container(workspace);
// override the default focused indicator because // override the default focused indicator because
// it's set differently for the get_workspaces reply // it's set differently for the get_workspaces reply
struct sway_seat *seat = struct sway_seat *seat = input_manager_get_default_seat(input_manager);
input_manager_get_default_seat(input_manager); struct sway_workspace *focused_ws = seat_get_focused_workspace(seat);
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);
}
bool focused = workspace == focused_ws; bool focused = workspace == focused_ws;
json_object_object_del(workspace_json, "focused"); json_object_object_del(workspace_json, "focused");
json_object_object_add(workspace_json, "focused", json_object_object_add(workspace_json, "focused",
json_object_new_boolean(focused)); json_object_new_boolean(focused));
json_object_array_add((json_object *)data, workspace_json); json_object_array_add((json_object *)data, workspace_json);
focused_ws = seat_get_focus_inactive(seat, workspace->parent); focused_ws = output_get_active_workspace(workspace->output);
if (focused_ws->type != C_WORKSPACE) {
focused_ws = container_parent(focused_ws, C_WORKSPACE);
}
bool visible = workspace == focused_ws; bool visible = workspace == focused_ws;
json_object_object_add(workspace_json, "visible", json_object_object_add(workspace_json, "visible",
json_object_new_boolean(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) { static void ipc_get_marks_callback(struct sway_container *con, void *data) {
json_object *marks = (json_object *)data; json_object *marks = (json_object *)data;
if (con->type == C_VIEW && con->sway_view->marks) { if (con->view && con->view->marks) {
for (int i = 0; i < con->sway_view->marks->length; ++i) { for (int i = 0; i < con->view->marks->length; ++i) {
char *mark = (char *)con->sway_view->marks->items[i]; char *mark = (char *)con->view->marks->items[i];
json_object_array_add(marks, json_object_new_string(mark)); 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: case IPC_GET_OUTPUTS:
{ {
json_object *outputs = json_object_new_array(); json_object *outputs = json_object_new_array();
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *container = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (container->type == C_OUTPUT) { json_object_array_add(outputs,
json_object_array_add(outputs, ipc_json_describe_node(&output->node));
ipc_json_describe_container(container));
}
} }
struct sway_output *output; struct sway_output *output;
wl_list_for_each(output, &root_container.sway_root->all_outputs, link) { wl_list_for_each(output, &root->all_outputs, link) {
if (!output->swayc) { if (!output->enabled) {
json_object_array_add(outputs, json_object_array_add(outputs,
ipc_json_describe_disabled_output(output)); ipc_json_describe_disabled_output(output));
} }
@ -717,8 +707,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
case IPC_GET_TREE: case IPC_GET_TREE:
{ {
json_object *tree = json_object *tree = ipc_json_describe_node_recursive(&root->node);
ipc_json_describe_container_recursive(&root_container);
const char *json_string = json_object_to_json_string(tree); const char *json_string = json_object_to_json_string(tree);
client_valid = client_valid =
ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); ipc_send_reply(client, json_string, (uint32_t) strlen(json_string));

@ -42,7 +42,6 @@ void sway_terminate(int exit_code) {
} }
void sig_handler(int signal) { void sig_handler(int signal) {
//close_views(&root_container);
sway_terminate(EXIT_SUCCESS); sway_terminate(EXIT_SUCCESS);
} }
@ -395,7 +394,7 @@ int main(int argc, char **argv) {
wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION); wlr_log(WLR_INFO, "Starting sway version " SWAY_VERSION);
root_create(); root = root_create();
if (!server_init(&server)) { if (!server_init(&server)) {
return 1; return 1;
@ -450,7 +449,8 @@ int main(int argc, char **argv) {
wlr_log(WLR_INFO, "Shutting down sway"); wlr_log(WLR_INFO, "Shutting down sway");
server_fini(&server); server_fini(&server);
root_destroy(); root_destroy(root);
root = NULL;
if (config) { if (config) {
free_config(config); free_config(config);

@ -151,6 +151,7 @@ sway_sources = files(
'tree/arrange.c', 'tree/arrange.c',
'tree/container.c', 'tree/container.c',
'tree/node.c',
'tree/root.c', 'tree/root.c',
'tree/view.c', 'tree/view.c',
'tree/workspace.c', 'tree/workspace.c',

@ -61,8 +61,7 @@ bool server_init(struct sway_server *server) {
server->new_output.notify = handle_new_output; server->new_output.notify = handle_new_output;
wl_signal_add(&server->backend->events.new_output, &server->new_output); wl_signal_add(&server->backend->events.new_output, &server->new_output);
wlr_xdg_output_manager_create(server->wl_display, wlr_xdg_output_manager_create(server->wl_display, root->output_layout);
root_container.sway_root->output_layout);
server->idle = wlr_idle_create(server->wl_display); server->idle = wlr_idle_create(server->wl_display);
server->idle_inhibit_manager_v1 = server->idle_inhibit_manager_v1 =
@ -131,7 +130,7 @@ bool server_init(struct sway_server *server) {
server->txn_timeout_ms = 200; server->txn_timeout_ms = 200;
} }
server->dirty_containers = create_list(); server->dirty_nodes = create_list();
server->transactions = create_list(); server->transactions = create_list();
input_manager = input_manager_create(server); input_manager = input_manager_create(server);
@ -145,7 +144,7 @@ void server_fini(struct sway_server *server) {
#endif #endif
wl_display_destroy_clients(server->wl_display); wl_display_destroy_clients(server->wl_display);
wl_display_destroy(server->wl_display); wl_display_destroy(server->wl_display);
list_free(server->dirty_containers); list_free(server->dirty_nodes);
list_free(server->transactions); list_free(server->transactions);
} }

@ -166,29 +166,23 @@ void arrange_container(struct sway_container *container) {
if (config->reloading) { if (config->reloading) {
return; return;
} }
if (container->type == C_VIEW) { if (container->view) {
view_autoconfigure(container->sway_view); view_autoconfigure(container->view);
container_set_dirty(container); node_set_dirty(&container->node);
return;
}
if (!sway_assert(container->type == C_CONTAINER, "Expected a container")) {
return; return;
} }
struct wlr_box box; struct wlr_box box;
container_get_box(container, &box); container_get_box(container, &box);
arrange_children(container->children, container->layout, &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) { if (config->reloading) {
return; return;
} }
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { struct sway_output *output = workspace->output;
return; struct wlr_box *area = &output->usable_area;
}
struct sway_container *output = workspace->parent;
struct wlr_box *area = &output->sway_output->usable_area;
wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
area->width, area->height, area->x, area->y); area->width, area->height, area->x, area->y);
workspace_remove_gaps(workspace); workspace_remove_gaps(workspace);
@ -197,21 +191,20 @@ void arrange_workspace(struct sway_container *workspace) {
double prev_y = workspace->y; double prev_y = workspace->y;
workspace->width = area->width; workspace->width = area->width;
workspace->height = area->height; workspace->height = area->height;
workspace->x = output->x + area->x; workspace->x = output->wlr_output->lx + area->x;
workspace->y = output->y + area->y; workspace->y = output->wlr_output->ly + area->y;
// Adjust any floating containers // Adjust any floating containers
double diff_x = workspace->x - prev_x; double diff_x = workspace->x - prev_x;
double diff_y = workspace->y - prev_y; double diff_y = workspace->y - prev_y;
if (diff_x != 0 || diff_y != 0) { if (diff_x != 0 || diff_y != 0) {
for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) { for (int i = 0; i < workspace->floating->length; ++i) {
struct sway_container *floater = struct sway_container *floater = workspace->floating->items[i];
workspace->sway_workspace->floating->items[i];
container_floating_translate(floater, diff_x, diff_y); container_floating_translate(floater, diff_x, diff_y);
double center_x = floater->x + floater->width / 2; double center_x = floater->x + floater->width / 2;
double center_y = floater->y + floater->height / 2; double center_y = floater->y + floater->height / 2;
struct wlr_box workspace_box; 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)) { if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) {
container_floating_move_to_center(floater); container_floating_move_to_center(floater);
} }
@ -219,43 +212,32 @@ void arrange_workspace(struct sway_container *workspace) {
} }
workspace_add_gaps(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, wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
workspace->x, workspace->y); workspace->x, workspace->y);
if (workspace->sway_workspace->fullscreen) { if (workspace->fullscreen) {
struct sway_container *fs = workspace->sway_workspace->fullscreen; struct sway_container *fs = workspace->fullscreen;
fs->x = workspace->parent->x; fs->x = output->wlr_output->lx;
fs->y = workspace->parent->y; fs->y = output->wlr_output->ly;
fs->width = workspace->parent->width; fs->width = output->wlr_output->width;
fs->height = workspace->parent->height; fs->height = output->wlr_output->height;
arrange_container(fs); arrange_container(fs);
} else { } else {
struct wlr_box box; struct wlr_box box;
container_get_box(workspace, &box); workspace_get_box(workspace, &box);
arrange_children(workspace->children, workspace->layout, &box); arrange_children(workspace->tiling, workspace->layout, &box);
arrange_floating(workspace->sway_workspace->floating); arrange_floating(workspace->floating);
} }
} }
void arrange_output(struct sway_container *output) { void arrange_output(struct sway_output *output) {
if (config->reloading) { if (config->reloading) {
return; return;
} }
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { // Outputs have no pending x/y/width/height,
return; // so all we do here is arrange the workspaces.
} for (int i = 0; i < output->workspaces->length; ++i) {
const struct wlr_box *output_box = wlr_output_layout_get_box( struct sway_workspace *workspace = output->workspaces->items[i];
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];
arrange_workspace(workspace); arrange_workspace(workspace);
} }
} }
@ -264,37 +246,31 @@ void arrange_root(void) {
if (config->reloading) { if (config->reloading) {
return; return;
} }
struct wlr_output_layout *output_layout =
root_container.sway_root->output_layout;
const struct wlr_box *layout_box = const struct wlr_box *layout_box =
wlr_output_layout_get_box(output_layout, NULL); wlr_output_layout_get_box(root->output_layout, NULL);
root_container.x = layout_box->x; root->x = layout_box->x;
root_container.y = layout_box->y; root->y = layout_box->y;
root_container.width = layout_box->width; root->width = layout_box->width;
root_container.height = layout_box->height; root->height = layout_box->height;
container_set_dirty(&root_container); for (int i = 0; i < root->outputs->length; ++i) {
for (int i = 0; i < root_container.children->length; ++i) { struct sway_output *output = root->outputs->items[i];
struct sway_container *output = root_container.children->items[i];
arrange_output(output); arrange_output(output);
} }
} }
void arrange_windows(struct sway_container *container) { void arrange_node(struct sway_node *node) {
switch (container->type) { switch (node->type) {
case C_ROOT: case N_ROOT:
arrange_root(); arrange_root();
break; break;
case C_OUTPUT: case N_OUTPUT:
arrange_output(container); arrange_output(node->sway_output);
break;
case C_WORKSPACE:
arrange_workspace(container);
break; break;
case C_CONTAINER: case N_WORKSPACE:
case C_VIEW: arrange_workspace(node->sway_workspace);
arrange_container(container);
break; break;
case C_TYPES: case N_CONTAINER:
arrange_container(node->sway_container);
break; break;
} }
} }

File diff suppressed because it is too large Load Diff

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

@ -2,28 +2,31 @@
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <wlr/types/wlr_output_damage.h>
#include "sway/ipc-server.h" #include "sway/ipc-server.h"
#include "sway/layers.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/output.h" #include "sway/tree/output.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "log.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 // Workspace output priority
for (int i = 0; i < root_container.children->length; i++) { for (int i = 0; i < root->outputs->length; i++) {
struct sway_container *other = root_container.children->items[i]; struct sway_output *other = root->outputs->items[i];
if (other == output) { if (other == output) {
continue; continue;
} }
for (int j = 0; j < other->children->length; j++) { for (int j = 0; j < other->workspaces->length; j++) {
struct sway_container *ws = other->children->items[j]; struct sway_workspace *ws = other->workspaces->items[j];
struct sway_container *highest = struct sway_output *highest =
workspace_output_get_highest_available(ws, NULL); workspace_output_get_highest_available(ws, NULL);
if (highest == output) { if (highest == output) {
container_remove_child(ws); workspace_detach(ws);
container_add_child(output, ws); output_add_workspace(output, ws);
ipc_event_workspace(NULL, ws, "move"); ipc_event_workspace(NULL, ws, "move");
j--; j--;
} }
@ -31,111 +34,111 @@ static void restore_workspaces(struct sway_container *output) {
} }
// Saved workspaces // Saved workspaces
list_t *saved = root_container.sway_root->saved_workspaces; for (int i = 0; i < root->saved_workspaces->length; ++i) {
for (int i = 0; i < saved->length; ++i) { struct sway_workspace *ws = root->saved_workspaces->items[i];
struct sway_container *ws = saved->items[i]; output_add_workspace(output, ws);
container_add_child(output, ws);
ipc_event_workspace(NULL, ws, "move"); ipc_event_workspace(NULL, ws, "move");
} }
saved->length = 0; root->saved_workspaces->length = 0;
output_sort_workspaces(output); output_sort_workspaces(output);
} }
struct sway_container *output_create( struct sway_output *output_create(struct wlr_output *wlr_output) {
struct sway_output *sway_output) { struct sway_output *output = calloc(1, sizeof(struct sway_output));
const char *name = sway_output->wlr_output->name; node_init(&output->node, N_OUTPUT, output);
char identifier[128]; output->wlr_output = wlr_output;
output_get_identifier(identifier, sizeof(identifier), sway_output); wlr_output->data = output;
struct output_config *oc = NULL, *all = NULL; wl_signal_add(&wlr_output->events.destroy, &output->destroy);
for (int i = 0; i < config->output_configs->length; ++i) {
struct output_config *cur = config->output_configs->items[i];
if (strcasecmp(name, cur->name) == 0 || wl_list_insert(&root->all_outputs, &output->link);
strcasecmp(identifier, cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched output config for %s", name);
oc = cur;
}
if (strcasecmp("*", cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
all = cur;
}
if (oc && all) { if (!wl_list_empty(&wlr_output->modes)) {
break; struct wlr_output_mode *mode =
} wl_container_of(wlr_output->modes.prev, mode, link);
} wlr_output_set_mode(wlr_output, mode);
if (!oc) {
oc = all;
} }
if (oc && !oc->enabled) { output->workspaces = create_list();
return NULL; output->current.workspaces = create_list();
}
struct sway_container *output = container_create(C_OUTPUT); return output;
output->sway_output = sway_output; }
output->name = strdup(name);
if (output->name == NULL) {
output_begin_destroy(output);
return NULL;
}
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); apply_output_config(oc, output);
container_add_child(&root_container, output); list_add(root->outputs, 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); restore_workspaces(output);
if (!output->children->length) { if (!output->workspaces->length) {
// Create workspace // Create workspace
char *ws_name = workspace_next_name(output->name); char *ws_name = workspace_next_name(wlr_output->name);
wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name);
struct sway_container *ws = workspace_create(output, ws_name); struct sway_workspace *ws = workspace_create(output, ws_name);
// Set each seat's focus if not already set // Set each seat's focus if not already set
struct sway_seat *seat = NULL; struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input_manager->seats, link) { wl_list_for_each(seat, &input_manager->seats, link) {
if (!seat->has_focus) { if (!seat->has_focus) {
seat_set_focus(seat, ws); seat_set_focus(seat, &ws->node);
} }
} }
free(ws_name); free(ws_name);
} }
container_create_notify(output); size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
return output; 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_container *output) { static void output_evacuate(struct sway_output *output) {
if (!output->children->length) { if (!output->workspaces->length) {
return; return;
} }
struct sway_container *fallback_output = NULL; struct sway_output *fallback_output = NULL;
if (root_container.children->length > 1) { if (root->outputs->length > 1) {
fallback_output = root_container.children->items[0]; fallback_output = root->outputs->items[0];
if (fallback_output == output) { if (fallback_output == output) {
fallback_output = root_container.children->items[1]; fallback_output = root->outputs->items[1];
} }
} }
while (output->children->length) { while (output->workspaces->length) {
struct sway_container *workspace = output->children->items[0]; struct sway_workspace *workspace = output->workspaces->items[0];
container_remove_child(workspace); workspace_detach(workspace);
if (workspace_is_empty(workspace)) { if (workspace_is_empty(workspace)) {
workspace_begin_destroy(workspace); workspace_begin_destroy(workspace);
continue; continue;
} }
struct sway_container *new_output = struct sway_output *new_output =
workspace_output_get_highest_available(workspace, output); workspace_output_get_highest_available(workspace, output);
if (!new_output) { if (!new_output) {
new_output = fallback_output; new_output = fallback_output;
@ -143,39 +146,31 @@ static void output_evacuate(struct sway_container *output) {
if (new_output) { if (new_output) {
workspace_output_add_priority(workspace, new_output); workspace_output_add_priority(workspace, new_output);
container_add_child(new_output, workspace); output_add_workspace(new_output, workspace);
output_sort_workspaces(new_output); output_sort_workspaces(new_output);
ipc_event_workspace(NULL, workspace, "move"); ipc_event_workspace(NULL, workspace, "move");
} else { } else {
list_add(root_container.sway_root->saved_workspaces, workspace); list_add(root->saved_workspaces, workspace);
} }
} }
} }
void output_destroy(struct sway_container *output) { void output_destroy(struct sway_output *output) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { if (!sway_assert(output->node.destroying,
"Tried to free output which wasn't marked as destroying")) {
return; return;
} }
if (!sway_assert(output->destroying, if (!sway_assert(output->wlr_output == NULL,
"Tried to free output which wasn't marked as destroying")) { "Tried to free output which still had a wlr_output")) {
return; return;
} }
if (!sway_assert(output->ntxnrefs == 0, "Tried to free output " if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output "
"which is still referenced by transactions")) { "which is still referenced by transactions")) {
return; return;
} }
free(output->name); list_free(output->workspaces);
free(output->formatted_title); list_free(output->current.workspaces);
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); free(output);
// NOTE: We don't actually destroy the sway_output here
} }
static void untrack_output(struct sway_container *con, void *data) { static void untrack_output(struct sway_container *con, void *data) {
@ -186,76 +181,131 @@ static void untrack_output(struct sway_container *con, void *data) {
} }
} }
void output_begin_destroy(struct sway_container *output) { void output_disable(struct sway_output *output) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { if (!sway_assert(output->enabled, "Expected an enabled output")) {
return; return;
} }
wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); wlr_log(WLR_DEBUG, "Disabling output '%s'", output->wlr_output->name);
wl_signal_emit(&output->events.destroy, output); wl_signal_emit(&output->events.destroy, output);
output_evacuate(output); output_evacuate(output);
output->destroying = true; root_for_each_container(untrack_output, output);
container_set_dirty(output);
int index = list_find(root->outputs, output);
list_del(root->outputs, index);
root_for_each_container(untrack_output, output->sway_output); 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);
wl_list_remove(&output->sway_output->mode.link); output->enabled = false;
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; arrange_root();
output->sway_output = NULL; }
if (output->parent) { void output_begin_destroy(struct sway_output *output) {
container_remove_child(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 sway_container *output_from_wlr_output(struct wlr_output *output) { struct output_config *output_find_config(struct sway_output *output) {
if (output == NULL) { const char *name = output->wlr_output->name;
char identifier[128];
output_get_identifier(identifier, sizeof(identifier), output);
struct output_config *oc = NULL, *all = NULL;
for (int i = 0; i < config->output_configs->length; ++i) {
struct output_config *cur = config->output_configs->items[i];
if (strcasecmp(name, cur->name) == 0 ||
strcasecmp(identifier, cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched output config for %s", name);
oc = cur;
}
if (strcasecmp("*", cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
all = cur;
}
if (oc && all) {
break;
}
}
if (!oc) {
oc = all;
}
if (oc && !oc->enabled) {
return NULL; return NULL;
} }
for (int i = 0; i < root_container.children->length; ++i) { return oc;
struct sway_container *o = root_container.children->items[i]; }
if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
return o; 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;
} }
return 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;
}
return output_from_wlr_output(wlr_adjacent);
} }
void output_for_each_workspace(struct sway_container *output, void output_add_workspace(struct sway_output *output,
void (*f)(struct sway_container *con, void *data), void *data) { struct sway_workspace *workspace) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { if (workspace->output) {
return; workspace_detach(workspace);
} }
for (int i = 0; i < output->children->length; ++i) { list_add(output->workspaces, workspace);
struct sway_container *workspace = output->children->items[i]; 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); 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) { void (*f)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { for (int i = 0; i < output->workspaces->length; ++i) {
return; struct sway_workspace *workspace = output->workspaces->items[i];
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
workspace_for_each_container(workspace, f, data); workspace_for_each_container(workspace, f, data);
} }
} }
struct sway_container *output_find_workspace(struct sway_container *output, struct sway_workspace *output_find_workspace(struct sway_output *output,
bool (*test)(struct sway_container *con, void *data), void *data) { bool (*test)(struct sway_workspace *ws, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) { for (int i = 0; i < output->workspaces->length; ++i) {
return NULL; struct sway_workspace *workspace = output->workspaces->items[i];
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
if (test(workspace, data)) { if (test(workspace, data)) {
return workspace; return workspace;
} }
@ -263,14 +313,11 @@ struct sway_container *output_find_workspace(struct sway_container *output,
return NULL; 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) { 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; struct sway_container *result = NULL;
for (int i = 0; i < output->children->length; ++i) { for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_container *workspace = output->children->items[i]; struct sway_workspace *workspace = output->workspaces->items[i];
if ((result = workspace_find_container(workspace, test, data))) { if ((result = workspace_find_container(workspace, test, data))) {
return result; 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) { static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
struct sway_container *a = *(void **)_a; struct sway_workspace *a = *(void **)_a;
struct sway_container *b = *(void **)_b; struct sway_workspace *b = *(void **)_b;
if (isdigit(a->name[0]) && isdigit(b->name[0])) { if (isdigit(a->name[0]) && isdigit(b->name[0])) {
int a_num = strtol(a->name, NULL, 10); 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; return 0;
} }
void output_sort_workspaces(struct sway_container *output) { void output_sort_workspaces(struct sway_output *output) {
list_stable_sort(output->children, sort_workspace_cmp_qsort); 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;
} }

@ -14,54 +14,45 @@
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
struct sway_container root_container; struct sway_root *root;
static void output_layout_handle_change(struct wl_listener *listener, static void output_layout_handle_change(struct wl_listener *listener,
void *data) { void *data) {
arrange_windows(&root_container); arrange_root();
transaction_commit_dirty(); transaction_commit_dirty();
} }
void root_create(void) { struct sway_root *root_create(void) {
root_container.id = 0; // normally assigned in new_swayc() struct sway_root *root = calloc(1, sizeof(struct sway_root));
root_container.type = C_ROOT; if (!root) {
root_container.layout = L_NONE; wlr_log(WLR_ERROR, "Unable to allocate sway_root");
root_container.name = strdup("root"); return NULL;
root_container.children = create_list(); }
root_container.current.children = create_list(); node_init(&root->node, N_ROOT, root);
wl_signal_init(&root_container.events.destroy); root->output_layout = wlr_output_layout_create();
wl_list_init(&root->all_outputs);
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);
#ifdef HAVE_XWAYLAND #ifdef HAVE_XWAYLAND
wl_list_init(&root_container.sway_root->xwayland_unmanaged); wl_list_init(&root->xwayland_unmanaged);
#endif #endif
wl_list_init(&root_container.sway_root->drag_icons); wl_list_init(&root->drag_icons);
wl_signal_init(&root_container.sway_root->events.new_container); wl_signal_init(&root->events.new_node);
root_container.sway_root->scratchpad = create_list(); root->outputs = create_list();
root_container.sway_root->saved_workspaces = create_list(); root->scratchpad = create_list();
root->saved_workspaces = create_list();
root_container.sway_root->output_layout_change.notify =
output_layout_handle_change; root->output_layout_change.notify = output_layout_handle_change;
wl_signal_add(&root_container.sway_root->output_layout->events.change, wl_signal_add(&root->output_layout->events.change,
&root_container.sway_root->output_layout_change); &root->output_layout_change);
return root;
} }
void root_destroy(void) { void root_destroy(struct sway_root *root) {
// sway_root wl_list_remove(&root->output_layout_change.link);
wl_list_remove(&root_container.sway_root->output_layout_change.link); list_free(root->scratchpad);
list_free(root_container.sway_root->scratchpad); list_free(root->saved_workspaces);
list_free(root_container.sway_root->saved_workspaces); list_free(root->outputs);
wlr_output_layout_destroy(root_container.sway_root->output_layout); wlr_output_layout_destroy(root->output_layout);
free(root_container.sway_root); free(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_scratchpad_add_container(struct sway_container *con) { void root_scratchpad_add_container(struct sway_container *con) {
@ -69,15 +60,21 @@ void root_scratchpad_add_container(struct sway_container *con) {
return; return;
} }
con->scratchpad = true; con->scratchpad = true;
list_add(root_container.sway_root->scratchpad, con); list_add(root->scratchpad, con);
struct sway_container *parent = con->parent; struct sway_container *parent = con->parent;
struct sway_workspace *workspace = con->workspace;
container_set_floating(con, true); container_set_floating(con, true);
container_remove_child(con); container_detach(con);
arrange_windows(parent);
struct sway_seat *seat = input_manager_current_seat(input_manager); 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) { void root_scratchpad_remove_container(struct sway_container *con) {
@ -85,28 +82,25 @@ void root_scratchpad_remove_container(struct sway_container *con) {
return; return;
} }
con->scratchpad = false; con->scratchpad = false;
int index = list_find(root_container.sway_root->scratchpad, con); int index = list_find(root->scratchpad, con);
if (index != -1) { if (index != -1) {
list_del(root_container.sway_root->scratchpad, index); list_del(root->scratchpad, index);
} }
} }
void root_scratchpad_show(struct sway_container *con) { void root_scratchpad_show(struct sway_container *con) {
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *ws = seat_get_focus(seat); struct sway_workspace *ws = seat_get_focused_workspace(seat);
if (ws->type != C_WORKSPACE) {
ws = container_parent(ws, C_WORKSPACE);
}
// If the current con or any of its parents are in fullscreen mode, we // If the current con or any of its parents are in fullscreen mode, we
// first need to disable it before showing the scratchpad con. // first need to disable it before showing the scratchpad con.
if (ws->sway_workspace->fullscreen) { if (ws->fullscreen) {
container_set_fullscreen(ws->sway_workspace->fullscreen, false); container_set_fullscreen(ws->fullscreen, false);
} }
// Show the container // Show the container
if (con->parent) { if (con->workspace) {
container_remove_child(con); container_detach(con);
} }
workspace_add_floating(ws, 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; double center_ly = con->y + con->height / 2;
struct wlr_box workspace_box; 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)) { if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) {
// Maybe resize it // Maybe resize it
if (con->width > ws->width || con->height > ws->height) { 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); container_floating_move_to(con, new_lx, new_ly);
} }
arrange_windows(ws); arrange_workspace(ws);
seat_set_focus(seat, seat_get_focus_inactive(seat, con)); seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node));
container_set_dirty(con->parent);
} }
void root_scratchpad_hide(struct sway_container *con) { void root_scratchpad_hide(struct sway_container *con) {
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = seat_get_focus(seat); struct sway_node *focus = seat_get_focus(seat);
struct sway_container *ws = container_parent(con, C_WORKSPACE); struct sway_workspace *ws = con->workspace;
container_remove_child(con); container_detach(con);
arrange_windows(ws); arrange_workspace(ws);
if (con == focus) { if (&con->node == focus) {
seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); 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 { struct pid_workspace {
@ -152,7 +144,7 @@ struct pid_workspace {
char *workspace; char *workspace;
struct timespec time_added; struct timespec time_added;
struct sway_container *output; struct sway_output *output;
struct wl_listener output_destroy; struct wl_listener output_destroy;
struct wl_list link; struct wl_list link;
@ -160,13 +152,13 @@ struct pid_workspace {
static struct wl_list pid_workspaces; 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) { if (!pid_workspaces.prev && !pid_workspaces.next) {
wl_list_init(&pid_workspaces); wl_list_init(&pid_workspaces);
return NULL; return NULL;
} }
struct sway_container *ws = NULL; struct sway_workspace *ws = NULL;
struct pid_workspace *pw = NULL; struct pid_workspace *pw = NULL;
wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); 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_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *ws = struct sway_workspace *ws = seat_get_focused_workspace(seat);
seat_get_focus_inactive(seat, &root_container);
if (ws && ws->type != C_WORKSPACE) {
ws = container_parent(ws, C_WORKSPACE);
}
if (!ws) { if (!ws) {
wlr_log(WLR_DEBUG, "Bailing out, no workspace"); wlr_log(WLR_DEBUG, "Bailing out, no workspace");
return; return;
} }
struct sway_container *output = ws->parent; struct sway_output *output = ws->output;
if (!output) { if (!output) {
wlr_log(WLR_DEBUG, "Bailing out, no output"); wlr_log(WLR_DEBUG, "Bailing out, no output");
return; return;
@ -255,30 +243,28 @@ void root_record_workspace_pid(pid_t pid) {
pw->pid = pid; pw->pid = pid;
memcpy(&pw->time_added, &now, sizeof(struct timespec)); memcpy(&pw->time_added, &now, sizeof(struct timespec));
pw->output_destroy.notify = pw_handle_output_destroy; pw->output_destroy.notify = pw_handle_output_destroy;
wl_signal_add(&output->sway_output->wlr_output->events.destroy, wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy);
&pw->output_destroy);
wl_list_insert(&pid_workspaces, &pw->link); 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) { void *data) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
output_for_each_workspace(output, f, data); output_for_each_workspace(output, f, data);
} }
} }
void root_for_each_container(void (*f)(struct sway_container *con, void *data), void root_for_each_container(void (*f)(struct sway_container *con, void *data),
void *data) { void *data) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
output_for_each_container(output, f, data); output_for_each_container(output, f, data);
} }
// Scratchpad // Scratchpad
for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { for (int i = 0; i < root->scratchpad->length; ++i) {
struct sway_container *container = struct sway_container *container = root->scratchpad->items[i];
root_container.sway_root->scratchpad->items[i];
// If the container has a parent then it's visible on a workspace // 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 // and will have been iterated in the previous for loop. So we only
// iterate the hidden scratchpad containers here. // 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( struct sway_output *root_find_output(
bool (*test)(struct sway_container *con, void *data), void *data) { bool (*test)(struct sway_output *output, void *data), void *data) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (test(output, data)) { if (test(output, data)) {
return output; return output;
} }
@ -300,11 +286,11 @@ struct sway_container *root_find_output(
return NULL; return NULL;
} }
struct sway_container *root_find_workspace( struct sway_workspace *root_find_workspace(
bool (*test)(struct sway_container *con, void *data), void *data) { bool (*test)(struct sway_workspace *ws, void *data), void *data) {
struct sway_container *result = NULL; struct sway_workspace *result = NULL;
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if ((result = output_find_workspace(output, test, data))) { if ((result = output_find_workspace(output, test, data))) {
return result; return result;
} }
@ -315,17 +301,16 @@ struct sway_container *root_find_workspace(
struct sway_container *root_find_container( struct sway_container *root_find_container(
bool (*test)(struct sway_container *con, void *data), void *data) { bool (*test)(struct sway_container *con, void *data), void *data) {
struct sway_container *result = NULL; struct sway_container *result = NULL;
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *output = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if ((result = output_find_container(output, test, data))) { if ((result = output_find_container(output, test, data))) {
return result; return result;
} }
} }
// Scratchpad // Scratchpad
for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { for (int i = 0; i < root->scratchpad->length; ++i) {
struct sway_container *container = struct sway_container *container = root->scratchpad->items[i];
root_container.sway_root->scratchpad->items[i];
if (!container->parent) { if (!container->parent) {
if (test(container, data)) { if (test(container, data)) {
return container; return container;
@ -337,3 +322,10 @@ struct sway_container *root_find_container(
} }
return NULL; 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;
}

@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type,
view->marks = create_list(); view->marks = create_list();
view->allow_request_urgent = true; view->allow_request_urgent = true;
wl_signal_init(&view->events.unmap); wl_signal_init(&view->events.unmap);
view->container = container_create(view);
} }
void view_destroy(struct sway_view *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")) { "Tried to free view which wasn't marked as destroying")) {
return; return;
} }
if (!sway_assert(view->swayc == NULL, if (!sway_assert(view->container == NULL,
"Tried to free view which still has a swayc " "Tried to free view which still has a container "
"(might have a pending transaction?)")) { "(might have a pending transaction?)")) {
return; return;
} }
@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) {
wlr_texture_destroy(view->marks_focused_inactive); wlr_texture_destroy(view->marks_focused_inactive);
wlr_texture_destroy(view->marks_unfocused); wlr_texture_destroy(view->marks_unfocused);
wlr_texture_destroy(view->marks_urgent); wlr_texture_destroy(view->marks_urgent);
free(view->title_format);
if (view->impl->destroy) { if (view->impl->destroy) {
view->impl->destroy(view); 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) { void view_begin_destroy(struct sway_view *view) {
if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
return; return;
} }
view->destroying = true; view->destroying = true;
if (!view->swayc) { if (!view->container) {
view_destroy(view); 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) { void view_autoconfigure(struct sway_view *view) {
if (!sway_assert(view->swayc, struct sway_output *output = view->container->workspace->output;
"Called view_autoconfigure() on a view without a swayc")) {
return;
}
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
if (view->swayc->is_fullscreen) { if (view->container->is_fullscreen) {
view->x = output->x; view->x = output->wlr_output->lx;
view->y = output->y; view->y = output->wlr_output->ly;
view->width = output->width; view->width = output->wlr_output->width;
view->height = output->height; view->height = output->wlr_output->height;
return; return;
} }
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); struct sway_workspace *ws = view->container->workspace;
int other_views = 0; bool other_views = false;
if (config->hide_edge_borders == E_SMART) { if (config->hide_edge_borders == E_SMART) {
struct sway_container *con = view->swayc; struct sway_container *con = view->container;
while (con != output) { while (con) {
if (con->layout != L_TABBED && con->layout != L_STACKED) { enum sway_container_layout layout = container_parent_layout(con);
other_views += con->children ? con->children->length - 1 : 0; if (layout != L_TABBED && layout != L_STACKED) {
if (other_views > 0) { list_t *siblings = container_get_siblings(con);
if (siblings && siblings->length > 1) {
other_views = true;
break; 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_top = view->border_bottom = true;
view->border_left = view->border_right = 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 // 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 // title area. We have to disable any top border because the title bar is
// rendered by the parent. // 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; view->border_top = false;
} else { } else {
y_offset = container_titlebar_height(); 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) { 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); struct sway_seat *seat = input_manager_current_seat(input_manager);
switch (config->focus_on_window_activation) { switch (config->focus_on_window_activation) {
case FOWA_SMART: case FOWA_SMART:
if (workspace_is_visible(ws)) { if (workspace_is_visible(ws)) {
seat_set_focus(seat, view->swayc); seat_set_focus(seat, &view->container->node);
} else { } else {
view_set_urgent(view, true); view_set_urgent(view, true);
} }
@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) {
view_set_urgent(view, true); view_set_urgent(view, true);
break; break;
case FOWA_FOCUS: case FOWA_FOCUS:
seat_set_focus(seat, view->swayc); seat_set_focus(seat, &view->container->node);
break; break;
case FOWA_NONE: case FOWA_NONE:
break; break;
@ -331,23 +325,12 @@ void view_close_popups(struct sway_view *view) {
} }
void view_damage_from(struct sway_view *view) { void view_damage_from(struct sway_view *view) {
for (int i = 0; i < root_container.children->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_container *cont = root_container.children->items[i]; struct sway_output *output = root->outputs->items[i];
if (cont->type == C_OUTPUT) { output_damage_from_view(output, view);
output_damage_from_view(cont->sway_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, void view_for_each_surface(struct sway_view *view,
wlr_surface_iterator_func_t iterator, void *user_data) { wlr_surface_iterator_func_t iterator, void *user_data) {
if (!view->surface) { 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) { void view_execute_criteria(struct sway_view *view) {
if (!view->swayc) {
return;
}
struct sway_seat *seat = input_manager_current_seat(input_manager); 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); list_t *criterias = criteria_for_view(view, CT_COMMAND);
for (int i = 0; i < criterias->length; i++) { for (int i = 0; i < criterias->length; i++) {
struct criteria *criteria = criterias->items[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'", wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
criteria->raw, view, criteria->cmdlist); criteria->raw, view, criteria->cmdlist);
seat_set_focus(seat, view->swayc); seat_set_focus(seat, &view->container->node);
list_add(view->executed_criteria, criteria); list_add(view->executed_criteria, criteria);
struct cmd_results *res = execute_command(criteria->cmdlist, NULL); struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
if (res->status != CMD_SUCCESS) { if (res->status != CMD_SUCCESS) {
@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) {
seat_set_focus(seat, prior_focus); 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); struct sway_seat *seat = input_manager_current_seat(input_manager);
// Check if there's any `assign` criteria for the view // Check if there's any `assign` criteria for the view
list_t *criterias = criteria_for_view(view, list_t *criterias = criteria_for_view(view,
CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); 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) { for (int i = 0; i < criterias->length; ++i) {
struct criteria *criteria = criterias->items[i]; struct criteria *criteria = criterias->items[i];
if (criteria->type == CT_ASSIGN_OUTPUT) { 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) { if (output) {
ws = seat_get_active_child(seat, output); ws = output_get_active_workspace(output);
break; break;
} }
} else { } else {
@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) {
} }
// Use the focused workspace // Use the focused workspace
ws = seat_get_focus_inactive(seat, &root_container); return seat_get_focused_workspace(seat);
if (ws->type != C_WORKSPACE) {
ws = container_parent(ws, C_WORKSPACE);
}
return ws;
} }
static bool should_focus(struct sway_view *view) { static bool should_focus(struct sway_view *view) {
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *prev_focus = struct sway_container *prev_con = seat_get_focused_container(seat);
seat_get_focus_inactive(seat, &root_container); struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? struct sway_workspace *map_ws = view->container->workspace;
prev_focus : container_parent(prev_focus, C_WORKSPACE);
struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE);
// Views can only take focus if they are mapped into the active workspace // Views can only take focus if they are mapped into the active workspace
if (prev_ws != map_ws) { 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 // If the view is the only one in the focused workspace, it'll get focus
// regardless of any no_focus criteria. // regardless of any no_focus criteria.
struct sway_container *parent = view->swayc->parent; if (!view->container->parent && !prev_con) {
if (parent->type == C_WORKSPACE && prev_focus == parent) { size_t num_children = view->container->workspace->tiling->length +
size_t num_children = parent->children->length + view->container->workspace->floating->length;
parent->sway_workspace->floating->length;
if (num_children == 1) { if (num_children == 1) {
return true; return true;
} }
@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
view->surface = wlr_surface; view->surface = wlr_surface;
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *ws = select_workspace(view); struct sway_workspace *ws = select_workspace(view);
struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); 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 // 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. // launch it as a tiled view in the root of the workspace instead.
if (container_is_floating(target_sibling)) { if (target_sibling && container_is_floating(target_sibling)) {
target_sibling = target_sibling->parent; 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); view_init_subsurfaces(view, wlr_surface);
wl_signal_add(&wlr_surface->events.new_subsurface, 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)) { if (view->impl->wants_floating && view->impl->wants_floating(view)) {
view->border = config->floating_border; view->border = config->floating_border;
view->border_thickness = config->floating_border_thickness; view->border_thickness = config->floating_border_thickness;
container_set_floating(view->swayc, true); container_set_floating(view->container, true);
} else { } else {
view->border = config->border; view->border = config->border;
view->border_thickness = config->border_thickness; 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)) { 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); view_update_title(view, false);
container_notify_subtree_changed(view->swayc->parent); container_update_representation(view->container);
view_execute_criteria(view); view_execute_criteria(view);
} }
@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) {
view->urgent_timer = NULL; view->urgent_timer = NULL;
} }
bool was_fullscreen = view->swayc->is_fullscreen; struct sway_container *parent = view->container->parent;
struct sway_container *parent = view->swayc->parent; struct sway_workspace *ws = view->container->workspace;
container_begin_destroy(view->swayc); container_begin_destroy(view->container);
struct sway_container *surviving_ancestor = container_reap_empty(parent); if (parent) {
container_reap_empty(parent);
} else {
workspace_consider_destroy(ws);
}
// If the workspace wasn't reaped if (!ws->node.destroying) {
if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { arrange_workspace(ws);
struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ?
surviving_ancestor :
container_parent(surviving_ancestor, C_WORKSPACE);
arrange_windows(was_fullscreen ? ws : surviving_ancestor);
workspace_detect_urgent(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) { 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")) { "Expected a floating container")) {
return; return;
} }
view->width = width; view->width = width;
view->height = height; view->height = height;
view->swayc->current.view_width = width; view->container->current.view_width = width;
view->swayc->current.view_height = height; view->container->current.view_height = height;
container_set_geometry_from_floating_view(view->swayc); container_set_geometry_from_floating_view(view->container);
} }
static void view_subsurface_create(struct sway_view *view, 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); wl_signal_add(&view->events.unmap, &child->view_unmap);
child->view_unmap.notify = view_child_handle_view_unmap; child->view_unmap.notify = view_child_handle_view_unmap;
struct sway_container *output = child->view->swayc->parent; struct sway_output *output = child->view->container->workspace->output;
if (output != NULL) { wlr_surface_send_enter(child->surface, output->wlr_output);
if (output->type != C_OUTPUT) {
output = container_parent(output, C_OUTPUT);
}
wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
}
view_init_subsurfaces(child->view, surface); view_init_subsurfaces(child->view, surface);
// TODO: only damage the whole child // TODO: only damage the whole child
if (child->view->swayc) { container_damage_whole(child->view->container);
container_damage_whole(child->view->swayc);
}
} }
void view_child_destroy(struct sway_view_child *child) { void view_child_destroy(struct sway_view_child *child) {
// TODO: only damage the whole child // TODO: only damage the whole child
if (child->view->swayc) { container_damage_whole(child->view->container);
container_damage_whole(child->view->swayc);
}
wl_list_remove(&child->surface_commit.link); wl_list_remove(&child->surface_commit.link);
wl_list_remove(&child->surface_destroy.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) { void view_update_title(struct sway_view *view, bool force) {
if (!view->swayc) {
return;
}
const char *title = view_get_title(view); const char *title = view_get_title(view);
if (!force) { 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; return;
} }
if (!title && !view->swayc->name) { if (!title && !view->container->title) {
return; return;
} }
} }
free(view->swayc->name); free(view->container->title);
free(view->swayc->formatted_title); free(view->container->formatted_title);
if (title) { if (title) {
size_t len = parse_title_format(view, NULL); size_t len = parse_title_format(view, NULL);
char *buffer = calloc(len + 1, sizeof(char)); 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); buffer = escape_title(buffer);
} }
view->swayc->name = strdup(title); view->container->title = strdup(title);
view->swayc->formatted_title = buffer; view->container->formatted_title = buffer;
} else { } else {
view->swayc->name = NULL; view->container->title = NULL;
view->swayc->formatted_title = NULL; view->container->formatted_title = NULL;
} }
container_calculate_title_height(view->swayc); container_calculate_title_height(view->container);
config_update_font_height(false); config_update_font_height(false);
// Update title after the global font height is updated // 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, static bool find_by_mark_iterator(struct sway_container *con,
void *data) { void *data) {
char *mark = 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) { struct sway_view *view_find_mark(char *mark) {
@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) {
if (!container) { if (!container) {
return NULL; return NULL;
} }
return container->sway_view; return container->view;
} }
bool view_find_and_unmark(char *mark) { bool view_find_and_unmark(char *mark) {
@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) {
if (!container) { if (!container) {
return false; return false;
} }
struct sway_view *view = container->sway_view; struct sway_view *view = container->view;
for (int i = 0; i < view->marks->length; ++i) { for (int i = 0; i < view->marks->length; ++i) {
char *view_mark = view->marks->items[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) { void view_clear_marks(struct sway_view *view) {
while (view->marks->length) { list_foreach(view->marks, free);
list_del(view->marks, 0); view->marks->length = 0;
ipc_event_window(view->swayc, "mark"); ipc_event_window(view->container, "mark");
}
} }
bool view_has_mark(struct sway_view *view, char *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) { void view_add_mark(struct sway_view *view, char *mark) {
list_add(view->marks, strdup(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, static void update_marks_texture(struct sway_view *view,
struct wlr_texture **texture, struct border_colors *class) { 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) { if (!output) {
return; return;
} }
@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view,
double scale = output->wlr_output->scale; double scale = output->wlr_output->scale;
int width = 0; int width = 0;
int height = view->swayc->title_height * scale; int height = view->container->title_height * scale;
cairo_t *c = cairo_create(NULL); cairo_t *c = cairo_create(NULL);
get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); 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); &config->border_colors.unfocused);
update_marks_texture(view, &view->marks_urgent, update_marks_texture(view, &view->marks_urgent,
&config->border_colors.urgent); &config->border_colors.urgent);
container_damage_whole(view->swayc); container_damage_whole(view->container);
} }
bool view_is_visible(struct sway_view *view) { bool view_is_visible(struct sway_view *view) {
if (!view->swayc || view->swayc->destroying) { if (view->container->node.destroying) {
return false; return false;
} }
struct sway_container *workspace = struct sway_workspace *workspace = view->container->workspace;
container_parent(view->swayc, C_WORKSPACE);
if (!workspace) { if (!workspace) {
return false; return false;
} }
// Determine if view is nested inside a floating container which is sticky. // Determine if view is nested inside a floating container which is sticky
// A simple floating view will have this ancestry: struct sway_container *floater = view->container;
// C_VIEW -> floating -> workspace while (floater->parent) {
// 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) {
floater = floater->parent; floater = floater->parent;
} }
bool is_sticky = container_is_floating(floater) && floater->is_sticky; bool is_sticky = container_is_floating(floater) && floater->is_sticky;
// Check view isn't in a tabbed or stacked container on an inactive tab // 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_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *container = view->swayc; struct sway_container *container = view->container;
while (container->type != C_WORKSPACE) { while (container) {
if (container->parent->layout == L_TABBED || enum sway_container_layout layout = container_parent_layout(container);
container->parent->layout == L_STACKED) { if (layout == L_TABBED || layout == L_STACKED) {
if (seat_get_active_child(seat, container->parent) != container) { struct sway_node *parent = container->parent ?
&container->parent->node : &container->workspace->node;
if (seat_get_active_child(seat, parent) != &container->node) {
return false; return false;
} }
} }
container = container->parent; container = container->parent;
} }
// Check view isn't hidden by another fullscreen view // Check view isn't hidden by another fullscreen view
if (workspace->sway_workspace->fullscreen && if (workspace->fullscreen &&
!container_is_fullscreen_or_child(view->swayc)) { !container_is_fullscreen_or_child(view->container)) {
return false; return false;
} }
// Check the workspace is visible // Check the workspace is visible
@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
} }
if (enable) { if (enable) {
struct sway_seat *seat = input_manager_current_seat(input_manager); 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; return;
} }
clock_gettime(CLOCK_MONOTONIC, &view->urgent); clock_gettime(CLOCK_MONOTONIC, &view->urgent);
@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) {
view->urgent_timer = NULL; 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(view->container->workspace);
workspace_detect_urgent(ws);
} }
bool view_is_urgent(struct sway_view *view) { bool view_is_urgent(struct sway_view *view) {

@ -12,128 +12,105 @@
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/node.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
struct sway_container *workspace_get_initial_output(const char *name) { struct sway_output *workspace_get_initial_output(const char *name) {
struct sway_container *parent;
// Search for workspace<->output pair // Search for workspace<->output pair
int e = config->workspace_outputs->length;
for (int i = 0; i < config->workspace_outputs->length; ++i) { for (int i = 0; i < config->workspace_outputs->length; ++i) {
struct workspace_output *wso = config->workspace_outputs->items[i]; struct workspace_output *wso = config->workspace_outputs->items[i];
if (strcasecmp(wso->workspace, name) == 0) { if (strcasecmp(wso->workspace, name) == 0) {
// Find output to use if it exists // Find output to use if it exists
e = root_container.children->length; struct sway_output *output = output_by_name(wso->output);
for (i = 0; i < e; ++i) { if (output) {
parent = root_container.children->items[i]; return output;
if (strcmp(parent->name, wso->output) == 0) {
return parent;
}
} }
break; break;
} }
} }
// Otherwise put it on the focused output // Otherwise put it on the focused output
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = struct sway_workspace *focus = seat_get_focused_workspace(seat);
seat_get_focus_inactive(seat, &root_container); return focus->output;
parent = focus;
parent = container_parent(parent, C_OUTPUT);
return parent;
} }
struct sway_container *workspace_create(struct sway_container *output, struct sway_workspace *workspace_create(struct sway_output *output,
const char *name) { const char *name) {
if (output == NULL) { if (output == NULL) {
output = workspace_get_initial_output(name); output = workspace_get_initial_output(name);
} }
wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); wlr_log(WLR_DEBUG, "Adding workspace %s for output %s", name,
struct sway_container *workspace = container_create(C_WORKSPACE); 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)); struct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace));
if (!swayws) { if (!ws) {
wlr_log(WLR_ERROR, "Unable to allocate sway_workspace");
return NULL; return NULL;
} }
swayws->swayc = workspace; node_init(&ws->node, N_WORKSPACE, ws);
swayws->floating = create_list(); ws->x = output->wlr_output->lx;
swayws->output_priority = create_list(); ws->y = output->wlr_output->ly;
workspace->sway_workspace = swayws; ws->width = output->wlr_output->width;
workspace_output_add_priority(workspace, output); ws->height = output->wlr_output->height;
ws->name = name ? strdup(name) : NULL;
container_add_child(output, workspace); 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);
output_add_workspace(output, ws);
output_sort_workspaces(output); 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) { void workspace_destroy(struct sway_workspace *workspace) {
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { if (!sway_assert(workspace->node.destroying,
return;
}
if (!sway_assert(workspace->destroying,
"Tried to free workspace which wasn't marked as destroying")) { "Tried to free workspace which wasn't marked as destroying")) {
return; 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")) { "which is still referenced by transactions")) {
return; 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->name);
free(workspace->formatted_title); free(workspace->representation);
wlr_texture_destroy(workspace->title_focused); list_foreach(workspace->output_priority, free);
wlr_texture_destroy(workspace->title_focused_inactive); list_free(workspace->output_priority);
wlr_texture_destroy(workspace->title_unfocused); list_free(workspace->floating);
wlr_texture_destroy(workspace->title_urgent); list_free(workspace->tiling);
list_free(workspace->children); list_free(workspace->current.floating);
list_free(workspace->current.children); list_free(workspace->current.tiling);
list_free(workspace->outputs);
free(workspace); free(workspace);
} }
void workspace_begin_destroy(struct sway_container *workspace) { void workspace_begin_destroy(struct sway_workspace *workspace) {
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
return;
}
wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name); wlr_log(WLR_DEBUG, "Destroying workspace '%s'", workspace->name);
wl_signal_emit(&workspace->events.destroy, workspace);
ipc_event_workspace(NULL, workspace, "empty"); // intentional ipc_event_workspace(NULL, workspace, "empty"); // intentional
wl_signal_emit(&workspace->node.events.destroy, &workspace->node);
workspace->destroying = true; if (workspace->output) {
container_set_dirty(workspace); workspace_detach(workspace);
if (workspace->parent) {
container_remove_child(workspace);
} }
workspace->node.destroying = true;
node_set_dirty(&workspace->node);
} }
void workspace_consider_destroy(struct sway_container *ws) { void workspace_consider_destroy(struct sway_workspace *ws) {
if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { if (ws->tiling->length == 0 && ws->floating->length == 0
return; && output_get_active_workspace(ws->output) != ws) {
}
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) {
workspace_begin_destroy(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 // As a fall back, get the current number of active workspaces
// and return that + 1 for the next workspace's name // 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); int l = snprintf(NULL, 0, "%d", ws_num);
char *name = malloc(l + 1); 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; return NULL;
} }
sprintf(name, "%d", ws_num++); sprintf(name, "%d", ws_num++);
return name; return name;
} }
static bool _workspace_by_number(struct sway_container *view, void *data) { static bool _workspace_by_number(struct sway_workspace *ws, void *data) {
if (view->type != C_WORKSPACE) {
return false;
}
char *name = data; char *name = data;
char *view_name = view->name; char *ws_name = ws->name;
while (isdigit(*name)) { while (isdigit(*name)) {
if (*name++ != *view_name++) { if (*name++ != *ws_name++) {
return false; 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); return root_find_workspace(_workspace_by_number, (void *) name);
} }
static bool _workspace_by_name(struct sway_container *view, void *data) { static bool _workspace_by_name(struct sway_workspace *ws, void *data) {
return (view->type == C_WORKSPACE) && return strcasecmp(ws->name, data) == 0;
(strcasecmp(view->name, (char *) 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_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *current_workspace = NULL, *current_output = NULL; struct sway_workspace *current = seat_get_focused_workspace(seat);
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);
}
if (strcmp(name, "prev") == 0) { if (strcmp(name, "prev") == 0) {
return workspace_prev(current_workspace); return workspace_prev(current);
} else if (strcmp(name, "prev_on_output") == 0) { } 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) { } else if (strcmp(name, "next") == 0) {
return workspace_next(current_workspace); return workspace_next(current);
} else if (strcmp(name, "next_on_output") == 0) { } 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) { } else if (strcmp(name, "current") == 0) {
return current_workspace; return current;
} else if (strcasecmp(name, "back_and_forth") == 0) { } else if (strcasecmp(name, "back_and_forth") == 0) {
return prev_workspace_name ? return prev_workspace_name ?
root_find_workspace(_workspace_by_name, (void*)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, * the end and beginning. If next is false, the previous workspace is returned,
* otherwise the next one is returned. * otherwise the next one is returned.
*/ */
static struct sway_container *workspace_output_prev_next_impl( static struct sway_workspace *workspace_output_prev_next_impl(
struct sway_container *output, int dir) { struct sway_output *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;
}
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = seat_get_focus_inactive(seat, output); struct sway_workspace *workspace = seat_get_focused_workspace(seat);
struct sway_container *workspace = (focus->type == C_WORKSPACE ?
focus :
container_parent(focus, C_WORKSPACE));
int index = list_find(output->children, workspace); int index = list_find(output->workspaces, workspace);
size_t new_index = wrap(index + dir, output->children->length); size_t new_index = wrap(index + dir, output->workspaces->length);
return output->children->items[new_index]; return output->workspaces->items[new_index];
} }
/** /**
* Get the previous or next workspace. If the first/last workspace on an output * 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. * is active, proceed to the previous/next output's previous/next workspace.
*/ */
static struct sway_container *workspace_prev_next_impl( static struct sway_workspace *workspace_prev_next_impl(
struct sway_container *workspace, int dir) { struct sway_workspace *workspace, int dir) {
if (!workspace) { struct sway_output *output = workspace->output;
return NULL; int index = list_find(output->workspaces, workspace);
}
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);
int new_index = index + dir; int new_index = index + dir;
if (new_index >= 0 && new_index < output->children->length) { if (new_index >= 0 && new_index < output->workspaces->length) {
return output->children->items[index + dir]; return output->workspaces->items[new_index];
} }
// Look on a different output // Look on a different output
int output_index = list_find(root_container.children, output); int output_index = list_find(root->outputs, output);
new_index = wrap(output_index + dir, root_container.children->length); new_index = wrap(output_index + dir, root->outputs->length);
output = root_container.children->items[new_index]; output = root->outputs->items[new_index];
if (dir == 1) { if (dir == 1) {
return output->children->items[0]; return output->workspaces->items[0];
} else { } 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) { struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
return workspace_output_prev_next_impl(current, 1); 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); return workspace_prev_next_impl(current, 1);
} }
struct sway_container *workspace_output_prev(struct sway_container *current) { struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
return workspace_output_prev_next_impl(current, -1); 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); 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) { bool no_auto_back_and_forth) {
if (!workspace) {
return false;
}
struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
seat_get_focus_inactive(seat, &root_container); struct sway_workspace *active_ws = seat_get_focused_workspace(seat);
if (!seat || !focus) {
return false;
}
struct sway_container *active_ws = focus;
if (active_ws->type != C_WORKSPACE) {
active_ws = container_parent(focus, C_WORKSPACE);
}
if (!no_auto_back_and_forth && config->auto_back_and_forth if (!no_auto_back_and_forth && config->auto_back_and_forth
&& active_ws == workspace && active_ws == workspace
&& prev_workspace_name) { && 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 ? workspace = new_ws ?
new_ws : new_ws :
workspace_create(NULL, prev_workspace_name); workspace_create(NULL, prev_workspace_name);
@ -447,21 +385,21 @@ bool workspace_switch(struct sway_container *workspace,
} }
// Move sticky containers to new workspace // Move sticky containers to new workspace
struct sway_container *next_output = workspace->parent; struct sway_output *next_output = workspace->output;
struct sway_container *next_output_prev_ws = struct sway_workspace *next_output_prev_ws =
seat_get_active_child(seat, next_output); output_get_active_workspace(next_output);
list_t *floating = next_output_prev_ws->sway_workspace->floating;
bool has_sticky = false; bool has_sticky = false;
if (workspace != next_output_prev_ws) { if (workspace != next_output_prev_ws) {
for (int i = 0; i < floating->length; ++i) { for (int i = 0; i < next_output_prev_ws->floating->length; ++i) {
struct sway_container *floater = floating->items[i]; struct sway_container *floater =
next_output_prev_ws->floating->items[i];
if (floater->is_sticky) { if (floater->is_sticky) {
has_sticky = true; has_sticky = true;
container_remove_child(floater); container_detach(floater);
workspace_add_floating(workspace, floater); workspace_add_floating(workspace, floater);
if (floater == focus) { if (&floater->node == focus) {
seat_set_focus(seat, NULL); seat_set_focus(seat, NULL);
seat_set_focus(seat, floater); seat_set_focus(seat, &floater->node);
} }
--i; --i;
} }
@ -470,9 +408,9 @@ bool workspace_switch(struct sway_container *workspace,
wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", wlr_log(WLR_DEBUG, "Switching to workspace %p:%s",
workspace, workspace->name); 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) { if (next == NULL) {
next = workspace; next = &workspace->node;
} }
if (has_sticky) { if (has_sticky) {
// If there's a sticky container, we might be setting focus to the same // 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); workspace_consider_destroy(active_ws);
} }
seat_set_focus(seat, next); seat_set_focus(seat, next);
struct sway_container *output = container_parent(workspace, C_OUTPUT); arrange_workspace(workspace);
arrange_windows(output);
return true; return true;
} }
bool workspace_is_visible(struct sway_container *ws) { bool workspace_is_visible(struct sway_workspace *ws) {
if (ws->destroying) { if (ws->node.destroying) {
return false; return false;
} }
struct sway_container *output = container_parent(ws, C_OUTPUT); return output_get_active_workspace(ws->output) == ws;
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;
} }
bool workspace_is_empty(struct sway_container *ws) { bool workspace_is_empty(struct sway_workspace *ws) {
if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { if (ws->tiling->length) {
return false;
}
if (ws->children->length) {
return false; return false;
} }
// Sticky views are not considered to be part of this workspace // Sticky views are not considered to be part of this workspace
list_t *floating = ws->sway_workspace->floating; for (int i = 0; i < ws->floating->length; ++i) {
for (int i = 0; i < floating->length; ++i) { struct sway_container *floater = ws->floating->items[i];
struct sway_container *floater = floating->items[i];
if (!floater->is_sticky) { if (!floater->is_sticky) {
return false; return false;
} }
@ -523,20 +450,19 @@ static int find_output(const void *id1, const void *id2) {
return strcmp(id1, id2) ? 0 : 1; return strcmp(id1, id2) ? 0 : 1;
} }
void workspace_output_raise_priority(struct sway_container *workspace, void workspace_output_raise_priority(struct sway_workspace *ws,
struct sway_container *old_output, struct sway_container *output) { struct sway_output *old_output, struct sway_output *output) {
struct sway_workspace *ws = workspace->sway_workspace;
int old_index = list_seq_find(ws->output_priority, find_output, int old_index = list_seq_find(ws->output_priority, find_output,
old_output->name); old_output->wlr_output->name);
if (old_index < 0) { if (old_index < 0) {
return; return;
} }
int new_index = list_seq_find(ws->output_priority, find_output, int new_index = list_seq_find(ws->output_priority, find_output,
output->name); output->wlr_output->name);
if (new_index < 0) { 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) { } else if (new_index > old_index) {
char *name = ws->output_priority->items[new_index]; char *name = ws->output_priority->items[new_index];
list_del(ws->output_priority, 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, void workspace_output_add_priority(struct sway_workspace *workspace,
struct sway_container *output) { struct sway_output *output) {
int index = list_seq_find(workspace->sway_workspace->output_priority, int index = list_seq_find(workspace->output_priority,
find_output, output->name); find_output, output->wlr_output->name);
if (index < 0) { if (index < 0) {
list_add(workspace->sway_workspace->output_priority, list_add(workspace->output_priority, strdup(output->wlr_output->name));
strdup(output->name));
} }
} }
static bool _output_by_name(struct sway_container *output, void *data) { struct sway_output *workspace_output_get_highest_available(
return output->type == C_OUTPUT && strcasecmp(output->name, data) == 0; 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];
struct sway_container *workspace_output_get_highest_available( if (exclude && strcasecmp(name, exclude->wlr_output->name) == 0) {
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) {
continue; continue;
} }
struct sway_container *output = root_find_output(_output_by_name, name); struct sway_output *output = output_by_name(name);
if (output) { if (output) {
return 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) { 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, bool new_urgent = (bool)workspace_find_container(workspace,
find_urgent_iterator, NULL); find_urgent_iterator, NULL);
if (workspace->sway_workspace->urgent != new_urgent) { if (workspace->urgent != new_urgent) {
workspace->sway_workspace->urgent = new_urgent; workspace->urgent = new_urgent;
ipc_event_workspace(NULL, workspace, "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) { void (*f)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
return;
}
// Tiling // Tiling
for (int i = 0; i < ws->children->length; ++i) { for (int i = 0; i < ws->tiling->length; ++i) {
struct sway_container *container = ws->children->items[i]; struct sway_container *container = ws->tiling->items[i];
f(container, data); f(container, data);
container_for_each_child(container, f, data); container_for_each_child(container, f, data);
} }
// Floating // Floating
for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { for (int i = 0; i < ws->floating->length; ++i) {
struct sway_container *container = struct sway_container *container = ws->floating->items[i];
ws->sway_workspace->floating->items[i];
f(container, data); f(container, data);
container_for_each_child(container, f, 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) { 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; struct sway_container *result = NULL;
// Tiling // Tiling
for (int i = 0; i < ws->children->length; ++i) { for (int i = 0; i < ws->tiling->length; ++i) {
struct sway_container *child = ws->children->items[i]; struct sway_container *child = ws->tiling->items[i];
if (test(child, data)) { if (test(child, data)) {
return child; return child;
} }
@ -627,8 +541,8 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
} }
} }
// Floating // Floating
for (int i = 0; i < ws->sway_workspace->floating->length; ++i) { for (int i = 0; i < ws->floating->length; ++i) {
struct sway_container *child = ws->sway_workspace->floating->items[i]; struct sway_container *child = ws->floating->items[i];
if (test(child, data)) { if (test(child, data)) {
return child; return child;
} }
@ -639,37 +553,76 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
return NULL; return NULL;
} }
struct sway_container *workspace_wrap_children(struct sway_container *ws) { struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
struct sway_container *middle = container_create(C_CONTAINER); struct sway_container *middle = container_create(NULL);
middle->layout = ws->layout; middle->layout = ws->layout;
while (ws->children->length) { while (ws->tiling->length) {
struct sway_container *child = ws->children->items[0]; struct sway_container *child = ws->tiling->items[0];
container_remove_child(child); container_detach(child);
container_add_child(middle, child); container_add_child(middle, child);
} }
container_add_child(ws, middle); workspace_add_tiling(ws, middle);
return middle; return middle;
} }
void workspace_add_floating(struct sway_container *workspace, void workspace_detach(struct sway_workspace *workspace) {
struct sway_container *con) { struct sway_output *output = workspace->output;
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { int index = list_find(output->workspaces, workspace);
return; if (index != -1) {
list_del(output->workspaces, index);
} }
if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { workspace->output = NULL;
return;
node_set_dirty(&workspace->node);
node_set_dirty(&output->node);
}
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);
}
list_add(workspace->sway_workspace->floating, con); void workspace_add_floating(struct sway_workspace *workspace,
con->parent = workspace; struct sway_container *con) {
container_set_dirty(workspace); if (con->workspace) {
container_set_dirty(con); 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_remove_gaps(struct sway_container *ws) { void workspace_insert_tiling(struct sway_workspace *workspace,
if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { struct sway_container *con, int index) {
return; 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) { if (ws->current_gaps == 0) {
return; return;
} }
@ -681,15 +634,12 @@ void workspace_remove_gaps(struct sway_container *ws) {
ws->current_gaps = 0; ws->current_gaps = 0;
} }
void workspace_add_gaps(struct sway_container *ws) { void workspace_add_gaps(struct sway_workspace *ws) {
if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) {
return;
}
if (ws->current_gaps > 0) { if (ws->current_gaps > 0) {
return; return;
} }
bool should_apply = 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) { if (!should_apply) {
return; return;
} }
@ -708,3 +658,36 @@ void workspace_add_gaps(struct sway_container *ws) {
ws->width -= 2 * ws->current_gaps; ws->width -= 2 * ws->current_gaps;
ws->height -= 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;
}

Loading…
Cancel
Save