Implement type safe arguments and demote sway_container

This commit changes the meaning of sway_container so that it only refers
to layout containers and view containers. Workspaces, outputs and the
root are no longer known as containers. Instead, root, outputs,
workspaces and containers are all a type of node, and containers come in
two types: layout containers and view containers.

In addition to the above, this implements type safe variables. This
means we use specific types such as sway_output and sway_workspace
instead of generic containers or nodes. However, it's worth noting that
in a few places places (eg. seat focus and transactions) referring to
them in a generic way is unavoidable which is why we still use nodes in
some places.

If you want a TL;DR, look at node.h, as well as the struct definitions
for root, output, workspace and container. Note that sway_output now
contains a workspaces list, and workspaces now contain a tiling and
floating list, and containers now contain a pointer back to the
workspace.

There are now functions for seat_get_focused_workspace and
seat_get_focused_container. The latter will return NULL if a workspace
itself is focused. Most other seat functions like seat_get_focus and
seat_set_focus now accept and return nodes.

In the config->handler_context struct, current_container has been
replaced with three pointers: node, container and workspace. node is the
same as what current_container was, while workspace is the workspace
that the node resides on and container is the actual container, which
may be NULL if a workspace itself is focused.

The global root_container variable has been replaced with one simply
called root, which is a pointer to the sway_root instance.

The way outputs are created, enabled, disabled and destroyed has
changed. Previously we'd wrap the sway_output in a container when it is
enabled, but as we don't have containers any more it needs a different
approach. The output_create and output_destroy functions previously
created/destroyed the container, but now they create/destroy the
sway_output. There is a new function output_disable to disable an output
without destroying it.

Containers have a new view property. If this is populated then the
container is a view container, otherwise it's a layout container. Like
before, this property is immutable for the life of the container.

Containers have both a `sway_container *parent` and
`sway_workspace *workspace`. As we use specific types now, parent cannot
point to a workspace so it'll be NULL for containers which are direct
children of the workspace. The workspace property is set for all
containers, except those which are hidden in the scratchpad as they have
no workspace.

In some cases we need to refer to workspaces in a container-like way.
For example, workspaces have layout and children, but when using
specific types this makes it difficult. Likewise, it's difficult for a
container to get its parent's layout when the parent could be another
container or a workspace. To make it easier, some helper functions have
been created: container_parent_layout and container_get_siblings.

container_remove_child has been renamed to container_detach and
container_replace_child has been renamed to container_replace.

`container_handle_fullscreen_reparent(con, old_parent)` has had the
old_parent removed. We now unfullscreen the workspace when detaching the
container, so this function is simplified and only needs one argument
now.

container_notify_subtree_changed has been renamed to
container_update_representation. This is more descriptive of its
purpose. I also wanted to be able to call it with whatever container was
changed rather than the container's parent, which makes bubbling up to
the workspace easier.

There are now state structs per node thing. ie. sway_output_state,
sway_workspace_state and sway_container_state.

The focus, move and layout commands have been completely refactored to
work with the specific types. I considered making these a separate PR,
but I'd be backporting my changes only to replace them again, and it's
easier just to test everything at once.
master
Ryan Dwyer 6 years ago
parent 309fcf2300
commit 7586f150c0

@ -407,7 +407,9 @@ struct sway_config {
struct output_config *output_config;
struct seat_config *seat_config;
struct sway_seat *seat;
struct sway_container *current_container;
struct sway_node *node;
struct sway_container *container;
struct sway_workspace *workspace;
bool using_criteria;
struct {
int argc;
@ -486,8 +488,7 @@ struct output_config *new_output_config(const char *name);
void merge_output_config(struct output_config *dst, struct output_config *src);
void apply_output_config(struct output_config *oc,
struct sway_container *output);
void apply_output_config(struct output_config *oc, struct sway_output *output);
struct output_config *store_output_config(struct output_config *oc);

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

@ -37,10 +37,10 @@ struct sway_input_manager {
struct sway_input_manager *input_manager_create(struct sway_server *server);
bool input_manager_has_focus(struct sway_input_manager *input,
struct sway_container *container);
struct sway_node *node);
void input_manager_set_focus(struct sway_input_manager *input,
struct sway_container *container);
struct sway_node *node);
void input_manager_configure_xcursor(struct sway_input_manager *input);

@ -13,9 +13,9 @@ struct sway_seat_device {
struct wl_list link; // sway_seat::devices
};
struct sway_seat_container {
struct sway_seat_node {
struct sway_seat *seat;
struct sway_container *container;
struct sway_node *node;
struct wl_list link; // sway_seat::focus_stack
@ -76,7 +76,7 @@ struct sway_seat {
uint32_t last_button_serial;
struct wl_listener focus_destroy;
struct wl_listener new_container;
struct wl_listener new_node;
struct wl_listener new_drag_icon;
struct wl_list devices; // sway_seat_device::link
@ -100,10 +100,10 @@ void seat_remove_device(struct sway_seat *seat,
void seat_configure_xcursor(struct sway_seat *seat);
void seat_set_focus(struct sway_seat *seat, struct sway_container *container);
void seat_set_focus(struct sway_seat *seat, struct sway_node *node);
void seat_set_focus_warp(struct sway_seat *seat,
struct sway_container *container, bool warp, bool notify);
struct sway_node *node, bool warp, bool notify);
void seat_set_focus_surface(struct sway_seat *seat,
struct wlr_surface *surface, bool unfocus);
@ -114,7 +114,11 @@ void seat_set_focus_layer(struct sway_seat *seat,
void seat_set_exclusive_client(struct sway_seat *seat,
struct wl_client *client);
struct sway_container *seat_get_focus(struct sway_seat *seat);
struct sway_node *seat_get_focus(struct sway_seat *seat);
struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat);
struct sway_container *seat_get_focused_container(struct sway_seat *seat);
/**
* Return the last container to be focused for the seat (or the most recently
@ -125,32 +129,31 @@ struct sway_container *seat_get_focus(struct sway_seat *seat);
* is destroyed, or focus moves to a container with children and we need to
* descend into the next leaf in focus order.
*/
struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_container *container);
struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_node *node);
struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat,
struct sway_container *container);
struct sway_workspace *workspace);
/**
* Descend into the focus stack to find the focus-inactive view. Useful for
* container placement when they change position in the tree.
*/
struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat,
struct sway_container *container);
struct sway_node *ancestor);
/**
* Return the immediate child of container which was most recently focused.
*/
struct sway_container *seat_get_active_child(struct sway_seat *seat,
struct sway_container *container);
struct sway_node *seat_get_active_child(struct sway_seat *seat,
struct sway_node *parent);
/**
* Iterate over the focus-inactive children of the container calling the
* function on each.
*/
void seat_focus_inactive_children_for_each(struct sway_seat *seat,
struct sway_container *container,
void (*f)(struct sway_container *container, void *data), void *data);
void seat_for_each_node(struct sway_seat *seat,
void (*f)(struct sway_node *node, void *data), void *data);
void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config);
@ -173,7 +176,7 @@ void seat_begin_resize_tiling(struct sway_seat *seat,
struct sway_container *con, uint32_t button, enum wlr_edges edge);
struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat,
struct sway_container *container);
struct sway_workspace *workspace);
void seat_end_mouse_operation(struct sway_seat *seat);

@ -7,8 +7,8 @@
json_object *ipc_json_get_version();
json_object *ipc_json_describe_disabled_output(struct sway_output *o);
json_object *ipc_json_describe_container(struct sway_container *c);
json_object *ipc_json_describe_container_recursive(struct sway_container *c);
json_object *ipc_json_describe_node(struct sway_node *node);
json_object *ipc_json_describe_node_recursive(struct sway_node *node);
json_object *ipc_json_describe_input(struct sway_input_device *device);
json_object *ipc_json_describe_seat(struct sway_seat *seat);
json_object *ipc_json_describe_bar_config(struct bar_config *bar);

@ -11,8 +11,8 @@ void ipc_init(struct sway_server *server);
struct sockaddr_un *ipc_user_sockaddr(void);
void ipc_event_workspace(struct sway_container *old,
struct sway_container *new, const char *change);
void ipc_event_workspace(struct sway_workspace *old,
struct sway_workspace *new, const char *change);
void ipc_event_window(struct sway_container *window, const char *change);
void ipc_event_barconfig_update(struct bar_config *bar);
void ipc_event_mode(const char *mode, bool pango);

