diff options
Diffstat (limited to 'sway/tree/container.c')
-rw-r--r-- | sway/tree/container.c | 209 |
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 | ||
240 | static struct sway_container *container_output_destroy( | 244 | static 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, | |||
678 | void container_for_each_descendant_dfs(struct sway_container *container, | 690 | void 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 | ||
693 | void container_for_each_descendant_bfs(struct sway_container *con, | 712 | void 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 | */ | ||
1024 | void 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 | */ | ||
1050 | static 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 | |||
1078 | void 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 | |||
1104 | void 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 | |||
1112 | static 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 | |||
1117 | bool container_has_urgent_child(struct sway_container *container) { | ||
1118 | return container_find(container, find_urgent_iterator, NULL); | ||
1119 | } | ||
1120 | |||
1121 | void 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 | } | ||