aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree/workspace.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree/workspace.c')
-rw-r--r--sway/tree/workspace.c175
1 files changed, 93 insertions, 82 deletions
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 921b7d19..a68dc927 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -1,4 +1,3 @@
1#define _POSIX_C_SOURCE 200809
2#include <ctype.h> 1#include <ctype.h>
3#include <limits.h> 2#include <limits.h>
4#include <stdbool.h> 3#include <stdbool.h>
@@ -48,14 +47,16 @@ struct sway_output *workspace_get_initial_output(const char *name) {
48 if (focus && focus->type == N_WORKSPACE) { 47 if (focus && focus->type == N_WORKSPACE) {
49 return focus->sway_workspace->output; 48 return focus->sway_workspace->output;
50 } else if (focus && focus->type == N_CONTAINER) { 49 } else if (focus && focus->type == N_CONTAINER) {
51 return focus->sway_container->workspace->output; 50 return focus->sway_container->pending.workspace->output;
52 } 51 }
53 // Fallback to the first output or noop output for headless 52 // Fallback to the first output or the headless output
54 return root->outputs->length ? root->outputs->items[0] : root->noop_output; 53 return root->outputs->length ? root->outputs->items[0] : root->fallback_output;
55} 54}
56 55
57struct sway_workspace *workspace_create(struct sway_output *output, 56struct sway_workspace *workspace_create(struct sway_output *output,
58 const char *name) { 57 const char *name) {
58 sway_assert(name, "NULL name given to workspace_create");
59
59 if (output == NULL) { 60 if (output == NULL) {
60 output = workspace_get_initial_output(name); 61 output = workspace_get_initial_output(name);
61 } 62 }
@@ -69,7 +70,19 @@ struct sway_workspace *workspace_create(struct sway_output *output,
69 return NULL; 70 return NULL;
70 } 71 }
71 node_init(&ws->node, N_WORKSPACE, ws); 72 node_init(&ws->node, N_WORKSPACE, ws);
72 ws->name = name ? strdup(name) : NULL; 73
74 bool failed = false;
75 ws->layers.tiling = alloc_scene_tree(root->staging, &failed);
76 ws->layers.fullscreen = alloc_scene_tree(root->staging, &failed);
77
78 if (failed) {
79 wlr_scene_node_destroy(&ws->layers.tiling->node);
80 wlr_scene_node_destroy(&ws->layers.fullscreen->node);
81 free(ws);
82 return NULL;
83 }
84
85 ws->name = strdup(name);
73 ws->prev_split_layout = L_NONE; 86 ws->prev_split_layout = L_NONE;
74 ws->layout = output_get_default_layout(output); 87 ws->layout = output_get_default_layout(output);
75 ws->floating = create_list(); 88 ws->floating = create_list();
@@ -114,7 +127,7 @@ struct sway_workspace *workspace_create(struct sway_output *output,
114 output_sort_workspaces(output); 127 output_sort_workspaces(output);
115 128
116 ipc_event_workspace(NULL, ws, "init"); 129 ipc_event_workspace(NULL, ws, "init");
117 wl_signal_emit(&root->events.new_node, &ws->node); 130 wl_signal_emit_mutable(&root->events.new_node, &ws->node);
118 131
119 return ws; 132 return ws;
120} 133}
@@ -129,6 +142,11 @@ void workspace_destroy(struct sway_workspace *workspace) {
129 return; 142 return;
130 } 143 }
131 144
145 scene_node_disown_children(workspace->layers.tiling);
146 scene_node_disown_children(workspace->layers.fullscreen);
147 wlr_scene_node_destroy(&workspace->layers.tiling->node);
148 wlr_scene_node_destroy(&workspace->layers.fullscreen->node);
149
132 free(workspace->name); 150 free(workspace->name);
133 free(workspace->representation); 151 free(workspace->representation);
134 list_free_items_and_destroy(workspace->output_priority); 152 list_free_items_and_destroy(workspace->output_priority);
@@ -142,7 +160,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
142void workspace_begin_destroy(struct sway_workspace *workspace) { 160void workspace_begin_destroy(struct sway_workspace *workspace) {
143 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); 161 sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name);
144 ipc_event_workspace(NULL, workspace, "empty"); // intentional 162 ipc_event_workspace(NULL, workspace, "empty"); // intentional
145 wl_signal_emit(&workspace->node.events.destroy, &workspace->node); 163 wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node);
146 164
147 if (workspace->output) { 165 if (workspace->output) {
148 workspace_detach(workspace); 166 workspace_detach(workspace);
@@ -174,22 +192,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) {
174static bool workspace_valid_on_output(const char *output_name, 192static bool workspace_valid_on_output(const char *output_name,
175 const char *ws_name) { 193 const char *ws_name) {
176 struct workspace_config *wsc = workspace_find_config(ws_name); 194 struct workspace_config *wsc = workspace_find_config(ws_name);
177 char identifier[128];
178 struct sway_output *output = output_by_name_or_id(output_name); 195 struct sway_output *output = output_by_name_or_id(output_name);
179 if (!output) { 196 if (!output) {
180 return false; 197 return false;
181 } 198 }
182 output_name = output->wlr_output->name;
183 output_get_identifier(identifier, sizeof(identifier), output);
184
185 if (!wsc) { 199 if (!wsc) {
186 return true; 200 return true;
187 } 201 }
188 202
189 for (int i = 0; i < wsc->outputs->length; i++) { 203 for (int i = 0; i < wsc->outputs->length; i++) {
190 if (strcmp(wsc->outputs->items[i], "*") == 0 || 204 if (output_match_name_or_id(output, wsc->outputs->items[i])) {
191 strcmp(wsc->outputs->items[i], output_name) == 0 ||
192 strcmp(wsc->outputs->items[i], identifier) == 0) {
193 return true; 205 return true;
194 } 206 }
195 } 207 }
@@ -222,10 +234,8 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
222 // not a command about workspaces 234 // not a command about workspaces
223 if (strcmp(_target, "next") == 0 || 235 if (strcmp(_target, "next") == 0 ||
224 strcmp(_target, "prev") == 0 || 236 strcmp(_target, "prev") == 0 ||
225 strncmp(_target, "next_on_output", 237 strcmp(_target, "next_on_output") == 0 ||
226 strlen("next_on_output")) == 0 || 238 strcmp(_target, "prev_on_output") == 0 ||
227 strncmp(_target, "prev_on_output",
228 strlen("next_on_output")) == 0 ||
229 strcmp(_target, "number") == 0 || 239 strcmp(_target, "number") == 0 ||
230 strcmp(_target, "back_and_forth") == 0 || 240 strcmp(_target, "back_and_forth") == 0 ||
231 strcmp(_target, "current") == 0) { 241 strcmp(_target, "current") == 0) {
@@ -286,13 +296,10 @@ char *workspace_next_name(const char *output_name) {
286 // assignments primarily, falling back to bindings and numbers. 296 // assignments primarily, falling back to bindings and numbers.
287 struct sway_mode *mode = config->current_mode; 297 struct sway_mode *mode = config->current_mode;
288 298
289 char identifier[128];
290 struct sway_output *output = output_by_name_or_id(output_name); 299 struct sway_output *output = output_by_name_or_id(output_name);
291 if (!output) { 300 if (!output) {
292 return NULL; 301 return NULL;
293 } 302 }
294 output_name = output->wlr_output->name;
295 output_get_identifier(identifier, sizeof(identifier), output);
296 303
297 int order = INT_MAX; 304 int order = INT_MAX;
298 char *target = NULL; 305 char *target = NULL;
@@ -312,9 +319,7 @@ char *workspace_next_name(const char *output_name) {
312 } 319 }
313 bool found = false; 320 bool found = false;
314 for (int j = 0; j < wsc->outputs->length; ++j) { 321 for (int j = 0; j < wsc->outputs->length; ++j) {
315 if (strcmp(wsc->outputs->items[j], "*") == 0 || 322 if (output_match_name_or_id(output, wsc->outputs->items[j])) {
316 strcmp(wsc->outputs->items[j], output_name) == 0 ||
317 strcmp(wsc->outputs->items[j], identifier) == 0) {
318 found = true; 323 found = true;
319 free(target); 324 free(target);
320 target = strdup(wsc->workspace); 325 target = strdup(wsc->workspace);
@@ -363,11 +368,11 @@ struct sway_workspace *workspace_by_name(const char *name) {
363 if (current && strcmp(name, "prev") == 0) { 368 if (current && strcmp(name, "prev") == 0) {
364 return workspace_prev(current); 369 return workspace_prev(current);
365 } else if (current && strcmp(name, "prev_on_output") == 0) { 370 } else if (current && strcmp(name, "prev_on_output") == 0) {
366 return workspace_output_prev(current, false); 371 return workspace_output_prev(current);
367 } else if (current && strcmp(name, "next") == 0) { 372 } else if (current && strcmp(name, "next") == 0) {
368 return workspace_next(current); 373 return workspace_next(current);
369 } else if (current && strcmp(name, "next_on_output") == 0) { 374 } else if (current && strcmp(name, "next_on_output") == 0) {
370 return workspace_output_next(current, false); 375 return workspace_output_next(current);
371 } else if (strcmp(name, "current") == 0) { 376 } else if (strcmp(name, "current") == 0) {
372 return current; 377 return current;
373 } else if (strcasecmp(name, "back_and_forth") == 0) { 378 } else if (strcasecmp(name, "back_and_forth") == 0) {
@@ -530,7 +535,7 @@ struct sway_workspace *workspace_next(struct sway_workspace *workspace) {
530 * otherwise the next one is returned. 535 * otherwise the next one is returned.
531 */ 536 */
532static struct sway_workspace *workspace_output_prev_next_impl( 537static struct sway_workspace *workspace_output_prev_next_impl(
533 struct sway_output *output, int dir, bool create) { 538 struct sway_output *output, int dir) {
534 struct sway_seat *seat = input_manager_current_seat(); 539 struct sway_seat *seat = input_manager_current_seat();
535 struct sway_workspace *workspace = seat_get_focused_workspace(seat); 540 struct sway_workspace *workspace = seat_get_focused_workspace(seat);
536 if (!workspace) { 541 if (!workspace) {
@@ -540,46 +545,43 @@ static struct sway_workspace *workspace_output_prev_next_impl(
540 } 545 }
541 546
542 int index = list_find(output->workspaces, workspace); 547 int index = list_find(output->workspaces, workspace);
543 if (!workspace_is_empty(workspace) && create &&
544 (index + dir < 0 || index + dir == output->workspaces->length)) {
545 struct sway_output *output = workspace->output;
546 char *next = workspace_next_name(output->wlr_output->name);
547 workspace_create(output, next);
548 free(next);
549 }
550 size_t new_index = wrap(index + dir, output->workspaces->length); 548 size_t new_index = wrap(index + dir, output->workspaces->length);
551 return output->workspaces->items[new_index]; 549 return output->workspaces->items[new_index];
552} 550}
553 551
554struct sway_workspace *workspace_output_next( 552
555 struct sway_workspace *current, bool create) { 553struct sway_workspace *workspace_output_next(struct sway_workspace *current) {
556 return workspace_output_prev_next_impl(current->output, 1, create); 554 return workspace_output_prev_next_impl(current->output, 1);
557} 555}
558 556
559struct sway_workspace *workspace_output_prev( 557struct sway_workspace *workspace_output_prev(struct sway_workspace *current) {
560 struct sway_workspace *current, bool create) { 558 return workspace_output_prev_next_impl(current->output, -1);
561 return workspace_output_prev_next_impl(current->output, -1, create);
562} 559}
563 560
564bool workspace_switch(struct sway_workspace *workspace, 561struct sway_workspace *workspace_auto_back_and_forth(
565 bool no_auto_back_and_forth) { 562 struct sway_workspace *workspace) {
566 struct sway_seat *seat = input_manager_current_seat(); 563 struct sway_seat *seat = input_manager_current_seat();
567 struct sway_workspace *active_ws = NULL; 564 struct sway_workspace *active_ws = NULL;
568 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); 565 struct sway_node *focus = seat_get_focus_inactive(seat, &root->node);
569 if (focus && focus->type == N_WORKSPACE) { 566 if (focus && focus->type == N_WORKSPACE) {
570 active_ws = focus->sway_workspace; 567 active_ws = focus->sway_workspace;
571 } else if (focus && focus->type == N_CONTAINER) { 568 } else if (focus && focus->type == N_CONTAINER) {
572 active_ws = focus->sway_container->workspace; 569 active_ws = focus->sway_container->pending.workspace;
573 } 570 }
574 571
575 if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws 572 if (config->auto_back_and_forth && active_ws && active_ws == workspace &&
576 && active_ws == workspace && seat->prev_workspace_name) { 573 seat->prev_workspace_name) {
577 struct sway_workspace *new_ws = 574 struct sway_workspace *new_ws =
578 workspace_by_name(seat->prev_workspace_name); 575 workspace_by_name(seat->prev_workspace_name);
579 workspace = new_ws ? 576 workspace = new_ws ?
580 new_ws : 577 new_ws :
581 workspace_create(NULL, seat->prev_workspace_name); 578 workspace_create(NULL, seat->prev_workspace_name);
582 } 579 }
580 return workspace;
581}
582
583bool workspace_switch(struct sway_workspace *workspace) {
584 struct sway_seat *seat = input_manager_current_seat();
583 585
584 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", 586 sway_log(SWAY_DEBUG, "Switching to workspace %p:%s",
585 workspace, workspace->name); 587 workspace, workspace->name);
@@ -657,15 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace,
657 659
658struct sway_output *workspace_output_get_highest_available( 660struct sway_output *workspace_output_get_highest_available(
659 struct sway_workspace *ws, struct sway_output *exclude) { 661 struct sway_workspace *ws, struct sway_output *exclude) {
660 char exclude_id[128] = {'\0'};
661 if (exclude) {
662 output_get_identifier(exclude_id, sizeof(exclude_id), exclude);
663 }
664
665 for (int i = 0; i < ws->output_priority->length; i++) { 662 for (int i = 0; i < ws->output_priority->length; i++) {
666 char *name = ws->output_priority->items[i]; 663 const char *name = ws->output_priority->items[i];
667 if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 664 if (exclude && output_match_name_or_id(exclude, name)) {
668 || strcmp(name, exclude_id) == 0)) {
669 continue; 665 continue;
670 } 666 }
671 667
@@ -689,7 +685,6 @@ void workspace_detect_urgent(struct sway_workspace *workspace) {
689 if (workspace->urgent != new_urgent) { 685 if (workspace->urgent != new_urgent) {
690 workspace->urgent = new_urgent; 686 workspace->urgent = new_urgent;
691 ipc_event_workspace(NULL, workspace, "urgent"); 687 ipc_event_workspace(NULL, workspace, "urgent");
692 output_damage_whole(workspace->output);
693 } 688 }
694} 689}
695 690
@@ -736,13 +731,13 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws,
736} 731}
737 732
738static void set_workspace(struct sway_container *container, void *data) { 733static void set_workspace(struct sway_container *container, void *data) {
739 container->workspace = container->parent->workspace; 734 container->pending.workspace = container->pending.parent->pending.workspace;
740} 735}
741 736
742static void workspace_attach_tiling(struct sway_workspace *ws, 737static void workspace_attach_tiling(struct sway_workspace *ws,
743 struct sway_container *con) { 738 struct sway_container *con) {
744 list_add(ws->tiling, con); 739 list_add(ws->tiling, con);
745 con->workspace = ws; 740 con->pending.workspace = ws;
746 container_for_each_child(con, set_workspace, NULL); 741 container_for_each_child(con, set_workspace, NULL);
747 container_handle_fullscreen_reparent(con); 742 container_handle_fullscreen_reparent(con);
748 workspace_update_representation(ws); 743 workspace_update_representation(ws);
@@ -753,7 +748,7 @@ static void workspace_attach_tiling(struct sway_workspace *ws,
753struct sway_container *workspace_wrap_children(struct sway_workspace *ws) { 748struct sway_container *workspace_wrap_children(struct sway_workspace *ws) {
754 struct sway_container *fs = ws->fullscreen; 749 struct sway_container *fs = ws->fullscreen;
755 struct sway_container *middle = container_create(NULL); 750 struct sway_container *middle = container_create(NULL);
756 middle->layout = ws->layout; 751 middle->pending.layout = ws->layout;
757 while (ws->tiling->length) { 752 while (ws->tiling->length) {
758 struct sway_container *child = ws->tiling->items[0]; 753 struct sway_container *child = ws->tiling->items[0];
759 container_detach(child); 754 container_detach(child);
@@ -771,9 +766,9 @@ void workspace_unwrap_children(struct sway_workspace *ws,
771 return; 766 return;
772 } 767 }
773 768
774 ws->layout = wrap->layout; 769 ws->layout = wrap->pending.layout;
775 while (wrap->children->length) { 770 while (wrap->pending.children->length) {
776 struct sway_container *child = wrap->children->items[0]; 771 struct sway_container *child = wrap->pending.children->items[0];
777 container_detach(child); 772 container_detach(child);
778 workspace_add_tiling(ws, child); 773 workspace_add_tiling(ws, child);
779 } 774 }
@@ -793,14 +788,18 @@ void workspace_detach(struct sway_workspace *workspace) {
793 788
794struct sway_container *workspace_add_tiling(struct sway_workspace *workspace, 789struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
795 struct sway_container *con) { 790 struct sway_container *con) {
796 if (con->workspace) { 791 if (con->pending.workspace) {
792 struct sway_container *old_parent = con->pending.parent;
797 container_detach(con); 793 container_detach(con);
794 if (old_parent) {
795 container_reap_empty(old_parent);
796 }
798 } 797 }
799 if (config->default_layout != L_NONE) { 798 if (config->default_layout != L_NONE) {
800 con = container_split(con, config->default_layout); 799 con = container_split(con, config->default_layout);
801 } 800 }
802 list_add(workspace->tiling, con); 801 list_add(workspace->tiling, con);
803 con->workspace = workspace; 802 con->pending.workspace = workspace;
804 container_for_each_child(con, set_workspace, NULL); 803 container_for_each_child(con, set_workspace, NULL);
805 container_handle_fullscreen_reparent(con); 804 container_handle_fullscreen_reparent(con);
806 workspace_update_representation(workspace); 805 workspace_update_representation(workspace);
@@ -811,11 +810,11 @@ struct sway_container *workspace_add_tiling(struct sway_workspace *workspace,
811 810
812void workspace_add_floating(struct sway_workspace *workspace, 811void workspace_add_floating(struct sway_workspace *workspace,
813 struct sway_container *con) { 812 struct sway_container *con) {
814 if (con->workspace) { 813 if (con->pending.workspace) {
815 container_detach(con); 814 container_detach(con);
816 } 815 }
817 list_add(workspace->floating, con); 816 list_add(workspace->floating, con);
818 con->workspace = workspace; 817 con->pending.workspace = workspace;
819 container_for_each_child(con, set_workspace, NULL); 818 container_for_each_child(con, set_workspace, NULL);
820 container_handle_fullscreen_reparent(con); 819 container_handle_fullscreen_reparent(con);
821 node_set_dirty(&workspace->node); 820 node_set_dirty(&workspace->node);
@@ -825,7 +824,7 @@ void workspace_add_floating(struct sway_workspace *workspace,
825void workspace_insert_tiling_direct(struct sway_workspace *workspace, 824void workspace_insert_tiling_direct(struct sway_workspace *workspace,
826 struct sway_container *con, int index) { 825 struct sway_container *con, int index) {
827 list_insert(workspace->tiling, index, con); 826 list_insert(workspace->tiling, index, con);
828 con->workspace = workspace; 827 con->pending.workspace = workspace;
829 container_for_each_child(con, set_workspace, NULL); 828 container_for_each_child(con, set_workspace, NULL);
830 container_handle_fullscreen_reparent(con); 829 container_handle_fullscreen_reparent(con);
831 workspace_update_representation(workspace); 830 workspace_update_representation(workspace);
@@ -835,7 +834,7 @@ void workspace_insert_tiling_direct(struct sway_workspace *workspace,
835 834
836struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace, 835struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
837 struct sway_container *con, int index) { 836 struct sway_container *con, int index) {
838 if (con->workspace) { 837 if (con->pending.workspace) {
839 container_detach(con); 838 container_detach(con);
840 } 839 }
841 if (config->default_layout != L_NONE) { 840 if (config->default_layout != L_NONE) {
@@ -845,24 +844,36 @@ struct sway_container *workspace_insert_tiling(struct sway_workspace *workspace,
845 return con; 844 return con;
846} 845}
847 846
847bool workspace_has_single_visible_container(struct sway_workspace *ws) {
848 struct sway_seat *seat = input_manager_get_default_seat();
849 struct sway_container *focus =
850 seat_get_focus_inactive_tiling(seat, ws);
851 if (focus && !focus->view) {
852 focus = seat_get_focus_inactive_view(seat, &focus->node);
853 }
854 return (focus && focus->view && view_ancestor_is_only_visible(focus->view));
855}
856
848void workspace_add_gaps(struct sway_workspace *ws) { 857void workspace_add_gaps(struct sway_workspace *ws) {
849 if (config->smart_gaps) { 858 if (config->smart_gaps == SMART_GAPS_ON
850 struct sway_seat *seat = input_manager_get_default_seat(); 859 && workspace_has_single_visible_container(ws)) {
851 struct sway_container *focus = 860 ws->current_gaps.top = 0;
852 seat_get_focus_inactive_tiling(seat, ws); 861 ws->current_gaps.right = 0;
853 if (focus && !focus->view) { 862 ws->current_gaps.bottom = 0;
854 focus = seat_get_focus_inactive_view(seat, &focus->node); 863 ws->current_gaps.left = 0;
855 } 864 return;
856 if (focus && focus->view && view_ancestor_is_only_visible(focus->view)) { 865 }
857 ws->current_gaps.top = 0; 866
858 ws->current_gaps.right = 0; 867 if (config->smart_gaps == SMART_GAPS_INVERSE_OUTER
859 ws->current_gaps.bottom = 0; 868 && !workspace_has_single_visible_container(ws)) {
860 ws->current_gaps.left = 0; 869 ws->current_gaps.top = 0;
861 return; 870 ws->current_gaps.right = 0;
862 } 871 ws->current_gaps.bottom = 0;
872 ws->current_gaps.left = 0;
873 } else {
874 ws->current_gaps = ws->gaps_outer;
863 } 875 }
864 876
865 ws->current_gaps = ws->gaps_outer;
866 // Add inner gaps and make sure we don't turn out negative 877 // Add inner gaps and make sure we don't turn out negative
867 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner); 878 ws->current_gaps.top = fmax(0, ws->current_gaps.top + ws->gaps_inner);
868 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner); 879 ws->current_gaps.right = fmax(0, ws->current_gaps.right + ws->gaps_inner);
@@ -905,7 +916,7 @@ struct sway_container *workspace_split(struct sway_workspace *workspace,
905 enum sway_container_layout old_layout = workspace->layout; 916 enum sway_container_layout old_layout = workspace->layout;
906 struct sway_container *middle = workspace_wrap_children(workspace); 917 struct sway_container *middle = workspace_wrap_children(workspace);
907 workspace->layout = layout; 918 workspace->layout = layout;
908 middle->layout = old_layout; 919 middle->pending.layout = old_layout;
909 920
910 struct sway_seat *seat; 921 struct sway_seat *seat;
911 wl_list_for_each(seat, &server.input->seats, link) { 922 wl_list_for_each(seat, &server.input->seats, link) {