@ -6,14 +6,20 @@
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_output.h>
#include "config.h"
#include "sway/tree/node.h"
#include "sway/tree/view.h"
struct sway_server;
struct sway_container;
struct sway_output_state {
list_t *workspaces;
struct sway_workspace *active_workspace;
};
struct sway_output {
struct sway_node node;
struct wlr_output *wlr_output;
struct sway_container *swayc;
struct sway_server *server;
struct wl_list layers[4]; // sway_layer_surface::link
@ -22,11 +28,15 @@ struct sway_output {
struct timespec last_frame;
struct wlr_output_damage *damage;
bool enabled;
list_t *workspaces;
struct sway_output_state current;
struct wl_listener destroy;
struct wl_listener mode;
struct wl_listener transform;
struct wl_listener scale;
struct wl_listener damage_destroy;
struct wl_listener damage_frame;
@ -39,13 +49,19 @@ struct sway_output {
} events;
};
struct sway_container *output_create(struct sway_output *sway_output);
struct sway_output *output_create(struct wlr_output *wlr_output);
void output_destroy(struct sway_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,
struct wlr_surface *surface, struct wlr_box *box, float rotation,
@ -64,15 +80,19 @@ void output_damage_box(struct sway_output *output, struct wlr_box *box);
void output_damage_whole_container(struct sway_output *output,
struct sway_container *con);
struct sway_container *output_by_name(const char *name);
struct sway_output *output_by_name(const char *name);
void output_sort_workspaces(struct sway_container *output);
void output_sort_workspaces(struct sway_output *output);
void output_enable(struct sway_output *output);
struct output_config *output_find_config(struct sway_output *output);
void output_enable(struct sway_output *output, struct output_config *oc);
void output_disable(struct sway_output *output);
bool output_has_opaque_overlay_layer_surface(struct sway_output *output);
struct sway_container *output_get_active_workspace(struct sway_output *output);
struct sway_workspace *output_get_active_workspace(struct sway_output *output);
void output_render(struct sway_output *output, struct timespec *when,
pixman_region32_t *damage);
@ -103,16 +123,23 @@ void output_drag_icons_for_each_surface(struct sway_output *output,
struct wl_list *drag_icons, sway_surface_iterator_func_t iterator,
void *user_data);
void output_for_each_workspace(struct sway_container *output,
void (*f)(struct sway_container *con, void *data), void *data);
void output_for_each_workspace(struct sway_output *output,
void (*f)(struct sway_workspace *ws, void *data), void *data);
void output_for_each_container(struct sway_container *output,
void output_for_each_container(struct sway_output *output,
void (*f)(struct sway_container *con, void *data), void *data);
struct sway_container *output_find_workspace(struct sway_container *output,
bool (*test)(struct sway_container *con, void *data), void *data);
struct sway_workspace *output_find_workspace(struct sway_output *output,
bool (*test)(struct sway_workspace *ws, void *data), void *data);
struct sway_container *output_find_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);
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

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

@ -1,16 +1,19 @@
#ifndef _SWAY_ARRANGE_H
#define _SWAY_ARRANGE_H
struct sway_output;
struct sway_workspace;
struct sway_container;
struct sway_node;
void arrange_container(struct sway_container *container);
void arrange_workspace(struct sway_container *workspace);
void arrange_workspace(struct sway_workspace *workspace);
void arrange_output(struct sway_container *output);
void arrange_output(struct sway_output *output);
void arrange_root(void);
void arrange_windows(struct sway_container *container);
void arrange_node(struct sway_node *node);
#endif

@ -5,8 +5,7 @@
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include "list.h"
extern struct sway_container root_container;
#include "sway/tree/node.h"
struct sway_view;
struct sway_seat;
@ -17,23 +16,6 @@ struct sway_seat;
#define TITLEBAR_H_PADDING 3
#define TITLEBAR_V_PADDING 4
/**
* Different kinds of containers.
*
* This enum is in order. A container will never be inside of a container below
* it on this list.
*/
enum sway_container_type {
C_ROOT,
C_OUTPUT,
C_WORKSPACE,
C_CONTAINER,
C_VIEW,
// Keep last
C_TYPES,
};
enum sway_container_layout {
L_NONE,
L_HORIZ,
@ -57,18 +39,14 @@ enum movement_direction;
enum wlr_direction;
struct sway_container_state {
// Container/swayc properties
// Container properties
enum sway_container_layout layout;
double swayc_x, swayc_y;
double swayc_width, swayc_height;
double con_x, con_y;
double con_width, con_height;
bool is_fullscreen;
bool has_gaps;
double current_gaps;
double gaps_inner;
double gaps_outer;
struct sway_workspace *workspace;
struct sway_container *parent;
list_t *children;
@ -86,35 +64,19 @@ struct sway_container_state {
bool border_left;
bool border_right;
bool using_csd;
// Workspace properties
struct sway_container *ws_fullscreen;
list_t *ws_floating;
};
struct sway_container {
union {
// TODO: Encapsulate state for other node types as well like C_CONTAINER
struct sway_root *sway_root;
struct sway_output *sway_output;
struct sway_workspace *sway_workspace;
struct sway_view *sway_view;
};
/**
* A unique ID to identify this container. Primarily used in the
* get_tree JSON output.
*/
size_t id;
struct sway_node node;
struct sway_view *view;
// The pending state is the main container properties, and the current state is in the below struct.
// This means most places of the code can refer to the main variables (pending state) and it'll just work.
struct sway_container_state current;
char *name; // The view's title (unformatted)
char *title; // The view's title (unformatted)
char *formatted_title; // The title displayed in the title bar
enum sway_container_type type;
enum sway_container_layout layout;
enum sway_container_layout prev_split_layout;
@ -132,14 +94,13 @@ struct sway_container {
// The gaps currently applied to the container.
double current_gaps;
bool has_gaps;
double gaps_inner;
double gaps_outer;
list_t *children;
struct sway_container *parent;
struct sway_workspace *workspace; // NULL when hidden in the scratchpad
struct sway_container *parent; // NULL if container in root of workspace
list_t *children; // struct sway_container
// Outputs currently being intersected
list_t *outputs; // struct sway_output
@ -157,42 +118,17 @@ struct sway_container {
struct wlr_texture *title_urgent;
size_t title_height;
// The number of transactions which reference this container.
size_t ntxnrefs;
// If this container is a view and is waiting for the client to respond to a
// configure then this will be populated, otherwise NULL.
struct sway_transaction_instruction *instruction;
bool destroying;
// If true, indicates that the container has pending state that differs from
// the current.
bool dirty;
struct {
struct wl_signal destroy;
} events;
};
struct sway_container *container_create(enum sway_container_type type);
const char *container_type_to_str(enum sway_container_type type);
/*
* Create a new view container. A view can be a child of a workspace container
* or a container container and are rendered in the order and structure of
* how they are attached to the tree.
*/
struct sway_container *container_view_create(
struct sway_container *sibling, struct sway_view *sway_view);
struct sway_container *container_create(struct sway_view *view);
void container_destroy(struct sway_container *con);
void container_begin_destroy(struct sway_container *con);
struct sway_container *container_close(struct sway_container *container);
/**
* Search a container's descendants a container based on test criteria. Returns
* the first container that passes the test.
@ -200,23 +136,17 @@ struct sway_container *container_close(struct sway_container *container);
struct sway_container *container_find_child(struct sway_container *container,
bool (*test)(struct sway_container *view, void *data), void *data);
/**
* Finds a parent container with the given struct sway_containerype.
*/
struct sway_container *container_parent(struct sway_container *container,
enum sway_container_type type);
/**
* Find a container at the given coordinates. Returns the the surface and
* surface-local coordinates of the given layout coordinates if the container
* is a view and the view contains a surface at those coordinates.
*/
struct sway_container *container_at(struct sway_container *workspace,
struct sway_container *container_at(struct sway_workspace *workspace,
double lx, double ly, struct wlr_surface **surface,
double *sx, double *sy);
struct sway_container *tiling_container_at(
struct sway_container *con, double lx, double ly,
struct sway_node *parent, double lx, double ly,
struct wlr_surface **surface, double *sx, double *sy);
void container_for_each_child(struct sway_container *container,
@ -228,16 +158,11 @@ void container_for_each_child(struct sway_container *container,
bool container_has_ancestor(struct sway_container *container,
struct sway_container *ancestor);
int container_count_descendants_of_type(struct sway_container *con,
enum sway_container_type type);
void container_create_notify(struct sway_container *container);
void container_update_textures_recursive(struct sway_container *con);
void container_damage_whole(struct sway_container *container);
struct sway_container *container_reap_empty(struct sway_container *con);
void container_reap_empty(struct sway_container *con);
struct sway_container *container_flatten(struct sway_container *container);
@ -248,11 +173,10 @@ void container_update_title_textures(struct sway_container *container);
*/
void container_calculate_title_height(struct sway_container *container);
/**
* Notify a container that a tree modification has changed in its children,
* so the container can update its tree representation.
*/
void container_notify_subtree_changed(struct sway_container *container);
size_t container_build_representation(enum sway_container_layout layout,
list_t *children, char *buffer);
void container_update_representation(struct sway_container *container);
/**
* Return the height of a regular title bar.
@ -288,8 +212,7 @@ void container_floating_translate(struct sway_container *con,
/**
* Choose an output for the floating container's new position.
*/
struct sway_container *container_floating_find_output(
struct sway_container *con);
struct sway_output *container_floating_find_output(struct sway_container *con);
/**
* Move a floating container to a new layout-local position.
@ -302,12 +225,6 @@ void container_floating_move_to(struct sway_container *con,
*/
void container_floating_move_to_center(struct sway_container *con);
/**
* Mark a container as dirty if it isn't already. Dirty containers will be
* included in the next transaction then unmarked as dirty.
*/
void container_set_dirty(struct sway_container *container);
bool container_has_urgent_child(struct sway_container *container);
/**
@ -342,10 +259,18 @@ void container_remove_gaps(struct sway_container *container);
void container_add_gaps(struct sway_container *container);
enum sway_container_layout container_parent_layout(struct sway_container *con);
enum sway_container_layout container_current_parent_layout(
struct sway_container *con);
list_t *container_get_siblings(const struct sway_container *container);
int container_sibling_index(const struct sway_container *child);
void container_handle_fullscreen_reparent(struct sway_container *con,
struct sway_container *old_parent);
list_t *container_get_current_siblings(struct sway_container *container);
void container_handle_fullscreen_reparent(struct sway_container *con);
void container_add_child(struct sway_container *parent,
struct sway_container *child);
@ -353,19 +278,16 @@ void container_add_child(struct sway_container *parent,
void container_insert_child(struct sway_container *parent,
struct sway_container *child, int i);
struct sway_container *container_add_sibling(struct sway_container *parent,
struct sway_container *child);
void container_add_sibling(struct sway_container *parent,
struct sway_container *child, int offset);
struct sway_container *container_remove_child(struct sway_container *child);
void container_detach(struct sway_container *child);
struct sway_container *container_replace_child(struct sway_container *child,
struct sway_container *new_child);
void container_replace(struct sway_container *container,
struct sway_container *replacement);
bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out);
enum sway_container_layout container_get_default_layout(
struct sway_container *con);
struct sway_container *container_split(struct sway_container *child,
enum sway_container_layout layout);

@ -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/render/wlr_texture.h>
#include "sway/tree/container.h"
#include "sway/tree/node.h"
#include "config.h"
#include "list.h"
extern struct sway_container root_container;
extern struct sway_root *root;
struct sway_root {
struct sway_node node;
struct wlr_output_layout *output_layout;
struct wl_listener output_layout_change;
@ -24,17 +26,21 @@ struct sway_root {
// Includes disabled outputs
struct wl_list all_outputs; // sway_output::link
double x, y;
double width, height;
list_t *outputs; // struct sway_output
list_t *scratchpad; // struct sway_container
list_t *saved_workspaces; // For when there's no connected outputs
struct {
struct wl_signal new_container;
struct wl_signal new_node;
} events;
};
void root_create(void);
struct sway_root *root_create(void);
void root_destroy(void);
void root_destroy(struct sway_root *root);
/**
* Move a container to the scratchpad.
@ -56,23 +62,25 @@ void root_scratchpad_show(struct sway_container *con);
*/
void root_scratchpad_hide(struct sway_container *con);
struct sway_container *root_workspace_for_pid(pid_t pid);
struct sway_workspace *root_workspace_for_pid(pid_t pid);
void root_record_workspace_pid(pid_t pid);
void root_for_each_workspace(void (*f)(struct sway_container *con, void *data),
void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
void *data);
void root_for_each_container(void (*f)(struct sway_container *con, void *data),
void *data);
struct sway_container *root_find_output(
bool (*test)(struct sway_container *con, void *data), void *data);
struct sway_output *root_find_output(
bool (*test)(struct sway_output *output, void *data), void *data);
struct sway_container *root_find_workspace(
bool (*test)(struct sway_container *con, void *data), void *data);
struct sway_workspace *root_find_workspace(
bool (*test)(struct sway_workspace *ws, void *data), void *data);
struct sway_container *root_find_container(
bool (*test)(struct sway_container *con, void *data), void *data);
void root_get_box(struct sway_root *root, struct wlr_box *box);
#endif

@ -58,7 +58,7 @@ struct sway_view {
enum sway_view_type type;
const struct sway_view_impl *impl;
struct sway_container *swayc; // NULL for unmapped views
struct sway_container *container; // NULL if unmapped and transactions finished
struct wlr_surface *surface; // NULL for unmapped views
// Geometry of the view itself (excludes borders) in layout coordinates
@ -254,7 +254,7 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
int height);
/**
* Configure the view's position and size based on the swayc's position and
* Configure the view's position and size based on the container's position and
* size, taking borders into consideration.
*/
void view_autoconfigure(struct sway_view *view);

@ -3,66 +3,98 @@
#include <stdbool.h>
#include "sway/tree/container.h"
#include "sway/tree/node.h"
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_container *swayc;
struct sway_node node;
struct sway_container *fullscreen;
char *name;
char *representation;
double x, y;
int width, height;
enum sway_container_layout layout;
enum sway_container_layout prev_split_layout;
double current_gaps;
bool has_gaps;
double gaps_inner;
double gaps_outer;
struct sway_output *output; // NULL if no outputs are connected
list_t *floating; // struct sway_container
list_t *tiling; // struct sway_container
list_t *output_priority;
bool urgent;
struct sway_workspace_state current;
};
extern char *prev_workspace_name;
struct sway_container *workspace_get_initial_output(const char *name);
struct sway_output *workspace_get_initial_output(const char *name);
struct sway_container *workspace_create(struct sway_container *output,
struct sway_workspace *workspace_create(struct sway_output *output,
const char *name);
void workspace_destroy(struct sway_container *workspace);
void workspace_destroy(struct sway_workspace *workspace);
void workspace_begin_destroy(struct sway_container *workspace);
void workspace_begin_destroy(struct sway_workspace *workspace);
void workspace_consider_destroy(struct sway_container *ws);
void workspace_consider_destroy(struct sway_workspace *ws);
char *workspace_next_name(const char *output_name);
bool workspace_switch(struct sway_container *workspace,
bool workspace_switch(struct sway_workspace *workspace,
bool no_auto_back_and_forth);
struct sway_container *workspace_by_number(const char* name);
struct sway_workspace *workspace_by_number(const char* name);
struct sway_container *workspace_by_name(const char*);
struct sway_workspace *workspace_by_name(const char*);
struct sway_container *workspace_output_next(struct sway_container *current);
struct sway_workspace *workspace_output_next(struct sway_workspace *current);
struct sway_container *workspace_next(struct sway_container *current);
struct sway_workspace *workspace_next(struct sway_workspace *current);
struct sway_container *workspace_output_prev(struct sway_container *current);
struct sway_workspace *workspace_output_prev(struct sway_workspace *current);
struct sway_container *workspace_prev(struct sway_container *current);
struct sway_workspace *workspace_prev(struct sway_workspace *current);
bool workspace_is_visible(struct sway_container *ws);
bool workspace_is_visible(struct sway_workspace *ws);
bool workspace_is_empty(struct sway_container *ws);
bool workspace_is_empty(struct sway_workspace *ws);
void workspace_output_raise_priority(struct sway_container *workspace,
struct sway_container *old_output, struct sway_container *new_output);
void workspace_output_raise_priority(struct sway_workspace *workspace,
struct sway_output *old_output, struct sway_output *new_output);
void workspace_output_add_priority(struct sway_container *workspace,
struct sway_container *output);
void workspace_output_add_priority(struct sway_workspace *workspace,
struct sway_output *output);
struct sway_container *workspace_output_get_highest_available(
struct sway_container *ws, struct sway_container *exclude);
struct sway_output *workspace_output_get_highest_available(
struct sway_workspace *ws, struct sway_output *exclude);
void workspace_detect_urgent(struct sway_container *workspace);
void workspace_detect_urgent(struct sway_workspace *workspace);
void workspace_for_each_container(struct sway_container *ws,
void workspace_for_each_container(struct sway_workspace *ws,
void (*f)(struct sway_container *con, void *data), void *data);
struct sway_container *workspace_find_container(struct sway_container *ws,
struct sway_container *workspace_find_container(struct sway_workspace *ws,
bool (*test)(struct sway_container *con, void *data), void *data);
/**
@ -70,13 +102,28 @@ struct sway_container *workspace_find_container(struct sway_container *ws,
* The new container will be the only direct tiling child of the workspace.
* The new container is returned.
*/
struct sway_container *workspace_wrap_children(struct sway_container *ws);
struct sway_container *workspace_wrap_children(struct sway_workspace *ws);
void workspace_add_floating(struct sway_container *workspace,
void workspace_detach(struct sway_workspace *workspace);
void workspace_add_tiling(struct sway_workspace *workspace,
struct sway_container *con);
void workspace_add_floating(struct sway_workspace *workspace,
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

@ -212,6 +212,24 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
return res;
}
static void set_config_node(struct sway_node *node) {
config->handler_context.node = node;
switch (node->type) {
case N_CONTAINER:
config->handler_context.container = node->sway_container;
config->handler_context.workspace = node->sway_container->workspace;
break;
case N_WORKSPACE:
config->handler_context.container = NULL;
config->handler_context.workspace = node->sway_workspace;
break;
default:
config->handler_context.container = NULL;
config->handler_context.workspace = NULL;
break;
}
}
struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
// Even though this function will process multiple commands we will only
// return the last error, if any (for now). (Since we have access to an
@ -295,12 +313,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
if (!config->handler_context.using_criteria) {
// without criteria, the command acts upon the focused
// container
config->handler_context.current_container =
seat_get_focus_inactive(seat, &root_container);
if (!sway_assert(config->handler_context.current_container,
"could not get focus-inactive for root container")) {
return NULL;
}
set_config_node(seat_get_focus_inactive(seat, &root->node));
struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);
@ -314,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
} else {
for (int i = 0; i < views->length; ++i) {
struct sway_view *view = views->items[i];
config->handler_context.current_container = view->swayc;
set_config_node(&view->container->node);
struct cmd_results *res = handler->handle(argc-1, argv+1);
if (res->status != CMD_SUCCESS) {
free_argv(argc, argv);

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

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

@ -34,211 +34,139 @@ static bool parse_movement_direction(const char *name,
}
/**
* Get swayc in the direction of newly entered output.
* Get node in the direction of newly entered output.
*/
static struct sway_container *get_swayc_in_output_direction(
struct sway_container *output, enum movement_direction dir,
struct sway_seat *seat) {
if (!output) {
return NULL;
}
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;
static struct sway_node *get_node_in_output_direction(
struct sway_output *output, enum movement_direction dir) {
struct sway_seat *seat = config->handler_context.seat;
struct sway_workspace *ws = output_get_active_workspace(output);
if (ws->fullscreen) {
return seat_get_focus_inactive(seat, &ws->fullscreen->node);
}
struct sway_container *container = NULL;
if (ws->children->length > 0) {
if (ws->tiling->length > 0) {
switch (dir) {
case MOVE_LEFT:
if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
// get most right child of new output
return ws->children->items[ws->children->length-1];
container = ws->tiling->items[ws->tiling->length-1];
} else {
return seat_get_focus_inactive(seat, ws);
container = seat_get_focus_inactive_tiling(seat, ws);
}
return &container->node;
case MOVE_RIGHT:
if (ws->layout == L_HORIZ || ws->layout == L_TABBED) {
// get most left child of new output
return ws->children->items[0];
container = ws->tiling->items[0];
} else {
return seat_get_focus_inactive(seat, ws);
container = seat_get_focus_inactive_tiling(seat, ws);
}
return &container->node;
case MOVE_UP:
if (ws->layout == L_VERT || ws->layout == L_STACKED) {
// get most bottom child of new output
container = ws->tiling->items[ws->tiling->length-1];
} else {
container = seat_get_focus_inactive_tiling(seat, ws);
}
return &container->node;
case MOVE_DOWN: {
struct sway_container *focused =
seat_get_focus_inactive(seat, ws);
if (focused && focused->parent) {
struct sway_container *parent = focused->parent;
if (parent->layout == L_VERT) {
if (dir == MOVE_UP) {
// get child furthest down on new output
int idx = parent->children->length - 1;
return parent->children->items[idx];
} else if (dir == MOVE_DOWN) {
// get child furthest up on new output
return parent->children->items[0];
}
}
return focused;
if (ws->layout == L_VERT || ws->layout == L_STACKED) {
// get most top child of new output
container = ws->tiling->items[0];
} else {
container = seat_get_focus_inactive_tiling(seat, ws);
}
break;
return &container->node;
}
default:
break;
}
}
return ws;
return &ws->node;
}
static struct sway_container *container_get_in_direction(
struct sway_container *container, struct sway_seat *seat,
enum movement_direction dir) {
struct sway_container *parent = container->parent;
static struct sway_node *node_get_in_direction(struct sway_container *container,
struct sway_seat *seat, enum movement_direction dir) {
if (dir == MOVE_CHILD) {
return seat_get_focus_inactive(seat, container);
return seat_get_active_child(seat, &container->node);
}
if (container->is_fullscreen) {
if (dir == MOVE_PARENT) {
return NULL;
}
container = container_parent(container, C_OUTPUT);
parent = container->parent;
} else {
if (dir == MOVE_PARENT) {
if (parent->type == C_OUTPUT || container_is_floating(container)) {
return NULL;
} else {
return parent;
}
// Fullscreen container with a direction - go straight to outputs
struct sway_output *output = container->workspace->output;
struct sway_output *new_output = output_get_in_direction(output, dir);
return get_node_in_output_direction(new_output, dir);
}
if (dir == MOVE_PARENT) {
return node_get_parent(&container->node);
}
struct sway_container *wrap_candidate = NULL;
while (true) {
struct sway_container *current = container;
while (current) {
bool can_move = false;
int desired;
int idx = list_find(container->parent->children, container);
if (idx == -1) {
return NULL;
}
if (parent->type == C_ROOT) {
enum wlr_direction wlr_dir = 0;
if (!sway_assert(sway_dir_to_wlr(dir, &wlr_dir),
"got invalid direction: %d", dir)) {
return NULL;
}
int lx = container->x + container->width / 2;
int ly = container->y + container->height / 2;
struct wlr_output_layout *layout =
root_container.sway_root->output_layout;
struct wlr_output *wlr_adjacent =
wlr_output_layout_adjacent_output(layout, wlr_dir,
container->sway_output->wlr_output, lx, ly);
struct sway_container *adjacent =
output_from_wlr_output(wlr_adjacent);
int idx = container_sibling_index(current);
enum sway_container_layout parent_layout =
container_parent_layout(current);
list_t *siblings = container_get_siblings(current);
if (!adjacent || adjacent == container) {
if (!wrap_candidate) {
return NULL;
}
return seat_get_focus_inactive_view(seat, wrap_candidate);
}
struct sway_container *next =
get_swayc_in_output_direction(adjacent, dir, seat);
if (next == NULL) {
return NULL;
}
struct sway_container *next_workspace = next;
if (next_workspace->type != C_WORKSPACE) {
next_workspace = container_parent(next_workspace, C_WORKSPACE);
}
sway_assert(next_workspace, "Next container has no workspace");
if (next_workspace->sway_workspace->fullscreen) {
return seat_get_focus_inactive(seat,
next_workspace->sway_workspace->fullscreen);
}
if (next->children && next->children->length) {
// TODO consider floating children as well
return seat_get_focus_inactive_view(seat, next);
} else {
return next;
}
} else {
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;
desired = idx + (dir == MOVE_LEFT ? -1 : 1);
}
} else {
if (parent->layout == L_VERT || parent->layout == L_STACKED) {
if (parent_layout == L_VERT || parent_layout == L_STACKED) {
can_move = true;
desired = idx + (dir == MOVE_UP ? -1 : 1);
}
}
}
if (can_move) {
// TODO handle floating
if (desired < 0 || desired >= parent->children->length) {
if (desired < 0 || desired >= siblings->length) {
can_move = false;
int len = parent->children->length;
int len = siblings->length;
if (config->focus_wrapping != WRAP_NO && !wrap_candidate
&& len > 1) {
if (desired < 0) {
wrap_candidate = parent->children->items[len-1];
wrap_candidate = siblings->items[len-1];
} else {
wrap_candidate = parent->children->items[0];
wrap_candidate = siblings->items[0];
}
if (config->focus_wrapping == WRAP_FORCE) {
return seat_get_focus_inactive_view(seat,
wrap_candidate);
struct sway_container *c = seat_get_focus_inactive_view(
seat, &wrap_candidate->node);
return &c->node;
}
}
} else {
struct sway_container *desired_con =
parent->children->items[desired];
wlr_log(WLR_DEBUG,
"cont %d-%p dir %i sibling %d: %p", idx,
container, dir, desired, desired_con);
return seat_get_focus_inactive_view(seat, desired_con);
struct sway_container *desired_con = siblings->items[desired];
struct sway_container *c = seat_get_focus_inactive_view(
seat, &desired_con->node);
return &c->node;
}
}
if (!can_move) {
container = parent;
parent = parent->parent;
if (!parent) {
// wrapping is the last chance
if (!wrap_candidate) {
return NULL;
}
return seat_get_focus_inactive_view(seat, wrap_candidate);
}
current = current->parent;
}
// Check a different output
struct sway_output *output = container->workspace->output;
struct sway_output *new_output = output_get_in_direction(output, dir);
if (new_output) {
return get_node_in_output_direction(new_output, dir);
}
return NULL;
}
static struct cmd_results *focus_mode(struct sway_container *con,
static struct cmd_results *focus_mode(struct sway_workspace *ws,
struct sway_seat *seat, bool floating) {
struct sway_container *ws = con->type == C_WORKSPACE ?
con : container_parent(con, C_WORKSPACE);
// If the container is in a floating split container,
// operate on the split container instead of the child.
if (container_is_floating_or_child(con)) {
while (con->parent->type != C_WORKSPACE) {
con = con->parent;
}
}
struct sway_container *new_focus = NULL;
if (floating) {
new_focus = seat_get_focus_inactive_floating(seat, ws);
@ -246,7 +174,7 @@ static struct cmd_results *focus_mode(struct sway_container *con,
new_focus = seat_get_focus_inactive_tiling(seat, ws);
}
if (new_focus) {
seat_set_focus(seat, new_focus);
seat_set_focus(seat, &new_focus->node);
} else {
return cmd_results_new(CMD_FAILURE, "focus",
"Failed to find a %s container in workspace",
@ -255,14 +183,14 @@ static struct cmd_results *focus_mode(struct sway_container *con,
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
static struct cmd_results *focus_output(struct sway_container *con,
struct sway_seat *seat, int argc, char **argv) {
static struct cmd_results *focus_output(struct sway_seat *seat,
int argc, char **argv) {
if (!argc) {
return cmd_results_new(CMD_INVALID, "focus",
"Expected 'focus output <direction|name>'");
}
char *identifier = join_args(argv, argc);
struct sway_container *output = output_by_name(identifier);
struct sway_output *output = output_by_name(identifier);
if (!output) {
enum movement_direction direction;
@ -272,14 +200,13 @@ static struct cmd_results *focus_output(struct sway_container *con,
return cmd_results_new(CMD_INVALID, "focus",
"There is no output with that name");
}
struct sway_container *focus = seat_get_focus(seat);
focus = container_parent(focus, C_OUTPUT);
output = container_get_in_direction(focus, seat, direction);
struct sway_workspace *ws = seat_get_focused_workspace(seat);
output = output_get_in_direction(ws->output, direction);
}
free(identifier);
if (output) {
seat_set_focus(seat, seat_get_focus_inactive(seat, output));
seat_set_focus(seat, seat_get_focus_inactive(seat, &output->node));
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@ -289,29 +216,32 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
if (config->reading || !config->active) {
return cmd_results_new(CMD_DEFER, NULL, NULL);
}
struct sway_container *con = config->handler_context.current_container;
struct sway_node *node = config->handler_context.node;
struct sway_container *container = config->handler_context.container;
struct sway_workspace *workspace = config->handler_context.workspace;
struct sway_seat *seat = config->handler_context.seat;
if (con->type < C_WORKSPACE) {
if (node->type < N_WORKSPACE) {
return cmd_results_new(CMD_FAILURE, "focus",
"Command 'focus' cannot be used above the workspace level");
}
if (argc == 0) {
seat_set_focus(seat, con);
seat_set_focus(seat, node);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
if (strcmp(argv[0], "floating") == 0) {
return focus_mode(con, seat, true);
return focus_mode(workspace, seat, true);
} else if (strcmp(argv[0], "tiling") == 0) {
return focus_mode(con, seat, false);
return focus_mode(workspace, seat, false);
} else if (strcmp(argv[0], "mode_toggle") == 0) {
return focus_mode(con, seat, !container_is_floating_or_child(con));
bool floating = container && container_is_floating_or_child(container);
return focus_mode(workspace, seat, !floating);
}
if (strcmp(argv[0], "output") == 0) {
argc--; argv++;
return focus_output(con, seat, argc, argv);
return focus_output(seat, argc, argv);
}
enum movement_direction direction = 0;
@ -321,8 +251,18 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
"or 'focus output <direction|name>'");
}
struct sway_container *next_focus = container_get_in_direction(
con, seat, direction);
if (node->type == N_WORKSPACE) {
// A workspace is focused, so just jump to the next output
struct sway_output *new_output =
output_get_in_direction(workspace->output, direction);
struct sway_node *node =
get_node_in_output_direction(new_output, direction);
seat_set_focus(seat, node);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
struct sway_node *next_focus =
node_get_in_direction(container, seat, direction);
if (next_focus) {
seat_set_focus(seat, next_focus);
}

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

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

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

@ -2,15 +2,27 @@
#include "log.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/tree/view.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "sway/commands.h"
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 sway_container *con =
config->handler_context.current_container;
struct sway_container *con = config->handler_context.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);
}

@ -4,21 +4,20 @@
#include "sway/commands.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/workspace.h"
#include "log.h"
static bool parse_layout_string(char *s, enum sway_container_layout *ptr) {
static enum sway_container_layout parse_layout_string(char *s) {
if (strcasecmp(s, "splith") == 0) {
*ptr = L_HORIZ;
return L_HORIZ;
} else if (strcasecmp(s, "splitv") == 0) {
*ptr = L_VERT;
return L_VERT;
} else if (strcasecmp(s, "tabbed") == 0) {
*ptr = L_TABBED;
return L_TABBED;
} else if (strcasecmp(s, "stacking") == 0) {
*ptr = L_STACKED;
} else {
return false;
return L_STACKED;
}
return true;
return L_NONE;
}
static const char* expected_syntax =
@ -26,53 +25,34 @@ static const char* expected_syntax =
"'layout toggle [split|all]' or "
"'layout toggle [split|tabbed|stacking|splitv|splith] [split|tabbed|stacking|splitv|splith]...'";
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;
static enum sway_container_layout get_layout_toggle(int argc, char **argv,
enum sway_container_layout layout,
enum sway_container_layout prev_split_layout) {
// "layout toggle"
if (argc == 0) {
return layout == L_HORIZ ? L_VERT : L_HORIZ;
}
struct sway_container *parent = config->handler_context.current_container;
if (container_is_floating(parent)) {
return cmd_results_new(CMD_FAILURE, "layout",
"Unable to change layout of floating windows");
}
while (parent->type == C_VIEW) {
parent = parent->parent;
if (argc == 2) {
// "layout toggle split" (same as "layout toggle")
if (strcasecmp(argv[1], "split") == 0) {
return layout == L_HORIZ ? L_VERT : L_HORIZ;
}
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) {
// "layout toggle all"
if (strcasecmp(argv[1], "all") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_STACKED :
parent->layout == L_STACKED ? L_TABBED : L_HORIZ;
} else if (strcasecmp(argv[1], "split") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
} else {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
return layout == L_HORIZ ? L_VERT :
layout == L_VERT ? L_STACKED :
layout == L_STACKED ? L_TABBED : L_HORIZ;
}
} else {
enum sway_container_layout parsed_layout;
return L_NONE;
}
enum sway_container_layout parsed;
int curr = 1;
for (; curr < argc; curr++) {
bool valid = parse_layout_string(argv[curr], &parsed_layout);
if ((valid && parsed_layout == parent->layout) ||
(strcmp(argv[curr], "split") == 0 &&
(parent->layout == L_VERT || parent->layout == L_HORIZ))) {
parsed = parse_layout_string(argv[curr]);
if (parsed == layout || (strcmp(argv[curr], "split") == 0 &&
(layout == L_VERT || layout == L_HORIZ))) {
break;
}
}
@ -81,29 +61,93 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
if (i >= argc) {
i = 1;
}
if (parse_layout_string(argv[i], &parent->layout)) {
break;
} else if (strcmp(argv[i], "split") == 0) {
parent->layout =
parent->layout == L_HORIZ ? L_VERT :
parent->layout == L_VERT ? L_HORIZ : parent->prev_split_layout;
break;
} // invalid layout strings are silently ignored
parsed = parse_layout_string(argv[i]);
if (parsed != L_NONE) {
return parsed;
}
if (strcmp(argv[i], "split") == 0) {
return layout == L_HORIZ ? L_VERT :
layout == L_VERT ? L_HORIZ : prev_split_layout;
}
// invalid layout strings are silently ignored
}
return L_NONE;
}
static enum sway_container_layout get_layout(int argc, char **argv,
enum sway_container_layout layout,
enum sway_container_layout prev_split_layout) {
// Check if assigned directly
enum sway_container_layout parsed = parse_layout_string(argv[0]);
if (parsed != L_NONE) {
return parsed;
}
if (strcasecmp(argv[0], "default") == 0) {
return prev_split_layout;
}
if (strcasecmp(argv[0], "toggle") == 0) {
argc--; argv++;
return get_layout_toggle(argc, argv, layout, prev_split_layout);
}
return L_NONE;
}
struct cmd_results *cmd_layout(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
return error;
}
struct sway_container *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 {
old_layout = workspace->layout;
new_layout = get_layout(argc, argv,
workspace->layout, workspace->prev_split_layout);
}
if (new_layout == L_NONE) {
return cmd_results_new(CMD_INVALID, "layout", expected_syntax);
}
if (new_layout != old_layout) {
if (container) {
if (old_layout != L_TABBED && old_layout != L_STACKED) {
container->prev_split_layout = old_layout;
}
if (parent->layout == L_NONE) {
parent->layout = container_get_default_layout(parent);
container->layout = new_layout;
container_update_representation(container);
arrange_container(container);
} else {
if (old_layout != L_TABBED && old_layout != L_STACKED) {
workspace->prev_split_layout = old_layout;
}
if (prev != parent->layout) {
if (prev != L_TABBED && prev != L_STACKED) {
parent->prev_split_layout = prev;
workspace->layout = new_layout;
workspace_update_representation(workspace);
arrange_workspace(workspace);
}
container_notify_subtree_changed(parent);
arrange_windows(parent->parent);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);

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

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;
}
struct sway_container *con =
config->handler_context.current_container;
struct sway_container *con = config->handler_context.container;
float opacity = 0.0f;

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

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

@ -10,6 +10,7 @@
#include "sway/commands.h"
#include "sway/tree/arrange.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
#include "log.h"
static const int MIN_SANE_W = 100, MIN_SANE_H = 60;
@ -75,7 +76,7 @@ static int parse_resize_amount(int argc, char **argv,
static void calculate_constraints(int *min_width, int *max_width,
int *min_height, int *max_height) {
struct sway_container *con = config->handler_context.current_container;
struct sway_container *con = config->handler_context.container;
if (config->floating_minimum_width == -1) { // no minimum
*min_width = 0;
@ -96,8 +97,7 @@ static void calculate_constraints(int *min_width, int *max_width,
if (config->floating_maximum_width == -1) { // no maximum
*max_width = INT_MAX;
} else if (config->floating_maximum_width == 0) { // automatic
struct sway_container *ws = container_parent(con, C_WORKSPACE);
*max_width = ws->width;
*max_width = con->workspace->width;
} else {
*max_width = config->floating_maximum_width;
}
@ -105,8 +105,7 @@ static void calculate_constraints(int *min_width, int *max_width,
if (config->floating_maximum_height == -1) { // no maximum
*max_height = INT_MAX;
} else if (config->floating_maximum_height == 0) { // automatic
struct sway_container *ws = container_parent(con, C_WORKSPACE);
*max_height = ws->height;
*max_height = con->workspace->height;
} else {
*max_height = config->floating_maximum_height;
}
@ -191,11 +190,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
normalize_axis(axis) == RESIZE_AXIS_HORIZONTAL ? L_HORIZ : L_VERT;
int minor_weight = 0;
int major_weight = 0;
while (parent->parent) {
struct sway_container *next = parent->parent;
if (next->layout == parallel_layout) {
for (int i = 0; i < next->children->length; i++) {
struct sway_container *sibling = next->children->items[i];
while (parent) {
list_t *siblings = container_get_siblings(parent);
if (container_parent_layout(parent) == parallel_layout) {
for (int i = 0; i < siblings->length; i++) {
struct sway_container *sibling = siblings->items[i];
int sibling_pos = parallel_coord(sibling, axis);
int focused_pos = parallel_coord(focused, axis);
@ -213,17 +212,13 @@ static void resize_tiled(struct sway_container *parent, int amount,
break;
}
}
parent = next;
parent = parent->parent;
}
if (parent->type == C_ROOT) {
if (!parent) {
// Can't resize in this direction
return;
}
wlr_log(WLR_DEBUG,
"Found the proper parent: %p. It has %d l conts, and %d r conts",
parent->parent, minor_weight, major_weight);
// Implement up/down/left/right direction by zeroing one of the weights,
// then setting the axis to be horizontal or vertical
if (axis == RESIZE_AXIS_UP || axis == RESIZE_AXIS_LEFT) {
@ -237,9 +232,10 @@ static void resize_tiled(struct sway_container *parent, int amount,
//TODO: Ensure rounding is done in such a way that there are NO pixel leaks
// ^ ?????
list_t *siblings = container_get_siblings(parent);
for (int i = 0; i < parent->parent->children->length; i++) {
struct sway_container *sibling = parent->parent->children->items[i];
for (int i = 0; i < siblings->length; i++) {
struct sway_container *sibling = siblings->items[i];
int sibling_pos = parallel_coord(sibling, axis);
int focused_pos = parallel_coord(focused, axis);
@ -277,8 +273,8 @@ static void resize_tiled(struct sway_container *parent, int amount,
enum wlr_edges major_edge = axis == RESIZE_AXIS_HORIZONTAL ?
WLR_EDGE_RIGHT : WLR_EDGE_BOTTOM;
for (int i = 0; i < parent->parent->children->length; i++) {
struct sway_container *sibling = parent->parent->children->items[i];
for (int i = 0; i < siblings->length; i++) {
struct sway_container *sibling = siblings->items[i];
int sibling_pos = parallel_coord(sibling, axis);
int focused_pos = parallel_coord(focused, axis);
@ -316,7 +312,11 @@ static void resize_tiled(struct sway_container *parent, int amount,
}
}
arrange_windows(parent->parent);
if (parent->parent) {
arrange_container(parent->parent);
} else {
arrange_workspace(parent->workspace);
}
}
void container_resize_tiled(struct sway_container *parent,
@ -346,7 +346,7 @@ void container_resize_tiled(struct sway_container *parent,
*/
static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
struct resize_amount *amount) {
struct sway_container *con = config->handler_context.current_container;
struct sway_container *con = config->handler_context.container;
int grow_width = 0, grow_height = 0;
switch (axis) {
case RESIZE_AXIS_HORIZONTAL:
@ -400,15 +400,15 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
con->width += grow_width;
con->height += grow_height;
if (con->type == C_VIEW) {
struct sway_view *view = con->sway_view;
if (con->view) {
struct sway_view *view = con->view;
view->x += grow_x;
view->y += grow_y;
view->width += grow_width;
view->height += grow_height;
}
arrange_windows(con);
arrange_container(con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@ -418,7 +418,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
*/
static struct cmd_results *resize_adjust_tiled(enum resize_axis axis,
struct resize_amount *amount) {
struct sway_container *current = config->handler_context.current_container;
struct sway_container *current = config->handler_context.container;
if (amount->unit == RESIZE_UNIT_DEFAULT) {
amount->unit = RESIZE_UNIT_PPT;
@ -456,13 +456,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
width->unit == RESIZE_UNIT_DEFAULT) {
// Convert to px
struct sway_container *parent = con->parent;
while (parent->type >= C_WORKSPACE && parent->layout != L_HORIZ) {
while (parent && parent->layout != L_HORIZ) {
parent = parent->parent;
}
if (parent->type >= C_WORKSPACE) {
if (parent) {
width->amount = parent->width * width->amount / 100;
width->unit = RESIZE_UNIT_PX;
} else {
width->amount = con->workspace->width * width->amount / 100;
}
width->unit = RESIZE_UNIT_PX;
}
if (width->unit == RESIZE_UNIT_PX) {
resize_tiled(con, width->amount - con->width,
@ -475,13 +477,15 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
height->unit == RESIZE_UNIT_DEFAULT) {
// Convert to px
struct sway_container *parent = con->parent;
while (parent->type >= C_WORKSPACE && parent->layout != L_VERT) {
while (parent && parent->layout != L_VERT) {
parent = parent->parent;
}
if (parent->type >= C_WORKSPACE) {
if (parent) {
height->amount = parent->height * height->amount / 100;
height->unit = RESIZE_UNIT_PX;
} else {
height->amount = con->workspace->height * height->amount / 100;
}
height->unit = RESIZE_UNIT_PX;
}
if (height->unit == RESIZE_UNIT_PX) {
resize_tiled(con, height->amount - con->height,
@ -508,15 +512,15 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
con->width = width->amount;
con->height = height->amount;
if (con->type == C_VIEW) {
struct sway_view *view = con->sway_view;
if (con->view) {
struct sway_view *view = con->view;
view->x -= grow_width / 2;
view->y -= grow_height / 2;
view->width += grow_width;
view->height += grow_height;
}
arrange_windows(con);
arrange_container(con);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
@ -555,7 +559,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) {
}
// If 0, don't resize that dimension
struct sway_container *con = config->handler_context.current_container;
struct sway_container *con = config->handler_context.container;
if (width.amount <= 0) {
width.amount = con->width;
}
@ -624,7 +628,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
first_amount.amount *= multiplier;
second_amount.amount *= multiplier;
struct sway_container *con = config->handler_context.current_container;
struct sway_container *con = config->handler_context.container;
if (container_is_floating(con)) {
// Floating containers can only resize in px. Choose an amount which
// uses px, with fallback to an amount that specified no unit.
@ -657,14 +661,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv,
}
struct cmd_results *cmd_resize(int argc, char **argv) {
struct sway_container *current = config->handler_context.current_container;
struct sway_container *current = config->handler_context.container;
if (!current) {
return cmd_results_new(CMD_INVALID, "resize", "Cannot resize nothing");
}
if (current->type != C_VIEW && current->type != C_CONTAINER) {
return cmd_results_new(CMD_INVALID, "resize",
"Can only resize views/containers");
}
struct cmd_results *error;
if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) {

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

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

@ -11,8 +11,8 @@
#include "util.h"
static void rebuild_marks_iterator(struct sway_container *con, void *data) {
if (con->type == C_VIEW) {
view_update_marks_textures(con->sway_view);
if (con->view) {
view_update_marks_textures(con->view);
}
}
@ -28,9 +28,9 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) {
root_for_each_container(rebuild_marks_iterator, NULL);
}
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *con = root_container.children->items[i];
output_damage_whole(con->sway_output);
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);

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

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

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

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

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

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

@ -58,7 +58,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
}
struct sway_container *ws = NULL;
struct sway_workspace *ws = NULL;
if (strcasecmp(argv[0], "number") == 0) {
if (argc < 2) {
return cmd_results_new(CMD_INVALID, "workspace",

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

@ -12,6 +12,7 @@
#include <strings.h>
#include <signal.h>
#include "sway/config.h"
#include "sway/output.h"
#include "stringop.h"
#include "list.h"
#include "log.h"
@ -218,17 +219,6 @@ void invoke_swaybar(struct bar_config *bar) {
close(filedes[1]);
}
static bool active_output(const char *name) {
struct sway_container *cont = NULL;
for (int i = 0; i < root_container.children->length; ++i) {
cont = root_container.children->items[i];
if (cont->type == C_OUTPUT && strcasecmp(name, cont->name) == 0) {
return true;
}
}
return false;
}
void load_swaybars() {
for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *bar = config->bars->items[i];
@ -236,7 +226,7 @@ void load_swaybars() {
if (bar->outputs) {
for (int j = 0; j < bar->outputs->length; ++j) {
char *o = bar->outputs->items[j];
if (!strcmp(o, "*") || active_output(o)) {
if (!strcmp(o, "*") || output_by_name(o)) {
apply = true;
break;
}

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

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

@ -10,6 +10,7 @@
#include "sway/server.h"
#include "sway/tree/container.h"
#include "sway/tree/root.h"
#include "sway/tree/workspace.h"
#include "cairo.h"
#include "config.h"
#include "pango.h"
@ -32,28 +33,78 @@ static const char *layout_to_str(enum sway_container_layout layout) {
return "L_NONE";
}
static int draw_container(cairo_t *cairo, struct sway_container *container,
struct sway_container *focus, int x, int y) {
static char *get_string(struct sway_node *node) {
char *buffer = malloc(512);
switch (node->type) {
case N_ROOT:
snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id,
root->width, root->height, root->x, root->y);
break;
case N_OUTPUT:
snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id,
node->sway_output->wlr_output->name,
node->sway_output->wlr_output->width,
node->sway_output->wlr_output->height,
node->sway_output->wlr_output->lx,
node->sway_output->wlr_output->ly);
break;
case N_WORKSPACE:
snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f",
node->id, node->sway_workspace->name,
layout_to_str(node->sway_workspace->layout),
node->sway_workspace->width, node->sway_workspace->height,
node->sway_workspace->x, node->sway_workspace->y);
break;
case N_CONTAINER:
snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f",
node->id, node->sway_container->title,
layout_to_str(node->sway_container->layout),
node->sway_container->width, node->sway_container->height,
node->sway_container->x, node->sway_container->y);
break;
}
return buffer;
}
static list_t *get_children(struct sway_node *node) {
switch (node->type) {
case N_ROOT:
return root->outputs;
case N_OUTPUT:
return node->sway_output->workspaces;
case N_WORKSPACE:
return node->sway_workspace->tiling;
case N_CONTAINER:
return node->sway_container->children;
}
return NULL;
}
static int draw_node(cairo_t *cairo, struct sway_node *node,
struct sway_node *focus, int x, int y) {
int text_width, text_height;
char *buffer = get_string(node);
get_text_size(cairo, "monospace", &text_width, &text_height,
1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
container_type_to_str(container->type), container->id, container->name,
layout_to_str(container->layout),
container->width, container->height, container->x, container->y);
1, false, buffer);
cairo_save(cairo);
cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
cairo_set_source_u32(cairo, 0xFFFFFFE0);
cairo_fill(cairo);
int height = text_height;
if (container->children) {
for (int i = 0; i < container->children->length; ++i) {
struct sway_container *child = container->children->items[i];
if (child->parent == container) {
list_t *children = get_children(node);
if (children) {
for (int i = 0; i < children->length; ++i) {
// This is really dirty - the list contains specific structs but
// we're casting them as nodes. This works because node is the first
// item in each specific struct. This is acceptable because this is
// debug code.
struct sway_node *child = children->items[i];
if (node_get_parent(child) == node) {
cairo_set_source_u32(cairo, 0x000000FF);
} else {
cairo_set_source_u32(cairo, 0xFF0000FF);
}
height += draw_container(cairo, child, focus, x + 10, y + height);
height += draw_node(cairo, child, focus, x + 10, y + height);
}
}
cairo_set_source_u32(cairo, 0xFFFFFFE0);
@ -61,13 +112,11 @@ static int draw_container(cairo_t *cairo, struct sway_container *container,
cairo_fill(cairo);
cairo_restore(cairo);
cairo_move_to(cairo, x, y);
if (focus == container) {
if (focus == node) {
cairo_set_source_u32(cairo, 0x0000FFFF);
}
pango_printf(cairo, "monospace", 1, false, "%s id:%zd '%s' %s %.fx%.f@%.f,%.f",
container_type_to_str(container->type), container->id, container->name,
layout_to_str(container->layout),
container->width, container->height, container->x, container->y);
pango_printf(cairo, "monospace", 1, false, buffer);
free(buffer);
return height;
}
@ -77,13 +126,13 @@ void update_debug_tree() {
}
int width = 640, height = 480;
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *container = root_container.children->items[i];
if (container->width > width) {
width = container->width;
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
if (output->wlr_output->width > width) {
width = output->wlr_output->width;
}
if (container->height > height) {
height = container->height;
if (output->wlr_output->height > height) {
height = output->wlr_output->height;
}
}
cairo_surface_t *surface =
@ -91,28 +140,22 @@ void update_debug_tree() {
cairo_t *cairo = cairo_create(surface);
PangoContext *pango = pango_cairo_create_context(cairo);
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input_manager->seats, link) {
break;
}
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_node *focus = seat_get_focus(seat);
struct sway_container *focus = NULL;
if (seat != NULL) {
focus = seat_get_focus(seat);
}
cairo_set_source_u32(cairo, 0x000000FF);
draw_container(cairo, &root_container, focus, 0, 0);
draw_node(cairo, &root->node, focus, 0, 0);
cairo_surface_flush(surface);
struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
if (root_container.sway_root->debug_tree) {
wlr_texture_destroy(root_container.sway_root->debug_tree);
if (root->debug_tree) {
wlr_texture_destroy(root->debug_tree);
}
unsigned char *data = cairo_image_surface_get_data(surface);
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
root_container.sway_root->debug_tree = texture;
root->debug_tree = texture;
cairo_surface_destroy(surface);
g_object_unref(pango);
cairo_destroy(cairo);

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

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

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

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

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

@ -11,10 +11,12 @@
#include "sway/desktop/transaction.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
static const struct sway_view_child_impl popup_impl;
@ -52,15 +54,15 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
struct sway_view *view = popup->child.view;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup;
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
struct sway_output *output = view->container->workspace->output;
// the output box expressed in the coordinate system of the toplevel parent
// of the popup
struct wlr_box output_toplevel_sx_box = {
.x = output->x - view->x,
.y = output->y - view->y,
.width = output->width,
.height = output->height,
.x = output->wlr_output->lx - view->x,
.y = output->wlr_output->ly - view->y,
.width = output->wlr_output->width,
.height = output->wlr_output->height,
};
wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@ -252,11 +254,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface;
if (!view->swayc) {
return;
}
if (view->swayc->instruction) {
if (view->container->node.instruction) {
wlr_xdg_surface_get_geometry(xdg_surface, &view->geometry);
transaction_notify_view_ready_by_serial(view,
xdg_surface->configure_serial);
@ -265,7 +263,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
if ((new_geo.width != view->width || new_geo.height != view->height) &&
container_is_floating(view->swayc)) {
container_is_floating(view->container)) {
// A floating view has unexpectedly sent a new size
desktop_damage_view(view);
view_update_size(view, new_geo.width, new_geo.height);
@ -319,10 +317,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
return;
}
container_set_fullscreen(view->swayc, e->fullscreen);
container_set_fullscreen(view->container, e->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
arrange_windows(output);
arrange_workspace(view->container->workspace);
transaction_commit_dirty();
}
@ -330,13 +327,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_move);
struct sway_view *view = &xdg_shell_view->view;
if (!container_is_floating(view->swayc)) {
if (!container_is_floating(view->container)) {
return;
}
struct wlr_xdg_toplevel_move_event *e = data;
struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) {
seat_begin_move(seat, view->swayc, seat->last_button);
seat_begin_move(seat, view->container, seat->last_button);
}
}
@ -344,13 +341,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_resize);
struct sway_view *view = &xdg_shell_view->view;
if (!container_is_floating(view->swayc)) {
if (!container_is_floating(view->container)) {
return;
}
struct wlr_xdg_toplevel_resize_event *e = data;
struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) {
seat_begin_resize_floating(seat, view->swayc,
seat_begin_resize_floating(seat, view->container,
seat->last_button, e->edges);
}
}
@ -399,11 +396,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
view_map(view, view->wlr_xdg_surface->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) {
container_set_fullscreen(view->swayc, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
arrange_windows(ws);
container_set_fullscreen(view->container, true);
arrange_workspace(view->container->workspace);
} else {
arrange_windows(view->swayc->parent);
if (view->container->parent) {
arrange_container(view->container->parent);
} else {
arrange_workspace(view->container->workspace);
}
}
transaction_commit_dirty();
@ -440,8 +440,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, destroy);
struct sway_view *view = &xdg_shell_view->view;
if (!sway_assert(view->swayc == NULL || view->swayc->destroying,
"Tried to destroy a mapped view")) {
if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
return;
}
wl_list_remove(&xdg_shell_view->destroy.link);

@ -10,10 +10,12 @@
#include "sway/desktop/transaction.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
#include "sway/tree/workspace.h"
static const struct sway_view_child_impl popup_impl;
@ -51,15 +53,15 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
struct sway_view *view = popup->child.view;
struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_xdg_surface_v6->popup;
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
struct sway_output *output = view->container->workspace->output;
// the output box expressed in the coordinate system of the toplevel parent
// of the popup
struct wlr_box output_toplevel_sx_box = {
.x = output->x - view->x,
.y = output->y - view->y,
.width = output->width,
.height = output->height,
.x = output->wlr_output->lx - view->x,
.y = output->wlr_output->ly - view->y,
.width = output->wlr_output->width,
.height = output->wlr_output->height,
};
wlr_xdg_popup_v6_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box);
@ -249,11 +251,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_view *view = &xdg_shell_v6_view->view;
struct wlr_xdg_surface_v6 *xdg_surface_v6 = view->wlr_xdg_surface_v6;
if (!view->swayc) {
return;
}
if (view->swayc->instruction) {
if (view->container->node.instruction) {
wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &view->geometry);
transaction_notify_view_ready_by_serial(view,
xdg_surface_v6->configure_serial);
@ -262,7 +260,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo);
if ((new_geo.width != view->width || new_geo.height != view->height) &&
container_is_floating(view->swayc)) {
container_is_floating(view->container)) {
// A floating view has unexpectedly sent a new size
desktop_damage_view(view);
view_update_size(view, new_geo.width, new_geo.height);
@ -316,10 +314,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
return;
}
container_set_fullscreen(view->swayc, e->fullscreen);
container_set_fullscreen(view->container, e->fullscreen);
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
arrange_windows(output);
arrange_workspace(view->container->workspace);
transaction_commit_dirty();
}
@ -327,13 +324,13 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, request_move);
struct sway_view *view = &xdg_shell_v6_view->view;
if (!container_is_floating(view->swayc)) {
if (!container_is_floating(view->container)) {
return;
}
struct wlr_xdg_toplevel_v6_move_event *e = data;
struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) {
seat_begin_move(seat, view->swayc, seat->last_button);
seat_begin_move(seat, view->container, seat->last_button);
}
}
@ -341,13 +338,13 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_v6_view *xdg_shell_v6_view =
wl_container_of(listener, xdg_shell_v6_view, request_resize);
struct sway_view *view = &xdg_shell_v6_view->view;
if (!container_is_floating(view->swayc)) {
if (!container_is_floating(view->container)) {
return;
}
struct wlr_xdg_toplevel_v6_resize_event *e = data;
struct sway_seat *seat = e->seat->seat->data;
if (e->serial == seat->last_button_serial) {
seat_begin_resize_floating(seat, view->swayc,
seat_begin_resize_floating(seat, view->container,
seat->last_button, e->edges);
}
}
@ -396,11 +393,14 @@ static void handle_map(struct wl_listener *listener, void *data) {
view_map(view, view->wlr_xdg_surface_v6->surface);
if (xdg_surface->toplevel->client_pending.fullscreen) {
container_set_fullscreen(view->swayc, true);
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
arrange_windows(ws);
container_set_fullscreen(view->container, true);
arrange_workspace(view->container->workspace);
} else {
arrange_windows(view->swayc->parent);
if (view->container->parent) {
arrange_container(view->container->parent);
} else {
arrange_workspace(view->container->workspace);
}
}
transaction_commit_dirty();

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

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

@ -293,12 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
struct sway_seat *seat;
wl_list_for_each(seat, &input_manager->seats, link) {
seat_set_exclusive_client(seat, NULL);
struct sway_container *previous = seat_get_focus(seat);
struct sway_node *previous = seat_get_focus(seat);
if (previous) {
wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous,
container_type_to_str(previous->type), previous->name);
// Hack to get seat to re-focus the return value of get_focus
seat_set_focus(seat, previous->parent);
seat_set_focus(seat, NULL);
seat_set_focus(seat, previous);
}
}
@ -369,10 +367,10 @@ struct sway_input_manager *input_manager_create(
}
bool input_manager_has_focus(struct sway_input_manager *input,
struct sway_container *container) {
struct sway_node *node) {
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input->seats, link) {
if (seat_get_focus(seat) == container) {
if (seat_get_focus(seat) == node) {
return true;
}
}
@ -381,10 +379,10 @@ bool input_manager_has_focus(struct sway_input_manager *input,
}
void input_manager_set_focus(struct sway_input_manager *input,
struct sway_container *container) {
struct sway_node *node) {
struct sway_seat *seat;
wl_list_for_each(seat, &input->seats, link) {
seat_set_focus(seat, container);
seat_set_focus(seat, node);
}
}

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

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

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

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

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

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

@ -166,29 +166,23 @@ void arrange_container(struct sway_container *container) {
if (config->reloading) {
return;
}
if (container->type == C_VIEW) {
view_autoconfigure(container->sway_view);
container_set_dirty(container);
return;
}
if (!sway_assert(container->type == C_CONTAINER, "Expected a container")) {
if (container->view) {
view_autoconfigure(container->view);
node_set_dirty(&container->node);
return;
}
struct wlr_box box;
container_get_box(container, &box);
arrange_children(container->children, container->layout, &box);
container_set_dirty(container);
node_set_dirty(&container->node);
}
void arrange_workspace(struct sway_container *workspace) {
void arrange_workspace(struct sway_workspace *workspace) {
if (config->reloading) {
return;
}
if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) {
return;
}
struct sway_container *output = workspace->parent;
struct wlr_box *area = &output->sway_output->usable_area;
struct sway_output *output = workspace->output;
struct wlr_box *area = &output->usable_area;
wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d",
area->width, area->height, area->x, area->y);
workspace_remove_gaps(workspace);
@ -197,21 +191,20 @@ void arrange_workspace(struct sway_container *workspace) {
double prev_y = workspace->y;
workspace->width = area->width;
workspace->height = area->height;
workspace->x = output->x + area->x;
workspace->y = output->y + area->y;
workspace->x = output->wlr_output->lx + area->x;
workspace->y = output->wlr_output->ly + area->y;
// Adjust any floating containers
double diff_x = workspace->x - prev_x;
double diff_y = workspace->y - prev_y;
if (diff_x != 0 || diff_y != 0) {
for (int i = 0; i < workspace->sway_workspace->floating->length; ++i) {
struct sway_container *floater =
workspace->sway_workspace->floating->items[i];
for (int i = 0; i < workspace->floating->length; ++i) {
struct sway_container *floater = workspace->floating->items[i];
container_floating_translate(floater, diff_x, diff_y);
double center_x = floater->x + floater->width / 2;
double center_y = floater->y + floater->height / 2;
struct wlr_box workspace_box;
container_get_box(workspace, &workspace_box);
workspace_get_box(workspace, &workspace_box);
if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) {
container_floating_move_to_center(floater);
}
@ -219,43 +212,32 @@ void arrange_workspace(struct sway_container *workspace) {
}
workspace_add_gaps(workspace);
container_set_dirty(workspace);
node_set_dirty(&workspace->node);
wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
workspace->x, workspace->y);
if (workspace->sway_workspace->fullscreen) {
struct sway_container *fs = workspace->sway_workspace->fullscreen;
fs->x = workspace->parent->x;
fs->y = workspace->parent->y;
fs->width = workspace->parent->width;
fs->height = workspace->parent->height;
if (workspace->fullscreen) {
struct sway_container *fs = workspace->fullscreen;
fs->x = output->wlr_output->lx;
fs->y = output->wlr_output->ly;
fs->width = output->wlr_output->width;
fs->height = output->wlr_output->height;
arrange_container(fs);
} else {
struct wlr_box box;
container_get_box(workspace, &box);
arrange_children(workspace->children, workspace->layout, &box);
arrange_floating(workspace->sway_workspace->floating);
workspace_get_box(workspace, &box);
arrange_children(workspace->tiling, workspace->layout, &box);
arrange_floating(workspace->floating);
}
}
void arrange_output(struct sway_container *output) {
void arrange_output(struct sway_output *output) {
if (config->reloading) {
return;
}
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return;
}
const struct wlr_box *output_box = wlr_output_layout_get_box(
root_container.sway_root->output_layout,
output->sway_output->wlr_output);
output->x = output_box->x;
output->y = output_box->y;
output->width = output_box->width;
output->height = output_box->height;
container_set_dirty(output);
wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
output->name, output->x, output->y);
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
// Outputs have no pending x/y/width/height,
// so all we do here is arrange the workspaces.
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
arrange_workspace(workspace);
}
}
@ -264,37 +246,31 @@ void arrange_root(void) {
if (config->reloading) {
return;
}
struct wlr_output_layout *output_layout =
root_container.sway_root->output_layout;
const struct wlr_box *layout_box =
wlr_output_layout_get_box(output_layout, NULL);
root_container.x = layout_box->x;
root_container.y = layout_box->y;
root_container.width = layout_box->width;
root_container.height = layout_box->height;
container_set_dirty(&root_container);
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *output = root_container.children->items[i];
wlr_output_layout_get_box(root->output_layout, NULL);
root->x = layout_box->x;
root->y = layout_box->y;
root->width = layout_box->width;
root->height = layout_box->height;
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
arrange_output(output);
}
}
void arrange_windows(struct sway_container *container) {
switch (container->type) {
case C_ROOT:
void arrange_node(struct sway_node *node) {
switch (node->type) {
case N_ROOT:
arrange_root();
break;
case C_OUTPUT:
arrange_output(container);
break;
case C_WORKSPACE:
arrange_workspace(container);
case N_OUTPUT:
arrange_output(node->sway_output);
break;
case C_CONTAINER:
case C_VIEW:
arrange_container(container);
case N_WORKSPACE:
arrange_workspace(node->sway_workspace);
break;
case C_TYPES:
case N_CONTAINER:
arrange_container(node->sway_container);
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 <string.h>
#include <strings.h>
#include <wlr/types/wlr_output_damage.h>
#include "sway/ipc-server.h"
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/tree/arrange.h"
#include "sway/tree/output.h"
#include "sway/tree/workspace.h"
#include "log.h"
#include "util.h"
static void restore_workspaces(struct sway_container *output) {
static void restore_workspaces(struct sway_output *output) {
// Workspace output priority
for (int i = 0; i < root_container.children->length; i++) {
struct sway_container *other = root_container.children->items[i];
for (int i = 0; i < root->outputs->length; i++) {
struct sway_output *other = root->outputs->items[i];
if (other == output) {
continue;
}
for (int j = 0; j < other->children->length; j++) {
struct sway_container *ws = other->children->items[j];
struct sway_container *highest =
for (int j = 0; j < other->workspaces->length; j++) {
struct sway_workspace *ws = other->workspaces->items[j];
struct sway_output *highest =
workspace_output_get_highest_available(ws, NULL);
if (highest == output) {
container_remove_child(ws);
container_add_child(output, ws);
workspace_detach(ws);
output_add_workspace(output, ws);
ipc_event_workspace(NULL, ws, "move");
j--;
}
@ -31,111 +34,111 @@ static void restore_workspaces(struct sway_container *output) {
}
// Saved workspaces
list_t *saved = root_container.sway_root->saved_workspaces;
for (int i = 0; i < saved->length; ++i) {
struct sway_container *ws = saved->items[i];
container_add_child(output, ws);
for (int i = 0; i < root->saved_workspaces->length; ++i) {
struct sway_workspace *ws = root->saved_workspaces->items[i];
output_add_workspace(output, ws);
ipc_event_workspace(NULL, ws, "move");
}
saved->length = 0;
root->saved_workspaces->length = 0;
output_sort_workspaces(output);
}
struct sway_container *output_create(
struct sway_output *sway_output) {
const char *name = sway_output->wlr_output->name;
char identifier[128];
output_get_identifier(identifier, sizeof(identifier), sway_output);
struct sway_output *output_create(struct wlr_output *wlr_output) {
struct sway_output *output = calloc(1, sizeof(struct sway_output));
node_init(&output->node, N_OUTPUT, output);
output->wlr_output = wlr_output;
wlr_output->data = output;
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];
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
if (strcasecmp(name, cur->name) == 0 ||
strcasecmp(identifier, cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched output config for %s", name);
oc = cur;
}
if (strcasecmp("*", cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
all = cur;
}
wl_list_insert(&root->all_outputs, &output->link);
if (oc && all) {
break;
}
}
if (!oc) {
oc = all;
if (!wl_list_empty(&wlr_output->modes)) {
struct wlr_output_mode *mode =
wl_container_of(wlr_output->modes.prev, mode, link);
wlr_output_set_mode(wlr_output, mode);
}
if (oc && !oc->enabled) {
return NULL;
}
output->workspaces = create_list();
output->current.workspaces = create_list();
struct sway_container *output = container_create(C_OUTPUT);
output->sway_output = sway_output;
output->name = strdup(name);
if (output->name == NULL) {
output_begin_destroy(output);
return NULL;
}
return output;
}
void output_enable(struct sway_output *output, struct output_config *oc) {
if (!sway_assert(!output->enabled, "output is already enabled")) {
return;
}
struct wlr_output *wlr_output = output->wlr_output;
output->enabled = true;
apply_output_config(oc, output);
container_add_child(&root_container, output);
load_swaybars();
struct wlr_box size;
wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
&size.height);
output->width = size.width;
output->height = size.height;
list_add(root->outputs, output);
restore_workspaces(output);
if (!output->children->length) {
if (!output->workspaces->length) {
// 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);
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
struct sway_seat *seat = NULL;
wl_list_for_each(seat, &input_manager->seats, link) {
if (!seat->has_focus) {
seat_set_focus(seat, ws);
seat_set_focus(seat, &ws->node);
}
}
free(ws_name);
}
container_create_notify(output);
return output;
size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
for (size_t i = 0; i < len; ++i) {
wl_list_init(&output->layers[i]);
}
wl_signal_init(&output->events.destroy);
input_manager_configure_xcursor(input_manager);
wl_signal_add(&wlr_output->events.mode, &output->mode);
wl_signal_add(&wlr_output->events.transform, &output->transform);
wl_signal_add(&wlr_output->events.scale, &output->scale);
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
output_add_listeners(output);
wl_signal_emit(&root->events.new_node, &output->node);
load_swaybars();
arrange_layers(output);
arrange_root();
}
static void output_evacuate(struct sway_container *output) {
if (!output->children->length) {
static void output_evacuate(struct sway_output *output) {
if (!output->workspaces->length) {
return;
}
struct sway_container *fallback_output = NULL;
if (root_container.children->length > 1) {
fallback_output = root_container.children->items[0];
struct sway_output *fallback_output = NULL;
if (root->outputs->length > 1) {
fallback_output = root->outputs->items[0];
if (fallback_output == output) {
fallback_output = root_container.children->items[1];
fallback_output = root->outputs->items[1];
}
}
while (output->children->length) {
struct sway_container *workspace = output->children->items[0];
while (output->workspaces->length) {
struct sway_workspace *workspace = output->workspaces->items[0];
container_remove_child(workspace);
workspace_detach(workspace);
if (workspace_is_empty(workspace)) {
workspace_begin_destroy(workspace);
continue;
}
struct sway_container *new_output =
struct sway_output *new_output =
workspace_output_get_highest_available(workspace, output);
if (!new_output) {
new_output = fallback_output;
@ -143,39 +146,31 @@ static void output_evacuate(struct sway_container *output) {
if (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);
ipc_event_workspace(NULL, workspace, "move");
} else {
list_add(root_container.sway_root->saved_workspaces, workspace);
list_add(root->saved_workspaces, workspace);
}
}
}
void output_destroy(struct sway_container *output) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
void output_destroy(struct sway_output *output) {
if (!sway_assert(output->node.destroying,
"Tried to free output which wasn't marked as destroying")) {
return;
}
if (!sway_assert(output->destroying,
"Tried to free output which wasn't marked as destroying")) {
if (!sway_assert(output->wlr_output == NULL,
"Tried to free output which still had a wlr_output")) {
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")) {
return;
}
free(output->name);
free(output->formatted_title);
wlr_texture_destroy(output->title_focused);
wlr_texture_destroy(output->title_focused_inactive);
wlr_texture_destroy(output->title_unfocused);
wlr_texture_destroy(output->title_urgent);
list_free(output->children);
list_free(output->current.children);
list_free(output->outputs);
list_free(output->workspaces);
list_free(output->current.workspaces);
free(output);
// NOTE: We don't actually destroy the sway_output here
}
static void untrack_output(struct sway_container *con, void *data) {
@ -186,76 +181,131 @@ static void untrack_output(struct sway_container *con, void *data) {
}
}
void output_begin_destroy(struct sway_container *output) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
void output_disable(struct sway_output *output) {
if (!sway_assert(output->enabled, "Expected an enabled output")) {
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);
output_evacuate(output);
output->destroying = true;
container_set_dirty(output);
root_for_each_container(untrack_output, output);
int index = list_find(root->outputs, output);
list_del(root->outputs, index);
root_for_each_container(untrack_output, output->sway_output);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->transform.link);
wl_list_remove(&output->scale.link);
wl_list_remove(&output->damage_destroy.link);
wl_list_remove(&output->damage_frame.link);
wl_list_remove(&output->sway_output->mode.link);
wl_list_remove(&output->sway_output->transform.link);
wl_list_remove(&output->sway_output->scale.link);
wl_list_remove(&output->sway_output->damage_destroy.link);
wl_list_remove(&output->sway_output->damage_frame.link);
output->enabled = false;
output->sway_output->swayc = NULL;
output->sway_output = NULL;
arrange_root();
}
if (output->parent) {
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);
output->node.destroying = true;
node_set_dirty(&output->node);
wl_list_remove(&output->link);
wl_list_remove(&output->destroy.link);
output->wlr_output->data = NULL;
output->wlr_output = NULL;
}
struct sway_container *output_from_wlr_output(struct wlr_output *output) {
if (output == NULL) {
return 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;
}
if (strcasecmp("*", cur->name) == 0) {
wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name);
all = cur;
}
if (oc && all) {
break;
}
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *o = root_container.children->items[i];
if (o->type == C_OUTPUT && o->sway_output->wlr_output == output) {
return o;
}
if (!oc) {
oc = all;
}
if (oc && !oc->enabled) {
return NULL;
}
return oc;
}
void output_for_each_workspace(struct sway_container *output,
void (*f)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return;
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->wlr_output->width / 2;
int ly = reference->wlr_output->ly + reference->wlr_output->height / 2;
struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
root->output_layout, wlr_dir, reference->wlr_output, lx, ly);
if (!wlr_adjacent) {
return NULL;
}
return output_from_wlr_output(wlr_adjacent);
}
void output_add_workspace(struct sway_output *output,
struct sway_workspace *workspace) {
if (workspace->output) {
workspace_detach(workspace);
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
list_add(output->workspaces, workspace);
workspace->output = output;
node_set_dirty(&output->node);
node_set_dirty(&workspace->node);
}
void output_for_each_workspace(struct sway_output *output,
void (*f)(struct sway_workspace *ws, void *data), void *data) {
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
f(workspace, data);
}
}
void output_for_each_container(struct sway_container *output,
void output_for_each_container(struct sway_output *output,
void (*f)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return;
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
workspace_for_each_container(workspace, f, data);
}
}
struct sway_container *output_find_workspace(struct sway_container *output,
bool (*test)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return NULL;
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
struct sway_workspace *output_find_workspace(struct sway_output *output,
bool (*test)(struct sway_workspace *ws, void *data), void *data) {
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
if (test(workspace, data)) {
return workspace;
}
@ -263,14 +313,11 @@ struct sway_container *output_find_workspace(struct sway_container *output,
return NULL;
}
struct sway_container *output_find_container(struct sway_container *output,
struct sway_container *output_find_container(struct sway_output *output,
bool (*test)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return NULL;
}
struct sway_container *result = NULL;
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
if ((result = workspace_find_container(workspace, test, data))) {
return result;
}
@ -279,8 +326,8 @@ struct sway_container *output_find_container(struct sway_container *output,
}
static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
struct sway_container *a = *(void **)_a;
struct sway_container *b = *(void **)_b;
struct sway_workspace *a = *(void **)_a;
struct sway_workspace *b = *(void **)_b;
if (isdigit(a->name[0]) && isdigit(b->name[0])) {
int a_num = strtol(a->name, NULL, 10);
@ -294,6 +341,27 @@ static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
return 0;
}
void output_sort_workspaces(struct sway_container *output) {
list_stable_sort(output->children, sort_workspace_cmp_qsort);
void output_sort_workspaces(struct sway_output *output) {
list_stable_sort(output->workspaces, sort_workspace_cmp_qsort);
}
void output_get_box(struct sway_output *output, struct wlr_box *box) {
box->x = output->wlr_output->lx;
box->y = output->wlr_output->ly;
box->width = output->wlr_output->width;
box->height = output->wlr_output->height;
}
enum sway_container_layout output_get_default_layout(
struct sway_output *output) {
if (config->default_layout != L_NONE) {
return config->default_layout;
}
if (config->default_orientation != L_NONE) {
return config->default_orientation;
}
if (output->wlr_output->height > output->wlr_output->width) {
return L_VERT;
}
return L_HORIZ;
}

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

@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type,
view->marks = create_list();
view->allow_request_urgent = true;
wl_signal_init(&view->events.unmap);
view->container = container_create(view);
}
void view_destroy(struct sway_view *view) {
@ -43,8 +45,8 @@ void view_destroy(struct sway_view *view) {
"Tried to free view which wasn't marked as destroying")) {
return;
}
if (!sway_assert(view->swayc == NULL,
"Tried to free view which still has a swayc "
if (!sway_assert(view->container == NULL,
"Tried to free view which still has a container "
"(might have a pending transaction?)")) {
return;
}
@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) {
wlr_texture_destroy(view->marks_focused_inactive);
wlr_texture_destroy(view->marks_unfocused);
wlr_texture_destroy(view->marks_urgent);
free(view->title_format);
if (view->impl->destroy) {
view->impl->destroy(view);
@ -65,23 +68,13 @@ void view_destroy(struct sway_view *view) {
}
}
/**
* The view may or may not be involved in a transaction. For example, a view may
* unmap then attempt to destroy itself before we've applied the new layout. If
* an unmapping view is still involved in a transaction then it'll still have a
* swayc.
*
* If there's no transaction we can simply free the view. Otherwise the
* destroying flag will make the view get freed when the transaction is
* finished.
*/
void view_begin_destroy(struct sway_view *view) {
if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) {
return;
}
view->destroying = true;
if (!view->swayc) {
if (!view->container) {
view_destroy(view);
}
}
@ -171,30 +164,27 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width,
}
void view_autoconfigure(struct sway_view *view) {
if (!sway_assert(view->swayc,
"Called view_autoconfigure() on a view without a swayc")) {
return;
}
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
struct sway_output *output = view->container->workspace->output;
if (view->swayc->is_fullscreen) {
view->x = output->x;
view->y = output->y;
view->width = output->width;
view->height = output->height;
if (view->container->is_fullscreen) {
view->x = output->wlr_output->lx;
view->y = output->wlr_output->ly;
view->width = output->wlr_output->width;
view->height = output->wlr_output->height;
return;
}
struct sway_container *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) {
struct sway_container *con = view->swayc;
while (con != output) {
if (con->layout != L_TABBED && con->layout != L_STACKED) {
other_views += con->children ? con->children->length - 1 : 0;
if (other_views > 0) {
struct sway_container *con = view->container;
while (con) {
enum sway_container_layout layout = container_parent_layout(con);
if (layout != L_TABBED && layout != L_STACKED) {
list_t *siblings = container_get_siblings(con);
if (siblings && siblings->length > 1) {
other_views = true;
break;
}
}
@ -202,7 +192,7 @@ void view_autoconfigure(struct sway_view *view) {
}
}
struct sway_container *con = view->swayc;
struct sway_container *con = view->container;
view->border_top = view->border_bottom = true;
view->border_left = view->border_right = true;
@ -228,7 +218,8 @@ void view_autoconfigure(struct sway_view *view) {
// In a tabbed or stacked container, the swayc's y is the bottom of the
// title area. We have to disable any top border because the title bar is
// rendered by the parent.
if (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED) {
enum sway_container_layout layout = container_parent_layout(con);
if (layout == L_TABBED || layout == L_STACKED) {
view->border_top = false;
} else {
y_offset = container_titlebar_height();
@ -281,13 +272,16 @@ void view_set_activated(struct sway_view *view, bool activated) {
}
void view_request_activate(struct sway_view *view) {
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
struct sway_workspace *ws = view->container->workspace;
if (!ws) { // hidden scratchpad container
return;
}
struct sway_seat *seat = input_manager_current_seat(input_manager);
switch (config->focus_on_window_activation) {
case FOWA_SMART:
if (workspace_is_visible(ws)) {
seat_set_focus(seat, view->swayc);
seat_set_focus(seat, &view->container->node);
} else {
view_set_urgent(view, true);
}
@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) {
view_set_urgent(view, true);
break;
case FOWA_FOCUS:
seat_set_focus(seat, view->swayc);
seat_set_focus(seat, &view->container->node);
break;
case FOWA_NONE:
break;
@ -331,21 +325,10 @@ void view_close_popups(struct sway_view *view) {
}
void view_damage_from(struct sway_view *view) {
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *cont = root_container.children->items[i];
if (cont->type == C_OUTPUT) {
output_damage_from_view(cont->sway_output, view);
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_from_view(output, view);
}
}
}
static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
box->x = output->x + view->swayc->x;
box->y = output->y + view->swayc->y;
box->width = view->width;
box->height = view->height;
}
void view_for_each_surface(struct sway_view *view,
@ -396,11 +379,8 @@ static bool view_has_executed_criteria(struct sway_view *view,
}
void view_execute_criteria(struct sway_view *view) {
if (!view->swayc) {
return;
}
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *prior_focus = seat_get_focus(seat);
struct sway_node *prior_focus = seat_get_focus(seat);
list_t *criterias = criteria_for_view(view, CT_COMMAND);
for (int i = 0; i < criterias->length; i++) {
struct criteria *criteria = criterias->items[i];
@ -411,7 +391,7 @@ void view_execute_criteria(struct sway_view *view) {
}
wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
criteria->raw, view, criteria->cmdlist);
seat_set_focus(seat, view->swayc);
seat_set_focus(seat, &view->container->node);
list_add(view->executed_criteria, criteria);
struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
if (res->status != CMD_SUCCESS) {
@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) {
seat_set_focus(seat, prior_focus);
}
static struct sway_container *select_workspace(struct sway_view *view) {
static struct sway_workspace *select_workspace(struct sway_view *view) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
// Check if there's any `assign` criteria for the view
list_t *criterias = criteria_for_view(view,
CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT);
struct sway_container *ws = NULL;
struct sway_workspace *ws = NULL;
for (int i = 0; i < criterias->length; ++i) {
struct criteria *criteria = criterias->items[i];
if (criteria->type == CT_ASSIGN_OUTPUT) {
struct sway_container *output = output_by_name(criteria->target);
struct sway_output *output = output_by_name(criteria->target);
if (output) {
ws = seat_get_active_child(seat, output);
ws = output_get_active_workspace(output);
break;
}
} else {
@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) {
}
// Use the focused workspace
ws = seat_get_focus_inactive(seat, &root_container);
if (ws->type != C_WORKSPACE) {
ws = container_parent(ws, C_WORKSPACE);
}
return ws;
return seat_get_focused_workspace(seat);
}
static bool should_focus(struct sway_view *view) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *prev_focus =
seat_get_focus_inactive(seat, &root_container);
struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ?
prev_focus : container_parent(prev_focus, C_WORKSPACE);
struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE);
struct sway_container *prev_con = seat_get_focused_container(seat);
struct sway_workspace *prev_ws = seat_get_focused_workspace(seat);
struct sway_workspace *map_ws = view->container->workspace;
// Views can only take focus if they are mapped into the active workspace
if (prev_ws != map_ws) {
@ -506,10 +480,9 @@ static bool should_focus(struct sway_view *view) {
// If the view is the only one in the focused workspace, it'll get focus
// regardless of any no_focus criteria.
struct sway_container *parent = view->swayc->parent;
if (parent->type == C_WORKSPACE && prev_focus == parent) {
size_t num_children = parent->children->length +
parent->sway_workspace->floating->length;
if (!view->container->parent && !prev_con) {
size_t num_children = view->container->workspace->tiling->length +
view->container->workspace->floating->length;
if (num_children == 1) {
return true;
}
@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
view->surface = wlr_surface;
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *ws = select_workspace(view);
struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws);
struct sway_workspace *ws = select_workspace(view);
struct sway_node *node = seat_get_focus_inactive(seat, &ws->node);
struct sway_container *target_sibling = node->type == N_CONTAINER ?
node->sway_container : NULL;
// If we're about to launch the view into the floating container, then
// launch it as a tiled view in the root of the workspace instead.
if (container_is_floating(target_sibling)) {
target_sibling = target_sibling->parent;
if (target_sibling && container_is_floating(target_sibling)) {
target_sibling = NULL;
}
view->swayc = container_view_create(target_sibling, view);
view->container = container_create(view);
if (target_sibling) {
container_add_sibling(target_sibling, view->container, 1);
} else {
workspace_add_tiling(ws, view->container);
}
ipc_event_window(view->container, "new");
view_init_subsurfaces(view, wlr_surface);
wl_signal_add(&wlr_surface->events.new_subsurface,
@ -548,7 +529,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
if (view->impl->wants_floating && view->impl->wants_floating(view)) {
view->border = config->floating_border;
view->border_thickness = config->floating_border_thickness;
container_set_floating(view->swayc, true);
container_set_floating(view->container, true);
} else {
view->border = config->border;
view->border_thickness = config->border_thickness;
@ -556,11 +537,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
}
if (should_focus(view)) {
input_manager_set_focus(input_manager, view->swayc);
input_manager_set_focus(input_manager, &view->container->node);
}
view_update_title(view, false);
container_notify_subtree_changed(view->swayc->parent);
container_update_representation(view->container);
view_execute_criteria(view);
}
@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) {
view->urgent_timer = NULL;
}
bool was_fullscreen = view->swayc->is_fullscreen;
struct sway_container *parent = view->swayc->parent;
container_begin_destroy(view->swayc);
struct sway_container *surviving_ancestor = container_reap_empty(parent);
struct sway_container *parent = view->container->parent;
struct sway_workspace *ws = view->container->workspace;
container_begin_destroy(view->container);
if (parent) {
container_reap_empty(parent);
} else {
workspace_consider_destroy(ws);
}
// If the workspace wasn't reaped
if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) {
struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ?
surviving_ancestor :
container_parent(surviving_ancestor, C_WORKSPACE);
arrange_windows(was_fullscreen ? ws : surviving_ancestor);
if (!ws->node.destroying) {
arrange_workspace(ws);
workspace_detect_urgent(ws);
}
@ -593,15 +574,15 @@ void view_unmap(struct sway_view *view) {
}
void view_update_size(struct sway_view *view, int width, int height) {
if (!sway_assert(container_is_floating(view->swayc),
if (!sway_assert(container_is_floating(view->container),
"Expected a floating container")) {
return;
}
view->width = width;
view->height = height;
view->swayc->current.view_width = width;
view->swayc->current.view_height = height;
container_set_geometry_from_floating_view(view->swayc);
view->container->current.view_width = width;
view->container->current.view_height = height;
container_set_geometry_from_floating_view(view->container);
}
static void view_subsurface_create(struct sway_view *view,
@ -670,27 +651,18 @@ void view_child_init(struct sway_view_child *child,
wl_signal_add(&view->events.unmap, &child->view_unmap);
child->view_unmap.notify = view_child_handle_view_unmap;
struct sway_container *output = child->view->swayc->parent;
if (output != NULL) {
if (output->type != C_OUTPUT) {
output = container_parent(output, C_OUTPUT);
}
wlr_surface_send_enter(child->surface, output->sway_output->wlr_output);
}
struct sway_output *output = child->view->container->workspace->output;
wlr_surface_send_enter(child->surface, output->wlr_output);
view_init_subsurfaces(child->view, surface);
// TODO: only damage the whole child
if (child->view->swayc) {
container_damage_whole(child->view->swayc);
}
container_damage_whole(child->view->container);
}
void view_child_destroy(struct sway_view_child *child) {
// TODO: only damage the whole child
if (child->view->swayc) {
container_damage_whole(child->view->swayc);
}
container_damage_whole(child->view->container);
wl_list_remove(&child->surface_commit.link);
wl_list_remove(&child->surface_destroy.link);
@ -808,22 +780,20 @@ static char *escape_title(char *buffer) {
}
void view_update_title(struct sway_view *view, bool force) {
if (!view->swayc) {
return;
}
const char *title = view_get_title(view);
if (!force) {
if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) {
if (title && view->container->title &&
strcmp(title, view->container->title) == 0) {
return;
}
if (!title && !view->swayc->name) {
if (!title && !view->container->title) {
return;
}
}
free(view->swayc->name);
free(view->swayc->formatted_title);
free(view->container->title);
free(view->container->formatted_title);
if (title) {
size_t len = parse_title_format(view, NULL);
char *buffer = calloc(len + 1, sizeof(char));
@ -836,25 +806,25 @@ void view_update_title(struct sway_view *view, bool force) {
buffer = escape_title(buffer);
}
view->swayc->name = strdup(title);
view->swayc->formatted_title = buffer;
view->container->title = strdup(title);
view->container->formatted_title = buffer;
} else {
view->swayc->name = NULL;
view->swayc->formatted_title = NULL;
view->container->title = NULL;
view->container->formatted_title = NULL;
}
container_calculate_title_height(view->swayc);
container_calculate_title_height(view->container);
config_update_font_height(false);
// Update title after the global font height is updated
container_update_title_textures(view->swayc);
container_update_title_textures(view->container);
ipc_event_window(view->swayc, "title");
ipc_event_window(view->container, "title");
}
static bool find_by_mark_iterator(struct sway_container *con,
void *data) {
char *mark = data;
return con->type == C_VIEW && view_has_mark(con->sway_view, mark);
return con->view && view_has_mark(con->view, mark);
}
struct sway_view *view_find_mark(char *mark) {
@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) {
if (!container) {
return NULL;
}
return container->sway_view;
return container->view;
}
bool view_find_and_unmark(char *mark) {
@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) {
if (!container) {
return false;
}
struct sway_view *view = container->sway_view;
struct sway_view *view = container->view;
for (int i = 0; i < view->marks->length; ++i) {
char *view_mark = view->marks->items[i];
@ -888,10 +858,9 @@ bool view_find_and_unmark(char *mark) {
}
void view_clear_marks(struct sway_view *view) {
while (view->marks->length) {
list_del(view->marks, 0);
ipc_event_window(view->swayc, "mark");
}
list_foreach(view->marks, free);
view->marks->length = 0;
ipc_event_window(view->container, "mark");
}
bool view_has_mark(struct sway_view *view, char *mark) {
@ -906,12 +875,13 @@ bool view_has_mark(struct sway_view *view, char *mark) {
void view_add_mark(struct sway_view *view, char *mark) {
list_add(view->marks, strdup(mark));
ipc_event_window(view->swayc, "mark");
ipc_event_window(view->container, "mark");
}
static void update_marks_texture(struct sway_view *view,
struct wlr_texture **texture, struct border_colors *class) {
struct sway_output *output = container_get_effective_output(view->swayc);
struct sway_output *output =
container_get_effective_output(view->container);
if (!output) {
return;
}
@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view,
double scale = output->wlr_output->scale;
int width = 0;
int height = view->swayc->title_height * scale;
int height = view->container->title_height * scale;
cairo_t *c = cairo_create(NULL);
get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer);
@ -994,44 +964,40 @@ void view_update_marks_textures(struct sway_view *view) {
&config->border_colors.unfocused);
update_marks_texture(view, &view->marks_urgent,
&config->border_colors.urgent);
container_damage_whole(view->swayc);
container_damage_whole(view->container);
}
bool view_is_visible(struct sway_view *view) {
if (!view->swayc || view->swayc->destroying) {
if (view->container->node.destroying) {
return false;
}
struct sway_container *workspace =
container_parent(view->swayc, C_WORKSPACE);
struct sway_workspace *workspace = view->container->workspace;
if (!workspace) {
return false;
}
// Determine if view is nested inside a floating container which is sticky.
// A simple floating view will have this ancestry:
// C_VIEW -> floating -> workspace
// A more complex ancestry could be:
// C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace
struct sway_container *floater = view->swayc;
while (floater->parent->type != C_WORKSPACE
&& floater->parent->parent->type != C_WORKSPACE) {
// Determine if view is nested inside a floating container which is sticky
struct sway_container *floater = view->container;
while (floater->parent) {
floater = floater->parent;
}
bool is_sticky = container_is_floating(floater) && floater->is_sticky;
// Check view isn't in a tabbed or stacked container on an inactive tab
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *container = view->swayc;
while (container->type != C_WORKSPACE) {
if (container->parent->layout == L_TABBED ||
container->parent->layout == L_STACKED) {
if (seat_get_active_child(seat, container->parent) != container) {
struct sway_container *container = view->container;
while (container) {
enum sway_container_layout layout = container_parent_layout(container);
if (layout == L_TABBED || layout == L_STACKED) {
struct sway_node *parent = container->parent ?
&container->parent->node : &container->workspace->node;
if (seat_get_active_child(seat, parent) != &container->node) {
return false;
}
}
container = container->parent;
}
// Check view isn't hidden by another fullscreen view
if (workspace->sway_workspace->fullscreen &&
!container_is_fullscreen_or_child(view->swayc)) {
if (workspace->fullscreen &&
!container_is_fullscreen_or_child(view->container)) {
return false;
}
// Check the workspace is visible
@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
}
if (enable) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
if (seat_get_focus(seat) == view->swayc) {
if (seat_get_focused_container(seat) == view->container) {
return;
}
clock_gettime(CLOCK_MONOTONIC, &view->urgent);
@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) {
view->urgent_timer = NULL;
}
}
container_damage_whole(view->swayc);
container_damage_whole(view->container);
ipc_event_window(view->swayc, "urgent");
ipc_event_window(view->container, "urgent");
struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
workspace_detect_urgent(ws);
workspace_detect_urgent(view->container->workspace);
}
bool view_is_urgent(struct sway_view *view) {

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

Loading…
Cancel
Save