From 3826535ab0eba08160809445e3f12e0281b824d2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Jul 2025 16:44:48 +0200 Subject: [PATCH 1/3] Wire up xdg-toplevel-tag-v1 --- include/sway/server.h | 2 ++ include/sway/tree/view.h | 6 ++++++ sway/desktop/xdg_shell.c | 16 +++++++++++++++- sway/server.c | 9 +++++++++ sway/tree/view.c | 7 +++++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/sway/server.h b/include/sway/server.h index 72bccd70c..f50d48f05 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -119,6 +119,8 @@ struct sway_server { struct wl_listener xdg_activation_v1_request_activate; struct wl_listener xdg_activation_v1_new_token; + struct wl_listener xdg_toplevel_tag_manager_v1_set_tag; + struct wl_listener request_set_cursor_shape; struct wlr_tearing_control_manager_v1 *tearing_control_v1; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 6151a0234..ae81c5bbf 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -25,6 +25,7 @@ enum sway_view_type { enum sway_view_prop { VIEW_PROP_TITLE, VIEW_PROP_APP_ID, + VIEW_PROP_TAG, VIEW_PROP_CLASS, VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, @@ -128,6 +129,7 @@ struct sway_xdg_shell_view { struct sway_view view; struct wlr_scene_tree *image_capture_tree; + char *tag; struct wl_listener commit; struct wl_listener request_move; @@ -236,6 +238,8 @@ const char *view_get_sandbox_app_id(struct sway_view *view); const char *view_get_sandbox_instance_id(struct sway_view *view); +const char *view_get_tag(struct sway_view *view); + const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, @@ -360,4 +364,6 @@ void view_send_frame_done(struct sway_view *view); bool view_can_tear(struct sway_view *view); +void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3852806e4..db09e91d7 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "log.h" #include "sway/decoration.h" @@ -157,7 +158,8 @@ static void get_constraints(struct sway_view *view, double *min_width, static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { - if (xdg_shell_view_from_view(view) == NULL) { + struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + if (xdg_shell_view == NULL) { return NULL; } switch (prop) { @@ -165,6 +167,8 @@ static const char *get_string_prop(struct sway_view *view, return view->wlr_xdg_toplevel->title; case VIEW_PROP_APP_ID: return view->wlr_xdg_toplevel->app_id; + case VIEW_PROP_TAG: + return xdg_shell_view->tag; default: return NULL; } @@ -265,6 +269,7 @@ static void destroy(struct sway_view *view) { if (xdg_shell_view == NULL) { return; } + free(xdg_shell_view->tag); free(xdg_shell_view); } @@ -581,3 +586,12 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { xdg_toplevel->base->data = xdg_shell_view; } + +void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data) { + const struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event *event = data; + struct sway_view *view = view_from_wlr_xdg_surface(event->toplevel->base); + struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + free(xdg_shell_view->tag); + xdg_shell_view->tag = strdup(event->tag); + view_execute_criteria(view); +} diff --git a/sway/server.c b/sway/server.c index fb6204326..8615066ee 100644 --- a/sway/server.c +++ b/sway/server.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include "config.h" #include "list.h" @@ -439,6 +440,13 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->xdg_activation_v1->events.new_token, &server->xdg_activation_v1_new_token); + struct wlr_xdg_toplevel_tag_manager_v1 *xdg_toplevel_tag_manager_v1 = + wlr_xdg_toplevel_tag_manager_v1_create(server->wl_display, 1); + server->xdg_toplevel_tag_manager_v1_set_tag.notify = + xdg_toplevel_tag_manager_v1_handle_set_tag; + wl_signal_add(&xdg_toplevel_tag_manager_v1->events.set_tag, + &server->xdg_toplevel_tag_manager_v1_set_tag); + struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = wlr_cursor_shape_manager_v1_create(server->wl_display, 1); server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; @@ -536,6 +544,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->tearing_control_new_object.link); wl_list_remove(&server->xdg_activation_v1_request_activate.link); wl_list_remove(&server->xdg_activation_v1_new_token.link); + wl_list_remove(&server->xdg_toplevel_tag_manager_v1_set_tag.link); wl_list_remove(&server->request_set_cursor_shape.link); wl_list_remove(&server->new_foreign_toplevel_capture_request.link); input_manager_finish(server->input); diff --git a/sway/tree/view.c b/sway/tree/view.c index 7bf185fea..61aa53776 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -190,6 +190,13 @@ const char *view_get_sandbox_instance_id(struct sway_view *view) { return security_context ? security_context->instance_id : NULL; } +const char *view_get_tag(struct sway_view *view) { + if (view->impl->get_string_prop) { + return view->impl->get_string_prop(view, VIEW_PROP_TAG); + } + return NULL; +} + const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL: From 08142c3f3ab41320cffe4d6cc03c09a9c6c53ce8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Jul 2025 16:45:16 +0200 Subject: [PATCH 2/3] Add xdg_toplevel tag to IPC --- sway/ipc-json.c | 3 +++ sway/sway-ipc.7.scd | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 3933f3ef7..3b69ad384 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -627,6 +627,9 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "sandbox_instance_id", sandbox_instance_id ? json_object_new_string(sandbox_instance_id) : NULL); + const char *tag = view_get_tag(c->view); + json_object_object_add(object, "tag", tag ? json_object_new_string(tag) : NULL); + json_object *idle_inhibitors = json_object_new_object(); struct sway_idle_inhibitor_v1 *user_inhibitor = diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 87f0828b7..4b0d5c962 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -419,6 +419,10 @@ node and will have the following properties: : string : (Only windows) The instance ID provided by the associated sandbox engine (or _null_) +|- tag +: string +: (Only windows) For an xdg-shell window, tag of the toplevel, if set. + Otherwise, _null_ |- window : integer : (Only xwayland windows) The X11 window ID for the xwayland window From cb33701f5ee742b659afd660fa98c294f6faf5a8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Jul 2025 16:45:29 +0200 Subject: [PATCH 3/3] Add xdg_toplevel tag to criteria --- include/sway/criteria.h | 1 + sway/criteria.c | 30 +++++++++++++++++++++++++++++- sway/sway.5.scd | 3 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 8ba8c9989..fad278e02 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -56,6 +56,7 @@ struct criteria { struct pattern *sandbox_engine; struct pattern *sandbox_app_id; struct pattern *sandbox_instance_id; + struct pattern *tag; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/sway/criteria.c b/sway/criteria.c index 29f73f697..e200d4c8f 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -37,7 +37,8 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->pid && !criteria->sandbox_engine && !criteria->sandbox_app_id - && !criteria->sandbox_instance_id; + && !criteria->sandbox_instance_id + && !criteria->tag; } // The error pointer is used for parsing functions, and saves having to pass it @@ -104,6 +105,7 @@ void criteria_destroy(struct criteria *criteria) { pattern_destroy(criteria->sandbox_engine); pattern_destroy(criteria->sandbox_app_id); pattern_destroy(criteria->sandbox_instance_id); + pattern_destroy(criteria->tag); free(criteria->target); free(criteria->cmdlist); free(criteria->raw); @@ -314,6 +316,26 @@ static bool criteria_matches_view(struct criteria *criteria, } } + if (criteria->tag) { + const char *tag = view_get_tag(view); + if (!tag) { + return false; + } + + switch (criteria->tag->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(tag, view_get_tag(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(tag, criteria->tag->regex) < 0) { + return false; + } + break; + } + } + if (!criteria_matches_container(criteria, view->container)) { return false; } @@ -544,6 +566,7 @@ enum criteria_token { T_SANDBOX_ENGINE, T_SANDBOX_APP_ID, T_SANDBOX_INSTANCE_ID, + T_TAG, T_INVALID, }; @@ -589,6 +612,8 @@ static enum criteria_token token_from_name(char *name) { return T_SANDBOX_APP_ID; } else if (strcmp(name, "sandbox_instance_id") == 0) { return T_SANDBOX_INSTANCE_ID; + } else if (strcmp(name, "tag") == 0) { + return T_TAG; } return T_INVALID; } @@ -700,6 +725,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_SANDBOX_INSTANCE_ID: pattern_create(&criteria->sandbox_instance_id, value); break; + case T_TAG: + pattern_create(&criteria->tag, value); + break; case T_INVALID: break; } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index dbdf1f1a2..689783a27 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -1055,6 +1055,9 @@ The following attributes may be matched with: Can be a regular expression. If value is \_\_focused\_\_, then the shell must be the same as that of the currently focused window. +*tag* + Compare value against the tag. _tag_ is specific to Wayland applications. + *tiling* Matches tiling windows.