diff options
author | emersion <contact@emersion.fr> | 2018-08-02 23:49:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-02 23:49:25 +0100 |
commit | 3a54e2291c017397ceff60511c29fe70d229bc8b (patch) | |
tree | d340b7776f945462f5ecffc830ada4d5fbe82f51 /sway/tree | |
parent | c35a34262f8da368f65d37f811a2264647e0dae6 (diff) | |
parent | e07da5fc5c6ac5c186662b56b08ca71531119de0 (diff) | |
download | sway-3a54e2291c017397ceff60511c29fe70d229bc8b.tar.xz |
Merge branch 'master' into wlr-gamma-control
Diffstat (limited to 'sway/tree')
-rw-r--r-- | sway/tree/arrange.c | 18 | ||||
-rw-r--r-- | sway/tree/container.c | 369 | ||||
-rw-r--r-- | sway/tree/layout.c | 122 | ||||
-rw-r--r-- | sway/tree/output.c | 2 | ||||
-rw-r--r-- | sway/tree/view.c | 315 | ||||
-rw-r--r-- | sway/tree/workspace.c | 268 |
6 files changed, 762 insertions, 332 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 533cf71c..5452b13c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -220,8 +220,22 @@ static void arrange_workspace(struct sway_container *workspace) { container_set_dirty(workspace); wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, workspace->x, workspace->y); - arrange_floating(workspace->sway_workspace->floating); - arrange_children_of(workspace); + if (workspace->sway_workspace->fullscreen) { + struct sway_container *fs = workspace->sway_workspace->fullscreen; + fs->x = workspace->parent->x; + fs->y = workspace->parent->y; + fs->width = workspace->parent->width; + fs->height = workspace->parent->height; + if (fs->type == C_VIEW) { + view_autoconfigure(fs->sway_view); + } else { + arrange_children_of(fs); + } + container_set_dirty(fs); + } else { + arrange_floating(workspace->sway_workspace->floating); + arrange_children_of(workspace); + } } static void arrange_output(struct sway_container *output) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 4dbfbb29..46c54e2d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -17,6 +17,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/scratchpad.h" #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" @@ -61,13 +62,17 @@ void container_create_notify(struct sway_container *container) { // TODO send ipc event type based on the container type wl_signal_emit(&root_container.sway_root->events.new_container, container); - if (container->type == C_VIEW || container->type == C_CONTAINER) { + if (container->type == C_VIEW) { ipc_event_window(container, "new"); + } else if (container->type == C_WORKSPACE) { + ipc_event_workspace(NULL, container, "init"); } } -static void container_update_textures_recursive(struct sway_container *con) { - container_update_title_textures(con); +void container_update_textures_recursive(struct sway_container *con) { + if (con->type == C_CONTAINER || con->type == C_VIEW) { + container_update_title_textures(con); + } if (con->type == C_VIEW) { view_update_marks_textures(con->sway_view); @@ -76,6 +81,10 @@ static void container_update_textures_recursive(struct sway_container *con) { struct sway_container *child = con->children->items[i]; container_update_textures_recursive(child); } + + if (con->type == C_WORKSPACE) { + container_update_textures_recursive(con->sway_workspace->floating); + } } } @@ -139,8 +148,6 @@ struct sway_container *container_create(enum sway_container_type type) { static void container_workspace_free(struct sway_workspace *ws) { list_foreach(ws->output_priority, free); list_free(ws->output_priority); - ws->floating->destroying = true; - container_free(ws->floating); free(ws); } @@ -193,6 +200,9 @@ void container_free(struct sway_container *cont) { free(cont); } +static struct sway_container *container_destroy_noreaping( + struct sway_container *con); + static struct sway_container *container_workspace_destroy( struct sway_container *workspace) { if (!sway_assert(workspace, "cannot destroy null workspace")) { @@ -237,6 +247,8 @@ static struct sway_container *container_workspace_destroy( } } + container_destroy_noreaping(workspace->sway_workspace->floating); + return output; } @@ -271,7 +283,7 @@ static struct sway_container *container_output_destroy( container_remove_child(workspace); if (!workspace_is_empty(workspace)) { container_add_child(new_output, workspace); - ipc_event_workspace(workspace, NULL, "move"); + ipc_event_workspace(NULL, workspace, "move"); } else { container_destroy(workspace); } @@ -309,7 +321,13 @@ static struct sway_container *container_destroy_noreaping( } wl_signal_emit(&con->events.destroy, con); - ipc_event_window(con, "close"); + + // emit IPC event + if (con->type == C_VIEW) { + ipc_event_window(con, "close"); + } else if (con->type == C_WORKSPACE) { + ipc_event_workspace(NULL, con, "empty"); + } // The below functions move their children to somewhere else. if (con->type == C_OUTPUT) { @@ -323,9 +341,15 @@ static struct sway_container *container_destroy_noreaping( } } + container_end_mouse_operation(con); + con->destroying = true; container_set_dirty(con); + if (con->scratchpad) { + scratchpad_remove_container(con); + } + if (!con->parent) { return NULL; } @@ -398,6 +422,10 @@ struct sway_container *container_flatten(struct sway_container *container) { * This function just wraps container_destroy_noreaping(), then does reaping. */ struct sway_container *container_destroy(struct sway_container *con) { + if (con->is_fullscreen) { + struct sway_container *ws = container_parent(con, C_WORKSPACE); + ws->sway_workspace->fullscreen = NULL; + } struct sway_container *parent = container_destroy_noreaping(con); if (!parent) { @@ -507,7 +535,7 @@ struct sway_container *container_parent(struct sway_container *container, return container; } -static struct sway_container *container_at_view(struct sway_container *swayc, +struct sway_container *container_at_view(struct sway_container *swayc, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { @@ -520,10 +548,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc, double _sx, _sy; struct wlr_surface *_surface = NULL; switch (sview->type) { +#ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: _surface = wlr_surface_surface_at(sview->surface, view_sx, view_sy, &_sx, &_sy); break; +#endif case SWAY_VIEW_XDG_SHELL_V6: _surface = wlr_xdg_surface_v6_surface_at( sview->wlr_xdg_surface_v6, @@ -539,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc, *sx = _sx; *sy = _sy; *surface = _surface; + return swayc; } - return swayc; + return NULL; } +static struct sway_container *tiling_container_at( + struct sway_container *con, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy); + /** * container_at for a container with layout L_TABBED. */ @@ -569,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return tiling_container_at(current, lx, ly, surface, sx, sy); } /** @@ -594,7 +629,7 @@ static struct sway_container *container_at_stacked( // Surfaces struct sway_container *current = seat_get_active_child(seat, parent); - return container_at(current, lx, ly, surface, sx, sy); + return tiling_container_at(current, lx, ly, surface, sx, sy); } /** @@ -612,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent, .height = child->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(child, lx, ly, surface, sx, sy); + return tiling_container_at(child, lx, ly, surface, sx, sy); } } return NULL; } -struct sway_container *container_at(struct sway_container *parent, - double lx, double ly, - struct wlr_surface **surface, double *sx, double *sy) { - if (!sway_assert(parent->type >= C_WORKSPACE, - "Expected workspace or deeper")) { - return NULL; - } - if (parent->type == C_VIEW) { - return container_at_view(parent, lx, ly, surface, sx, sy); - } - if (!parent->children->length) { - return NULL; - } - - switch (parent->layout) { - case L_HORIZ: - case L_VERT: - return container_at_linear(parent, lx, ly, surface, sx, sy); - case L_TABBED: - return container_at_tabbed(parent, lx, ly, surface, sx, sy); - case L_STACKED: - return container_at_stacked(parent, lx, ly, surface, sx, sy); - case L_FLOATING: - sway_assert(false, "Didn't expect to see floating here"); - return NULL; - case L_NONE: - return NULL; - } - - return NULL; -} - -struct sway_container *floating_container_at(double lx, double ly, +static struct sway_container *floating_container_at(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { for (int i = 0; i < root_container.children->length; ++i) { struct sway_container *output = root_container.children->items[i]; @@ -672,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly, .height = floater->height, }; if (wlr_box_contains_point(&box, lx, ly)) { - return container_at(floater, lx, ly, surface, sx, sy); + return tiling_container_at(floater, lx, ly, + surface, sx, sy); } } } @@ -680,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly, return NULL; } +static struct sway_container *tiling_container_at( + struct sway_container *con, double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (con->type == C_VIEW) { + return container_at_view(con, lx, ly, surface, sx, sy); + } + if (!con->children->length) { + return NULL; + } + + switch (con->layout) { + case L_HORIZ: + case L_VERT: + return container_at_linear(con, lx, ly, surface, sx, sy); + case L_TABBED: + return container_at_tabbed(con, lx, ly, surface, sx, sy); + case L_STACKED: + return container_at_stacked(con, lx, ly, surface, sx, sy); + case L_FLOATING: + sway_assert(false, "Didn't expect to see floating here"); + return NULL; + case L_NONE: + return NULL; + } + return NULL; +} + +static bool surface_is_popup(struct wlr_surface *surface) { + if (wlr_surface_is_xdg_surface(surface)) { + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_from_wlr_surface(surface); + while (xdg_surface) { + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + return true; + } + xdg_surface = xdg_surface->toplevel->parent; + } + return false; + } + + if (wlr_surface_is_xdg_surface_v6(surface)) { + struct wlr_xdg_surface_v6 *xdg_surface_v6 = + wlr_xdg_surface_v6_from_wlr_surface(surface); + while (xdg_surface_v6) { + if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { + return true; + } + xdg_surface_v6 = xdg_surface_v6->toplevel->parent; + } + return false; + } + + return false; +} + +struct sway_container *container_at(struct sway_container *workspace, + double lx, double ly, + struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { + return NULL; + } + struct sway_container *c; + // Focused view's popups + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = + seat_get_focus_inactive(seat, &root_container); + if (focus && focus->type == C_VIEW) { + container_at_view(focus, lx, ly, surface, sx, sy); + if (*surface && surface_is_popup(*surface)) { + return focus; + } + *surface = NULL; + } + // Floating + if ((c = floating_container_at(lx, ly, surface, sx, sy))) { + return c; + } + // Tiling + if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { + return c; + } + return NULL; +} + void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { @@ -934,36 +1022,104 @@ size_t container_titlebar_height() { return config->font_height + TITLEBAR_V_PADDING * 2; } +void container_init_floating(struct sway_container *con) { + if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, + "Expected a view or container")) { + return; + } + struct sway_container *ws = container_parent(con, C_WORKSPACE); + int min_width, min_height; + int max_width, max_height; + + if (config->floating_minimum_width == -1) { // no minimum + min_width = 0; + } else if (config->floating_minimum_width == 0) { // automatic + min_width = 75; + } else { + min_width = config->floating_minimum_width; + } + + if (config->floating_minimum_height == -1) { // no minimum + min_height = 0; + } else if (config->floating_minimum_height == 0) { // automatic + min_height = 50; + } else { + min_height = config->floating_minimum_height; + } + + if (config->floating_maximum_width == -1) { // no maximum + max_width = INT_MAX; + } else if (config->floating_maximum_width == 0) { // automatic + max_width = ws->width * 0.6666; + } else { + max_width = config->floating_maximum_width; + } + + if (config->floating_maximum_height == -1) { // no maximum + max_height = INT_MAX; + } else if (config->floating_maximum_height == 0) { // automatic + max_height = ws->height * 0.6666; + } else { + max_height = config->floating_maximum_height; + } + + if (con->type == C_CONTAINER) { + con->width = max_width; + con->height = max_height; + con->x = ws->x + (ws->width - con->width) / 2; + con->y = ws->y + (ws->height - con->height) / 2; + } else { + struct sway_view *view = con->sway_view; + view->width = fmax(min_width, fmin(view->natural_width, max_width)); + view->height = fmax(min_height, fmin(view->natural_height, max_height)); + view->x = ws->x + (ws->width - view->width) / 2; + view->y = ws->y + (ws->height - view->height) / 2; + + // If the view's border is B_NONE then these properties are ignored. + view->border_top = view->border_bottom = true; + view->border_left = view->border_right = true; + + container_set_geometry_from_floating_view(view->swayc); + } +} + void container_set_floating(struct sway_container *container, bool enable) { if (container_is_floating(container) == enable) { return; } - struct sway_container *workspace = container_parent(container, C_WORKSPACE); struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *workspace = container_parent(container, C_WORKSPACE); if (enable) { container_remove_child(container); container_add_child(workspace->sway_workspace->floating, container); + container_init_floating(container); if (container->type == C_VIEW) { - view_init_floating(container->sway_view); view_set_tiled(container->sway_view, false); } - seat_set_focus(seat, seat_get_focus_inactive(seat, container)); - container_reap_empty_recursive(workspace); } else { // Returning to tiled + if (container->scratchpad) { + scratchpad_remove_container(container); + } container_remove_child(container); - container_add_child(workspace, container); + struct sway_container *reference = + seat_get_focus_inactive_tiling(seat, workspace); + if (reference->type == C_VIEW) { + reference = reference->parent; + } + container_add_child(reference, container); container->width = container->parent->width; container->height = container->parent->height; if (container->type == C_VIEW) { view_set_tiled(container->sway_view, true); } container->is_sticky = false; - container_reap_empty_recursive(workspace->sway_workspace->floating); } + container_end_mouse_operation(container); + ipc_event_window(container, "floating"); } @@ -1009,7 +1165,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { /** * Translate the container's position as well as all children. */ -static void container_floating_translate(struct sway_container *con, +void container_floating_translate(struct sway_container *con, double x_amount, double y_amount) { con->x += x_amount; con->y += y_amount; @@ -1105,3 +1261,110 @@ static bool find_urgent_iterator(struct sway_container *con, bool container_has_urgent_child(struct sway_container *container) { return container_find(container, find_urgent_iterator, NULL); } + +void container_end_mouse_operation(struct sway_container *container) { + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + if (seat->op_container == container) { + seat_end_mouse_operation(seat); + } + } +} + +static void set_fullscreen_iterator(struct sway_container *con, void *data) { + if (con->type != C_VIEW) { + return; + } + if (con->sway_view->impl->set_fullscreen) { + bool *enable = data; + con->sway_view->impl->set_fullscreen(con->sway_view, *enable); + } +} + +void container_set_fullscreen(struct sway_container *container, bool enable) { + if (container->is_fullscreen == enable) { + return; + } + + struct sway_container *workspace = container_parent(container, C_WORKSPACE); + if (enable && workspace->sway_workspace->fullscreen) { + container_set_fullscreen(workspace->sway_workspace->fullscreen, false); + } + + container_for_each_descendant_dfs(container, + set_fullscreen_iterator, &enable); + + container->is_fullscreen = enable; + + if (enable) { + workspace->sway_workspace->fullscreen = container; + container->saved_x = container->x; + container->saved_y = container->y; + container->saved_width = container->width; + container->saved_height = container->height; + + struct sway_seat *seat; + struct sway_container *focus, *focus_ws; + wl_list_for_each(seat, &input_manager->seats, link) { + focus = seat_get_focus(seat); + if (focus) { + focus_ws = focus; + if (focus_ws->type != C_WORKSPACE) { + focus_ws = container_parent(focus_ws, C_WORKSPACE); + } + if (focus_ws == workspace) { + seat_set_focus(seat, container); + } + } + } + } else { + workspace->sway_workspace->fullscreen = NULL; + if (container_is_floating(container)) { + container->x = container->saved_x; + container->y = container->saved_y; + container->width = container->saved_width; + container->height = container->saved_height; + } else { + container->width = container->saved_width; + container->height = container->saved_height; + } + } + + container_end_mouse_operation(container); + + ipc_event_window(container, "fullscreen_mode"); +} + +bool container_is_floating_or_child(struct sway_container *container) { + do { + if (container->parent && container->parent->layout == L_FLOATING) { + return true; + } + container = container->parent; + } while (container && container->type != C_WORKSPACE); + + return false; +} + +bool container_is_fullscreen_or_child(struct sway_container *container) { + do { + if (container->is_fullscreen) { + return true; + } + container = container->parent; + } while (container && container->type != C_WORKSPACE); + + return false; +} + +struct sway_container *container_wrap_children(struct sway_container *parent) { + struct sway_container *middle = container_create(C_CONTAINER); + middle->layout = parent->layout; + while (parent->children->length) { + struct sway_container *child = parent->children->items[0]; + container_remove_child(child); + container_add_child(middle, child); + } + container_add_child(parent, middle); + return middle; +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1f898f8a..1f82e534 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -6,6 +6,7 @@ #include <string.h> #include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output_layout.h> +#include "config.h" #include "sway/debug.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" @@ -39,9 +40,12 @@ void layout_init(void) { root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); root_container.sway_root->output_layout = wlr_output_layout_create(); wl_list_init(&root_container.sway_root->outputs); +#ifdef HAVE_XWAYLAND wl_list_init(&root_container.sway_root->xwayland_unmanaged); +#endif wl_list_init(&root_container.sway_root->drag_icons); wl_signal_init(&root_container.sway_root->events.new_container); + root_container.sway_root->scratchpad = create_list(); root_container.sway_root->output_layout_change.notify = output_layout_handle_change; @@ -62,10 +66,9 @@ static int index_child(const struct sway_container *child) { static void container_handle_fullscreen_reparent(struct sway_container *con, struct sway_container *old_parent) { - if (con->type != C_VIEW || !con->sway_view->is_fullscreen) { + if (!con->is_fullscreen) { return; } - struct sway_view *view = con->sway_view; struct sway_container *old_workspace = old_parent; if (old_workspace && old_workspace->type != C_WORKSPACE) { old_workspace = container_parent(old_workspace, C_WORKSPACE); @@ -81,19 +84,27 @@ static void container_handle_fullscreen_reparent(struct sway_container *con, // Mark the new workspace as fullscreen if (new_workspace->sway_workspace->fullscreen) { - view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false); + container_set_fullscreen( + new_workspace->sway_workspace->fullscreen, false); } - new_workspace->sway_workspace->fullscreen = view; - // Resize view to new output dimensions + new_workspace->sway_workspace->fullscreen = con; + + // Resize container to new output dimensions struct sway_container *output = new_workspace->parent; - view->x = output->x; - view->y = output->y; - view->width = output->width; - view->height = output->height; con->x = output->x; con->y = output->y; con->width = output->width; con->height = output->height; + + if (con->type == C_VIEW) { + struct sway_view *view = con->sway_view; + view->x = output->x; + view->y = output->y; + view->width = output->width; + view->height = output->height; + } else { + arrange_windows(new_workspace); + } } void container_insert_child(struct sway_container *parent, @@ -135,10 +146,14 @@ void container_add_child(struct sway_container *parent, list_add(parent->children, child); child->parent = parent; container_handle_fullscreen_reparent(child, old_parent); + if (old_parent) { + container_set_dirty(old_parent); + } + container_set_dirty(child); } struct sway_container *container_remove_child(struct sway_container *child) { - if (child->type == C_VIEW && child->sway_view->is_fullscreen) { + if (child->is_fullscreen) { struct sway_container *workspace = container_parent(child, C_WORKSPACE); workspace->sway_workspace->fullscreen = NULL; } @@ -153,6 +168,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { child->parent = NULL; container_notify_subtree_changed(parent); + container_set_dirty(parent); + container_set_dirty(child); + return parent; } @@ -199,7 +217,9 @@ void container_move_to(struct sway_container *container, container_sort_workspaces(new_parent); seat_set_focus(seat, new_parent); workspace_output_raise_priority(container, old_parent, new_parent); - ipc_event_workspace(container, NULL, "move"); + ipc_event_workspace(NULL, container, "move"); + } else if (container->type == C_VIEW) { + ipc_event_window(container, "move"); } container_notify_subtree_changed(old_parent); container_notify_subtree_changed(new_parent); @@ -218,10 +238,10 @@ void container_move_to(struct sway_container *container, if (focus_ws->type != C_WORKSPACE) { focus_ws = container_parent(focus_ws, C_WORKSPACE); } - seat_set_focus(seat, - new_workspace->sway_workspace->fullscreen->swayc); - if (focus_ws != new_workspace) { - seat_set_focus(seat, focus); + if (focus_ws == new_workspace) { + struct sway_container *new_focus = seat_get_focus_inactive(seat, + new_workspace->sway_workspace->fullscreen); + seat_set_focus(seat, new_focus); } } } @@ -364,10 +384,18 @@ void container_move(struct sway_container *container, struct sway_container *sibling = NULL; struct sway_container *current = container; struct sway_container *parent = current->parent; + struct sway_container *top = &root_container; // If moving a fullscreen view, only consider outputs - if (container->type == C_VIEW && container->sway_view->is_fullscreen) { + if (container->is_fullscreen) { current = container_parent(container, C_OUTPUT); + } else if (container_is_fullscreen_or_child(container) || + container_is_floating_or_child(container)) { + // If we've fullscreened a split container, only allow the child to move + // around within the fullscreen parent. + // Same with floating a split container. + struct sway_container *ws = container_parent(container, C_WORKSPACE); + top = ws->sway_workspace->fullscreen; } struct sway_container *new_parent = container_flatten(parent); @@ -377,7 +405,7 @@ void container_move(struct sway_container *container, } while (!sibling) { - if (current->type == C_ROOT) { + if (current == top) { return; } @@ -441,8 +469,12 @@ void container_move(struct sway_container *container, if ((index == parent->children->length - 1 && offs > 0) || (index == 0 && offs < 0)) { if (current->parent == container->parent) { - if (parent->layout == L_TABBED - || parent->layout == L_STACKED) { + if (parent->parent->layout == L_FLOATING) { + return; + } + if (!parent->is_fullscreen && + (parent->layout == L_TABBED || + parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; @@ -463,10 +495,14 @@ void container_move(struct sway_container *container, sibling = parent->children->items[index + offs]; wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); } - } else if (parent->layout == L_TABBED - || parent->layout == L_STACKED) { + } else if (!parent->is_fullscreen && + parent->parent->layout != L_FLOATING && + (parent->layout == L_TABBED || + parent->layout == L_STACKED)) { move_out_of_tabs_stacks(container, current, move_dir, offs); return; + } else if (parent->parent->layout == L_FLOATING) { + return; } else { wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); current = current->parent; @@ -544,6 +580,10 @@ void container_move(struct sway_container *container, container_notify_subtree_changed(old_parent); container_notify_subtree_changed(container->parent); + if (container->type == C_VIEW) { + ipc_event_window(container, "move"); + } + if (old_parent) { seat_set_focus(config->handler_context.seat, old_parent); seat_set_focus(config->handler_context.seat, container); @@ -558,10 +598,11 @@ void container_move(struct sway_container *container, next_ws = container_parent(next_ws, C_WORKSPACE); } if (last_ws && next_ws && last_ws != next_ws) { - ipc_event_workspace(last_ws, container, "focus"); + ipc_event_workspace(last_ws, next_ws, "focus"); workspace_detect_urgent(last_ws); workspace_detect_urgent(next_ws); } + container_end_mouse_operation(container); } enum sway_container_layout container_get_default_layout( @@ -691,22 +732,18 @@ struct sway_container *container_get_in_direction( enum movement_direction dir) { struct sway_container *parent = container->parent; - if (container_is_floating(container)) { - return NULL; + if (dir == MOVE_CHILD) { + return seat_get_focus_inactive(seat, container); } - - if (container->type == C_VIEW && container->sway_view->is_fullscreen) { - if (dir == MOVE_PARENT || dir == MOVE_CHILD) { + if (container->is_fullscreen) { + if (dir == MOVE_PARENT) { return NULL; } container = container_parent(container, C_OUTPUT); parent = container->parent; } else { - if (dir == MOVE_CHILD) { - return seat_get_focus_inactive(seat, container); - } if (dir == MOVE_PARENT) { - if (parent->type == C_OUTPUT) { + if (parent->type == C_OUTPUT || container_is_floating(container)) { return NULL; } else { return parent; @@ -755,7 +792,8 @@ struct sway_container *container_get_in_direction( } sway_assert(next_workspace, "Next container has no workspace"); if (next_workspace->sway_workspace->fullscreen) { - return next_workspace->sway_workspace->fullscreen->swayc; + return seat_get_focus_inactive(seat, + next_workspace->sway_workspace->fullscreen); } if (next->children && next->children->length) { // TODO consider floating children as well @@ -963,13 +1001,13 @@ static void swap_focus(struct sway_container *con1, if (focus == con1 && (con2->parent->layout == L_TABBED || con2->parent->layout == L_STACKED)) { if (workspace_is_visible(ws2)) { - seat_set_focus_warp(seat, con2, false); + seat_set_focus_warp(seat, con2, false, true); } seat_set_focus(seat, ws1 != ws2 ? con2 : con1); } else if (focus == con2 && (con1->parent->layout == L_TABBED || con1->parent->layout == L_STACKED)) { if (workspace_is_visible(ws1)) { - seat_set_focus_warp(seat, con1, false); + seat_set_focus_warp(seat, con1, false, true); } seat_set_focus(seat, ws1 != ws2 ? con1 : con2); } else if (ws1 != ws2) { @@ -1002,13 +1040,13 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); - int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; - int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; + int fs1 = con1->is_fullscreen; + int fs2 = con2->is_fullscreen; if (fs1) { - view_set_fullscreen(con1->sway_view, false); + container_set_fullscreen(con1, false); } if (fs2) { - view_set_fullscreen(con2->sway_view, false); + container_set_fullscreen(con2, false); } struct sway_seat *seat = input_manager_get_default_seat(input_manager); @@ -1041,10 +1079,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { prev_workspace_name = stored_prev_name; } - if (fs1 && con2->type == C_VIEW) { - view_set_fullscreen(con2->sway_view, true); + if (fs1) { + container_set_fullscreen(con2, true); } - if (fs2 && con1->type == C_VIEW) { - view_set_fullscreen(con1->sway_view, true); + if (fs2) { + container_set_fullscreen(con1, true); } } diff --git a/sway/tree/output.c b/sway/tree/output.c index da535c18..31e3bf9b 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) { if (highest == output) { container_remove_child(ws); container_add_child(output, ws); - ipc_event_workspace(ws, NULL, "move"); + ipc_event_workspace(NULL, ws, "move"); j--; } } diff --git a/sway/tree/view.c b/sway/tree/view.c index 7881e6d7..97318daa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -2,7 +2,12 @@ #include <stdlib.h> #include <wayland-server.h> #include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_output_layout.h> +#include "config.h" +#ifdef HAVE_XWAYLAND +#include <wlr/xwayland.h> +#endif #include "list.h" #include "log.h" #include "sway/criteria.h" @@ -107,14 +112,14 @@ const char *view_get_instance(struct sway_view *view) { } return NULL; } - +#ifdef HAVE_XWAYLAND uint32_t view_get_x11_window_id(struct sway_view *view) { if (view->impl->get_int_prop) { return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); } return 0; } - +#endif const char *view_get_window_role(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); @@ -135,12 +140,27 @@ const char *view_get_shell(struct sway_view *view) { return "xdg_shell_v6"; case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; +#ifdef HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; +#endif } return "unknown"; } +void view_get_constraints(struct sway_view *view, double *min_width, + double *max_width, double *min_height, double *max_height) { + if (view->impl->get_constraints) { + view->impl->get_constraints(view, + min_width, max_width, min_height, max_height); + } else { + *min_width = DBL_MIN; + *max_width = DBL_MAX; + *min_height = DBL_MIN; + *max_height = DBL_MAX; + } +} + uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, int height) { if (view->impl->configure) { @@ -149,55 +169,6 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, return 0; } -void view_init_floating(struct sway_view *view) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - int min_width, min_height; - int max_width, max_height; - - if (config->floating_minimum_width == -1) { // no minimum - min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - min_width = 75; - } else { - min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - min_height = 50; - } else { - min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1) { // no maximum - max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - max_width = ws->width * 0.6666; - } else { - max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1) { // no maximum - max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - max_height = ws->height * 0.6666; - } else { - max_height = config->floating_maximum_height; - } - - view->width = fmax(min_width, fmin(view->natural_width, max_width)); - view->height = fmax(min_height, fmin(view->natural_height, max_height)); - view->x = ws->x + (ws->width - view->width) / 2; - view->y = ws->y + (ws->height - view->height) / 2; - - // If the view's border is B_NONE then these properties are ignored. - view->border_top = view->border_bottom = true; - view->border_left = view->border_right = true; - - container_set_geometry_from_floating_view(view->swayc); -} - void view_autoconfigure(struct sway_view *view) { if (!sway_assert(view->swayc, "Called view_autoconfigure() on a view without a swayc")) { @@ -206,7 +177,7 @@ void view_autoconfigure(struct sway_view *view) { struct sway_container *output = container_parent(view->swayc, C_OUTPUT); - if (view->is_fullscreen) { + if (view->swayc->is_fullscreen) { view->x = output->x; view->y = output->y; view->width = output->width; @@ -214,10 +185,6 @@ void view_autoconfigure(struct sway_view *view) { return; } - if (container_is_floating(view->swayc)) { - return; - } - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); int other_views = 0; @@ -330,72 +297,18 @@ void view_set_tiled(struct sway_view *view, bool tiled) { } } -void view_set_fullscreen(struct sway_view *view, bool fullscreen) { - if (view->is_fullscreen == fullscreen) { - return; - } - - struct sway_container *workspace = - container_parent(view->swayc, C_WORKSPACE); - - if (view->impl->set_fullscreen) { - view->impl->set_fullscreen(view, fullscreen); - } - - view->is_fullscreen = fullscreen; - - if (fullscreen) { - if (workspace->sway_workspace->fullscreen) { - view_set_fullscreen(workspace->sway_workspace->fullscreen, false); - } - workspace->sway_workspace->fullscreen = view; - view->saved_x = view->x; - view->saved_y = view->y; - view->saved_width = view->width; - view->saved_height = view->height; - view->swayc->saved_x = view->swayc->x; - view->swayc->saved_y = view->swayc->y; - view->swayc->saved_width = view->swayc->width; - view->swayc->saved_height = view->swayc->height; - - struct sway_seat *seat; - struct sway_container *focus, *focus_ws; - wl_list_for_each(seat, &input_manager->seats, link) { - focus = seat_get_focus(seat); - if (focus) { - focus_ws = focus; - if (focus && focus_ws->type != C_WORKSPACE) { - focus_ws = container_parent(focus_ws, C_WORKSPACE); - } - seat_set_focus(seat, view->swayc); - if (focus_ws != workspace) { - seat_set_focus(seat, focus); - } - } - } - } else { - workspace->sway_workspace->fullscreen = NULL; - if (container_is_floating(view->swayc)) { - view->x = view->saved_x; - view->y = view->saved_y; - view->width = view->saved_width; - view->height = view->saved_height; - container_set_geometry_from_floating_view(view->swayc); - } else { - view->swayc->width = view->swayc->saved_width; - view->swayc->height = view->swayc->saved_height; - } - } - - ipc_event_window(view->swayc, "fullscreen_mode"); -} - void view_close(struct sway_view *view) { if (view->impl->close) { view->impl->close(view); } } +void view_close_popups(struct sway_view *view) { + if (view->impl->close_popups) { + view->impl->close_popups(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]; @@ -426,6 +339,16 @@ void view_for_each_surface(struct sway_view *view, } } +void view_for_each_popup(struct sway_view *view, + wlr_surface_iterator_func_t iterator, void *user_data) { + if (!view->surface) { + return; + } + if (view->impl->for_each_popup) { + view->impl->for_each_popup(view, iterator, user_data); + } +} + static void view_subsurface_create(struct sway_view *view, struct wlr_subsurface *subsurface); @@ -521,12 +444,82 @@ void view_execute_criteria(struct sway_view *view) { seat_set_focus(seat, prior_focus); } +static struct sway_container *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_OUTPUT); + struct sway_container *ws = NULL; + for (int i = 0; i < criterias->length; ++i) { + struct criteria *criteria = criterias->items[i]; + if (criteria->type == CT_ASSIGN_WORKSPACE) { + ws = workspace_by_name(criteria->target); + if (!ws) { + ws = workspace_create(NULL, criteria->target); + } + break; + } else { + // CT_ASSIGN_OUTPUT + struct sway_container *output = output_by_name(criteria->target); + if (output) { + ws = seat_get_active_child(seat, output); + break; + } + } + } + list_free(criterias); + if (ws) { + return ws; + } + + // Check if there's a PID mapping + pid_t pid; +#ifdef HAVE_XWAYLAND + if (view->type == SWAY_VIEW_XWAYLAND) { + struct wlr_xwayland_surface *surf = + wlr_xwayland_surface_from_wlr_surface(view->surface); + pid = surf->pid; + } else { + struct wl_client *client = + wl_resource_get_client(view->surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); + } +#else + struct wl_client *client = + wl_resource_get_client(view->surface->resource); + wl_client_get_credentials(client, &pid, NULL, NULL); +#endif + ws = workspace_for_pid(pid); + if (ws) { + return ws; + } + + // 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; +} + 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); + + // Views can only take focus if they are mapped into the active workspace + if (prev_ws != map_ws) { + return false; + } + // 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; - struct sway_seat *seat = input_manager_current_seat(input_manager); - if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { + if (parent->type == C_WORKSPACE && prev_focus == parent) { size_t num_children = parent->children->length + parent->sway_workspace->floating->children->length; if (num_children == 1) { @@ -545,42 +538,19 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; } + view->surface = wlr_surface; struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = - seat_get_focus_inactive(seat, &root_container); - struct sway_container *cont = NULL; + struct sway_container *ws = select_workspace(view); + struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); - // Check if there's any `assign` criteria for the view - list_t *criterias = criteria_for_view(view, - CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); - struct sway_container *workspace = NULL; - if (criterias->length) { - struct criteria *criteria = criterias->items[0]; - if (criteria->type == CT_ASSIGN_WORKSPACE) { - workspace = workspace_by_name(criteria->target); - if (!workspace) { - workspace = workspace_create(NULL, criteria->target); - } - focus = seat_get_focus_inactive(seat, workspace); - } else { - // CT_ASSIGN_OUTPUT - struct sway_container *output = output_by_name(criteria->target); - if (output) { - focus = seat_get_focus_inactive(seat, output); - } - } - } // 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(focus)) { - focus = focus->parent->parent; + if (container_is_floating(target_sibling)) { + target_sibling = target_sibling->parent->parent; } - list_free(criterias); - cont = container_view_create(focus, view); - view->surface = wlr_surface; - view->swayc = cont; + view->swayc = container_view_create(target_sibling, view); view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -601,10 +571,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { } if (should_focus(view)) { - input_manager_set_focus(input_manager, cont); - if (workspace) { - workspace_switch(workspace); - } + input_manager_set_focus(input_manager, view->swayc); } view_update_title(view, false); @@ -628,10 +595,8 @@ void view_unmap(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); struct sway_container *parent; - if (view->is_fullscreen) { - ws->sway_workspace->fullscreen = NULL; + if (container_is_fullscreen_or_child(view->swayc)) { parent = container_destroy(view->swayc); - arrange_windows(ws->parent); } else { parent = container_destroy(view->swayc); @@ -784,11 +749,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); return view_from_wlr_xdg_surface_v6(xdg_surface_v6); } +#ifdef HAVE_XWAYLAND if (wlr_surface_is_xwayland_surface(wlr_surface)) { struct wlr_xwayland_surface *xsurface = wlr_xwayland_surface_from_wlr_surface(wlr_surface); return view_from_wlr_xwayland_surface(xsurface); } +#endif if (wlr_surface_is_subsurface(wlr_surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(wlr_surface); @@ -915,6 +882,8 @@ void view_update_title(struct sway_view *view, bool force) { // Update title after the global font height is updated container_update_title_textures(view->swayc); + + ipc_event_window(view->swayc, "title"); } static bool find_by_mark_iterator(struct sway_container *con, @@ -937,6 +906,7 @@ bool view_find_and_unmark(char *mark) { free(view_mark); list_del(view->marks, i); view_update_marks_textures(view); + ipc_event_window(container, "mark"); return true; } } @@ -944,11 +914,10 @@ bool view_find_and_unmark(char *mark) { } void view_clear_marks(struct sway_view *view) { - for (int i = 0; i < view->marks->length; ++i) { - free(view->marks->items[i]); + while (view->marks->length) { + list_del(view->marks, 0); + ipc_event_window(view->swayc, "mark"); } - list_free(view->marks); - view->marks = create_list(); } bool view_has_mark(struct sway_view *view, char *mark) { @@ -961,6 +930,11 @@ bool view_has_mark(struct sway_view *view, char *mark) { return false; } +void view_add_mark(struct sway_view *view, char *mark) { + list_add(view->marks, strdup(mark)); + ipc_event_window(view->swayc, "mark"); +} + static void update_marks_texture(struct sway_view *view, struct wlr_texture **texture, struct border_colors *class) { struct sway_container *output = container_parent(view->swayc, C_OUTPUT); @@ -1055,6 +1029,9 @@ bool view_is_visible(struct sway_view *view) { } struct sway_container *workspace = container_parent(view->swayc, C_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 @@ -1079,7 +1056,8 @@ bool view_is_visible(struct sway_view *view) { container = container->parent; } // Check view isn't hidden by another fullscreen view - if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { + if (workspace->sway_workspace->fullscreen && + !container_is_fullscreen_or_child(view->swayc)) { return false; } // Check the workspace is visible @@ -1117,3 +1095,22 @@ void view_set_urgent(struct sway_view *view, bool enable) { bool view_is_urgent(struct sway_view *view) { return view->urgent.tv_sec || view->urgent.tv_nsec; } + +void view_remove_saved_buffer(struct sway_view *view) { + if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) { + return; + } + wlr_buffer_unref(view->saved_buffer); + view->saved_buffer = NULL; +} + +void view_save_buffer(struct sway_view *view) { + if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) { + view_remove_saved_buffer(view); + } + if (view->surface && wlr_surface_has_buffer(view->surface)) { + view->saved_buffer = wlr_buffer_ref(view->surface->buffer); + view->saved_buffer_width = view->surface->current.width; + view->saved_buffer_height = view->surface->current.height; + } +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 622f01ec..588e2aae 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -9,6 +9,7 @@ #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" +#include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -107,96 +108,100 @@ static bool workspace_valid_on_output(const char *output_name, return true; } -char *workspace_next_name(const char *output_name) { - wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", - output_name); - // Scan all workspace bindings to find the next available workspace name, - // if none are found/available then default to a number - struct sway_mode *mode = config->current_mode; +static void workspace_name_from_binding(const struct sway_binding * binding, + const char* output_name, int *min_order, char **earliest_name) { + char *cmdlist = strdup(binding->command); + char *dup = cmdlist; + char *name = NULL; - // TODO: iterate over keycode bindings too - int order = INT_MAX; - char *target = NULL; - for (int i = 0; i < mode->keysym_bindings->length; ++i) { - struct sway_binding *binding = mode->keysym_bindings->items[i]; - char *cmdlist = strdup(binding->command); - char *dup = cmdlist; - char *name = NULL; - - // workspace n - char *cmd = argsep(&cmdlist, " "); - if (cmdlist) { - name = argsep(&cmdlist, ",;"); - } + // workspace n + char *cmd = argsep(&cmdlist, " "); + if (cmdlist) { + name = argsep(&cmdlist, ",;"); + } - if (strcmp("workspace", cmd) == 0 && name) { - char *_target = strdup(name); - _target = do_var_replacement(_target); - strip_quotes(_target); - while (isspace(*_target)) { - memmove(_target, _target+1, strlen(_target+1)); - } - wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", - _target); + if (strcmp("workspace", cmd) == 0 && name) { + char *_target = strdup(name); + _target = do_var_replacement(_target); + strip_quotes(_target); + wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", + _target); - // Make sure that the command references an actual workspace - // not a command about workspaces - if (strcmp(_target, "next") == 0 || + // Make sure that the command references an actual workspace + // not a command about workspaces + if (strcmp(_target, "next") == 0 || strcmp(_target, "prev") == 0 || strcmp(_target, "next_on_output") == 0 || strcmp(_target, "prev_on_output") == 0 || strcmp(_target, "number") == 0 || strcmp(_target, "back_and_forth") == 0 || - strcmp(_target, "current") == 0) - { - free(_target); - free(dup); - continue; - } - - // If the command is workspace number <name>, isolate the name - if (strncmp(_target, "number ", strlen("number ")) == 0) { - size_t length = strlen(_target) - strlen("number ") + 1; - char *temp = malloc(length); - strncpy(temp, _target + strlen("number "), length - 1); - temp[length - 1] = '\0'; - free(_target); - _target = temp; - wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); - - // Make sure the workspace number doesn't already exist - if (workspace_by_number(_target)) { - free(_target); - free(dup); - continue; - } - } + strcmp(_target, "current") == 0) { + free(_target); + free(dup); + return; + } - // Make sure that the workspace doesn't already exist - if (workspace_by_name(_target)) { + // If the command is workspace number <name>, isolate the name + if (strncmp(_target, "number ", strlen("number ")) == 0) { + size_t length = strlen(_target) - strlen("number ") + 1; + char *temp = malloc(length); + strncpy(temp, _target + strlen("number "), length - 1); + temp[length - 1] = '\0'; + free(_target); + _target = temp; + wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); + + // Make sure the workspace number doesn't already exist + if (workspace_by_number(_target)) { free(_target); free(dup); - continue; + return; } + } - // make sure that the workspace can appear on the given - // output - if (!workspace_valid_on_output(output_name, _target)) { - free(_target); - free(dup); - continue; - } + // Make sure that the workspace doesn't already exist + if (workspace_by_name(_target)) { + free(_target); + free(dup); + return; + } - if (binding->order < order) { - order = binding->order; - free(target); - target = _target; - wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); - } else { - free(_target); - } + // make sure that the workspace can appear on the given + // output + if (!workspace_valid_on_output(output_name, _target)) { + free(_target); + free(dup); + return; } - free(dup); + + if (binding->order < *min_order) { + *min_order = binding->order; + free(*earliest_name); + *earliest_name = _target; + wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); + } else { + free(_target); + } + } + free(dup); +} + +char *workspace_next_name(const char *output_name) { + wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", + output_name); + // Scan all workspace bindings to find the next available workspace name, + // if none are found/available then default to a number + struct sway_mode *mode = config->current_mode; + + int order = INT_MAX; + char *target = NULL; + for (int i = 0; i < mode->keysym_bindings->length; ++i) { + workspace_name_from_binding(mode->keysym_bindings->items[i], + output_name, &order, &target); + } + for (int i = 0; i < mode->keycode_bindings->length; ++i) { + workspace_name_from_binding(mode->keycode_bindings->items[i], + output_name, &order, &target); } if (target != NULL) { return target; @@ -529,3 +534,116 @@ void workspace_detect_urgent(struct sway_container *workspace) { container_damage_whole(workspace); } } + +struct pid_workspace { + pid_t pid; + char *workspace; + struct timespec time_added; + + struct sway_container *output; + struct wl_listener output_destroy; + + struct wl_list link; +}; + +static struct wl_list pid_workspaces; + +struct sway_container *workspace_for_pid(pid_t pid) { + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + return NULL; + } + + struct sway_container *ws = NULL; + struct pid_workspace *pw = NULL; + + wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); + + do { + struct pid_workspace *_pw = NULL; + wl_list_for_each(_pw, &pid_workspaces, link) { + if (pid == _pw->pid) { + pw = _pw; + wlr_log(WLR_DEBUG, + "found pid_workspace for pid %d, workspace %s", + pid, pw->workspace); + goto found; + } + } + pid = get_parent_pid(pid); + } while (pid > 1); +found: + + if (pw && pw->workspace) { + ws = workspace_by_name(pw->workspace); + + if (!ws) { + wlr_log(WLR_DEBUG, + "Creating workspace %s for pid %d because it disappeared", + pw->workspace, pid); + ws = workspace_create(pw->output, pw->workspace); + } + + wl_list_remove(&pw->output_destroy.link); + wl_list_remove(&pw->link); + free(pw->workspace); + free(pw); + } + + return ws; +} + +static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { + struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); + pw->output = NULL; + wl_list_remove(&pw->output_destroy.link); + wl_list_init(&pw->output_destroy.link); +} + +void workspace_record_pid(pid_t pid) { + wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + } + + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *ws = + seat_get_focus_inactive(seat, &root_container); + if (ws && ws->type != C_WORKSPACE) { + ws = container_parent(ws, C_WORKSPACE); + } + if (!ws) { + wlr_log(WLR_DEBUG, "Bailing out, no workspace"); + return; + } + struct sway_container *output = ws->parent; + if (!output) { + wlr_log(WLR_DEBUG, "Bailing out, no output"); + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // Remove expired entries + static const int timeout = 60; + struct pid_workspace *old, *_old; + wl_list_for_each_safe(old, _old, &pid_workspaces, link) { + if (now.tv_sec - old->time_added.tv_sec >= timeout) { + wl_list_remove(&old->output_destroy.link); + wl_list_remove(&old->link); + free(old->workspace); + free(old); + } + } + + struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); + pw->workspace = strdup(ws->name); + pw->output = output; + pw->pid = pid; + memcpy(&pw->time_added, &now, sizeof(struct timespec)); + pw->output_destroy.notify = pw_handle_output_destroy; + wl_signal_add(&output->sway_output->wlr_output->events.destroy, + &pw->output_destroy); + wl_list_insert(&pid_workspaces, &pw->link); +} |