diff --git a/include/sway/commands.h b/include/sway/commands.h index 389c382eb2..a29fdad82f 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -106,6 +106,7 @@ sway_cmd cmd_exec_process; sway_cmd cmd_allow_tearing; sway_cmd cmd_assign; +sway_cmd cmd_assign_parent_workspace; sway_cmd cmd_bar; sway_cmd cmd_bindcode; sway_cmd cmd_bindgesture; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index fad278e021..e7e76e5b7b 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -17,6 +17,7 @@ enum criteria_type { CT_ASSIGN_WORKSPACE = 1 << 2, CT_ASSIGN_WORKSPACE_NUMBER = 1 << 3, CT_NO_FOCUS = 1 << 4, + CT_ASSIGN_PARENT_WORKSPACE = 1 << 5, }; enum pattern_type { diff --git a/sway/commands.c b/sway/commands.c index c2c12ee655..0e7e501662 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -43,6 +43,7 @@ struct cmd_results *checkarg(int argc, const char *name, enum expected_args type /* Keep alphabetized */ static const struct cmd_handler handlers[] = { { "assign", cmd_assign }, + { "assign_parent_workspace", cmd_assign_parent_workspace }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, { "bindgesture", cmd_bindgesture }, diff --git a/sway/commands/assign_parent_workspace.c b/sway/commands/assign_parent_workspace.c new file mode 100644 index 0000000000..ab809eef05 --- /dev/null +++ b/sway/commands/assign_parent_workspace.c @@ -0,0 +1,34 @@ +#include +#include "sway/commands.h" +#include "sway/criteria.h" +#include "list.h" +#include "log.h" + +struct cmd_results *cmd_assign_parent_workspace(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "assign_parent_workspace", EXPECTED_AT_LEAST, 1))) { + return error; + } + + char *err_str = NULL; + struct criteria *criteria = criteria_parse(argv[0], &err_str); + if (!criteria) { + error = cmd_results_new(CMD_INVALID, "%s", err_str); + free(err_str); + return error; + } + + criteria->type = CT_ASSIGN_PARENT_WORKSPACE; + + // Check if it already exists + if (criteria_already_exists(criteria)) { + sway_log(SWAY_DEBUG, "assign_parent_workspace already exists: '%s'", criteria->raw); + criteria_destroy(criteria); + return cmd_results_new(CMD_SUCCESS, NULL); + } + + list_add(config->criteria, criteria); + sway_log(SWAY_DEBUG, "assign_parent_workspace: '%s' added", criteria->raw); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/meson.build b/sway/meson.build index cb03a4d288..c2e8b2f928 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -44,6 +44,7 @@ sway_sources = files( 'commands/allow_tearing.c', 'commands/assign.c', + 'commands/assign_parent_workspace.c', 'commands/bar.c', 'commands/bind.c', 'commands/border.c', diff --git a/sway/tree/view.c b/sway/tree/view.c index 3d6211249f..a4dc986062 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -577,12 +577,51 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { view->ctx = ctx; } +static struct sway_workspace *get_parent_workspace(struct sway_view *view) { + // Try to get the parent view's workspace for both XDG and XWayland windows + struct sway_view *parent_view = NULL; + + // Check for XDG Shell parent + if (view->wlr_xdg_toplevel && view->wlr_xdg_toplevel->parent) { + struct wlr_xdg_surface *parent_surface = view->wlr_xdg_toplevel->parent->base; + if (parent_surface) { + struct sway_view *xdg_parent_view = view_from_wlr_xdg_surface(parent_surface); + if (xdg_parent_view && xdg_parent_view->container) { + parent_view = xdg_parent_view; + } + } + } + +#if WLR_HAS_XWAYLAND + // Check for XWayland parent (only for XWayland views!) + if (!parent_view && view->type == SWAY_VIEW_XWAYLAND && + view->wlr_xwayland_surface && view->wlr_xwayland_surface->parent) { + struct wlr_xwayland_surface *parent_xsurface = view->wlr_xwayland_surface->parent; + + // Check if parent surface is mapped and has valid data + if (parent_xsurface->surface && parent_xsurface->surface->mapped && parent_xsurface->data) { + struct sway_view *xw_parent_view = view_from_wlr_xwayland_surface(parent_xsurface); + if (xw_parent_view && xw_parent_view->container) { + parent_view = xw_parent_view; + } + } + } +#endif + + // If we found a parent view with a container, return its workspace + if (parent_view && parent_view->container) { + return parent_view->container->pending.workspace; + } + + return NULL; +} + static struct sway_workspace *select_workspace(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(); // 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); + CT_ASSIGN_WORKSPACE | CT_ASSIGN_WORKSPACE_NUMBER | CT_ASSIGN_OUTPUT | CT_ASSIGN_PARENT_WORKSPACE); struct sway_workspace *ws = NULL; for (int i = 0; i < criterias->length; ++i) { struct criteria *criteria = criterias->items[i]; @@ -592,6 +631,12 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { ws = output_get_active_workspace(output); break; } + } else if (criteria->type == CT_ASSIGN_PARENT_WORKSPACE) { + // Assign to parent's workspace if parent exists + ws = get_parent_workspace(view); + if (ws) { + break; + } } else { // CT_ASSIGN_WORKSPACE(_NUMBER) ws = criteria->type == CT_ASSIGN_WORKSPACE_NUMBER ?