Merge pull request #2540 from RyanDwyer/typesafety

Implement type safe arguments and demote sway_container
master
Drew DeVault 6 years ago committed by GitHub
commit 610eb94617
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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,18 @@ struct sway_output {
struct timespec last_frame; struct timespec last_frame;
struct wlr_output_damage *damage; struct wlr_output_damage *damage;
int lx, ly;
int width, height;
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 +52,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 +83,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 +126,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);
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;
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 *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,58 +34,49 @@ 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);
} }
break;
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);
} }
break;
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);
}
break;
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; break;
} }
@ -94,151 +85,90 @@ static struct sway_container *get_swayc_in_output_direction(
} }
} }
return ws; if (container) {
container = seat_get_focus_inactive_view(seat, &container->node);
return &container->node;
} }
static struct sway_container *container_get_in_direction( return &ws->node;
struct sway_container *container, struct sway_seat *seat,
enum movement_direction dir) {
struct sway_container *parent = container->parent;
if (dir == MOVE_CHILD) {
return seat_get_focus_inactive(seat, container);
} }
static struct sway_node *node_get_in_direction(struct sway_container *container,
struct sway_seat *seat, enum movement_direction dir) {
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;
} else {
return parent;
}
} }
if (dir == MOVE_PARENT) {
return node_get_parent(&container->node);
} }
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 (!wrap_candidate) {
return NULL;
}
return seat_get_focus_inactive_view(seat, wrap_candidate);
}
struct sway_container *next =
get_swayc_in_output_direction(adjacent, dir, seat);
if (next == NULL) {
return NULL;
}
struct sway_container *next_workspace = next;
if (next_workspace->type != C_WORKSPACE) {
next_workspace = container_parent(next_workspace, C_WORKSPACE);
}
sway_assert(next_workspace, "Next container has no workspace");
if (next_workspace->sway_workspace->fullscreen) {
return seat_get_focus_inactive(seat,
next_workspace->sway_workspace->fullscreen);
}
if (next->children && next->children->length) {
// TODO consider floating children as well
return seat_get_focus_inactive_view(seat, next);
} else {
return next;
}
} else {
if (dir == MOVE_LEFT || dir == MOVE_RIGHT) { if (dir == MOVE_LEFT || dir == MOVE_RIGHT) {
if (parent->layout == L_HORIZ || parent->layout == L_TABBED) { if (parent_layout == L_HORIZ || parent_layout == L_TABBED) {
can_move = true; can_move = true;
desired = idx + (dir == MOVE_LEFT ? -1 : 1); desired = idx + (dir == MOVE_LEFT ? -1 : 1);
} }
} else { } else {
if (parent->layout == L_VERT || parent->layout == L_STACKED) { if (parent_layout == L_VERT || parent_layout == L_STACKED) {
can_move = true; can_move = true;
desired = idx + (dir == MOVE_UP ? -1 : 1); 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 +176,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 +185,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 +202,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 +218,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 +253,34 @@ 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 (direction == MOVE_CHILD) {
con, seat, direction); struct sway_node *focus = seat_get_active_child(seat, node);
if (focus) {
seat_set_focus(seat, focus);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
if (node->type == N_WORKSPACE) {
if (direction == MOVE_PARENT) {
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
// Jump to the next output
struct sway_output *new_output =
output_get_in_direction(workspace->output, direction);
if (!new_output) {
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
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;
} else {
ws->gaps_outer = total;
} }
arrange_workspace(ws);
} else {
struct sway_container *c = config->handler_context.container;
c->has_gaps = true; c->has_gaps = true;
if (inner) { if (inner) {
c->gaps_inner = total; c->gaps_inner = total;
} else { } else {
c->gaps_outer = total; 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,53 +25,34 @@ 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]...'";
struct cmd_results *cmd_layout(int argc, char **argv) { static enum sway_container_layout get_layout_toggle(int argc, char **argv,
struct cmd_results *error = NULL; enum sway_container_layout layout,
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) { enum sway_container_layout prev_split_layout) {
return error; // "layout toggle"
if (argc == 0) {
return layout == L_HORIZ ? L_VERT : L_HORIZ;
} }
struct sway_container *parent = config->handler_context.current_container;
if (container_is_floating(parent)) { if (argc == 2) {
return cmd_results_new(CMD_FAILURE, "layout", // "layout toggle split" (same as "layout toggle")
"Unable to change layout of floating windows"); if (strcasecmp(argv[1], "split") == 0) {
} return layout == L_HORIZ ? L_VERT : L_HORIZ;
while (parent->type == C_VIEW) {
parent = parent->parent;
} }
// "layout toggle all"
enum sway_container_layout prev = parent->layout;
bool assigned_directly = parse_layout_string(argv[0], &parent->layout);
if (!assigned_directly) {
if (strcasecmp(argv[0], "default") == 0) {
parent->layout = parent->prev_split_layout;
} else if (strcasecmp(argv[0], "toggle") == 0) {
if (argc == 1) {
parent->layout =
parent->layout == L_STACKED ? L_TABBED :
parent->layout == L_TABBED ? parent->prev_split_layout : L_STACKED;
} else if (argc == 2) {
if (strcasecmp(argv[1], "all") == 0) { if (strcasecmp(argv[1], "all") == 0) {
parent->layout = return layout == L_HORIZ ? L_VERT :
parent->layout == L_HORIZ ? L_VERT : layout == L_VERT ? L_STACKED :
parent->layout == L_VERT ? L_STACKED : layout == L_STACKED ? L_TABBED : L_HORIZ;
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 { return L_NONE;
enum sway_container_layout parsed_layout; }
enum sway_container_layout parsed;
int curr = 1; int curr = 1;
for (; curr < argc; curr++) { for (; curr < argc; curr++) {
bool valid = parse_layout_string(argv[curr], &parsed_layout); parsed = parse_layout_string(argv[curr]);
if ((valid && parsed_layout == parent->layout) || if (parsed == layout || (strcmp(argv[curr], "split") == 0 &&
(strcmp(argv[curr], "split") == 0 && (layout == L_VERT || layout == L_HORIZ))) {
(parent->layout == L_VERT || parent->layout == L_HORIZ))) {
break; break;
} }
} }
@ -81,29 +61,92 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
if (i >= argc) { if (i >= argc) {
i = 1; i = 1;
} }
if (parse_layout_string(argv[i], &parent->layout)) { parsed = parse_layout_string(argv[i]);
break; if (parsed != L_NONE) {
} else if (strcmp(argv[i], "split") == 0) { return parsed;
parent->layout = }
parent->layout == L_HORIZ ? L_VERT : if (strcmp(argv[i], "split") == 0) {
parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout; return layout == L_HORIZ ? L_VERT :
break; layout == L_VERT ? L_HORIZ : prev_split_layout;
} // invalid layout strings are silently ignored }
// 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) {
return get_layout_toggle(argc, argv, layout, prev_split_layout);
}
return L_NONE;
}
struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error;
} }
struct sway_container *container = config->handler_context.container;
struct sway_workspace *workspace = config->handler_context.workspace;
if (container && container_is_floating(container)) {
return cmd_results_new(CMD_FAILURE, "layout",
"Unable to change layout of floating windows");
} }
// Typically we change the layout of the current container, but if the
// current container is a view (it usually is) then we'll change the layout
// of the parent instead, as it doesn't make sense for views to have layout.
if (container && container->view) {
container = container->parent;
}
// We could be working with a container OR a workspace. These are different
// structures, so we set up pointers to they layouts so we can refer them in
// an abstract way.
enum sway_container_layout new_layout = L_NONE;
enum sway_container_layout old_layout = L_NONE;
if (container) {
old_layout = container->layout;
new_layout = get_layout(argc, argv,
container->layout, container->prev_split_layout);
} else { } else {
old_layout = workspace->layout;
new_layout = get_layout(argc, argv,
workspace->layout, workspace->prev_split_layout);
}
if (new_layout == L_NONE) {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax); return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
} }
if (new_layout != old_layout) {
if (container) {
if (old_layout != L_TABBED && old_layout != L_STACKED) {
container->prev_split_layout = old_layout;
} }
if (parent->layout == L_NONE) { container->layout = new_layout;
parent->layout = container_get_default_layout(parent); container_update_representation(container);
arrange_container(container);
} else {
if (old_layout != L_TABBED && old_layout != L_STACKED) {
workspace->prev_split_layout = old_layout;
} }
if (prev != parent->layout) { workspace->layout = new_layout;
if (prev != L_TABBED && prev != L_STACKED) { workspace_update_representation(workspace);
parent->prev_split_layout = prev; 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->width,
node->sway_output->height,
node->sway_output->lx,
node->sway_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->width > width) {
width = container->width; width = output->width;
} }
if (container->height > height) { if (output->height > height) {
height = container->height; height = 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->width,
.height = output->swayc->current.swayc_height, .height = 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->width,
.y2 = output->swayc->current.swayc_height, .y2 = 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,8 +382,12 @@ 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) {
// 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); 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,
struct wlr_surface *surface, struct wlr_box *_box, float rotation, struct wlr_surface *surface, struct wlr_box *_box, float rotation,
@ -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")) { if (!oc || oc->enabled) {
return; output_enable(output, oc);
} }
output->swayc = output_create(output); transaction_commit_dirty();
if (!output->swayc) {
// Output is disabled
return;
}
size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
for (size_t i = 0; i < len; ++i) {
wl_list_init(&output->layers[i]);
} }
wl_signal_init(&output->events.destroy);
input_manager_configure_xcursor(input_manager); void output_add_listeners(struct sway_output *output) {
wl_signal_add(&wlr_output->events.mode, &output->mode);
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->width,
.height = output->swayc->current.swayc_height, .height = 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:
break; sway_assert(false, "Never reached");
case C_OUTPUT:
output_destroy(con);
break; break;
case C_WORKSPACE: case N_OUTPUT:
workspace_destroy(con); output_destroy(node->sway_output);
break; break;
case C_CONTAINER: case N_WORKSPACE:
case C_VIEW: workspace_destroy(node->sway_workspace);
container_destroy(con);
break; break;
case C_TYPES: case N_CONTAINER:
container_destroy(node->sway_container);
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,77 +167,71 @@ 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,
* Apply a transaction to the "current" state of the tree. struct sway_output_state *state) {
*/ output_damage_whole(output);
static void transaction_apply(struct sway_transaction *transaction) { list_free(output->current.workspaces);
wlr_log(WLR_DEBUG, "Applying transaction %p", transaction); memcpy(&output->current, state, sizeof(struct sway_output_state));
if (debug.txn_timings) { output_damage_whole(output);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec *commit = &transaction->commit_time;
float ms = (now.tv_sec - commit->tv_sec) * 1000 +
(now.tv_nsec - commit->tv_nsec) / 1000000.0;
wlr_log(WLR_DEBUG, "Transaction %p: %.1fms waiting "
"(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60));
} }
// Apply the instruction state to the container's current state static void apply_workspace_state(struct sway_workspace *ws,
for (int i = 0; i < transaction->instructions->length; ++i) { struct sway_workspace_state *state) {
struct sway_transaction_instruction *instruction = output_damage_whole(ws->current.output);
transaction->instructions->items[i]; list_free(ws->current.floating);
struct sway_container *container = instruction->container; 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 // Damage the old location
desktop_damage_whole_container(container); desktop_damage_whole_container(container);
if (container->type == C_VIEW && container->sway_view->saved_buffer) { if (view && view->saved_buffer) {
struct sway_view *view = container->sway_view;
struct wlr_box box = { struct wlr_box box = {
.x = container->current.view_x - view->saved_geometry.x, .x = container->current.view_x - view->saved_geometry.x,
.y = container->current.view_y - view->saved_geometry.y, .y = container->current.view_y - view->saved_geometry.y,
@ -193,21 +247,18 @@ static void transaction_apply(struct sway_transaction *transaction) {
// Any child containers which are being deleted will be cleaned up in // Any child containers which are being deleted will be cleaned up in
// transaction_destroy(). // transaction_destroy().
list_free(container->current.children); list_free(container->current.children);
list_free(container->current.ws_floating);
memcpy(&container->current, &instruction->state, memcpy(&container->current, state, sizeof(struct sway_container_state));
sizeof(struct sway_container_state));
if (container->type == C_VIEW && container->sway_view->saved_buffer) { if (view && view->saved_buffer) {
if (!container->destroying || container->ntxnrefs == 1) { if (!container->node.destroying || container->node.ntxnrefs == 1) {
view_remove_saved_buffer(container->sway_view); view_remove_saved_buffer(view);
} }
} }
// Damage the new location // Damage the new location
desktop_damage_whole_container(container); desktop_damage_whole_container(container);
if (container->type == C_VIEW && container->sway_view->surface) { if (view && view->surface) {
struct sway_view *view = container->sway_view;
struct wlr_surface *surface = view->surface; struct wlr_surface *surface = view->surface;
struct wlr_box box = { struct wlr_box box = {
.x = container->current.view_x - view->geometry.x, .x = container->current.view_x - view->geometry.x,
@ -218,17 +269,56 @@ static void transaction_apply(struct sway_transaction *transaction) {
desktop_damage_box(&box); desktop_damage_box(&box);
} }
container->instruction = NULL; if (!container->node.destroying) {
if (container->type == C_CONTAINER || container->type == C_VIEW) {
container_discover_outputs(container); container_discover_outputs(container);
} }
} }
/**
* Apply a transaction to the "current" state of the tree.
*/
static void transaction_apply(struct sway_transaction *transaction) {
wlr_log(WLR_DEBUG, "Applying transaction %p", transaction);
if (debug.txn_timings) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec *commit = &transaction->commit_time;
float ms = (now.tv_sec - commit->tv_sec) * 1000 +
(now.tv_nsec - commit->tv_nsec) / 1000000.0;
wlr_log(WLR_DEBUG, "Transaction %p: %.1fms waiting "
"(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60));
}
// Apply the instruction state to the node's current state
for (int i = 0; i < transaction->instructions->length; ++i) {
struct sway_transaction_instruction *instruction =
transaction->instructions->items[i];
struct sway_node *node = instruction->node;
switch (node->type) {
case N_ROOT:
break;
case N_OUTPUT:
apply_output_state(node->sway_output, instruction->output_state);
break;
case N_WORKSPACE:
apply_workspace_state(node->sway_workspace,
instruction->workspace_state);
break;
case N_CONTAINER:
apply_container_state(node->sway_container,
instruction->container_state);
break;
}
node->instruction = NULL;
}
} }
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,13 +54,13 @@ 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->lx - view->x,
.y = output->y - view->y, .y = output->ly - view->y,
.width = output->width, .width = output->width,
.height = output->height, .height = output->height,
}; };
@ -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,13 +53,13 @@ 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->lx - view->x,
.y = output->y - view->y, .y = output->ly - view->y,
.width = output->width, .width = output->width,
.height = output->height, .height = output->height,
}; };
@ -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,54 @@ 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 NULL;
}
if (ws->fullscreen) {
struct sway_container *con =
tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
if (con) {
return &con->node;
} }
if (ws->sway_workspace->fullscreen) { return NULL;
return tiling_container_at(ws->sway_workspace->fullscreen, lx, ly,
surface, sx, sy);
} }
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 NULL;
} }
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 NULL;
} }
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 NULL;
}
c = seat_get_active_child(seat, output->swayc);
if (c) {
return c;
}
if (!c && output->swayc->children->length) {
c = output->swayc->children->items[0];
return c;
} }
return output->swayc; return &ws->node;
} }
/** /**
@ -160,13 +148,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 +167,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 +208,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 +249,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 +257,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 +301,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 +344,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 +422,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) { } else if (node->type == N_CONTAINER && node->sway_container->view) {
seat_set_focus_warp(seat, c, false, true); // Focus node if the following are true:
}
} else if (c->type == C_VIEW) {
// Focus c 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)) { next_focus->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 +469,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)) {
@ -637,8 +620,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
// Determine what's under the cursor // Determine what's under the cursor
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
double sx, sy; double sx, sy;
struct sway_container *cont = 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);
struct sway_container *cont = node && node->type == N_CONTAINER ?
node->sway_container : NULL;
bool is_floating = cont && container_is_floating(cont); bool is_floating = cont && container_is_floating(cont);
bool is_floating_or_child = cont && container_is_floating_or_child(cont); bool is_floating_or_child = cont && container_is_floating_or_child(cont);
bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont);
@ -670,6 +655,12 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
return; return;
} }
// Handle clicking an empty workspace
if (node && node->type == N_WORKSPACE) {
seat_set_focus(seat, node);
return;
}
// Handle clicking a layer surface // Handle clicking a layer surface
if (surface && wlr_surface_is_layer_surface(surface)) { if (surface && wlr_surface_is_layer_surface(surface)) {
struct wlr_layer_surface *layer = struct wlr_layer_surface *layer =
@ -684,7 +675,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 +704,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 +716,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 +738,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 +753,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 +761,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;
} }
@ -808,7 +799,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
event->x, event->y, &lx, &ly); event->x, event->y, &lx, &ly);
double sx, sy; double sx, sy;
container_at_coords(seat, lx, ly, &surface, &sx, &sy); node_at_coords(seat, lx, ly, &surface, &sx, &sy);
seat->touch_id = event->touch_id; seat->touch_id = event->touch_id;
seat->touch_x = lx; seat->touch_x = lx;
@ -850,7 +841,7 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
event->x, event->y, &lx, &ly); event->x, event->y, &lx, &ly);
double sx, sy; double sx, sy;
container_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
if (seat->touch_id == event->touch_id) { if (seat->touch_id == event->touch_id) {
seat->touch_x = lx; seat->touch_x = lx;
@ -1025,8 +1016,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) {
if (container->type == C_VIEW) {
wlr_seat_keyboard_clear_focus(seat->wlr_seat); wlr_seat_keyboard_clear_focus(seat->wlr_seat);
view_set_activated(container->sway_view, false); if (node->type == N_WORKSPACE) {
workspace_for_each_container(node->sway_workspace, send_unfocus, seat);
} 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,71 @@ 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) { // Unfocus the previous focus
struct sway_seat_container *seat_con = if (last_focus) {
seat_container_from_container(seat, container); seat_send_unfocus(last_focus, seat);
if (seat_con == NULL) { node_set_dirty(last_focus);
return; struct sway_node *parent = node_get_parent(last_focus);
if (parent) {
node_set_dirty(parent);
}
} }
// put all the ancestors of this container on top of the focus stack // Put the container parents on the focus stack, then the workspace, then
struct sway_seat_container *parent = // the focused container.
seat_container_from_container(seat, container->parent); if (container) {
struct sway_container *parent = 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);
wl_list_insert(&seat->focus_stack, &seat_con->link);
if (last_focus) {
seat_send_unfocus(last_focus, seat);
container_set_dirty(last_focus);
} }
seat_send_focus(container, seat); if (new_workspace) {
struct sway_seat_node *seat_node =
container_set_dirty(container); seat_node_from_node(seat, &new_workspace->node);
container_set_dirty(container->parent); // for focused_inactive_child 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);
seat_send_focus(&container->node, seat);
} }
// 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 +666,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 +693,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.
if (container) {
struct sway_container *floater = container; struct sway_container *floater = container;
while (floater->parent && floater->parent->type != C_WORKSPACE) { while (floater->parent) {
floater = floater->parent; floater = floater->parent;
} }
if (container_is_floating(floater)) { if (container_is_floating(floater)) {
list_move_to_end(floater->parent->sway_workspace->floating, floater); list_move_to_end(floater->workspace->floating, floater);
node_set_dirty(&floater->workspace->node);
}
} }
if (last_focus) { if (last_focus) {
@ -725,13 +710,17 @@ 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 = 0;
double y = container->y + container->height / 2.0; double y = 0;
struct wlr_output *wlr_output = if (container) {
new_output->sway_output->wlr_output; x = container->x + container->width / 2.0;
if (!wlr_output_layout_contains_point( y = container->y + container->height / 2.0;
root_container.sway_root->output_layout, } else {
wlr_output, seat->cursor->cursor->x, x = new_workspace->x + new_workspace->width / 2.0;
y = new_workspace->y + new_workspace->height / 2.0;
}
if (!wlr_output_layout_contains_point(root->output_layout,
new_output->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 +733,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 +743,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 +760,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 +782,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 +794,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 +817,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;
case C_OUTPUT:
ipc_json_describe_output(c, object);
break; break;
case C_CONTAINER: case N_OUTPUT:
case C_VIEW: ipc_json_describe_output(node->sway_output, object);
ipc_json_describe_view(c, object);
break; break;
case C_WORKSPACE: case N_CONTAINER:
ipc_json_describe_workspace(c, object); ipc_json_describe_view(node->sway_container, object);
break; break;
case C_TYPES: case N_WORKSPACE:
default: ipc_json_describe_workspace(node->sway_workspace, object);
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));

@ -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_container(container)); ipc_json_describe_node(&output->node));
}
} }
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,37 @@ 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->lx;
fs->y = workspace->parent->y; fs->y = output->ly;
fs->width = workspace->parent->width; fs->width = output->width;
fs->height = workspace->parent->height; fs->height = 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")) {
return;
}
const struct wlr_box *output_box = wlr_output_layout_get_box( const struct wlr_box *output_box = wlr_output_layout_get_box(
root_container.sway_root->output_layout, root->output_layout, output->wlr_output);
output->sway_output->wlr_output); output->lx = output_box->x;
output->x = output_box->x; output->ly = output_box->y;
output->y = output_box->y;
output->width = output_box->width; output->width = output_box->width;
output->height = output_box->height; output->height = output_box->height;
container_set_dirty(output);
wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", for (int i = 0; i < output->workspaces->length; ++i) {
output->name, output->x, output->y); 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];
arrange_workspace(workspace); arrange_workspace(workspace);
} }
} }
@ -264,37 +251,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,116 @@ 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; output->lx = wlr_output->lx;
wlr_output_effective_resolution(sway_output->wlr_output, &size.width, output->ly = wlr_output->ly;
&size.height); wlr_output_transformed_resolution(wlr_output,
output->width = size.width; &output->width, &output->height);
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 +151,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 +186,128 @@ 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);
root_for_each_container(untrack_output, output->sway_output); int index = list_find(root->outputs, output);
list_del(root->outputs, index);
wl_list_remove(&output->sway_output->mode.link); wl_list_remove(&output->mode.link);
wl_list_remove(&output->sway_output->transform.link); wl_list_remove(&output->transform.link);
wl_list_remove(&output->sway_output->scale.link); wl_list_remove(&output->scale.link);
wl_list_remove(&output->sway_output->damage_destroy.link); wl_list_remove(&output->damage_destroy.link);
wl_list_remove(&output->sway_output->damage_frame.link); wl_list_remove(&output->damage_frame.link);
output->sway_output->swayc = NULL; output->enabled = false;
output->sway_output = NULL;
if (output->parent) { arrange_root();
container_remove_child(output);
} }
void output_begin_destroy(struct sway_output *output) {
if (!sway_assert(!output->enabled, "Expected a disabled output")) {
return;
} }
wlr_log(WLR_DEBUG, "Destroying output '%s'", output->wlr_output->name);
struct sway_container *output_from_wlr_output(struct wlr_output *output) { output->node.destroying = true;
if (output == NULL) { node_set_dirty(&output->node);
return NULL;
wl_list_remove(&output->link);
wl_list_remove(&output->destroy.link);
output->wlr_output->data = NULL;
output->wlr_output = NULL;
}
struct output_config *output_find_config(struct sway_output *output) {
const char *name = output->wlr_output->name;
char identifier[128];
output_get_identifier(identifier, sizeof(identifier), 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;
} }
for (int i = 0; i < root_container.children->length; ++i) { if (strcasecmp("*", cur->name) == 0) {
struct sway_container *o = root_container.children->items[i]; wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) { all = cur;
return o;
} }
if (oc && all) {
break;
}
}
if (!oc) {
oc = all;
}
return oc;
}
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;
}
int lx = reference->wlr_output->lx + reference->width / 2;
int ly = reference->wlr_output->ly + reference->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 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 +315,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 +328,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 +343,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->lx;
box->y = output->ly;
box->width = output->width;
box->height = 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->height > 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 = root->output_layout_change.notify = output_layout_handle_change;
output_layout_handle_change; wl_signal_add(&root->output_layout->events.change,
wl_signal_add(&root_container.sway_root->output_layout->events.change, &root->output_layout_change);
&root_container.sway_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,31 @@ 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, if (!view->container->workspace) {
"Called view_autoconfigure() on a view without a swayc")) { // Hidden in the scratchpad
return; return;
} }
struct sway_output *output = view->container->workspace->output;
struct sway_container *output = container_parent(view->swayc, C_OUTPUT); if (view->container->is_fullscreen) {
view->x = output->lx;
if (view->swayc->is_fullscreen) { view->y = output->ly;
view->x = output->x;
view->y = output->y;
view->width = output->width; view->width = output->width;
view->height = output->height; view->height = 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 +196,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 +222,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 +276,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 +294,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 +329,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 +383,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 +395,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 +407,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 +468,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 +484,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 +506,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);
} 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 +533,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 +541,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 +559,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 if (ws) {
workspace_consider_destroy(ws);
}
// If the workspace wasn't reaped if (ws && !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 +578,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 +655,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 +784,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 +810,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 +837,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 +846,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 +862,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 +879,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 +923,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 +968,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 +1017,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 +1028,13 @@ 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); if (view->container->workspace) {
workspace_detect_urgent(ws); workspace_detect_urgent(view->container->workspace);
}
} }
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; struct sway_workspace *ws = calloc(1, sizeof(struct sway_workspace));
workspace->y = output->y; if (!ws) {
workspace->width = output->width; wlr_log(WLR_ERROR, "Unable to allocate sway_workspace");
workspace->height = output->height;
workspace->name = !name ? NULL : strdup(name);
workspace->prev_split_layout = L_NONE;
workspace->layout = container_get_default_layout(output);
struct sway_workspace *swayws = calloc(1, sizeof(struct sway_workspace));
if (!swayws) {
return NULL; return NULL;
} }
swayws->swayc = workspace; node_init(&ws->node, N_WORKSPACE, ws);
swayws->floating = create_list(); ws->x = output->lx;
swayws->output_priority = create_list(); ws->y = output->ly;
workspace->sway_workspace = swayws; ws->width = output->width;
workspace_output_add_priority(workspace, output); ws->height = 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);
void workspace_destroy(struct sway_container *workspace) { return ws;
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
return;
} }
if (!sway_assert(workspace->destroying,
void workspace_destroy(struct sway_workspace *workspace) {
if (!sway_assert(workspace->node.destroying,
"Tried to free workspace which wasn't marked as destroying")) { "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);
}
} }
void workspace_consider_destroy(struct sway_container *ws) { workspace->node.destroying = true;
if (!sway_assert(ws->type == C_WORKSPACE, "Expected a workspace")) { node_set_dirty(&workspace->node);
return;
} }
struct sway_seat *seat = input_manager_current_seat(input_manager);
if (ws->children->length == 0 && ws->sway_workspace->floating->length == 0 void workspace_consider_destroy(struct sway_workspace *ws) {
&& seat_get_active_child(seat, ws->parent) != ws) { if (ws->tiling->length == 0 && ws->floating->length == 0
&& output_get_active_workspace(ws->output) != 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_output *output = workspace->output;
int index = list_find(output->workspaces, workspace);
if (index != -1) {
list_del(output->workspaces, index);
}
workspace->output = NULL;
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) { struct sway_container *con) {
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { if (con->workspace) {
return; container_detach(con);
} }
if (!sway_assert(con->parent == NULL, "Expected an orphan container")) { list_add(workspace->tiling, con);
return; 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