mirror of
https://github.com/gwenhael-le-moine/sway-patched-tray-menu.git
synced 2024-12-28 22:23:42 +01:00
e3a3917d3a
Instead of using container->width/height as both the input and output of the layout calculation have container->width_fraction/height_fraction as the share of the parent this container occupies and calculate the layout based on that. That way the container arrangement can always be recalculated even if width/height have been altered by things like fullscreen. To do this several parts are reworked: - The vertical and horizontal arrangement code is ajusted to work with fractions instead of directly with width/height - The resize code is then changed to manipulate the fractions when working on tiled containers. - Finally the places that manipulated width/height are adjusted to match. The adjusted parts are container split, swap, and the input seat code. It's possible that some parts of the code are now adjusting width and height only for those to be immediately recalculated. That's harmless and since non-tiled containers are still sized with width/height directly it may avoid breaking other corner cases. Fixes #3547 Fixes #4297
247 lines
7.1 KiB
C
247 lines
7.1 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#include <strings.h>
|
|
#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"
|
|
#include "sway/tree/workspace.h"
|
|
#include "stringop.h"
|
|
|
|
static const char expected_syntax[] =
|
|
"Expected 'swap container with id|con_id|mark <arg>'";
|
|
|
|
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->width_fraction = con1->width_fraction;
|
|
temp->height_fraction = con1->height_fraction;
|
|
temp->parent = con1->parent;
|
|
temp->workspace = con1->workspace;
|
|
|
|
con1->x = con2->x;
|
|
con1->y = con2->y;
|
|
con1->width = con2->width;
|
|
con1->height = con2->height;
|
|
con1->width_fraction = con2->width_fraction;
|
|
con1->height_fraction = con2->height_fraction;
|
|
|
|
con2->x = temp->x;
|
|
con2->y = temp->y;
|
|
con2->width = temp->width;
|
|
con2->height = temp->height;
|
|
con2->width_fraction = temp->width_fraction;
|
|
con2->height_fraction = temp->height_fraction;
|
|
|
|
int temp_index = container_sibling_index(con1);
|
|
if (con2->parent) {
|
|
container_insert_child(con2->parent, con1,
|
|
container_sibling_index(con2));
|
|
} else {
|
|
workspace_insert_tiling(con2->workspace, con1,
|
|
container_sibling_index(con2));
|
|
}
|
|
if (temp->parent) {
|
|
container_insert_child(temp->parent, con2, temp_index);
|
|
} else {
|
|
workspace_insert_tiling(temp->workspace, 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_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(seat, &con2->node);
|
|
}
|
|
seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1);
|
|
} else if (focus == con2 && (layout1 == L_TABBED
|
|
|| layout1 == L_STACKED)) {
|
|
if (workspace_is_visible(ws1)) {
|
|
seat_set_focus(seat, &con1->node);
|
|
}
|
|
seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2);
|
|
} else if (ws1 != ws2) {
|
|
seat_set_focus_container(seat, focus == con1 ? con2 : con1);
|
|
} else {
|
|
seat_set_focus_container(seat, focus);
|
|
}
|
|
} else {
|
|
seat_set_focus_container(seat, focus);
|
|
}
|
|
|
|
if (root->fullscreen_global) {
|
|
seat_set_focus(seat,
|
|
seat_get_focus_inactive(seat, &root->fullscreen_global->node));
|
|
}
|
|
}
|
|
|
|
void container_swap(struct sway_container *con1, struct sway_container *con2) {
|
|
if (!sway_assert(con1 && con2, "Cannot swap with nothing")) {
|
|
return;
|
|
}
|
|
if (!sway_assert(!container_has_ancestor(con1, con2)
|
|
&& !container_has_ancestor(con2, con1),
|
|
"Cannot swap ancestor and descendant")) {
|
|
return;
|
|
}
|
|
if (!sway_assert(!container_is_floating(con1)
|
|
&& !container_is_floating(con2),
|
|
"Swapping with floating containers is not supported")) {
|
|
return;
|
|
}
|
|
|
|
sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu",
|
|
con1->node.id, con2->node.id);
|
|
|
|
enum sway_fullscreen_mode fs1 = con1->fullscreen_mode;
|
|
enum sway_fullscreen_mode fs2 = con2->fullscreen_mode;
|
|
if (fs1) {
|
|
container_fullscreen_disable(con1);
|
|
}
|
|
if (fs2) {
|
|
container_fullscreen_disable(con2);
|
|
}
|
|
|
|
struct sway_seat *seat = config->handler_context.seat;
|
|
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);
|
|
if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a"
|
|
"workspace. This should not happen")) {
|
|
return;
|
|
}
|
|
|
|
char *stored_prev_name = NULL;
|
|
if (seat->prev_workspace_name) {
|
|
stored_prev_name = strdup(seat->prev_workspace_name);
|
|
}
|
|
|
|
swap_places(con1, con2);
|
|
|
|
if (!workspace_is_visible(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->node));
|
|
}
|
|
|
|
swap_focus(con1, con2, seat, focus);
|
|
|
|
if (stored_prev_name) {
|
|
free(seat->prev_workspace_name);
|
|
seat->prev_workspace_name = stored_prev_name;
|
|
}
|
|
|
|
if (fs1) {
|
|
container_set_fullscreen(con2, fs1);
|
|
}
|
|
if (fs2) {
|
|
container_set_fullscreen(con1, fs2);
|
|
}
|
|
}
|
|
|
|
static bool test_con_id(struct sway_container *container, void *data) {
|
|
size_t *con_id = data;
|
|
return container->node.id == *con_id;
|
|
}
|
|
|
|
#if HAVE_XWAYLAND
|
|
static bool test_id(struct sway_container *container, void *data) {
|
|
xcb_window_t *wid = data;
|
|
return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
|
|
&& container->view->wlr_xwayland_surface->window_id == *wid);
|
|
}
|
|
#endif
|
|
|
|
static bool test_mark(struct sway_container *container, void *mark) {
|
|
if (container->marks->length) {
|
|
return !list_seq_find(container->marks,
|
|
(int (*)(const void *, const void *))strcmp, mark);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct cmd_results *cmd_swap(int argc, char **argv) {
|
|
struct cmd_results *error = NULL;
|
|
if ((error = checkarg(argc, "swap", EXPECTED_AT_LEAST, 4))) {
|
|
return error;
|
|
}
|
|
if (!root->outputs->length) {
|
|
return cmd_results_new(CMD_INVALID,
|
|
"Can't run this command while there's no outputs connected.");
|
|
}
|
|
|
|
if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) {
|
|
return cmd_results_new(CMD_INVALID, expected_syntax);
|
|
}
|
|
|
|
struct sway_container *current = config->handler_context.container;
|
|
struct sway_container *other = NULL;
|
|
|
|
char *value = join_args(argv + 3, argc - 3);
|
|
if (strcasecmp(argv[2], "id") == 0) {
|
|
#if HAVE_XWAYLAND
|
|
xcb_window_t id = strtol(value, NULL, 0);
|
|
other = root_find_container(test_id, &id);
|
|
#endif
|
|
} else if (strcasecmp(argv[2], "con_id") == 0) {
|
|
size_t con_id = atoi(value);
|
|
other = root_find_container(test_con_id, &con_id);
|
|
} else if (strcasecmp(argv[2], "mark") == 0) {
|
|
other = root_find_container(test_mark, value);
|
|
} else {
|
|
free(value);
|
|
return cmd_results_new(CMD_INVALID, expected_syntax);
|
|
}
|
|
|
|
if (!other) {
|
|
error = cmd_results_new(CMD_FAILURE,
|
|
"Failed to find %s '%s'", argv[2], value);
|
|
} else if (!current) {
|
|
error = cmd_results_new(CMD_FAILURE,
|
|
"Can only swap with containers and views");
|
|
} else if (container_has_ancestor(current, other)
|
|
|| container_has_ancestor(other, current)) {
|
|
error = cmd_results_new(CMD_FAILURE,
|
|
"Cannot swap ancestor and descendant");
|
|
} else if (container_is_floating(current) || container_is_floating(other)) {
|
|
error = cmd_results_new(CMD_FAILURE,
|
|
"Swapping with floating containers is not supported");
|
|
}
|
|
|
|
free(value);
|
|
|
|
if (error) {
|
|
return error;
|
|
}
|
|
|
|
container_swap(current, other);
|
|
|
|
if (root->fullscreen_global) {
|
|
arrange_root();
|
|
} else {
|
|
arrange_node(node_get_parent(¤t->node));
|
|
if (node_get_parent(&other->node) != node_get_parent(¤t->node)) {
|
|
arrange_node(node_get_parent(&other->node));
|
|
}
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL);
|
|
}
|