aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree/container.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r--sway/tree/container.c209
1 files changed, 174 insertions, 35 deletions
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 6f6137c4..4f743c40 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -11,11 +11,15 @@
11#include "cairo.h" 11#include "cairo.h"
12#include "pango.h" 12#include "pango.h"
13#include "sway/config.h" 13#include "sway/config.h"
14#include "sway/desktop.h"
15#include "sway/desktop/transaction.h"
14#include "sway/input/input-manager.h" 16#include "sway/input/input-manager.h"
15#include "sway/input/seat.h" 17#include "sway/input/seat.h"
16#include "sway/ipc-server.h" 18#include "sway/ipc-server.h"
17#include "sway/output.h" 19#include "sway/output.h"
20#include "sway/scratchpad.h"
18#include "sway/server.h" 21#include "sway/server.h"
22#include "sway/tree/arrange.h"
19#include "sway/tree/layout.h" 23#include "sway/tree/layout.h"
20#include "sway/tree/view.h" 24#include "sway/tree/view.h"
21#include "sway/tree/workspace.h" 25#include "sway/tree/workspace.h"
@@ -28,7 +32,7 @@ static list_t *get_bfs_queue() {
28 if (!bfs_queue) { 32 if (!bfs_queue) {
29 bfs_queue = create_list(); 33 bfs_queue = create_list();
30 if (!bfs_queue) { 34 if (!bfs_queue) {
31 wlr_log(L_ERROR, "could not allocate list for bfs queue"); 35 wlr_log(WLR_ERROR, "could not allocate list for bfs queue");
32 return NULL; 36 return NULL;
33 } 37 }
34 } 38 }
@@ -151,18 +155,11 @@ void container_free(struct sway_container *cont) {
151 return; 155 return;
152 } 156 }
153 free(cont->name); 157 free(cont->name);
158 free(cont->formatted_title);
154 wlr_texture_destroy(cont->title_focused); 159 wlr_texture_destroy(cont->title_focused);
155 wlr_texture_destroy(cont->title_focused_inactive); 160 wlr_texture_destroy(cont->title_focused_inactive);
156 wlr_texture_destroy(cont->title_unfocused); 161 wlr_texture_destroy(cont->title_unfocused);
157 wlr_texture_destroy(cont->title_urgent); 162 wlr_texture_destroy(cont->title_urgent);
158
159 for (int i = 0; i < server.destroying_containers->length; ++i) {
160 if (server.destroying_containers->items[i] == cont) {
161 list_del(server.destroying_containers, i);
162 break;
163 }
164 }
165
166 list_free(cont->instructions); 163 list_free(cont->instructions);
167 list_free(cont->children); 164 list_free(cont->children);
168 list_free(cont->current.children); 165 list_free(cont->current.children);
@@ -203,16 +200,23 @@ static struct sway_container *container_workspace_destroy(
203 return NULL; 200 return NULL;
204 } 201 }
205 202
206 // Do not destroy this if it's the last workspace on this output
207 struct sway_container *output = container_parent(workspace, C_OUTPUT); 203 struct sway_container *output = container_parent(workspace, C_OUTPUT);
208 if (output && output->children->length == 1) { 204
205 // If we're destroying the output, it will be NULL here. Return the root so
206 // that it doesn't appear that the workspace has refused to be destoyed,
207 // which would leave it in a broken state with no parent.
208 if (output == NULL) {
209 return &root_container;
210 }
211
212 // Do not destroy this if it's the last workspace on this output
213 if (output->children->length == 1) {
209 return NULL; 214 return NULL;
210 } 215 }
211 216
212 wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); 217 wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name);
213 218
214 struct sway_container *parent = workspace->parent; 219 if (!workspace_is_empty(workspace)) {
215 if (!workspace_is_empty(workspace) && output) {
216 // Move children to a different workspace on this output 220 // Move children to a different workspace on this output
217 struct sway_container *new_workspace = NULL; 221 struct sway_container *new_workspace = NULL;
218 for (int i = 0; i < output->children->length; i++) { 222 for (int i = 0; i < output->children->length; i++) {
@@ -222,7 +226,7 @@ static struct sway_container *container_workspace_destroy(
222 } 226 }
223 } 227 }
224 228
225 wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", 229 wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'",
226 workspace->name, new_workspace->name); 230 workspace->name, new_workspace->name);
227 for (int i = 0; i < workspace->children->length; i++) { 231 for (int i = 0; i < workspace->children->length; i++) {
228 container_move_to(workspace->children->items[i], new_workspace); 232 container_move_to(workspace->children->items[i], new_workspace);
@@ -234,7 +238,7 @@ static struct sway_container *container_workspace_destroy(
234 } 238 }
235 } 239 }
236 240
237 return parent; 241 return output;
238} 242}
239 243
240static struct sway_container *container_output_destroy( 244static struct sway_container *container_output_destroy(
@@ -288,7 +292,7 @@ static struct sway_container *container_output_destroy(
288 output->sway_output->swayc = NULL; 292 output->sway_output->swayc = NULL;
289 output->sway_output = NULL; 293 output->sway_output = NULL;
290 294
291 wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); 295 wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name);
292 296
293 return &root_container; 297 return &root_container;
294} 298}
@@ -315,13 +319,19 @@ static struct sway_container *container_destroy_noreaping(
315 // Workspaces will refuse to be destroyed if they're the last workspace 319 // Workspaces will refuse to be destroyed if they're the last workspace
316 // on their output. 320 // on their output.
317 if (!container_workspace_destroy(con)) { 321 if (!container_workspace_destroy(con)) {
318 wlr_log(L_ERROR, "workspace doesn't want to destroy"); 322 wlr_log(WLR_ERROR, "workspace doesn't want to destroy");
319 return NULL; 323 return NULL;
320 } 324 }
321 } 325 }
322 326
327 container_end_mouse_operation(con);
328
323 con->destroying = true; 329 con->destroying = true;
324 list_add(server.destroying_containers, con); 330 container_set_dirty(con);
331
332 if (con->scratchpad) {
333 scratchpad_remove_container(con);
334 }
325 335
326 if (!con->parent) { 336 if (!con->parent) {
327 return NULL; 337 return NULL;
@@ -342,7 +352,7 @@ bool container_reap_empty(struct sway_container *con) {
342 break; 352 break;
343 case C_WORKSPACE: 353 case C_WORKSPACE:
344 if (!workspace_is_visible(con) && workspace_is_empty(con)) { 354 if (!workspace_is_visible(con) && workspace_is_empty(con)) {
345 wlr_log(L_DEBUG, "Destroying workspace via reaper"); 355 wlr_log(WLR_DEBUG, "Destroying workspace via reaper");
346 container_destroy_noreaping(con); 356 container_destroy_noreaping(con);
347 return true; 357 return true;
348 } 358 }
@@ -435,7 +445,7 @@ struct sway_container *container_view_create(struct sway_container *sibling,
435 } 445 }
436 const char *title = view_get_title(sway_view); 446 const char *title = view_get_title(sway_view);
437 struct sway_container *swayc = container_create(C_VIEW); 447 struct sway_container *swayc = container_create(C_VIEW);
438 wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", 448 wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s",
439 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); 449 swayc, title, sibling, sibling ? sibling->type : 0, sibling->name);
440 // Setup values 450 // Setup values
441 swayc->sway_view = sway_view; 451 swayc->sway_view = sway_view;
@@ -657,7 +667,9 @@ struct sway_container *floating_container_at(double lx, double ly,
657 if (!workspace_is_visible(workspace)) { 667 if (!workspace_is_visible(workspace)) {
658 continue; 668 continue;
659 } 669 }
660 for (int k = 0; k < ws->floating->children->length; ++k) { 670 // Items at the end of the list are on top, so iterate the list in
671 // reverse.
672 for (int k = ws->floating->children->length - 1; k >= 0; --k) {
661 struct sway_container *floater = 673 struct sway_container *floater =
662 ws->floating->children->items[k]; 674 ws->floating->children->items[k];
663 struct wlr_box box = { 675 struct wlr_box box = {
@@ -678,16 +690,23 @@ struct sway_container *floating_container_at(double lx, double ly,
678void container_for_each_descendant_dfs(struct sway_container *container, 690void container_for_each_descendant_dfs(struct sway_container *container,
679 void (*f)(struct sway_container *container, void *data), 691 void (*f)(struct sway_container *container, void *data),
680 void *data) { 692 void *data) {
681 if (container) { 693 if (!container) {
682 if (container->children) { 694 return;
683 for (int i = 0; i < container->children->length; ++i) { 695 }
684 struct sway_container *child = 696 if (container->children) {
685 container->children->items[i]; 697 for (int i = 0; i < container->children->length; ++i) {
686 container_for_each_descendant_dfs(child, f, data); 698 struct sway_container *child = container->children->items[i];
687 } 699 container_for_each_descendant_dfs(child, f, data);
688 } 700 }
689 f(container, data);
690 } 701 }
702 if (container->type == C_WORKSPACE) {
703 struct sway_container *floating = container->sway_workspace->floating;
704 for (int i = 0; i < floating->children->length; ++i) {
705 struct sway_container *child = floating->children->items[i];
706 container_for_each_descendant_dfs(child, f, data);
707 }
708 }
709 f(container, data);
691} 710}
692 711
693void container_for_each_descendant_bfs(struct sway_container *con, 712void container_for_each_descendant_bfs(struct sway_container *con,
@@ -698,7 +717,7 @@ void container_for_each_descendant_bfs(struct sway_container *con,
698 } 717 }
699 718
700 if (queue == NULL) { 719 if (queue == NULL) {
701 wlr_log(L_ERROR, "could not allocate list"); 720 wlr_log(WLR_ERROR, "could not allocate list");
702 return; 721 return;
703 } 722 }
704 723
@@ -782,7 +801,7 @@ static void update_title_texture(struct sway_container *con,
782 801
783 double scale = output->sway_output->wlr_output->scale; 802 double scale = output->sway_output->wlr_output->scale;
784 int width = 0; 803 int width = 0;
785 int height = config->font_height * scale; 804 int height = con->title_height * scale;
786 805
787 cairo_t *c = cairo_create(NULL); 806 cairo_t *c = cairo_create(NULL);
788 get_text_size(c, config->font, &width, NULL, scale, config->pango_markup, 807 get_text_size(c, config->font, &width, NULL, scale, config->pango_markup,
@@ -935,11 +954,15 @@ void container_set_floating(struct sway_container *container, bool enable) {
935 container_add_child(workspace->sway_workspace->floating, container); 954 container_add_child(workspace->sway_workspace->floating, container);
936 if (container->type == C_VIEW) { 955 if (container->type == C_VIEW) {
937 view_init_floating(container->sway_view); 956 view_init_floating(container->sway_view);
957 view_set_tiled(container->sway_view, false);
938 } 958 }
939 seat_set_focus(seat, seat_get_focus_inactive(seat, container)); 959 seat_set_focus(seat, seat_get_focus_inactive(seat, container));
940 container_reap_empty_recursive(workspace); 960 container_reap_empty_recursive(workspace);
941 } else { 961 } else {
942 // Returning to tiled 962 // Returning to tiled
963 if (container->scratchpad) {
964 scratchpad_remove_container(container);
965 }
943 container_remove_child(container); 966 container_remove_child(container);
944 container_add_child(workspace, container); 967 container_add_child(workspace, container);
945 container->width = container->parent->width; 968 container->width = container->parent->width;
@@ -951,6 +974,8 @@ void container_set_floating(struct sway_container *container, bool enable) {
951 container_reap_empty_recursive(workspace->sway_workspace->floating); 974 container_reap_empty_recursive(workspace->sway_workspace->floating);
952 } 975 }
953 976
977 container_end_mouse_operation(container);
978
954 ipc_event_window(container, "floating"); 979 ipc_event_window(container, "floating");
955} 980}
956 981
@@ -963,9 +988,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
963 return; 988 return;
964 } 989 }
965 struct sway_view *view = con->sway_view; 990 struct sway_view *view = con->sway_view;
966 size_t border_width = view->border_thickness * (view->border != B_NONE); 991 size_t border_width = 0;
967 size_t top = 992 size_t top = 0;
968 view->border == B_NORMAL ? container_titlebar_height() : border_width; 993
994 if (!view->using_csd) {
995 border_width = view->border_thickness * (view->border != B_NONE);
996 top = view->border == B_NORMAL ?
997 container_titlebar_height() : border_width;
998 }
969 999
970 con->x = view->x - border_width; 1000 con->x = view->x - border_width;
971 con->y = view->y - top; 1001 con->y = view->y - top;
@@ -987,3 +1017,112 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) {
987 box->width = container->width; 1017 box->width = container->width;
988 box->height = container->height; 1018 box->height = container->height;
989} 1019}
1020
1021/**
1022 * Translate the container's position as well as all children.
1023 */
1024void container_floating_translate(struct sway_container *con,
1025 double x_amount, double y_amount) {
1026 con->x += x_amount;
1027 con->y += y_amount;
1028 con->current.swayc_x += x_amount;
1029 con->current.swayc_y += y_amount;
1030 if (con->type == C_VIEW) {
1031 con->sway_view->x += x_amount;
1032 con->sway_view->y += y_amount;
1033 con->current.view_x += x_amount;
1034 con->current.view_y += y_amount;
1035 } else {
1036 for (int i = 0; i < con->children->length; ++i) {
1037 struct sway_container *child = con->children->items[i];
1038 container_floating_translate(child, x_amount, y_amount);
1039 }
1040 }
1041}
1042
1043/**
1044 * Choose an output for the floating container's new position.
1045 *
1046 * If the center of the container intersects an output then we'll choose that
1047 * one, otherwise we'll choose whichever output is closest to the container's
1048 * center.
1049 */
1050static struct sway_container *container_floating_find_output(
1051 struct sway_container *con) {
1052 double center_x = con->x + con->width / 2;
1053 double center_y = con->y + con->height / 2;
1054 struct sway_container *closest_output = NULL;
1055 double closest_distance = DBL_MAX;
1056 for (int i = 0; i < root_container.children->length; ++i) {
1057 struct sway_container *output = root_container.children->items[i];
1058 struct wlr_box output_box;
1059 double closest_x, closest_y;
1060 container_get_box(output, &output_box);
1061 wlr_box_closest_point(&output_box, center_x, center_y,
1062 &closest_x, &closest_y);
1063 if (center_x == closest_x && center_y == closest_y) {
1064 // The center of the floating container is on this output
1065 return output;
1066 }
1067 double x_dist = closest_x - center_x;
1068 double y_dist = closest_y - center_y;
1069 double distance = x_dist * x_dist + y_dist * y_dist;
1070 if (distance < closest_distance) {
1071 closest_output = output;
1072 closest_distance = distance;
1073 }
1074 }
1075 return closest_output;
1076}
1077
1078void container_floating_move_to(struct sway_container *con,
1079 double lx, double ly) {
1080 if (!sway_assert(container_is_floating(con),
1081 "Expected a floating container")) {
1082 return;
1083 }
1084 desktop_damage_whole_container(con);
1085 container_floating_translate(con, lx - con->x, ly - con->y);
1086 desktop_damage_whole_container(con);
1087 struct sway_container *old_workspace = container_parent(con, C_WORKSPACE);
1088 struct sway_container *new_output = container_floating_find_output(con);
1089 if (!sway_assert(new_output, "Unable to find any output")) {
1090 return;
1091 }
1092 struct sway_container *new_workspace =
1093 output_get_active_workspace(new_output->sway_output);
1094 if (old_workspace != new_workspace) {
1095 container_remove_child(con);
1096 container_add_child(new_workspace->sway_workspace->floating, con);
1097 arrange_windows(old_workspace);
1098 arrange_windows(new_workspace);
1099 workspace_detect_urgent(old_workspace);
1100 workspace_detect_urgent(new_workspace);
1101 }
1102}
1103
1104void container_set_dirty(struct sway_container *container) {
1105 if (container->dirty) {
1106 return;
1107 }
1108 container->dirty = true;
1109 list_add(server.dirty_containers, container);
1110}
1111
1112static bool find_urgent_iterator(struct sway_container *con,
1113 void *data) {
1114 return con->type == C_VIEW && view_is_urgent(con->sway_view);
1115}
1116
1117bool container_has_urgent_child(struct sway_container *container) {
1118 return container_find(container, find_urgent_iterator, NULL);
1119}
1120
1121void container_end_mouse_operation(struct sway_container *container) {
1122 struct sway_seat *seat;
1123 wl_list_for_each(seat, &input_manager->seats, link) {
1124 if (seat->op_container == container) {
1125 seat_end_mouse_operation(seat);
1126 }
1127 }
1128}