From e9fe1141e6f4d97e179740248c414b3ad383ad65 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 6 May 2018 11:40:28 +1000 Subject: [PATCH] Criteria improvements Adds support for the following criteria tokens: id (X11 window ID), instance, tiling, workspace. Ensures each view will only execute a criteria once, by storing a list of executed criteria in the view. This is the same strategy used by i3. This also adds a check for criteria when an xwayland view changes its title, class, instance or window type. --- include/sway/tree/view.h | 23 +++++++++++- sway/criteria.c | 32 ++++++++++++----- sway/desktop/wl_shell.c | 4 +-- sway/desktop/xdg_shell_v6.c | 4 +-- sway/desktop/xwayland.c | 71 +++++++++++++++++++++++++++++++++++-- sway/tree/view.c | 65 +++++++++++++++++++++++++++------ 6 files changed, 171 insertions(+), 28 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 9d4256f7b..c645efcff 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -20,11 +20,15 @@ enum sway_view_prop { VIEW_PROP_APP_ID, VIEW_PROP_CLASS, VIEW_PROP_INSTANCE, + VIEW_PROP_WINDOW_TYPE, + VIEW_PROP_WINDOW_ROLE, + VIEW_PROP_X11_WINDOW_ID, }; struct sway_view_impl { - const char *(*get_prop)(struct sway_view *view, + const char *(*get_string_prop)(struct sway_view *view, enum sway_view_prop prop); + uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); void (*configure)(struct sway_view *view, double ox, double oy, int width, int height); void (*set_activated)(struct sway_view *view, bool activated); @@ -52,6 +56,8 @@ struct sway_view { enum sway_container_border border; int border_thickness; + list_t *executed_criteria; + union { struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; struct wlr_xwayland_surface *wlr_xwayland_surface; @@ -91,6 +97,9 @@ struct sway_xwayland_view { struct wl_listener request_maximize; struct wl_listener request_configure; struct wl_listener request_fullscreen; + struct wl_listener set_title; + struct wl_listener set_class; + struct wl_listener set_window_type; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -165,6 +174,12 @@ const char *view_get_class(struct sway_view *view); const char *view_get_instance(struct sway_view *view); +uint32_t view_get_x11_window_id(struct sway_view *view); + +uint32_t view_get_window_type(struct sway_view *view); + +uint32_t view_get_window_role(struct sway_view *view); + const char *view_get_type(struct sway_view *view); void view_configure(struct sway_view *view, double ox, double oy, int width, @@ -217,4 +232,10 @@ void view_child_destroy(struct sway_view_child *child); */ void view_update_title(struct sway_view *view, bool force); +/** + * Run any criteria that match the view and haven't been run on this view + * before. + */ +void view_execute_criteria(struct sway_view *view); + #endif diff --git a/sway/criteria.c b/sway/criteria.c index 22e9a49b6..efeb1d6aa 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -287,7 +287,7 @@ static bool criteria_test(struct sway_container *cont, list_t *tokens) { break; } if (crit->regex && regex_cmp(class, crit->regex) == 0) { - matches++; + ++matches; } break; } @@ -308,8 +308,16 @@ static bool criteria_test(struct sway_container *cont, list_t *tokens) { // TODO break; case CRIT_ID: - // TODO - break; + { + char *endptr; + size_t crit_id = strtoul(crit->raw, &endptr, 10); + + if (*endptr == 0 + && view_get_x11_window_id(cont->sway_view) == crit_id) { + ++matches; + } + break; + } case CRIT_APP_ID: { const char *app_id = view_get_app_id(cont->sway_view); @@ -318,7 +326,7 @@ static bool criteria_test(struct sway_container *cont, list_t *tokens) { } if (crit->regex && regex_cmp(app_id, crit->regex) == 0) { - matches++; + ++matches; } break; } @@ -330,12 +338,13 @@ static bool criteria_test(struct sway_container *cont, list_t *tokens) { } if (crit->regex && regex_cmp(instance, crit->regex) == 0) { - matches++; + ++matches; } break; } case CRIT_TILING: // TODO + ++matches; break; case CRIT_TITLE: { @@ -345,7 +354,7 @@ static bool criteria_test(struct sway_container *cont, list_t *tokens) { } if (crit->regex && regex_cmp(title, crit->regex) == 0) { - matches++; + ++matches; } break; } @@ -359,8 +368,13 @@ static bool criteria_test(struct sway_container *cont, list_t *tokens) { // TODO break; case CRIT_WORKSPACE: - // TODO - break; + { + struct sway_container *ws = container_parent(cont, C_WORKSPACE); + if (ws && strcmp(ws->name, crit->raw) == 0) { + ++matches; + } + break; + } default: sway_abort("Invalid criteria type (%i)", crit->type); break; @@ -440,6 +454,6 @@ list_t *container_for_crit_tokens(list_t *tokens) { &list_tokens); // TODO look in the scratchpad - + return list_tokens.list; } diff --git a/sway/desktop/wl_shell.c b/sway/desktop/wl_shell.c index e97a898e2..2a9f54505 100644 --- a/sway/desktop/wl_shell.c +++ b/sway/desktop/wl_shell.c @@ -20,7 +20,7 @@ static struct sway_wl_shell_view *wl_shell_view_from_view( return (struct sway_wl_shell_view *)view; } -static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (wl_shell_view_from_view(view) == NULL) { return NULL; } @@ -70,7 +70,7 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) { } static const struct sway_view_impl view_impl = { - .get_prop = get_prop, + .get_string_prop = get_string_prop, .configure = configure, .close = _close, .destroy = destroy, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index fcee8ce94..0be87aeea 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -80,7 +80,7 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( return (struct sway_xdg_shell_v6_view *)view; } -static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xdg_shell_v6_view_from_view(view) == NULL) { return NULL; } @@ -158,7 +158,7 @@ static void destroy(struct sway_view *view) { } static const struct sway_view_impl view_impl = { - .get_prop = get_prop, + .get_string_prop = get_string_prop, .configure = configure, .set_activated = set_activated, .set_fullscreen = set_fullscreen, diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b4eda71fc..e77483d9b 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -126,7 +126,7 @@ static struct sway_xwayland_view *xwayland_view_from_view( return (struct sway_xwayland_view *)view; } -static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { if (xwayland_view_from_view(view) == NULL) { return NULL; } @@ -135,11 +135,29 @@ static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { return view->wlr_xwayland_surface->title; case VIEW_PROP_CLASS: return view->wlr_xwayland_surface->class; + case VIEW_PROP_INSTANCE: + return view->wlr_xwayland_surface->instance; default: return NULL; } } +static uint32_t get_int_prop(struct sway_view *view, enum sway_view_prop prop) { + if (xwayland_view_from_view(view) == NULL) { + return 0; + } + switch (prop) { + case VIEW_PROP_X11_WINDOW_ID: + return view->wlr_xwayland_surface->window_id; + case VIEW_PROP_WINDOW_TYPE: + return *view->wlr_xwayland_surface->window_type; + case VIEW_PROP_WINDOW_ROLE: + return 0; + default: + return 0; + } +} + static void configure(struct sway_view *view, double ox, double oy, int width, int height) { struct sway_xwayland_view *xwayland_view = xwayland_view_from_view(view); @@ -200,13 +218,17 @@ static void destroy(struct sway_view *view) { wl_list_remove(&xwayland_view->destroy.link); wl_list_remove(&xwayland_view->request_configure.link); wl_list_remove(&xwayland_view->request_fullscreen.link); + wl_list_remove(&xwayland_view->set_title.link); + wl_list_remove(&xwayland_view->set_class.link); + wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->map.link); wl_list_remove(&xwayland_view->unmap.link); free(xwayland_view); } static const struct sway_view_impl view_impl = { - .get_prop = get_prop, + .get_string_prop = get_string_prop, + .get_int_prop = get_int_prop, .configure = configure, .set_activated = set_activated, .set_fullscreen = set_fullscreen, @@ -223,7 +245,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { view_update_size(view, xwayland_view->pending_width, xwayland_view->pending_height); view_damage(view, false); - view_update_title(view, false); } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -285,6 +306,40 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) view_set_fullscreen(view, xsurface->fullscreen); } +static void handle_set_title(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_title); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface) { + return; + } + view_update_title(view, false); + view_execute_criteria(view); +} + +static void handle_set_class(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_class); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface) { + return; + } + view_execute_criteria(view); +} + +static void handle_set_window_type(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_window_type); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface) { + return; + } + view_execute_criteria(view); +} + void handle_xwayland_surface(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, xwayland_surface); @@ -323,6 +378,16 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->request_fullscreen); xwayland_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); + xwayland_view->set_title.notify = handle_set_title; + + wl_signal_add(&xsurface->events.set_class, &xwayland_view->set_class); + xwayland_view->set_class.notify = handle_set_class; + + wl_signal_add(&xsurface->events.set_window_type, + &xwayland_view->set_window_type); + xwayland_view->set_window_type.notify = handle_set_window_type; + wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); xwayland_view->unmap.notify = handle_unmap; diff --git a/sway/tree/view.c b/sway/tree/view.c index fe944466c..48c231321 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,6 +3,7 @@ #include #include #include +#include "list.h" #include "log.h" #include "sway/criteria.h" #include "sway/commands.h" @@ -19,6 +20,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { view->type = type; view->impl = impl; + view->executed_criteria = create_list(); wl_signal_init(&view->events.unmap); } @@ -31,6 +33,8 @@ void view_destroy(struct sway_view *view) { view_unmap(view); } + list_free(view->executed_criteria); + container_destroy(view->swayc); if (view->impl->destroy) { @@ -41,33 +45,54 @@ void view_destroy(struct sway_view *view) { } const char *view_get_title(struct sway_view *view) { - if (view->impl->get_prop) { - return view->impl->get_prop(view, VIEW_PROP_TITLE); + if (view->impl->get_string_prop) { + return view->impl->get_string_prop(view, VIEW_PROP_TITLE); } return NULL; } const char *view_get_app_id(struct sway_view *view) { - if (view->impl->get_prop) { - return view->impl->get_prop(view, VIEW_PROP_APP_ID); + if (view->impl->get_string_prop) { + return view->impl->get_string_prop(view, VIEW_PROP_APP_ID); } return NULL; } const char *view_get_class(struct sway_view *view) { - if (view->impl->get_prop) { - return view->impl->get_prop(view, VIEW_PROP_CLASS); + if (view->impl->get_string_prop) { + return view->impl->get_string_prop(view, VIEW_PROP_CLASS); } return NULL; } const char *view_get_instance(struct sway_view *view) { - if (view->impl->get_prop) { - return view->impl->get_prop(view, VIEW_PROP_INSTANCE); + if (view->impl->get_string_prop) { + return view->impl->get_string_prop(view, VIEW_PROP_INSTANCE); } return NULL; } +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; +} + +uint32_t view_get_window_type(struct sway_view *view) { + if (view->impl->get_int_prop) { + return view->impl->get_int_prop(view, VIEW_PROP_WINDOW_TYPE); + } + return 0; +} + +uint32_t view_get_window_role(struct sway_view *view) { + if (view->impl->get_int_prop) { + return view->impl->get_int_prop(view, VIEW_PROP_WINDOW_ROLE); + } + return 0; +} + const char *view_get_type(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_WL_SHELL: @@ -282,8 +307,19 @@ static void view_handle_container_reparent(struct wl_listener *listener, } } -static void view_execute_criteria(struct sway_view *view) { - if (!sway_assert(view->swayc, "cannot run criteria for unmapped view")) { +static bool view_has_executed_criteria(struct sway_view *view, + struct criteria *criteria) { + for (int i = 0; i < view->executed_criteria->length; ++i) { + struct criteria *item = view->executed_criteria->items[i]; + if (item == criteria) { + return true; + } + } + return false; +} + +void view_execute_criteria(struct sway_view *view) { + if (!view->swayc) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); @@ -292,8 +328,14 @@ static void view_execute_criteria(struct sway_view *view) { list_t *criteria = criteria_for(view->swayc); for (int i = 0; i < criteria->length; i++) { struct criteria *crit = criteria->items[i]; - wlr_log(L_DEBUG, "for_window '%s' matches new view %p, cmd: '%s'", + wlr_log(L_DEBUG, "Checking criteria %s", crit->crit_raw); + if (view_has_executed_criteria(view, crit)) { + wlr_log(L_DEBUG, "Criteria already executed"); + continue; + } + wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", crit->crit_raw, view, crit->cmdlist); + list_add(view->executed_criteria, crit); struct cmd_results *res = execute_command(crit->cmdlist, NULL); if (res->status != CMD_SUCCESS) { wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); @@ -336,6 +378,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view_damage(view, true); view_handle_container_reparent(&view->container_reparent, NULL); + view_update_title(view, false); view_execute_criteria(view); }