From 7586f150c058997d9dde387ea7c091ffa7a3c3c7 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Thu, 30 Aug 2018 21:00:10 +1000 Subject: Implement type safe arguments and demote sway_container This commit changes the meaning of sway_container so that it only refers to layout containers and view containers. Workspaces, outputs and the root are no longer known as containers. Instead, root, outputs, workspaces and containers are all a type of node, and containers come in two types: layout containers and view containers. In addition to the above, this implements type safe variables. This means we use specific types such as sway_output and sway_workspace instead of generic containers or nodes. However, it's worth noting that in a few places places (eg. seat focus and transactions) referring to them in a generic way is unavoidable which is why we still use nodes in some places. If you want a TL;DR, look at node.h, as well as the struct definitions for root, output, workspace and container. Note that sway_output now contains a workspaces list, and workspaces now contain a tiling and floating list, and containers now contain a pointer back to the workspace. There are now functions for seat_get_focused_workspace and seat_get_focused_container. The latter will return NULL if a workspace itself is focused. Most other seat functions like seat_get_focus and seat_set_focus now accept and return nodes. In the config->handler_context struct, current_container has been replaced with three pointers: node, container and workspace. node is the same as what current_container was, while workspace is the workspace that the node resides on and container is the actual container, which may be NULL if a workspace itself is focused. The global root_container variable has been replaced with one simply called root, which is a pointer to the sway_root instance. The way outputs are created, enabled, disabled and destroyed has changed. Previously we'd wrap the sway_output in a container when it is enabled, but as we don't have containers any more it needs a different approach. The output_create and output_destroy functions previously created/destroyed the container, but now they create/destroy the sway_output. There is a new function output_disable to disable an output without destroying it. Containers have a new view property. If this is populated then the container is a view container, otherwise it's a layout container. Like before, this property is immutable for the life of the container. Containers have both a `sway_container *parent` and `sway_workspace *workspace`. As we use specific types now, parent cannot point to a workspace so it'll be NULL for containers which are direct children of the workspace. The workspace property is set for all containers, except those which are hidden in the scratchpad as they have no workspace. In some cases we need to refer to workspaces in a container-like way. For example, workspaces have layout and children, but when using specific types this makes it difficult. Likewise, it's difficult for a container to get its parent's layout when the parent could be another container or a workspace. To make it easier, some helper functions have been created: container_parent_layout and container_get_siblings. container_remove_child has been renamed to container_detach and container_replace_child has been renamed to container_replace. `container_handle_fullscreen_reparent(con, old_parent)` has had the old_parent removed. We now unfullscreen the workspace when detaching the container, so this function is simplified and only needs one argument now. container_notify_subtree_changed has been renamed to container_update_representation. This is more descriptive of its purpose. I also wanted to be able to call it with whatever container was changed rather than the container's parent, which makes bubbling up to the workspace easier. There are now state structs per node thing. ie. sway_output_state, sway_workspace_state and sway_container_state. The focus, move and layout commands have been completely refactored to work with the specific types. I considered making these a separate PR, but I'd be backporting my changes only to replace them again, and it's easier just to test everything at once. --- sway/tree/view.c | 277 ++++++++++++++++++++++++------------------------------- 1 file changed, 121 insertions(+), 156 deletions(-) (limited to 'sway/tree/view.c') diff --git a/sway/tree/view.c b/sway/tree/view.c index 6bd0ef67..452c2bd1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -33,6 +33,8 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->marks = create_list(); view->allow_request_urgent = true; wl_signal_init(&view->events.unmap); + + view->container = container_create(view); } void view_destroy(struct sway_view *view) { @@ -43,8 +45,8 @@ void view_destroy(struct sway_view *view) { "Tried to free view which wasn't marked as destroying")) { return; } - if (!sway_assert(view->swayc == NULL, - "Tried to free view which still has a swayc " + if (!sway_assert(view->container == NULL, + "Tried to free view which still has a container " "(might have a pending transaction?)")) { return; } @@ -57,6 +59,7 @@ void view_destroy(struct sway_view *view) { wlr_texture_destroy(view->marks_focused_inactive); wlr_texture_destroy(view->marks_unfocused); wlr_texture_destroy(view->marks_urgent); + free(view->title_format); if (view->impl->destroy) { view->impl->destroy(view); @@ -65,23 +68,13 @@ void view_destroy(struct sway_view *view) { } } -/** - * The view may or may not be involved in a transaction. For example, a view may - * unmap then attempt to destroy itself before we've applied the new layout. If - * an unmapping view is still involved in a transaction then it'll still have a - * swayc. - * - * If there's no transaction we can simply free the view. Otherwise the - * destroying flag will make the view get freed when the transaction is - * finished. - */ void view_begin_destroy(struct sway_view *view) { if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { return; } view->destroying = true; - if (!view->swayc) { + if (!view->container) { view_destroy(view); } } @@ -171,30 +164,27 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, } void view_autoconfigure(struct sway_view *view) { - if (!sway_assert(view->swayc, - "Called view_autoconfigure() on a view without a swayc")) { - return; - } - - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); + struct sway_output *output = view->container->workspace->output; - if (view->swayc->is_fullscreen) { - view->x = output->x; - view->y = output->y; - view->width = output->width; - view->height = output->height; + if (view->container->is_fullscreen) { + view->x = output->wlr_output->lx; + view->y = output->wlr_output->ly; + view->width = output->wlr_output->width; + view->height = output->wlr_output->height; return; } - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_workspace *ws = view->container->workspace; - int other_views = 0; + bool other_views = false; if (config->hide_edge_borders == E_SMART) { - struct sway_container *con = view->swayc; - while (con != output) { - if (con->layout != L_TABBED && con->layout != L_STACKED) { - other_views += con->children ? con->children->length - 1 : 0; - if (other_views > 0) { + struct sway_container *con = view->container; + while (con) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout != L_TABBED && layout != L_STACKED) { + list_t *siblings = container_get_siblings(con); + if (siblings && siblings->length > 1) { + other_views = true; break; } } @@ -202,7 +192,7 @@ void view_autoconfigure(struct sway_view *view) { } } - struct sway_container *con = view->swayc; + struct sway_container *con = view->container; view->border_top = view->border_bottom = true; view->border_left = view->border_right = true; @@ -228,7 +218,8 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the swayc's y is the bottom of the // title area. We have to disable any top border because the title bar is // rendered by the parent. - if (con->parent->layout == L_TABBED || con->parent->layout == L_STACKED) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout == L_TABBED || layout == L_STACKED) { view->border_top = false; } else { y_offset = container_titlebar_height(); @@ -281,13 +272,16 @@ void view_set_activated(struct sway_view *view, bool activated) { } void view_request_activate(struct sway_view *view) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_workspace *ws = view->container->workspace; + if (!ws) { // hidden scratchpad container + return; + } struct sway_seat *seat = input_manager_current_seat(input_manager); switch (config->focus_on_window_activation) { case FOWA_SMART: if (workspace_is_visible(ws)) { - seat_set_focus(seat, view->swayc); + seat_set_focus(seat, &view->container->node); } else { view_set_urgent(view, true); } @@ -296,7 +290,7 @@ void view_request_activate(struct sway_view *view) { view_set_urgent(view, true); break; case FOWA_FOCUS: - seat_set_focus(seat, view->swayc); + seat_set_focus(seat, &view->container->node); break; case FOWA_NONE: break; @@ -331,23 +325,12 @@ void view_close_popups(struct sway_view *view) { } void view_damage_from(struct sway_view *view) { - for (int i = 0; i < root_container.children->length; ++i) { - struct sway_container *cont = root_container.children->items[i]; - if (cont->type == C_OUTPUT) { - output_damage_from_view(cont->sway_output, view); - } + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + output_damage_from_view(output, view); } } -static void view_get_layout_box(struct sway_view *view, struct wlr_box *box) { - struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - - box->x = output->x + view->swayc->x; - box->y = output->y + view->swayc->y; - box->width = view->width; - box->height = view->height; -} - void view_for_each_surface(struct sway_view *view, wlr_surface_iterator_func_t iterator, void *user_data) { if (!view->surface) { @@ -396,11 +379,8 @@ static bool view_has_executed_criteria(struct sway_view *view, } void view_execute_criteria(struct sway_view *view) { - if (!view->swayc) { - return; - } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *prior_focus = seat_get_focus(seat); + struct sway_node *prior_focus = seat_get_focus(seat); list_t *criterias = criteria_for_view(view, CT_COMMAND); for (int i = 0; i < criterias->length; i++) { struct criteria *criteria = criterias->items[i]; @@ -411,7 +391,7 @@ void view_execute_criteria(struct sway_view *view) { } wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", criteria->raw, view, criteria->cmdlist); - seat_set_focus(seat, view->swayc); + seat_set_focus(seat, &view->container->node); list_add(view->executed_criteria, criteria); struct cmd_results *res = execute_command(criteria->cmdlist, NULL); if (res->status != CMD_SUCCESS) { @@ -423,19 +403,19 @@ void view_execute_criteria(struct sway_view *view) { seat_set_focus(seat, prior_focus); } -static struct sway_container *select_workspace(struct sway_view *view) { +static struct sway_workspace *select_workspace(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(input_manager); // Check if there's any `assign` criteria for the view list_t *criterias = criteria_for_view(view, CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT); - struct sway_container *ws = NULL; + struct sway_workspace *ws = NULL; for (int i = 0; i < criterias->length; ++i) { struct criteria *criteria = criterias->items[i]; if (criteria->type == CT_ASSIGN_OUTPUT) { - struct sway_container *output = output_by_name(criteria->target); + struct sway_output *output = output_by_name(criteria->target); if (output) { - ws = seat_get_active_child(seat, output); + ws = output_get_active_workspace(output); break; } } else { @@ -484,20 +464,14 @@ static struct sway_container *select_workspace(struct sway_view *view) { } // Use the focused workspace - ws = seat_get_focus_inactive(seat, &root_container); - if (ws->type != C_WORKSPACE) { - ws = container_parent(ws, C_WORKSPACE); - } - return ws; + return seat_get_focused_workspace(seat); } static bool should_focus(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *prev_focus = - seat_get_focus_inactive(seat, &root_container); - struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? - prev_focus : container_parent(prev_focus, C_WORKSPACE); - struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_container *prev_con = seat_get_focused_container(seat); + struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); + struct sway_workspace *map_ws = view->container->workspace; // Views can only take focus if they are mapped into the active workspace if (prev_ws != map_ws) { @@ -506,10 +480,9 @@ static bool should_focus(struct sway_view *view) { // If the view is the only one in the focused workspace, it'll get focus // regardless of any no_focus criteria. - struct sway_container *parent = view->swayc->parent; - if (parent->type == C_WORKSPACE && prev_focus == parent) { - size_t num_children = parent->children->length + - parent->sway_workspace->floating->length; + if (!view->container->parent && !prev_con) { + size_t num_children = view->container->workspace->tiling->length + + view->container->workspace->floating->length; if (num_children == 1) { return true; } @@ -529,16 +502,24 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->surface = wlr_surface; struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *ws = select_workspace(view); - struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); + struct sway_workspace *ws = select_workspace(view); + struct sway_node *node = seat_get_focus_inactive(seat, &ws->node); + struct sway_container *target_sibling = node->type == N_CONTAINER ? + node->sway_container : NULL; // If we're about to launch the view into the floating container, then // launch it as a tiled view in the root of the workspace instead. - if (container_is_floating(target_sibling)) { - target_sibling = target_sibling->parent; + if (target_sibling && container_is_floating(target_sibling)) { + target_sibling = NULL; } - view->swayc = container_view_create(target_sibling, view); + view->container = container_create(view); + if (target_sibling) { + container_add_sibling(target_sibling, view->container, 1); + } else { + workspace_add_tiling(ws, view->container); + } + ipc_event_window(view->container, "new"); view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -548,7 +529,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (view->impl->wants_floating && view->impl->wants_floating(view)) { view->border = config->floating_border; view->border_thickness = config->floating_border_thickness; - container_set_floating(view->swayc, true); + container_set_floating(view->container, true); } else { view->border = config->border; view->border_thickness = config->border_thickness; @@ -556,11 +537,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { } if (should_focus(view)) { - input_manager_set_focus(input_manager, view->swayc); + input_manager_set_focus(input_manager, &view->container->node); } view_update_title(view, false); - container_notify_subtree_changed(view->swayc->parent); + container_update_representation(view->container); view_execute_criteria(view); } @@ -574,17 +555,17 @@ void view_unmap(struct sway_view *view) { view->urgent_timer = NULL; } - bool was_fullscreen = view->swayc->is_fullscreen; - struct sway_container *parent = view->swayc->parent; - container_begin_destroy(view->swayc); - struct sway_container *surviving_ancestor = container_reap_empty(parent); + struct sway_container *parent = view->container->parent; + struct sway_workspace *ws = view->container->workspace; + container_begin_destroy(view->container); + if (parent) { + container_reap_empty(parent); + } else { + workspace_consider_destroy(ws); + } - // If the workspace wasn't reaped - if (surviving_ancestor && surviving_ancestor->type >= C_WORKSPACE) { - struct sway_container *ws = surviving_ancestor->type == C_WORKSPACE ? - surviving_ancestor : - container_parent(surviving_ancestor, C_WORKSPACE); - arrange_windows(was_fullscreen ? ws : surviving_ancestor); + if (!ws->node.destroying) { + arrange_workspace(ws); workspace_detect_urgent(ws); } @@ -593,15 +574,15 @@ void view_unmap(struct sway_view *view) { } void view_update_size(struct sway_view *view, int width, int height) { - if (!sway_assert(container_is_floating(view->swayc), + if (!sway_assert(container_is_floating(view->container), "Expected a floating container")) { return; } view->width = width; view->height = height; - view->swayc->current.view_width = width; - view->swayc->current.view_height = height; - container_set_geometry_from_floating_view(view->swayc); + view->container->current.view_width = width; + view->container->current.view_height = height; + container_set_geometry_from_floating_view(view->container); } static void view_subsurface_create(struct sway_view *view, @@ -670,27 +651,18 @@ void view_child_init(struct sway_view_child *child, wl_signal_add(&view->events.unmap, &child->view_unmap); child->view_unmap.notify = view_child_handle_view_unmap; - struct sway_container *output = child->view->swayc->parent; - if (output != NULL) { - if (output->type != C_OUTPUT) { - output = container_parent(output, C_OUTPUT); - } - wlr_surface_send_enter(child->surface, output->sway_output->wlr_output); - } + struct sway_output *output = child->view->container->workspace->output; + wlr_surface_send_enter(child->surface, output->wlr_output); view_init_subsurfaces(child->view, surface); // TODO: only damage the whole child - if (child->view->swayc) { - container_damage_whole(child->view->swayc); - } + container_damage_whole(child->view->container); } void view_child_destroy(struct sway_view_child *child) { // TODO: only damage the whole child - if (child->view->swayc) { - container_damage_whole(child->view->swayc); - } + container_damage_whole(child->view->container); wl_list_remove(&child->surface_commit.link); wl_list_remove(&child->surface_destroy.link); @@ -808,22 +780,20 @@ static char *escape_title(char *buffer) { } void view_update_title(struct sway_view *view, bool force) { - if (!view->swayc) { - return; - } const char *title = view_get_title(view); if (!force) { - if (title && view->swayc->name && strcmp(title, view->swayc->name) == 0) { + if (title && view->container->title && + strcmp(title, view->container->title) == 0) { return; } - if (!title && !view->swayc->name) { + if (!title && !view->container->title) { return; } } - free(view->swayc->name); - free(view->swayc->formatted_title); + free(view->container->title); + free(view->container->formatted_title); if (title) { size_t len = parse_title_format(view, NULL); char *buffer = calloc(len + 1, sizeof(char)); @@ -836,25 +806,25 @@ void view_update_title(struct sway_view *view, bool force) { buffer = escape_title(buffer); } - view->swayc->name = strdup(title); - view->swayc->formatted_title = buffer; + view->container->title = strdup(title); + view->container->formatted_title = buffer; } else { - view->swayc->name = NULL; - view->swayc->formatted_title = NULL; + view->container->title = NULL; + view->container->formatted_title = NULL; } - container_calculate_title_height(view->swayc); + container_calculate_title_height(view->container); config_update_font_height(false); // Update title after the global font height is updated - container_update_title_textures(view->swayc); + container_update_title_textures(view->container); - ipc_event_window(view->swayc, "title"); + ipc_event_window(view->container, "title"); } static bool find_by_mark_iterator(struct sway_container *con, void *data) { char *mark = data; - return con->type == C_VIEW && view_has_mark(con->sway_view, mark); + return con->view && view_has_mark(con->view, mark); } struct sway_view *view_find_mark(char *mark) { @@ -863,7 +833,7 @@ struct sway_view *view_find_mark(char *mark) { if (!container) { return NULL; } - return container->sway_view; + return container->view; } bool view_find_and_unmark(char *mark) { @@ -872,7 +842,7 @@ bool view_find_and_unmark(char *mark) { if (!container) { return false; } - struct sway_view *view = container->sway_view; + struct sway_view *view = container->view; for (int i = 0; i < view->marks->length; ++i) { char *view_mark = view->marks->items[i]; @@ -888,10 +858,9 @@ bool view_find_and_unmark(char *mark) { } void view_clear_marks(struct sway_view *view) { - while (view->marks->length) { - list_del(view->marks, 0); - ipc_event_window(view->swayc, "mark"); - } + list_foreach(view->marks, free); + view->marks->length = 0; + ipc_event_window(view->container, "mark"); } bool view_has_mark(struct sway_view *view, char *mark) { @@ -906,12 +875,13 @@ bool view_has_mark(struct sway_view *view, char *mark) { void view_add_mark(struct sway_view *view, char *mark) { list_add(view->marks, strdup(mark)); - ipc_event_window(view->swayc, "mark"); + ipc_event_window(view->container, "mark"); } static void update_marks_texture(struct sway_view *view, struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(view->swayc); + struct sway_output *output = + container_get_effective_output(view->container); if (!output) { return; } @@ -949,7 +919,7 @@ static void update_marks_texture(struct sway_view *view, double scale = output->wlr_output->scale; int width = 0; - int height = view->swayc->title_height * scale; + int height = view->container->title_height * scale; cairo_t *c = cairo_create(NULL); get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); @@ -994,44 +964,40 @@ void view_update_marks_textures(struct sway_view *view) { &config->border_colors.unfocused); update_marks_texture(view, &view->marks_urgent, &config->border_colors.urgent); - container_damage_whole(view->swayc); + container_damage_whole(view->container); } bool view_is_visible(struct sway_view *view) { - if (!view->swayc || view->swayc->destroying) { + if (view->container->node.destroying) { return false; } - struct sway_container *workspace = - container_parent(view->swayc, C_WORKSPACE); + struct sway_workspace *workspace = view->container->workspace; if (!workspace) { return false; } - // Determine if view is nested inside a floating container which is sticky. - // A simple floating view will have this ancestry: - // C_VIEW -> floating -> workspace - // A more complex ancestry could be: - // C_VIEW -> C_CONTAINER (tabbed) -> floating -> workspace - struct sway_container *floater = view->swayc; - while (floater->parent->type != C_WORKSPACE - && floater->parent->parent->type != C_WORKSPACE) { + // Determine if view is nested inside a floating container which is sticky + struct sway_container *floater = view->container; + while (floater->parent) { floater = floater->parent; } bool is_sticky = container_is_floating(floater) && floater->is_sticky; // Check view isn't in a tabbed or stacked container on an inactive tab struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *container = view->swayc; - while (container->type != C_WORKSPACE) { - if (container->parent->layout == L_TABBED || - container->parent->layout == L_STACKED) { - if (seat_get_active_child(seat, container->parent) != container) { + struct sway_container *container = view->container; + while (container) { + enum sway_container_layout layout = container_parent_layout(container); + if (layout == L_TABBED || layout == L_STACKED) { + struct sway_node *parent = container->parent ? + &container->parent->node : &container->workspace->node; + if (seat_get_active_child(seat, parent) != &container->node) { return false; } } container = container->parent; } // Check view isn't hidden by another fullscreen view - if (workspace->sway_workspace->fullscreen && - !container_is_fullscreen_or_child(view->swayc)) { + if (workspace->fullscreen && + !container_is_fullscreen_or_child(view->container)) { return false; } // Check the workspace is visible @@ -1047,7 +1013,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { } if (enable) { struct sway_seat *seat = input_manager_current_seat(input_manager); - if (seat_get_focus(seat) == view->swayc) { + if (seat_get_focused_container(seat) == view->container) { return; } clock_gettime(CLOCK_MONOTONIC, &view->urgent); @@ -1058,12 +1024,11 @@ void view_set_urgent(struct sway_view *view, bool enable) { view->urgent_timer = NULL; } } - container_damage_whole(view->swayc); + container_damage_whole(view->container); - ipc_event_window(view->swayc, "urgent"); + ipc_event_window(view->container, "urgent"); - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - workspace_detect_urgent(ws); + workspace_detect_urgent(view->container->workspace); } bool view_is_urgent(struct sway_view *view) { -- cgit v1.2.3-54-g00ecf