swayfx/sway/tree/layout.c

403 lines
11 KiB
C
Raw Normal View History

2017-11-20 09:04:28 +11:00
#define _POSIX_C_SOURCE 200809L
2017-11-26 02:59:49 +11:00
#include <math.h>
2017-11-20 09:04:28 +11:00
#include <stdbool.h>
2017-11-23 12:39:27 +11:00
#include <stdlib.h>
2017-11-20 09:04:28 +11:00
#include <string.h>
2017-11-26 02:59:49 +11:00
#include <wlr/types/wlr_output.h>
2017-11-20 09:04:28 +11:00
#include <wlr/types/wlr_output_layout.h>
#include "config.h"
2018-04-07 01:49:27 +10:00
#include "sway/debug.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/layout.h"
2017-11-26 02:59:49 +11:00
#include "sway/output.h"
2018-03-31 02:58:17 +11:00
#include "sway/tree/workspace.h"
#include "sway/tree/view.h"
2018-02-15 06:30:27 +11:00
#include "sway/input/seat.h"
#include "sway/ipc-server.h"
2017-11-20 09:04:28 +11:00
#include "list.h"
#include "log.h"
void container_handle_fullscreen_reparent(struct sway_container *con,
2018-04-18 00:10:32 +10:00
struct sway_container *old_parent) {
2018-07-25 20:56:23 +10:00
if (!con->is_fullscreen) {
2018-04-18 00:10:32 +10:00
return;
}
struct sway_container *old_workspace = old_parent;
if (old_workspace && old_workspace->type != C_WORKSPACE) {
old_workspace = container_parent(old_workspace, C_WORKSPACE);
}
struct sway_container *new_workspace = container_parent(con, C_WORKSPACE);
2018-04-18 00:10:32 +10:00
if (old_workspace == new_workspace) {
return;
}
// Unmark the old workspace as fullscreen
if (old_workspace) {
old_workspace->sway_workspace->fullscreen = NULL;
}
// Mark the new workspace as fullscreen
if (new_workspace->sway_workspace->fullscreen) {
2018-07-25 20:56:23 +10:00
container_set_fullscreen(
new_workspace->sway_workspace->fullscreen, false);
2018-04-18 00:10:32 +10:00
}
2018-07-25 20:56:23 +10:00
new_workspace->sway_workspace->fullscreen = con;
// Resize container to new output dimensions
struct sway_container *output = new_workspace->parent;
con->x = output->x;
con->y = output->y;
con->width = output->width;
con->height = output->height;
2018-07-25 20:56:23 +10:00
if (con->type == C_VIEW) {
struct sway_view *view = con->sway_view;
view->x = output->x;
view->y = output->y;
view->width = output->width;
view->height = output->height;
} else {
arrange_windows(new_workspace);
}
2018-04-18 00:10:32 +10:00
}
void container_insert_child(struct sway_container *parent,
struct sway_container *child, int i) {
2018-04-07 01:52:23 +10:00
struct sway_container *old_parent = child->parent;
if (old_parent) {
container_remove_child(child);
}
2018-07-10 07:54:30 +10:00
wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i);
list_insert(parent->children, i, child);
child->parent = parent;
2018-04-18 00:10:32 +10:00
container_handle_fullscreen_reparent(child, old_parent);
}
struct sway_container *container_add_sibling(struct sway_container *fixed,
struct sway_container *active) {
2018-02-05 05:39:10 +11:00
// TODO handle floating
struct sway_container *old_parent = NULL;
if (active->parent) {
old_parent = active->parent;
container_remove_child(active);
}
struct sway_container *parent = fixed->parent;
int i = container_sibling_index(fixed);
2018-02-05 05:39:10 +11:00
list_insert(parent->children, i + 1, active);
active->parent = parent;
2018-04-18 00:10:32 +10:00
container_handle_fullscreen_reparent(active, old_parent);
2018-02-05 05:39:10 +11:00
return active->parent;
}
void container_add_child(struct sway_container *parent,
struct sway_container *child) {
2018-07-10 07:54:30 +10:00
wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)",
2017-11-20 09:04:28 +11:00
child, child->type, child->width, child->height,
parent, parent->type, parent->width, parent->height);
2018-04-18 00:10:32 +10:00
struct sway_container *old_parent = child->parent;
2017-11-20 09:04:28 +11:00
list_add(parent->children, child);
child->parent = parent;
container_handle_fullscreen_reparent(child, old_parent);
if (old_parent) {
container_set_dirty(old_parent);
}
container_set_dirty(child);
2018-03-31 02:58:17 +11:00
}
struct sway_container *container_remove_child(struct sway_container *child) {
2018-07-25 20:56:23 +10:00
if (child->is_fullscreen) {
2018-04-18 00:10:32 +10:00
struct sway_container *workspace = container_parent(child, C_WORKSPACE);
workspace->sway_workspace->fullscreen = NULL;
}
struct sway_container *parent = child->parent;
list_t *list = container_is_floating(child) ?
parent->sway_workspace->floating : parent->children;
int index = list_find(list, child);
if (index != -1) {
list_del(list, index);
2017-11-26 08:30:15 +11:00
}
child->parent = NULL;
container_notify_subtree_changed(parent);
2018-05-19 22:54:50 +10:00
container_set_dirty(parent);
container_set_dirty(child);
2018-03-31 15:44:17 +11:00
return parent;
}
bool sway_dir_to_wlr(enum movement_direction dir, enum wlr_direction *out) {
switch (dir) {
case MOVE_UP:
*out = WLR_DIRECTION_UP;
break;
case MOVE_DOWN:
*out = WLR_DIRECTION_DOWN;
break;
case MOVE_LEFT:
*out = WLR_DIRECTION_LEFT;
break;
case MOVE_RIGHT:
*out = WLR_DIRECTION_RIGHT;
break;
default:
return false;
}
return true;
}
enum sway_container_layout container_get_default_layout(
2018-04-04 03:23:34 +10:00
struct sway_container *con) {
if (con->type != C_OUTPUT) {
con = container_parent(con, C_OUTPUT);
}
if (!sway_assert(con != NULL,
2018-04-04 09:23:59 +10:00
"container_get_default_layout must be called on an attached"
2018-04-04 03:23:34 +10:00
" container below the root container")) {
return 0;
}
2017-11-23 12:39:27 +11:00
if (config->default_layout != L_NONE) {
2018-03-31 01:43:55 +11:00
return config->default_layout;
2017-11-23 12:39:27 +11:00
} else if (config->default_orientation != L_NONE) {
return config->default_orientation;
2018-04-04 03:23:34 +10:00
} else if (con->width >= con->height) {
2017-11-23 12:39:27 +11:00
return L_HORIZ;
} else {
return L_VERT;
}
}
2018-03-31 15:44:17 +11:00
struct sway_container *container_replace_child(struct sway_container *child,
struct sway_container *new_child) {
struct sway_container *parent = child->parent;
if (parent == NULL) {
return NULL;
}
list_t *list = container_is_floating(child) ?
parent->sway_workspace->floating : parent->children;
int i = list_find(list, child);
if (new_child->parent) {
container_remove_child(new_child);
}
list->items[i] = new_child;
2018-03-31 15:44:17 +11:00
new_child->parent = parent;
child->parent = NULL;
// Set geometry for new child
new_child->x = child->x;
new_child->y = child->y;
new_child->width = child->width;
new_child->height = child->height;
// reset geometry for child
child->width = 0;
child->height = 0;
return parent;
}
struct sway_container *container_split(struct sway_container *child,
enum sway_container_layout layout) {
// TODO floating: cannot split a floating container
if (!sway_assert(child, "child cannot be null")) {
return NULL;
}
2018-04-06 13:22:33 +10:00
if (child->type == C_WORKSPACE && child->children->length == 0) {
// Special case: this just behaves like splitt
child->prev_split_layout = child->layout;
2018-04-06 13:22:33 +10:00
child->layout = layout;
return child;
}
2018-03-31 15:44:17 +11:00
struct sway_container *cont = container_create(C_CONTAINER);
2018-07-10 07:54:30 +10:00
wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child);
2018-03-31 15:44:17 +11:00
2018-06-09 23:34:56 +10:00
remove_gaps(child);
cont->prev_split_layout = L_NONE;
2018-03-31 15:44:17 +11:00
cont->width = child->width;
cont->height = child->height;
cont->x = child->x;
cont->y = child->y;
struct sway_seat *seat = input_manager_get_default_seat(input_manager);
bool set_focus = (seat_get_focus(seat) == child);
2018-06-09 23:34:56 +10:00
add_gaps(cont);
2018-03-31 15:44:17 +11:00
if (child->type == C_WORKSPACE) {
struct sway_container *workspace = child;
2018-04-03 05:40:40 +10:00
while (workspace->children->length) {
struct sway_container *ws_child = workspace->children->items[0];
container_remove_child(ws_child);
container_add_child(cont, ws_child);
2018-03-31 15:44:17 +11:00
}
container_add_child(workspace, cont);
enum sway_container_layout old_layout = workspace->layout;
workspace->layout = layout;
cont->layout = old_layout;
2018-04-03 05:40:40 +10:00
} else {
cont->layout = layout;
2018-03-31 15:44:17 +11:00
container_replace_child(child, cont);
container_add_child(cont, child);
}
2018-04-03 05:40:40 +10:00
if (set_focus) {
seat_set_focus(seat, cont);
seat_set_focus(seat, child);
}
container_notify_subtree_changed(cont);
2018-03-31 15:44:17 +11:00
return cont;
2018-02-15 06:30:27 +11:00
}
2018-04-05 11:32:31 +10:00
void container_recursive_resize(struct sway_container *container,
double amount, enum wlr_edges edge) {
2018-04-05 11:32:31 +10:00
bool layout_match = true;
2018-07-10 07:54:30 +10:00
wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount);
if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) {
container->width += amount;
2018-04-05 11:32:31 +10:00
layout_match = container->layout == L_HORIZ;
} else if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) {
container->height += amount;
2018-04-05 11:32:31 +10:00
layout_match = container->layout == L_VERT;
}
if (container->children) {
for (int i = 0; i < container->children->length; i++) {
struct sway_container *child = container->children->items[i];
double amt = layout_match ?
amount / container->children->length : amount;
container_recursive_resize(child, amt, edge);
}
}
}
2018-05-27 01:02:21 +10:00
static void swap_places(struct sway_container *con1,
struct sway_container *con2) {
struct sway_container *temp = malloc(sizeof(struct sway_container));
temp->x = con1->x;
temp->y = con1->y;
temp->width = con1->width;
temp->height = con1->height;
temp->parent = con1->parent;
con1->x = con2->x;
con1->y = con2->y;
con1->width = con2->width;
con1->height = con2->height;
con2->x = temp->x;
con2->y = temp->y;
con2->width = temp->width;
con2->height = temp->height;
int temp_index = container_sibling_index(con1);
container_insert_child(con2->parent, con1, container_sibling_index(con2));
2018-05-27 01:02:21 +10:00
container_insert_child(temp->parent, con2, temp_index);
free(temp);
}
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)) {
if (workspace_is_visible(ws2)) {
seat_set_focus_warp(seat, con2, false, true);
2018-05-27 01:02:21 +10:00
}
seat_set_focus(seat, ws1 != ws2 ? con2 : con1);
} else if (focus == con2 && (con1->parent->layout == L_TABBED
|| con1->parent->layout == L_STACKED)) {
if (workspace_is_visible(ws1)) {
seat_set_focus_warp(seat, con1, false, true);
2018-05-27 01:02:21 +10:00
}
seat_set_focus(seat, ws1 != ws2 ? con1 : con2);
} else if (ws1 != ws2) {
seat_set_focus(seat, focus == con1 ? con2 : con1);
} else {
seat_set_focus(seat, focus);
}
} else {
seat_set_focus(seat, focus);
}
}
void container_swap(struct sway_container *con1, struct sway_container *con2) {
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;
}
2018-05-28 12:45:42 +10:00
if (!sway_assert(!container_has_ancestor(con1, con2)
&& !container_has_ancestor(con2, con1),
"Cannot swap ancestor and descendant")) {
2018-05-27 01:02:21 +10:00
return;
}
if (!sway_assert(!container_is_floating(con1)
&& !container_is_floating(con2),
2018-05-27 01:02:21 +10:00
"Swapping with floating containers is not supported")) {
return;
}
2018-07-10 07:54:30 +10:00
wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id);
2018-05-27 01:02:21 +10:00
2018-07-25 20:56:23 +10:00
int fs1 = con1->is_fullscreen;
int fs2 = con2->is_fullscreen;
2018-05-27 01:02:21 +10:00
if (fs1) {
2018-07-25 20:56:23 +10:00
container_set_fullscreen(con1, false);
2018-05-27 01:02:21 +10:00
}
if (fs2) {
2018-07-25 20:56:23 +10:00
container_set_fullscreen(con2, false);
2018-05-27 01:02:21 +10:00
}
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);
char *stored_prev_name = NULL;
if (prev_workspace_name) {
stored_prev_name = strdup(prev_workspace_name);
}
swap_places(con1, con2);
if (!workspace_is_visible(vis1)) {
seat_set_focus(seat, seat_get_focus_inactive(seat, vis1));
}
if (!workspace_is_visible(vis2)) {
seat_set_focus(seat, seat_get_focus_inactive(seat, vis2));
}
swap_focus(con1, con2, seat, focus);
if (stored_prev_name) {
free(prev_workspace_name);
prev_workspace_name = stored_prev_name;
}
2018-07-25 20:56:23 +10:00
if (fs1) {
container_set_fullscreen(con2, true);
2018-05-27 01:02:21 +10:00
}
2018-07-25 20:56:23 +10:00
if (fs2) {
container_set_fullscreen(con1, true);
2018-05-27 01:02:21 +10:00
}
}