Merge pull request #2540 from RyanDwyer/typesafety

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

@ -407,7 +407,9 @@ struct sway_config {
struct output_config *output_config;
struct 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,18 @@ struct sway_output {
struct timespec last_frame;
struct wlr_output_damage *damage;
int lx, ly;
int width, height;
bool enabled;
list_t *workspaces;
struct sway_output_state current;
struct wl_listener destroy;
struct wl_listener mode;
struct wl_listener transform;
struct wl_listener scale;
struct wl_listener damage_destroy;
struct wl_listener damage_frame;
@ -39,13 +52,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 +83,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 +126,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,
void container_add_sibling(struct sway_container *parent,
struct sway_container *child);
struct sway_container *container_remove_child(struct sway_container *child);
void container_detach(struct sway_container *child);
struct sway_container *container_replace_child(struct sway_container *child,
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,58 +34,49 @@ static bool parse_movement_direction(const char *name,
}
/**
* Get swayc in the direction of newly entered output.
* Get node in the direction of newly entered output.
*/
static struct sway_container *get_swayc_in_output_direction(
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);
}
break;
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);
}
break;
case MOVE_UP:
if (ws->layout == L_VERT || ws->layout == L_STACKED) {
// get most bottom child of new output
container = ws->tiling->items[ws->tiling->length-1];
} else {
container = seat_get_focus_inactive_tiling(seat, ws);
}
break;
case MOVE_DOWN: {
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;
}
@ -94,151 +85,90 @@ static struct sway_container *get_swayc_in_output_direction(
}
}
return ws;
}
if (container) {
container = seat_get_focus_inactive_view(seat, &container->node);
return &container->node;
}
static struct sway_container *container_get_in_direction(
struct sway_container *container, struct sway_seat *seat,
enum movement_direction dir) {
struct sway_container *parent = container->parent;
return &ws->node;
}
if (dir == MOVE_CHILD) {
return seat_get_focus_inactive(seat, container);
}
static struct sway_node *node_get_in_direction(struct sway_container *container,
struct sway_seat *seat, enum movement_direction dir) {
if (container->is_fullscreen) {
if (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 +176,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 +185,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 +202,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 +218,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 +253,34 @@ 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 (direction == MOVE_CHILD) {
struct sway_node *focus = seat_get_active_child(seat, node);
if (focus) {
seat_set_focus(seat, focus);
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
if (node->type == N_WORKSPACE) {
if (direction == MOVE_PARENT) {
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
// Jump to the next output
struct sway_output *new_output =
output_get_in_direction(workspace->output, direction);
if (!new_output) {
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
struct sway_node *node =
get_node_in_output_direction(new_output, direction);
seat_set_focus(seat, node);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
struct sway_node *next_focus =
node_get_in_direction(container, seat, direction);
if (next_focus) {
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,92 @@ 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) {
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->width,
node->sway_output->height,
node->sway_output->lx,
node->sway_output->ly);
break;
case N_WORKSPACE:
snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f",
node->id, node->sway_workspace->name,
layout_to_str(node->sway_workspace->layout),
node->sway_workspace->width, node->sway_workspace->height,
node->sway_workspace->x, node->sway_workspace->y);
break;
case N_CONTAINER:
snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f",
node->id, node->sway_container->title,
layout_to_str(node->sway_container->layout),
node->sway_container->width, node->sway_container->height,
node->sway_container->x, node->sway_container->y);
break;
}
return buffer;
}
static list_t *get_children(struct sway_node *node) {
switch (node->type) {
case N_ROOT:
return root->outputs;
case N_OUTPUT:
return node->sway_output->workspaces;
case N_WORKSPACE:
return node->sway_workspace->tiling;
case N_CONTAINER:
return node->sway_container->children;
}
return NULL;
}
static int draw_node(cairo_t *cairo, struct sway_node *node,
struct sway_node *focus, int x, int y) {
int text_width, text_height;
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->width > width) {
width = output->width;
}
if (container->height > height) {
height = container->height;
if (output->height > height) {
height = 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->width,
.height = 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->width,
.y2 = 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->width,
.height = 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,13 +54,13 @@ 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,
.x = output->lx - view->x,
.y = output->ly - view->y,
.width = output->width,
.height = output->height,
};
@ -252,11 +254,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_view *view = &xdg_shell_view->view;
struct 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,13 +53,13 @@ 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,
.x = output->lx - view->x,
.y = output->ly - view->y,
.width = output->width,
.height = output->height,
};
@ -249,11 +251,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_view *view = &xdg_shell_v6_view->view;
struct 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,54 @@ 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 NULL;
}
if (ws->fullscreen) {
struct sway_container *con =
tiling_container_at(&ws->fullscreen->node, lx, ly, surface, sx, sy);
if (con) {
return &con->node;
}
if (ws->sway_workspace->fullscreen) {
return 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 NULL;
}
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 NULL;
}
if ((*surface = layer_surface_at(output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
ox, oy, sx, sy))) {
return ws;
}
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 NULL;
}
return output->swayc;
return &ws->node;
}
/**
@ -160,13 +148,14 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
// Iterate the parents until we find one with the layout we want,
// 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 +167,10 @@ static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) {
static enum wlr_edges find_edge(struct sway_container *cont,
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 +208,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 +249,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 +257,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 +301,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 +344,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 +422,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);
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);
}
if (output != focus) {
seat_set_focus_warp(seat, c, false, true);
}
} else if (c->type == C_VIEW) {
// Focus c if the following are true:
} else if (node->type == N_CONTAINER && node->sway_container->view) {
// Focus node if the following are true:
// - cursor is over a new view, i.e. entered a new window; and
// - the new view is visible, i.e. not hidden in a stack or tab; and
// - the seat does not have a keyboard grab
if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
c != prev_c &&
view_is_visible(c->sway_view)) {
seat_set_focus_warp(seat, c, false, true);
node != prev_node &&
view_is_visible(node->sway_container->view)) {
seat_set_focus_warp(seat, node, false, true);
} else {
struct sway_container *next_focus =
seat_get_focus_inactive(seat, &root_container);
if (next_focus && next_focus->type == C_VIEW &&
view_is_visible(next_focus->sway_view)) {
struct sway_node *next_focus =
seat_get_focus_inactive(seat, &root->node);
if (next_focus && next_focus->type == N_CONTAINER &&
next_focus->sway_container->view &&
view_is_visible(next_focus->sway_container->view)) {
seat_set_focus_warp(seat, next_focus, false, true);
}
}
@ -486,12 +469,12 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
if (client != cursor->image_client) {
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)) {
@ -637,8 +620,10 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
// Determine what's under the cursor
struct wlr_surface *surface = NULL;
double sx, sy;
struct sway_container *cont = container_at_coords(seat,
struct sway_node *node = node_at_coords(seat,
cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
struct sway_container *cont = node && node->type == N_CONTAINER ?
node->sway_container : NULL;
bool is_floating = cont && container_is_floating(cont);
bool is_floating_or_child = cont && container_is_floating_or_child(cont);
bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont);
@ -670,6 +655,12 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
return;
}
// Handle clicking an empty workspace
if (node && node->type == N_WORKSPACE) {
seat_set_focus(seat, node);
return;
}
// Handle clicking a layer surface
if (surface && wlr_surface_is_layer_surface(surface)) {
struct wlr_layer_surface *layer =
@ -684,7 +675,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 +704,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 +716,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 +738,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 +753,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 +761,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;
}
@ -808,7 +799,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
event->x, event->y, &lx, &ly);
double sx, sy;
container_at_coords(seat, lx, ly, &surface, &sx, &sy);
node_at_coords(seat, lx, ly, &surface, &sx, &sy);
seat->touch_id = event->touch_id;
seat->touch_x = lx;
@ -850,7 +841,7 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
event->x, event->y, &lx, &ly);
double sx, sy;
container_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
if (seat->touch_id == event->touch_id) {
seat->touch_x = lx;
@ -1025,8 +1016,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
cursor->previous.y = wlr_cursor->y;
cursor->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,71 @@ void seat_set_focus_warp(struct sway_seat *seat,
return;
}
struct sway_container *new_workspace = container;
if (new_workspace->type != C_WORKSPACE) {
new_workspace = container_parent(new_workspace, C_WORKSPACE);
}
struct sway_workspace *new_workspace = node->type == N_WORKSPACE ?
node->sway_workspace : node->sway_container->workspace;
struct sway_container *container = node->type == N_CONTAINER ?
node->sway_container : NULL;
if (last_workspace == new_workspace
&& last_workspace->sway_workspace->fullscreen
&& !container_is_fullscreen_or_child(container)) {
// Deny setting focus to a view which is hidden by a fullscreen container
if (new_workspace && new_workspace->fullscreen && container &&
!container_is_fullscreen_or_child(container)) {
return;
}
struct sway_container *last_output = last_focus;
if (last_output && last_output->type != C_OUTPUT) {
last_output = container_parent(last_output, C_OUTPUT);
}
struct sway_container *new_output = container;
if (new_output->type != C_OUTPUT) {
new_output = container_parent(new_output, C_OUTPUT);
}
struct sway_output *last_output = last_workspace ?
last_workspace->output : NULL;
struct sway_output *new_output = new_workspace->output;
// find new output's old workspace, which might have to be removed if empty
struct sway_container *new_output_last_ws = NULL;
struct sway_workspace *new_output_last_ws = NULL;
if (new_output && last_output != new_output) {
new_output_last_ws = seat_get_active_child(seat, new_output);
new_output_last_ws = output_get_active_workspace(new_output);
}
if (container->parent) {
struct sway_seat_container *seat_con =
seat_container_from_container(seat, container);
if (seat_con == NULL) {
return;
// Unfocus the previous focus
if (last_focus) {
seat_send_unfocus(last_focus, seat);
node_set_dirty(last_focus);
struct sway_node *parent = node_get_parent(last_focus);
if (parent) {
node_set_dirty(parent);
}
}
// put all the ancestors of this container on top of the focus stack
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 (last_focus) {
seat_send_unfocus(last_focus, seat);
container_set_dirty(last_focus);
}
seat_send_focus(container, seat);
container_set_dirty(container);
container_set_dirty(container->parent); // for focused_inactive_child
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);
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 +666,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 +693,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) {
@ -725,13 +710,17 @@ 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,
double x = 0;
double y = 0;
if (container) {
x = container->x + container->width / 2.0;
y = container->y + container->height / 2.0;
} else {
x = new_workspace->x + new_workspace->width / 2.0;
y = new_workspace->y + new_workspace->height / 2.0;
}
if (!wlr_output_layout_contains_point(root->output_layout,
new_output->wlr_output, seat->cursor->cursor->x,
seat->cursor->cursor->y)) {
wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
cursor_send_pointer_motion(seat->cursor, 0, true);
@ -744,9 +733,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 +743,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 +760,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 +782,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 +794,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 +817,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;
}
if (con->type == C_VIEW) {
return con;
struct sway_node *seat_get_focus_inactive(struct sway_seat *seat,
struct sway_node *node) {
if (node_is_view(node)) {
return node;
}
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,64 +204,97 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
}
}
static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
json_object *focus = data;
json_object_array_add(focus, json_object_new_int(c->id));
}
struct focus_inactive_data {
struct sway_node *node;
json_object *object;
};
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);
break;
case C_OUTPUT:
ipc_json_describe_output(c, object);
switch (node->type) {
case N_ROOT:
ipc_json_describe_root(root, object);
break;
case C_CONTAINER:
case C_VIEW:
ipc_json_describe_view(c, object);
case N_OUTPUT:
ipc_json_describe_output(node->sway_output, object);
break;
case C_WORKSPACE:
ipc_json_describe_workspace(c, object);
case N_CONTAINER:
ipc_json_describe_view(node->sway_container, object);
break;
case C_TYPES:
default:
case N_WORKSPACE:
ipc_json_describe_workspace(node->sway_workspace, object);
break;
}
return object;
}
json_object *ipc_json_describe_container_recursive(struct sway_container *c) {
json_object *object = ipc_json_describe_container(c);
json_object *ipc_json_describe_node_recursive(struct sway_node *node) {
json_object *object = ipc_json_describe_node(node);
int i;
json_object *children = json_object_new_array();
if (c->type != C_VIEW && c->children) {
for (i = 0; i < c->children->length; ++i) {
json_object_array_add(children, ipc_json_describe_container_recursive(c->children->items[i]));
switch (node->type) {
case N_ROOT:
for (i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
json_object_array_add(children,
ipc_json_describe_node_recursive(&output->node));
}
break;
case N_OUTPUT:
for (i = 0; i < node->sway_output->workspaces->length; ++i) {
struct sway_workspace *ws = node->sway_output->workspaces->items[i];
json_object_array_add(children,
ipc_json_describe_node_recursive(&ws->node));
}
break;
case N_WORKSPACE:
for (i = 0; i < node->sway_workspace->tiling->length; ++i) {
struct sway_container *con = node->sway_workspace->tiling->items[i];
json_object_array_add(children,
ipc_json_describe_node_recursive(&con->node));
}
break;
case N_CONTAINER:
if (node->sway_container->children) {
for (i = 0; i < node->sway_container->children->length; ++i) {
struct sway_container *child =
node->sway_container->children->items[i];
json_object_array_add(children,
ipc_json_describe_node_recursive(&child->node));
}
}
break;
}
json_object_object_add(object, "nodes", children);
@ -329,7 +344,7 @@ json_object *ipc_json_describe_seat(struct sway_seat *seat) {
}
json_object *object = json_object_new_object();
struct sway_container *focus = seat_get_focus(seat);
struct sway_node *focus = seat_get_focus(seat);
json_object_object_add(object, "name",
json_object_new_string(seat->wlr_seat->name));

@ -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,37 @@ 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->lx;
fs->y = output->ly;
fs->width = output->width;
fs->height = 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;
root->output_layout, output->wlr_output);
output->lx = output_box->x;
output->ly = 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];
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
arrange_workspace(workspace);
}
}
@ -264,37 +251,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,116 @@ 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();
list_add(root->outputs, output);
struct wlr_box size;
wlr_output_effective_resolution(sway_output->wlr_output, &size.width,
&size.height);
output->width = size.width;
output->height = size.height;
output->lx = wlr_output->lx;
output->ly = wlr_output->ly;
wlr_output_transformed_resolution(wlr_output,
&output->width, &output->height);
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 +151,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 +186,128 @@ 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);
root_for_each_container(untrack_output, output->sway_output);
int index = list_find(root->outputs, output);
list_del(root->outputs, index);
wl_list_remove(&output->sway_output->mode.link);
wl_list_remove(&output->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);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->transform.link);
wl_list_remove(&output->scale.link);
wl_list_remove(&output->damage_destroy.link);
wl_list_remove(&output->damage_frame.link);
output->sway_output->swayc = NULL;
output->sway_output = NULL;
output->enabled = false;
if (output->parent) {
container_remove_child(output);
arrange_root();
}
void output_begin_destroy(struct sway_output *output) {
if (!sway_assert(!output->enabled, "Expected a disabled output")) {
return;
}
wlr_log(WLR_DEBUG, "Destroying output '%s'", output->wlr_output->name);
output->node.destroying = true;
node_set_dirty(&output->node);
wl_list_remove(&output->link);
wl_list_remove(&output->destroy.link);
output->wlr_output->data = NULL;
output->wlr_output = NULL;
}
struct 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;
}
return oc;
}
struct sway_output *output_from_wlr_output(struct wlr_output *output) {
return output->data;
}
struct sway_output *output_get_in_direction(struct sway_output *reference,
enum movement_direction direction) {
enum wlr_direction wlr_dir = 0;
if (!sway_assert(sway_dir_to_wlr(direction, &wlr_dir),
"got invalid direction: %d", direction)) {
return NULL;
}
int lx = reference->wlr_output->lx + reference->width / 2;
int ly = reference->wlr_output->ly + reference->height / 2;
struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output(
root->output_layout, wlr_dir, reference->wlr_output, lx, ly);
if (!wlr_adjacent) {
return NULL;
}
return output_from_wlr_output(wlr_adjacent);
}
void output_for_each_workspace(struct sway_container *output,
void (*f)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return;
void output_add_workspace(struct sway_output *output,
struct sway_workspace *workspace) {
if (workspace->output) {
workspace_detach(workspace);
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
list_add(output->workspaces, workspace);
workspace->output = output;
node_set_dirty(&output->node);
node_set_dirty(&workspace->node);
}
void output_for_each_workspace(struct sway_output *output,
void (*f)(struct sway_workspace *ws, void *data), void *data) {
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
f(workspace, data);
}
}
void output_for_each_container(struct sway_container *output,
void output_for_each_container(struct sway_output *output,
void (*f)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return;
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
workspace_for_each_container(workspace, f, data);
}
}
struct sway_container *output_find_workspace(struct sway_container *output,
bool (*test)(struct sway_container *con, void *data), void *data) {
if (!sway_assert(output->type == C_OUTPUT, "Expected an output")) {
return NULL;
}
for (int i = 0; i < output->children->length; ++i) {
struct sway_container *workspace = output->children->items[i];
struct sway_workspace *output_find_workspace(struct sway_output *output,
bool (*test)(struct sway_workspace *ws, void *data), void *data) {
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
if (test(workspace, data)) {
return workspace;
}
@ -263,14 +315,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 +328,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 +343,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->lx;
box->y = output->ly;
box->width = output->width;
box->height = output->height;
}
enum sway_container_layout output_get_default_layout(
struct sway_output *output) {
if (config->default_layout != L_NONE) {
return config->default_layout;
}
if (config->default_orientation != L_NONE) {
return config->default_orientation;
}
if (output->height > output->width) {
return L_VERT;
}
return L_HORIZ;
}

@ -14,54 +14,45 @@
#include "log.h"
#include "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,31 @@ 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")) {
if (!view->container->workspace) {
// Hidden in the scratchpad
return;
}
struct sway_output *output = view->container->workspace->output;
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
if (view->swayc->is_fullscreen) {
view->x = output->x;
view->y = output->y;
if (view->container->is_fullscreen) {
view->x = output->lx;
view->y = output->ly;
view->width = output->width;
view->height = 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 +196,7 @@ void view_autoconfigure(struct sway_view *view) {
}
}
struct sway_container *con = view->swayc;
struct sway_container *con = view->container;
view->border_top = view->border_bottom = true;
view->border_left = view->border_right = true;
@ -228,7 +222,8 @@ void view_autoconfigure(struct sway_view *view) {
// In a tabbed or stacked container, the swayc's y is the bottom of the
// 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 +276,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 +294,7 @@ void view_request_activate(struct sway_view *view) {
view_set_urgent(view, true);
break;
case FOWA_FOCUS:
seat_set_focus(seat, view->swayc);
seat_set_focus(seat, &view->container->node);
break;
case FOWA_NONE:
break;
@ -331,23 +329,12 @@ void view_close_popups(struct sway_view *view) {
}
void view_damage_from(struct sway_view *view) {
for (int i = 0; i < root_container.children->length; ++i) {
struct sway_container *cont = root_container.children->items[i];
if (cont->type == C_OUTPUT) {
output_damage_from_view(cont->sway_output, view);
}
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_from_view(output, view);
}
}
static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) {
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
box->x = output->x + view->swayc->x;
box->y = output->y + view->swayc->y;
box->width = view->width;
box->height = view->height;
}
void view_for_each_surface(struct sway_view *view,
wlr_surface_iterator_func_t iterator, void *user_data) {
if (!view->surface) {
@ -396,11 +383,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 +395,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 +407,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 +468,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 +484,9 @@ static bool should_focus(struct sway_view *view) {
// If the view is the only one in the focused workspace, it'll get focus
// 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 +506,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);
} 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 +533,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 +541,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 +559,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 if (ws) {
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 && !ws->node.destroying) {
arrange_workspace(ws);
workspace_detect_urgent(ws);
}
@ -593,15 +578,15 @@ void view_unmap(struct sway_view *view) {
}
void view_update_size(struct sway_view *view, int width, int height) {
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 +655,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 +784,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 +810,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 +837,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 +846,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 +862,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 +879,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 +923,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 +968,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 +1017,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 +1028,13 @@ 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);
if (view->container->workspace) {
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->lx;
ws->y = output->ly;
ws->width = output->width;
ws->height = 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