From 3b0c26d149dfe5e05df338692db8255a01f0998d Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 9 May 2018 14:23:20 +1000 Subject: Overhaul criteria implementation The criteria struct now uses properties for each token type rather than the list_t list of tokens. The reason for this is that different token types have different data types: pcre, string and number to name a few. This solution should be more flexible moving forward. A bonus of this is that criteria is now easier to understand when looking at the struct definition. The criteria parser has been rewritten because the previous one didn't support valueless pairs (eg. [class="foo" floating]). Criteria now has types. Types at the moment are CT_COMMAND, CT_ASSIGN_WORKSPACE and CT_ASSIGN_OUTPUT. i3 uses types as well. Previously the assign command was creating a criteria with 'move to workspace ' as its command, but this caused the window to appear briefly on the focused workspace before being moved to the assigned workspace. It now creates the view directly in the assigned workspace. Each view will only execute a given criteria once. This is achieved by storing a list of executed criteria in the view. This is the same strategy used by i3. Escaping now works properly. Previously you could do things like [class="Fire\"fox"] and the stored value would be 'Fire\"fox', but it should be (and now is) 'Fire"fox'. The public functions in criteria.c are now all prefixed with criteria_. Xwayland views now listen to the set_title, set_class and set_window_type events and criteria will be run when these happen. XDG shell has none of these events so it continues to update the title in handle_commit. Each view type's get_prop function has been split into get_string_prop and get_int_prop because some properties like the X11 window ID and window type are numeric. The following new criteria tokens are now supported: * id (X11 window ID) * instance * tiling * workspace --- sway/tree/view.c | 97 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 22 deletions(-) (limited to 'sway/tree') diff --git a/sway/tree/view.c b/sway/tree/view.c index afd7eade..7d921e0e 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,47 @@ 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; +} + const char *view_get_type(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_WL_SHELL: @@ -282,19 +300,36 @@ 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); struct sway_container *prior_workspace = container_parent(view->swayc, C_WORKSPACE); - 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'", - crit->crit_raw, view, crit->cmdlist); - struct cmd_results *res = execute_command(crit->cmdlist, NULL); + list_t *criterias = criteria_for_view(view, CT_COMMAND); + for (int i = 0; i < criterias->length; i++) { + struct criteria *criteria = criterias->items[i]; + wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); + if (view_has_executed_criteria(view, criteria)) { + wlr_log(L_DEBUG, "Criteria already executed"); + continue; + } + wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", + criteria->raw, view, criteria->cmdlist); + list_add(view->executed_criteria, criteria); + struct cmd_results *res = execute_command(criteria->cmdlist, NULL); if (res->status != CMD_SUCCESS) { wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); } @@ -303,7 +338,7 @@ static void view_execute_criteria(struct sway_view *view) { // so always refocus in-between command lists seat_set_focus(seat, view->swayc); } - list_free(criteria); + list_free(criterias); seat_set_focus(seat, seat_get_focus_inactive(seat, prior_workspace)); } @@ -313,9 +348,26 @@ void view_map(struct sway_view *view, struct wlr_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 = container_view_create(focus, view); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *cont = NULL; + + // Check if there's any `assign` criteria for the view + list_t *criterias = criteria_for_view(view, + CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); + if (criterias->length) { + struct criteria *criteria = criterias->items[0]; + if (criteria->type == CT_ASSIGN_WORKSPACE) { + struct sway_container *workspace = workspace_by_name(criteria->target); + if (!workspace) { + workspace = workspace_create(NULL, criteria->target); + } + focus = seat_get_focus_inactive(seat, workspace); + } else { + // TODO: CT_ASSIGN_OUTPUT + } + } + free(criterias); + cont = container_view_create(focus, view); view->surface = wlr_surface; view->swayc = cont; @@ -333,10 +385,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { arrange_children_of(cont->parent); input_manager_set_focus(input_manager, cont); + view_update_title(view, false); + view_execute_criteria(view); + container_damage_whole(cont); view_handle_container_reparent(&view->container_reparent, NULL); - - view_execute_criteria(view); } void view_unmap(struct sway_view *view) { -- cgit v1.2.3 From 935bda0f336bb9ca45a7fb96d7ed5f08ef04432f Mon Sep 17 00:00:00 2001 From: Dudemanguy911 Date: Fri, 11 May 2018 01:20:20 +0000 Subject: fix crash on fullscreen toggle --- sway/tree/view.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'sway/tree') diff --git a/sway/tree/view.c b/sway/tree/view.c index afd7eade..424c1084 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -163,13 +163,15 @@ void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen) { struct sway_container *focus, *focus_ws; wl_list_for_each(seat, &input_manager->seats, link) { focus = seat_get_focus(seat); - focus_ws = focus; - if (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); + 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 { -- cgit v1.2.3 From 94e42f985778ce402b4c47bf8c38ee4233b6204d Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 11 May 2018 14:58:24 +1000 Subject: Implement __focused__ criteria --- include/sway/tree/view.h | 2 + sway/criteria.c | 204 +++++++++++++++++++++++++++++++++++++---------- sway/tree/view.c | 7 ++ 3 files changed, 171 insertions(+), 42 deletions(-) (limited to 'sway/tree') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 144ad038..65cadea1 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -176,6 +176,8 @@ const char *view_get_instance(struct sway_view *view); uint32_t view_get_x11_window_id(struct sway_view *view); +const char *view_get_window_role(struct sway_view *view); + uint32_t view_get_window_type(struct sway_view *view); const char *view_get_type(struct sway_view *view); diff --git a/sway/criteria.c b/sway/criteria.c index 7da790e6..294b2922 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -190,19 +190,128 @@ static bool generate_regex(pcre **regex, char *value) { return true; } +enum criteria_token { + T_APP_ID, + T_CLASS, + T_CON_ID, + T_CON_MARK, + T_FLOATING, + T_ID, + T_INSTANCE, + T_TILING, + T_TITLE, + T_URGENT, + T_WINDOW_ROLE, + T_WINDOW_TYPE, + T_WORKSPACE, + + T_INVALID, +}; + +static enum criteria_token token_from_name(char *name) { + if (strcmp(name, "app_id") == 0) { + return T_APP_ID; + } else if (strcmp(name, "class") == 0) { + return T_CLASS; + } else if (strcmp(name, "con_id") == 0) { + return T_CON_ID; + } else if (strcmp(name, "con_mark") == 0) { + return T_CON_MARK; + } else if (strcmp(name, "id") == 0) { + return T_ID; + } else if (strcmp(name, "instance") == 0) { + return T_INSTANCE; + } else if (strcmp(name, "title") == 0) { + return T_TITLE; + } else if (strcmp(name, "urgent") == 0) { + return T_URGENT; + } else if (strcmp(name, "window_role") == 0) { + return T_WINDOW_ROLE; + } else if (strcmp(name, "window_type") == 0) { + return T_WINDOW_TYPE; + } else if (strcmp(name, "workspace") == 0) { + return T_WORKSPACE; + } + return T_INVALID; +} + +/** + * Get a property of the focused view. + * + * Note that we are taking the focused view at the time of criteria parsing, not + * at the time of execution. This is because __focused__ only makes sense when + * using criteria via IPC. Using __focused__ in config is not useful because + * criteria is only executed once per view. + */ +static char *get_focused_prop(enum criteria_token token) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + + if (!focus || focus->type != C_VIEW) { + return NULL; + } + struct sway_view *view = focus->sway_view; + const char *value = NULL; + + switch (token) { + case T_APP_ID: + value = view_get_app_id(view); + break; + case T_CLASS: + value = view_get_class(view); + break; + case T_INSTANCE: + value = view_get_instance(view); + break; + case T_TITLE: + value = view_get_class(view); + break; + case T_WINDOW_ROLE: + value = view_get_class(view); + break; + case T_WORKSPACE: + { + struct sway_container *ws = container_parent(focus, C_WORKSPACE); + if (ws) { + value = ws->name; + } + } + break; + case T_CON_ID: // These do not support __focused__ + case T_CON_MARK: + case T_FLOATING: + case T_ID: + case T_TILING: + case T_URGENT: + case T_WINDOW_TYPE: + case T_INVALID: + break; + } + if (value) { + return strdup(value); + } + return NULL; +} + static bool parse_token(struct criteria *criteria, char *name, char *value) { + enum criteria_token token = token_from_name(name); + if (token == T_INVALID) { + const char *fmt = "Token '%s' is not recognized"; + int len = strlen(fmt) + strlen(name) - 1; + error = malloc(len); + snprintf(error, len, fmt, name); + return false; + } + + char *effective_value = NULL; + if (value && strcmp(value, "__focused__") == 0) { + effective_value = get_focused_prop(token); + } else if (value) { + effective_value = strdup(value); + } + // Require value, unless token is floating or tiled - if (!value && (strcmp(name, "title") == 0 - || strcmp(name, "app_id") == 0 - || strcmp(name, "class") == 0 - || strcmp(name, "instance") == 0 - || strcmp(name, "con_id") == 0 - || strcmp(name, "con_mark") == 0 - || strcmp(name, "window_role") == 0 - || strcmp(name, "window_type") == 0 - || strcmp(name, "id") == 0 - || strcmp(name, "urgent") == 0 - || strcmp(name, "workspace") == 0)) { + if (!effective_value && token != T_FLOATING && token != T_TILING) { const char *fmt = "Token '%s' requires a value"; int len = strlen(fmt) + strlen(name) - 1; error = malloc(len); @@ -210,53 +319,64 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { return false; } - if (strcmp(name, "title") == 0) { - generate_regex(&criteria->title, value); - } else if (strcmp(name, "app_id") == 0) { - generate_regex(&criteria->app_id, value); - } else if (strcmp(name, "class") == 0) { - generate_regex(&criteria->class, value); - } else if (strcmp(name, "instance") == 0) { - generate_regex(&criteria->instance, value); - } else if (strcmp(name, "con_id") == 0) { - char *endptr; - criteria->con_id = strtoul(value, &endptr, 10); + char *endptr = NULL; + switch (token) { + case T_TITLE: + generate_regex(&criteria->title, effective_value); + break; + case T_APP_ID: + generate_regex(&criteria->app_id, effective_value); + break; + case T_CLASS: + generate_regex(&criteria->class, effective_value); + break; + case T_INSTANCE: + generate_regex(&criteria->instance, effective_value); + break; + case T_CON_ID: + criteria->con_id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'con_id' should be numeric"); } - } else if (strcmp(name, "con_mark") == 0) { - generate_regex(&criteria->con_mark, value); - } else if (strcmp(name, "window_role") == 0) { - generate_regex(&criteria->window_role, value); - } else if (strcmp(name, "window_type") == 0) { + break; + case T_CON_MARK: + generate_regex(&criteria->con_mark, effective_value); + break; + case T_WINDOW_ROLE: + generate_regex(&criteria->window_role, effective_value); + break; + case T_WINDOW_TYPE: // TODO: This is a string but will be stored as an enum or integer - } else if (strcmp(name, "id") == 0) { - char *endptr; - criteria->id = strtoul(value, &endptr, 10); + break; + case T_ID: + criteria->id = strtoul(effective_value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'id' should be numeric"); } - } else if (strcmp(name, "floating") == 0) { + break; + case T_FLOATING: criteria->floating = true; - } else if (strcmp(name, "tiling") == 0) { + break; + case T_TILING: criteria->tiling = true; - } else if (strcmp(name, "urgent") == 0) { - if (strcmp(value, "latest") == 0) { + break; + case T_URGENT: + if (strcmp(effective_value, "latest") == 0) { criteria->urgent = 'l'; - } else if (strcmp(value, "oldest") == 0) { + } else if (strcmp(effective_value, "oldest") == 0) { criteria->urgent = 'o'; } else { error = strdup("The value for 'urgent' must be 'latest' or 'oldest'"); } - } else if (strcmp(name, "workspace") == 0) { - criteria->workspace = strdup(value); - } else { - const char *fmt = "Token '%s' is not recognized"; - int len = strlen(fmt) + strlen(name) - 1; - error = malloc(len); - snprintf(error, len, fmt, name); + break; + case T_WORKSPACE: + criteria->workspace = strdup(effective_value); + break; + case T_INVALID: + break; } + free(effective_value); if (error) { return false; diff --git a/sway/tree/view.c b/sway/tree/view.c index 7d921e0e..ca84d82e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -79,6 +79,13 @@ uint32_t view_get_x11_window_id(struct sway_view *view) { return 0; } +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); + } + return NULL; +} + 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); -- cgit v1.2.3 From ac0e62584f6101277b76622a7274866cd50f615c Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 12 May 2018 08:52:48 -0400 Subject: Revert "Merge pull request #1953 from RyanDwyer/criteria-focused" This reverts commit 2511adffc29996b64d01d85b3de31de9a2af9096, reversing changes made to 3e1bf721c69cb6df70c3dc3d3d4933e987339676. --- include/sway/tree/view.h | 2 - sway/criteria.c | 204 ++++++++++------------------------------------- sway/tree/view.c | 7 -- 3 files changed, 42 insertions(+), 171 deletions(-) (limited to 'sway/tree') diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 65cadea1..144ad038 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -176,8 +176,6 @@ const char *view_get_instance(struct sway_view *view); uint32_t view_get_x11_window_id(struct sway_view *view); -const char *view_get_window_role(struct sway_view *view); - uint32_t view_get_window_type(struct sway_view *view); const char *view_get_type(struct sway_view *view); diff --git a/sway/criteria.c b/sway/criteria.c index 294b2922..7da790e6 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -190,128 +190,19 @@ static bool generate_regex(pcre **regex, char *value) { return true; } -enum criteria_token { - T_APP_ID, - T_CLASS, - T_CON_ID, - T_CON_MARK, - T_FLOATING, - T_ID, - T_INSTANCE, - T_TILING, - T_TITLE, - T_URGENT, - T_WINDOW_ROLE, - T_WINDOW_TYPE, - T_WORKSPACE, - - T_INVALID, -}; - -static enum criteria_token token_from_name(char *name) { - if (strcmp(name, "app_id") == 0) { - return T_APP_ID; - } else if (strcmp(name, "class") == 0) { - return T_CLASS; - } else if (strcmp(name, "con_id") == 0) { - return T_CON_ID; - } else if (strcmp(name, "con_mark") == 0) { - return T_CON_MARK; - } else if (strcmp(name, "id") == 0) { - return T_ID; - } else if (strcmp(name, "instance") == 0) { - return T_INSTANCE; - } else if (strcmp(name, "title") == 0) { - return T_TITLE; - } else if (strcmp(name, "urgent") == 0) { - return T_URGENT; - } else if (strcmp(name, "window_role") == 0) { - return T_WINDOW_ROLE; - } else if (strcmp(name, "window_type") == 0) { - return T_WINDOW_TYPE; - } else if (strcmp(name, "workspace") == 0) { - return T_WORKSPACE; - } - return T_INVALID; -} - -/** - * Get a property of the focused view. - * - * Note that we are taking the focused view at the time of criteria parsing, not - * at the time of execution. This is because __focused__ only makes sense when - * using criteria via IPC. Using __focused__ in config is not useful because - * criteria is only executed once per view. - */ -static char *get_focused_prop(enum criteria_token token) { - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - - if (!focus || focus->type != C_VIEW) { - return NULL; - } - struct sway_view *view = focus->sway_view; - const char *value = NULL; - - switch (token) { - case T_APP_ID: - value = view_get_app_id(view); - break; - case T_CLASS: - value = view_get_class(view); - break; - case T_INSTANCE: - value = view_get_instance(view); - break; - case T_TITLE: - value = view_get_class(view); - break; - case T_WINDOW_ROLE: - value = view_get_class(view); - break; - case T_WORKSPACE: - { - struct sway_container *ws = container_parent(focus, C_WORKSPACE); - if (ws) { - value = ws->name; - } - } - break; - case T_CON_ID: // These do not support __focused__ - case T_CON_MARK: - case T_FLOATING: - case T_ID: - case T_TILING: - case T_URGENT: - case T_WINDOW_TYPE: - case T_INVALID: - break; - } - if (value) { - return strdup(value); - } - return NULL; -} - static bool parse_token(struct criteria *criteria, char *name, char *value) { - enum criteria_token token = token_from_name(name); - if (token == T_INVALID) { - const char *fmt = "Token '%s' is not recognized"; - int len = strlen(fmt) + strlen(name) - 1; - error = malloc(len); - snprintf(error, len, fmt, name); - return false; - } - - char *effective_value = NULL; - if (value && strcmp(value, "__focused__") == 0) { - effective_value = get_focused_prop(token); - } else if (value) { - effective_value = strdup(value); - } - // Require value, unless token is floating or tiled - if (!effective_value && token != T_FLOATING && token != T_TILING) { + if (!value && (strcmp(name, "title") == 0 + || strcmp(name, "app_id") == 0 + || strcmp(name, "class") == 0 + || strcmp(name, "instance") == 0 + || strcmp(name, "con_id") == 0 + || strcmp(name, "con_mark") == 0 + || strcmp(name, "window_role") == 0 + || strcmp(name, "window_type") == 0 + || strcmp(name, "id") == 0 + || strcmp(name, "urgent") == 0 + || strcmp(name, "workspace") == 0)) { const char *fmt = "Token '%s' requires a value"; int len = strlen(fmt) + strlen(name) - 1; error = malloc(len); @@ -319,64 +210,53 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { return false; } - char *endptr = NULL; - switch (token) { - case T_TITLE: - generate_regex(&criteria->title, effective_value); - break; - case T_APP_ID: - generate_regex(&criteria->app_id, effective_value); - break; - case T_CLASS: - generate_regex(&criteria->class, effective_value); - break; - case T_INSTANCE: - generate_regex(&criteria->instance, effective_value); - break; - case T_CON_ID: - criteria->con_id = strtoul(effective_value, &endptr, 10); + if (strcmp(name, "title") == 0) { + generate_regex(&criteria->title, value); + } else if (strcmp(name, "app_id") == 0) { + generate_regex(&criteria->app_id, value); + } else if (strcmp(name, "class") == 0) { + generate_regex(&criteria->class, value); + } else if (strcmp(name, "instance") == 0) { + generate_regex(&criteria->instance, value); + } else if (strcmp(name, "con_id") == 0) { + char *endptr; + criteria->con_id = strtoul(value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'con_id' should be numeric"); } - break; - case T_CON_MARK: - generate_regex(&criteria->con_mark, effective_value); - break; - case T_WINDOW_ROLE: - generate_regex(&criteria->window_role, effective_value); - break; - case T_WINDOW_TYPE: + } else if (strcmp(name, "con_mark") == 0) { + generate_regex(&criteria->con_mark, value); + } else if (strcmp(name, "window_role") == 0) { + generate_regex(&criteria->window_role, value); + } else if (strcmp(name, "window_type") == 0) { // TODO: This is a string but will be stored as an enum or integer - break; - case T_ID: - criteria->id = strtoul(effective_value, &endptr, 10); + } else if (strcmp(name, "id") == 0) { + char *endptr; + criteria->id = strtoul(value, &endptr, 10); if (*endptr != 0) { error = strdup("The value for 'id' should be numeric"); } - break; - case T_FLOATING: + } else if (strcmp(name, "floating") == 0) { criteria->floating = true; - break; - case T_TILING: + } else if (strcmp(name, "tiling") == 0) { criteria->tiling = true; - break; - case T_URGENT: - if (strcmp(effective_value, "latest") == 0) { + } else if (strcmp(name, "urgent") == 0) { + if (strcmp(value, "latest") == 0) { criteria->urgent = 'l'; - } else if (strcmp(effective_value, "oldest") == 0) { + } else if (strcmp(value, "oldest") == 0) { criteria->urgent = 'o'; } else { error = strdup("The value for 'urgent' must be 'latest' or 'oldest'"); } - break; - case T_WORKSPACE: - criteria->workspace = strdup(effective_value); - break; - case T_INVALID: - break; + } else if (strcmp(name, "workspace") == 0) { + criteria->workspace = strdup(value); + } else { + const char *fmt = "Token '%s' is not recognized"; + int len = strlen(fmt) + strlen(name) - 1; + error = malloc(len); + snprintf(error, len, fmt, name); } - free(effective_value); if (error) { return false; diff --git a/sway/tree/view.c b/sway/tree/view.c index 3b3b6eaf..7431ac06 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -79,13 +79,6 @@ uint32_t view_get_x11_window_id(struct sway_view *view) { return 0; } -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); - } - return NULL; -} - 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); -- cgit v1.2.3 From 32a572cecfd0f6072a78ce0a381a2f8365f9010a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 12 May 2018 08:52:54 -0400 Subject: Revert "Merge pull request #1943 from RyanDwyer/criteria-improvements" This reverts commit 3e1bf721c69cb6df70c3dc3d3d4933e987339676, reversing changes made to 2217518bd554d0f11dafa7ec4e8f35f2e4762fbd. --- include/sway/criteria.h | 73 ++--- include/sway/tree/view.h | 21 +- sway/commands.c | 42 +-- sway/commands/assign.c | 58 ++-- sway/commands/for_window.c | 35 ++- sway/criteria.c | 684 ++++++++++++++++++++++++-------------------- sway/desktop/wl_shell.c | 4 +- sway/desktop/xdg_shell_v6.c | 4 +- sway/desktop/xwayland.c | 69 +---- sway/sway.5.txt | 43 +-- sway/tree/view.c | 97 ++----- 11 files changed, 517 insertions(+), 613 deletions(-) (limited to 'sway/tree') diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 74da132c..ec256ddb 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -1,61 +1,42 @@ #ifndef _SWAY_CRITERIA_H #define _SWAY_CRITERIA_H -#include +#include "tree/container.h" #include "list.h" -#include "tree/view.h" - -enum criteria_type { - CT_COMMAND = 1 << 0, - CT_ASSIGN_OUTPUT = 1 << 1, - CT_ASSIGN_WORKSPACE = 1 << 2, -}; +/** + * Maps criteria (as a list of criteria tokens) to a command list. + * + * A list of tokens together represent a single criteria string (e.g. + * '[class="abc" title="xyz"]' becomes two criteria tokens). + * + * for_window: Views matching all criteria will have the bound command list + * executed on them. + * + * Set via `for_window `. + */ struct criteria { - enum criteria_type type; - char *raw; // entire criteria string (for logging) + list_t *tokens; // struct crit_token, contains compiled regex. + char *crit_raw; // entire criteria string (for logging) + char *cmdlist; - char *target; // workspace or output name for `assign` criteria - - pcre *title; - pcre *app_id; - pcre *class; - pcre *instance; - pcre *con_mark; - uint32_t con_id; // internal ID - uint32_t id; // X11 window ID - pcre *window_role; - uint32_t window_type; - bool floating; - bool tiling; - char urgent; // 'l' for latest or 'o' for oldest - char *workspace; }; -bool criteria_is_empty(struct criteria *criteria); +int criteria_cmp(const void *item, const void *data); +void free_criteria(struct criteria *crit); -void criteria_destroy(struct criteria *criteria); +// Pouplate list with crit_tokens extracted from criteria string, returns error +// string or NULL if successful. +char *extract_crit_tokens(list_t *tokens, const char *criteria); -/** - * Generate a criteria struct from a raw criteria string such as - * [class="foo" instance="bar"] (brackets inclusive). - * - * The error argument is expected to be an address of a null pointer. If an - * error is encountered, the function will return NULL and the pointer will be - * changed to point to the error string. This string should be freed afterwards. - */ -struct criteria *criteria_parse(char *raw, char **error); +// Returns list of criteria that match given container. These criteria have +// been set with `for_window` commands and have an associated cmdlist. +list_t *criteria_for(struct sway_container *cont); -/** - * Compile a list of criterias matching the given view. - * - * Criteria types can be bitwise ORed. - */ -list_t *criteria_for_view(struct sway_view *view, enum criteria_type types); +// Returns a list of all containers that match the given list of tokens. +list_t *container_for_crit_tokens(list_t *tokens); -/** - * Compile a list of views matching the given criteria. - */ -list_t *criteria_get_views(struct criteria *criteria); +// Returns true if any criteria in the given list matches this container +bool criteria_any(struct sway_container *cont, list_t *criteria); #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 144ad038..4ecd8c44 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -20,15 +20,11 @@ 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_string_prop)(struct sway_view *view, + const char *(*get_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); @@ -56,8 +52,6 @@ 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; @@ -97,9 +91,6 @@ 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; @@ -174,10 +165,6 @@ 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); - const char *view_get_type(struct sway_view *view); void view_configure(struct sway_view *view, double ox, double oy, int width, @@ -230,10 +217,4 @@ 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/commands.c b/sway/commands.c index 811f6cfa..2e1cdc2c 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -12,7 +12,6 @@ #include "sway/security.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" -#include "sway/tree/view.h" #include "stringop.h" #include "log.h" @@ -284,7 +283,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { char *head = exec; char *cmdlist; char *cmd; - list_t *views = NULL; + list_t *containers = NULL; if (seat == NULL) { // passing a NULL seat means we just pick the default seat @@ -301,18 +300,31 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { // Extract criteria (valid for this command list only). bool has_criteria = false; if (*head == '[') { - char *error = NULL; - struct criteria *criteria = criteria_parse(head, &error); - if (!criteria) { - results = cmd_results_new(CMD_INVALID, head, - "%s", error); - free(error); + has_criteria = true; + ++head; + char *criteria_string = argsep(&head, "]"); + if (head) { + ++head; + list_t *tokens = create_list(); + char *error; + + if ((error = extract_crit_tokens(tokens, criteria_string))) { + wlr_log(L_DEBUG, "criteria string parse error: %s", error); + results = cmd_results_new(CMD_INVALID, criteria_string, + "Can't parse criteria string: %s", error); + free(error); + free(tokens); + goto cleanup; + } + containers = container_for_crit_tokens(tokens); + + free(tokens); + } else { + if (!results) { + results = cmd_results_new(CMD_INVALID, criteria_string, "Unmatched ["); + } goto cleanup; } - views = criteria_get_views(criteria); - head += strlen(criteria->raw); - criteria_destroy(criteria); - has_criteria = true; // Skip leading whitespace head += strspn(head, whitespace); } @@ -369,9 +381,8 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } free_cmd_results(res); } else { - for (int i = 0; i < views->length; ++i) { - struct sway_view *view = views->items[i]; - config->handler_context.current_container = view->swayc; + for (int i = 0; i < containers->length; ++i) { + config->handler_context.current_container = containers->items[i]; struct cmd_results *res = handler->handle(argc-1, argv+1); if (res->status != CMD_SUCCESS) { free_argv(argc, argv); @@ -389,7 +400,6 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { } while(head); cleanup: free(exec); - free(views); if (!results) { results = cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/assign.c b/sway/commands/assign.c index 9d15e166..eb7329aa 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -5,7 +5,6 @@ #include "sway/criteria.h" #include "list.h" #include "log.h" -#include "stringop.h" struct cmd_results *cmd_assign(int argc, char **argv) { struct cmd_results *error = NULL; @@ -13,39 +12,46 @@ struct cmd_results *cmd_assign(int argc, char **argv) { return error; } - // Create criteria - char *err_str = NULL; - struct criteria *criteria = criteria_parse(argv[0], &err_str); - if (!criteria) { - error = cmd_results_new(CMD_INVALID, "assign", err_str); - free(err_str); - return error; - } - - ++argv; - int target_len = argc - 1; + char *criteria = *argv++; if (strncmp(*argv, "→", strlen("→")) == 0) { if (argc < 3) { return cmd_results_new(CMD_INVALID, "assign", "Missing workspace"); } - ++argv; - --target_len; + argv++; } - if (strcmp(*argv, "output") == 0) { - criteria->type = CT_ASSIGN_OUTPUT; - ++argv; - --target_len; - } else { - criteria->type = CT_ASSIGN_WORKSPACE; + char *movecmd = "move container to workspace "; + size_t arglen = strlen(movecmd) + strlen(*argv) + 1; + char *cmdlist = calloc(1, arglen); + if (!cmdlist) { + return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate command list"); } + snprintf(cmdlist, arglen, "%s%s", movecmd, *argv); - criteria->target = join_args(argv, target_len); - - list_add(config->criteria, criteria); - wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", criteria->raw, - criteria->target); + struct criteria *crit = malloc(sizeof(struct criteria)); + if (!crit) { + free(cmdlist); + return cmd_results_new(CMD_FAILURE, "assign", "Unable to allocate criteria"); + } + crit->crit_raw = strdup(criteria); + crit->cmdlist = cmdlist; + crit->tokens = create_list(); + char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + if (err_str) { + error = cmd_results_new(CMD_INVALID, "assign", err_str); + free(err_str); + free_criteria(crit); + } else if (crit->tokens->length == 0) { + error = cmd_results_new(CMD_INVALID, "assign", "Found no name/value pairs in criteria"); + free_criteria(crit); + } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) { + wlr_log(L_DEBUG, "assign: Duplicate, skipping."); + free_criteria(crit); + } else { + wlr_log(L_DEBUG, "assign: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist); + list_add(config->criteria, crit); + } + return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index 8c425a1d..dd5461f0 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c @@ -11,20 +11,31 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) { return error; } + // add command to a criteria/command pair that is run against views when they appear. + char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1); - char *err_str = NULL; - struct criteria *criteria = criteria_parse(argv[0], &err_str); - if (!criteria) { + struct criteria *crit = calloc(sizeof(struct criteria), 1); + if (!crit) { + return cmd_results_new(CMD_FAILURE, "for_window", "Unable to allocate criteria"); + } + crit->crit_raw = strdup(criteria); + crit->cmdlist = cmdlist; + crit->tokens = create_list(); + char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw); + + if (err_str) { error = cmd_results_new(CMD_INVALID, "for_window", err_str); free(err_str); - return error; + free_criteria(crit); + } else if (crit->tokens->length == 0) { + error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria"); + free_criteria(crit); + } else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) { + wlr_log(L_DEBUG, "for_window: Duplicate, skipping."); + free_criteria(crit); + } else { + wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist); + list_add(config->criteria, crit); } - - criteria->type = CT_COMMAND; - criteria->cmdlist = join_args(argv + 1, argc - 1); - - list_add(config->criteria, criteria); - wlr_log(L_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); - - return cmd_results_new(CMD_SUCCESS, NULL, NULL); + return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/criteria.c b/sway/criteria.c index 7da790e6..22e9a49b 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -11,381 +11,435 @@ #include "list.h" #include "log.h" -bool criteria_is_empty(struct criteria *criteria) { - return !criteria->title - && !criteria->app_id - && !criteria->class - && !criteria->instance - && !criteria->con_mark - && !criteria->con_id - && !criteria->id - && !criteria->window_role - && !criteria->window_type - && !criteria->floating - && !criteria->tiling - && !criteria->urgent - && !criteria->workspace; -} - -void criteria_destroy(struct criteria *criteria) { - pcre_free(criteria->title); - pcre_free(criteria->app_id); - pcre_free(criteria->class); - pcre_free(criteria->instance); - pcre_free(criteria->con_mark); - pcre_free(criteria->window_role); - free(criteria->workspace); - - free(criteria->raw); - free(criteria); -} - -static int regex_cmp(const char *item, const pcre *regex) { - return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); -} +enum criteria_type { // *must* keep in sync with criteria_strings[] + CRIT_APP_ID, + CRIT_CLASS, + CRIT_CON_ID, + CRIT_CON_MARK, + CRIT_FLOATING, + CRIT_ID, + CRIT_INSTANCE, + CRIT_TILING, + CRIT_TITLE, + CRIT_URGENT, + CRIT_WINDOW_ROLE, + CRIT_WINDOW_TYPE, + CRIT_WORKSPACE, + CRIT_LAST +}; -static bool criteria_matches_view(struct criteria *criteria, - struct sway_view *view) { - if (criteria->title) { - const char *title = view_get_title(view); - if (!title || regex_cmp(title, criteria->title) != 0) { - return false; - } - } +static const char * const criteria_strings[CRIT_LAST] = { + [CRIT_APP_ID] = "app_id", + [CRIT_CLASS] = "class", + [CRIT_CON_ID] = "con_id", + [CRIT_CON_MARK] = "con_mark", + [CRIT_FLOATING] = "floating", + [CRIT_ID] = "id", + [CRIT_INSTANCE] = "instance", + [CRIT_TILING] = "tiling", + [CRIT_TITLE] = "title", + [CRIT_URGENT] = "urgent", // either "latest" or "oldest" ... + [CRIT_WINDOW_ROLE] = "window_role", + [CRIT_WINDOW_TYPE] = "window_type", + [CRIT_WORKSPACE] = "workspace" +}; - if (criteria->app_id) { - const char *app_id = view_get_app_id(view); - if (!app_id || regex_cmp(app_id, criteria->app_id) != 0) { - return false; - } - } +/** + * A single criteria token (ie. value/regex pair), + * e.g. 'class="some class regex"'. + */ +struct crit_token { + enum criteria_type type; + pcre *regex; + char *raw; +}; - if (criteria->class) { - const char *class = view_get_class(view); - if (!class || regex_cmp(class, criteria->class) != 0) { - return false; - } - } +static void free_crit_token(struct crit_token *crit) { + pcre_free(crit->regex); + free(crit->raw); + free(crit); +} - if (criteria->instance) { - const char *instance = view_get_instance(view); - if (!instance || regex_cmp(instance, criteria->instance) != 0) { - return false; - } +static void free_crit_tokens(list_t *crit_tokens) { + for (int i = 0; i < crit_tokens->length; i++) { + free_crit_token(crit_tokens->items[i]); } + list_free(crit_tokens); +} - if (criteria->con_mark) { - // TODO - return false; +// Extracts criteria string from its brackets. Returns new (duplicate) +// substring. +static char *criteria_from(const char *arg) { + char *criteria = NULL; + if (*arg == '[') { + criteria = strdup(arg + 1); + } else { + criteria = strdup(arg); } - if (criteria->con_id) { // Internal ID - if (!view->swayc || view->swayc->id != criteria->con_id) { - return false; - } + int last = strlen(criteria) - 1; + if (criteria[last] == ']') { + criteria[last] = '\0'; } + return criteria; +} - if (criteria->id) { // X11 window ID - uint32_t x11_window_id = view_get_x11_window_id(view); - if (!x11_window_id || x11_window_id != criteria->id) { - return false; +// Return instances of c found in str. +static int countchr(char *str, char c) { + int found = 0; + for (int i = 0; str[i]; i++) { + if (str[i] == c) { + ++found; } } + return found; +} - if (criteria->window_role) { - // TODO - } - - if (criteria->window_type) { - uint32_t type = view_get_window_type(view); - if (!type || type != criteria->window_type) { - return false; +// criteria_str is e.g. '[class="some class regex" instance="instance name"]'. +// +// Will create array of pointers in buf, where first is duplicate of given +// string (must be freed) and the rest are pointers to names and values in the +// base string (every other, naturally). argc will be populated with the length +// of buf. +// +// Returns error string or NULL if successful. +static char *crit_tokens(int *argc, char ***buf, + const char * const criteria_str) { + wlr_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str); + char *base = criteria_from(criteria_str); + char *head = base; + char *namep = head; // start of criteria name + char *valp = NULL; // start of value + + // We're going to place EOS markers where we need to and fill up an array + // of pointers to the start of each token (either name or value). + int pairs = countchr(base, '='); + int max_tokens = pairs * 2 + 1; // this gives us at least enough slots + + char **argv = *buf = calloc(max_tokens, sizeof(char*)); + argv[0] = base; // this needs to be freed by caller + bool quoted = true; + + *argc = 1; // uneven = name, even = value + while (*head && *argc < max_tokens) { + if (namep != head && *(head - 1) == '\\') { + // escaped character: don't try to parse this + } else if (*head == '=' && namep != head) { + if (*argc % 2 != 1) { + // we're not expecting a name + return strdup("Unable to parse criteria: " + "Found out of place equal sign"); + } else { + // name ends here + char *end = head; // don't want to rewind the head + while (*(end - 1) == ' ') { + --end; + } + *end = '\0'; + if (*(namep) == ' ') { + namep = strrchr(namep, ' ') + 1; + } + argv[*argc] = namep; + *argc += 1; + } + } else if (*head == '"') { + if (*argc % 2 != 0) { + // we're not expecting a value + return strdup("Unable to parse criteria: " + "Found quoted value where it was not expected"); + } else if (!valp) { // value starts here + valp = head + 1; + quoted = true; + } else { + // value ends here + argv[*argc] = valp; + *argc += 1; + *head = '\0'; + valp = NULL; + namep = head + 1; + } + } else if (*argc % 2 == 0 && *head != ' ') { + // parse unquoted values + if (!valp) { + quoted = false; + valp = head; // value starts here + } + } else if (valp && !quoted && *head == ' ') { + // value ends here + argv[*argc] = valp; + *argc += 1; + *head = '\0'; + valp = NULL; + namep = head + 1; } + head++; } - if (criteria->floating) { - // TODO - return false; - } - - if (criteria->tiling) { - // TODO - } - - if (criteria->urgent) { - // TODO - return false; - } - - if (criteria->workspace) { - if (!view->swayc) { - return false; - } - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - if (!ws || strcmp(ws->name, criteria->workspace) != 0) { - return false; - } + // catch last unquoted value if needed + if (valp && !quoted && !*head) { + argv[*argc] = valp; + *argc += 1; } - return true; + return NULL; } -list_t *criteria_for_view(struct sway_view *view, enum criteria_type types) { - list_t *criterias = config->criteria; - list_t *matches = create_list(); - for (int i = 0; i < criterias->length; ++i) { - struct criteria *criteria = criterias->items[i]; - if ((criteria->type & types) && criteria_matches_view(criteria, view)) { - list_add(matches, criteria); +// Returns error string on failure or NULL otherwise. +static char *parse_criteria_name(enum criteria_type *type, char *name) { + *type = CRIT_LAST; + for (int i = 0; i < CRIT_LAST; i++) { + if (strcmp(criteria_strings[i], name) == 0) { + *type = (enum criteria_type) i; + break; } } - return matches; -} - -struct match_data { - struct criteria *criteria; - list_t *matches; -}; - -static void criteria_get_views_iterator(struct sway_container *container, - void *data) { - struct match_data *match_data = data; - if (container->type == C_VIEW) { - if (criteria_matches_view(match_data->criteria, container->sway_view)) { - list_add(match_data->matches, container->sway_view); - } + if (*type == CRIT_LAST) { + const char *fmt = "Criteria type '%s' is invalid or unsupported."; + int len = strlen(name) + strlen(fmt) - 1; + char *error = malloc(len); + snprintf(error, len, fmt, name); + return error; + } else if (*type == CRIT_URGENT || *type == CRIT_WINDOW_ROLE || + *type == CRIT_WINDOW_TYPE) { + // (we're just being helpful here) + const char *fmt = "\"%s\" criteria currently unsupported, " + "no window will match this"; + int len = strlen(fmt) + strlen(name) - 1; + char *error = malloc(len); + snprintf(error, len, fmt, name); + return error; } + return NULL; } -list_t *criteria_get_views(struct criteria *criteria) { - list_t *matches = create_list(); - struct match_data data = { - .criteria = criteria, - .matches = matches, - }; - container_for_each_descendant_dfs(&root_container, - criteria_get_views_iterator, &data); - return matches; -} - -// The error pointer is used for parsing functions, and saves having to pass it -// as an argument in several places. -char *error = NULL; - // Returns error string on failure or NULL otherwise. -static bool generate_regex(pcre **regex, char *value) { +static char *generate_regex(pcre **regex, char *value) { const char *reg_err; int offset; *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); if (!*regex) { - const char *fmt = "Regex compilation for '%s' failed: %s"; + const char *fmt = "Regex compilation (for '%s') failed: %s"; int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; - error = malloc(len); + char *error = malloc(len); snprintf(error, len, fmt, value, reg_err); - return false; + return error; } + return NULL; +} - return true; +// Test whether the criterion corresponds to the currently focused window +static bool crit_is_focused(const char *value) { + return !strcmp(value, "focused") || !strcmp(value, "__focused__"); } -static bool parse_token(struct criteria *criteria, char *name, char *value) { - // Require value, unless token is floating or tiled - if (!value && (strcmp(name, "title") == 0 - || strcmp(name, "app_id") == 0 - || strcmp(name, "class") == 0 - || strcmp(name, "instance") == 0 - || strcmp(name, "con_id") == 0 - || strcmp(name, "con_mark") == 0 - || strcmp(name, "window_role") == 0 - || strcmp(name, "window_type") == 0 - || strcmp(name, "id") == 0 - || strcmp(name, "urgent") == 0 - || strcmp(name, "workspace") == 0)) { - const char *fmt = "Token '%s' requires a value"; - int len = strlen(fmt) + strlen(name) - 1; - error = malloc(len); - snprintf(error, len, fmt, name); - return false; +// Populate list with crit_tokens extracted from criteria string, returns error +// string or NULL if successful. +char *extract_crit_tokens(list_t *tokens, const char * const criteria) { + int argc; + char **argv = NULL, *error = NULL; + if ((error = crit_tokens(&argc, &argv, criteria))) { + goto ect_cleanup; } - - if (strcmp(name, "title") == 0) { - generate_regex(&criteria->title, value); - } else if (strcmp(name, "app_id") == 0) { - generate_regex(&criteria->app_id, value); - } else if (strcmp(name, "class") == 0) { - generate_regex(&criteria->class, value); - } else if (strcmp(name, "instance") == 0) { - generate_regex(&criteria->instance, value); - } else if (strcmp(name, "con_id") == 0) { - char *endptr; - criteria->con_id = strtoul(value, &endptr, 10); - if (*endptr != 0) { - error = strdup("The value for 'con_id' should be numeric"); - } - } else if (strcmp(name, "con_mark") == 0) { - generate_regex(&criteria->con_mark, value); - } else if (strcmp(name, "window_role") == 0) { - generate_regex(&criteria->window_role, value); - } else if (strcmp(name, "window_type") == 0) { - // TODO: This is a string but will be stored as an enum or integer - } else if (strcmp(name, "id") == 0) { - char *endptr; - criteria->id = strtoul(value, &endptr, 10); - if (*endptr != 0) { - error = strdup("The value for 'id' should be numeric"); - } - } else if (strcmp(name, "floating") == 0) { - criteria->floating = true; - } else if (strcmp(name, "tiling") == 0) { - criteria->tiling = true; - } else if (strcmp(name, "urgent") == 0) { - if (strcmp(value, "latest") == 0) { - criteria->urgent = 'l'; - } else if (strcmp(value, "oldest") == 0) { - criteria->urgent = 'o'; + for (int i = 1; i + 1 < argc; i += 2) { + char* name = argv[i], *value = argv[i + 1]; + struct crit_token *token = calloc(1, sizeof(struct crit_token)); + token->raw = strdup(value); + + if ((error = parse_criteria_name(&token->type, name))) { + free_crit_token(token); + goto ect_cleanup; + } else if (token->type == CRIT_URGENT || crit_is_focused(value)) { + wlr_log(L_DEBUG, "%s -> \"%s\"", name, value); + list_add(tokens, token); + } else if((error = generate_regex(&token->regex, value))) { + free_crit_token(token); + goto ect_cleanup; } else { - error = - strdup("The value for 'urgent' must be 'latest' or 'oldest'"); + wlr_log(L_DEBUG, "%s -> /%s/", name, value); + list_add(tokens, token); } - } else if (strcmp(name, "workspace") == 0) { - criteria->workspace = strdup(value); - } else { - const char *fmt = "Token '%s' is not recognized"; - int len = strlen(fmt) + strlen(name) - 1; - error = malloc(len); - snprintf(error, len, fmt, name); } - - if (error) { - return false; - } - - return true; +ect_cleanup: + free(argv[0]); // base string + free(argv); + return error; } -static void skip_spaces(char **head) { - while (**head == ' ') { - ++*head; - } +static int regex_cmp(const char *item, const pcre *regex) { + return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } -// Remove escaping slashes from value -static void unescape(char *value) { - if (!strchr(value, '\\')) { - return; - } - char *copy = calloc(strlen(value) + 1, 1); - char *readhead = value; - char *writehead = copy; - while (*readhead) { - if (*readhead == '\\' && *(readhead + 1) == '"') { - // skip the slash - ++readhead; - } - *writehead = *readhead; - ++writehead; - ++readhead; +// test a single view if it matches list of criteria tokens (all of them). +static bool criteria_test(struct sway_container *cont, list_t *tokens) { + if (cont->type != C_CONTAINER && cont->type != C_VIEW) { + return false; } - strcpy(value, copy); - free(copy); -} + int matches = 0; + for (int i = 0; i < tokens->length; i++) { + struct crit_token *crit = tokens->items[i]; + switch (crit->type) { + case CRIT_CLASS: + { + const char *class = view_get_class(cont->sway_view); + if (!class) { + break; + } + if (crit->regex && regex_cmp(class, crit->regex) == 0) { + matches++; + } + break; + } + case CRIT_CON_ID: + { + char *endptr; + size_t crit_id = strtoul(crit->raw, &endptr, 10); -/** - * Parse a raw criteria string such as [class="foo" instance="bar"] into a - * criteria struct. - * - * If errors are found, NULL will be returned and the error argument will be - * populated with an error string. - */ -struct criteria *criteria_parse(char *raw, char **error_arg) { - free(error); - error = NULL; - - char *head = raw; - skip_spaces(&head); - if (*head != '[') { - *error_arg = strdup("No criteria"); - return NULL; - } - ++head; - - struct criteria *criteria = calloc(sizeof(struct criteria), 1); - char *name = NULL, *value = NULL; - bool in_quotes = false; - - while (*head && *head != ']') { - skip_spaces(&head); - // Parse token name - char *namestart = head; - while ((*head >= 'a' && *head <= 'z') || *head == '_') { - ++head; - } - name = calloc(head - namestart + 1, 1); - strncpy(name, namestart, head - namestart); - // Parse token value - skip_spaces(&head); - value = NULL; - if (*head == '=') { - ++head; - skip_spaces(&head); - if (*head == '"') { - in_quotes = true; - ++head; + if (*endptr == 0 && cont->id == crit_id) { + ++matches; + } + break; } - char *valuestart = head; - if (in_quotes) { - while (*head && (*head != '"' || *(head - 1) == '\\')) { - ++head; + case CRIT_CON_MARK: + // TODO + break; + case CRIT_FLOATING: + // TODO + break; + case CRIT_ID: + // TODO + break; + case CRIT_APP_ID: + { + const char *app_id = view_get_app_id(cont->sway_view); + if (!app_id) { + break; } - if (!*head) { - *error_arg = strdup("Quote mismatch in criteria"); - goto cleanup; + + if (crit->regex && regex_cmp(app_id, crit->regex) == 0) { + matches++; } - } else { - while (*head && *head != ' ' && *head != ']') { - ++head; + break; + } + case CRIT_INSTANCE: + { + const char *instance = view_get_instance(cont->sway_view); + if (!instance) { + break; + } + + if (crit->regex && regex_cmp(instance, crit->regex) == 0) { + matches++; } + break; } - value = calloc(head - valuestart + 1, 1); - strncpy(value, valuestart, head - valuestart); - if (in_quotes) { - ++head; - in_quotes = false; + case CRIT_TILING: + // TODO + break; + case CRIT_TITLE: + { + const char *title = view_get_title(cont->sway_view); + if (!title) { + break; + } + + if (crit->regex && regex_cmp(title, crit->regex) == 0) { + matches++; + } + break; } - unescape(value); + case CRIT_URGENT: + // TODO "latest" or "oldest" + break; + case CRIT_WINDOW_ROLE: + // TODO + break; + case CRIT_WINDOW_TYPE: + // TODO + break; + case CRIT_WORKSPACE: + // TODO + break; + default: + sway_abort("Invalid criteria type (%i)", crit->type); + break; } - wlr_log(L_DEBUG, "Found pair: %s=%s", name, value); - if (!parse_token(criteria, name, value)) { - *error_arg = error; - goto cleanup; + } + return matches == tokens->length; +} + +int criteria_cmp(const void *a, const void *b) { + if (a == b) { + return 0; + } else if (!a) { + return -1; + } else if (!b) { + return 1; + } + const struct criteria *crit_a = a, *crit_b = b; + int cmp = lenient_strcmp(crit_a->cmdlist, crit_b->cmdlist); + if (cmp != 0) { + return cmp; + } + return lenient_strcmp(crit_a->crit_raw, crit_b->crit_raw); +} + +void free_criteria(struct criteria *crit) { + if (crit->tokens) { + free_crit_tokens(crit->tokens); + } + if (crit->cmdlist) { + free(crit->cmdlist); + } + if (crit->crit_raw) { + free(crit->crit_raw); + } + free(crit); +} + +bool criteria_any(struct sway_container *cont, list_t *criteria) { + for (int i = 0; i < criteria->length; i++) { + struct criteria *bc = criteria->items[i]; + if (criteria_test(cont, bc->tokens)) { + return true; } - skip_spaces(&head); - free(name); - free(value); - name = NULL; - value = NULL; } - if (*head != ']') { - *error_arg = strdup("No closing brace found in criteria"); - goto cleanup; + return false; +} + +list_t *criteria_for(struct sway_container *cont) { + list_t *criteria = config->criteria, *matches = create_list(); + for (int i = 0; i < criteria->length; i++) { + struct criteria *bc = criteria->items[i]; + if (criteria_test(cont, bc->tokens)) { + list_add(matches, bc); + } } + return matches; +} - if (criteria_is_empty(criteria)) { - *error_arg = strdup("Criteria is empty"); - goto cleanup; +struct list_tokens { + list_t *list; + list_t *tokens; +}; + +static void container_match_add(struct sway_container *container, + struct list_tokens *list_tokens) { + if (criteria_test(container, list_tokens->tokens)) { + list_add(list_tokens->list, container); } +} - ++head; - int len = head - raw; - criteria->raw = calloc(len + 1, 1); - strncpy(criteria->raw, raw, len); - return criteria; +list_t *container_for_crit_tokens(list_t *tokens) { + struct list_tokens list_tokens = + (struct list_tokens){create_list(), tokens}; -cleanup: - free(name); - free(value); - criteria_destroy(criteria); - return NULL; + container_for_each_descendant_dfs(&root_container, + (void (*)(struct sway_container *, void *))container_match_add, + &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 cb3774f7..99e8947b 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_string_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_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_string_prop = get_string_prop, + .get_prop = get_prop, .configure = configure, .close = _close, .destroy = destroy, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index f685ef71..8ecb330d 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_string_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_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_string_prop = get_string_prop, + .get_prop = get_prop, .configure = configure, .set_activated = set_activated, .set_fullscreen = set_fullscreen, diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 554c070e..8f935760 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_string_prop(struct sway_view *view, enum sway_view_prop prop) { +static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { if (xwayland_view_from_view(view) == NULL) { return NULL; } @@ -135,27 +135,11 @@ static const char *get_string_prop(struct sway_view *view, enum sway_view_prop p 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; - 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); @@ -216,17 +200,13 @@ 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_string_prop = get_string_prop, - .get_int_prop = get_int_prop, + .get_prop = get_prop, .configure = configure, .set_activated = set_activated, .set_fullscreen = set_fullscreen, @@ -243,6 +223,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { view_update_size(view, xwayland_view->pending_width, xwayland_view->pending_height); view_damage_from(view); + view_update_title(view, false); } static void handle_unmap(struct wl_listener *listener, void *data) { @@ -304,40 +285,6 @@ 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->mapped) { - 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->mapped) { - 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->mapped) { - 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); @@ -376,16 +323,6 @@ 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/sway.5.txt b/sway/sway.5.txt index 704bb699..03975349 100644 --- a/sway/sway.5.txt +++ b/sway/sway.5.txt @@ -485,15 +485,10 @@ Mark all Firefox windows with "Browser": Currently supported attributes: -**app_id**:: - Compare value against the app id. Can be a regular expression. If value is - __focused__, then the app id must be the same as that of the currently - focused window. - **class**:: - Compare value against the window class. Can be a regular expression. If - value is __focused__, then the window class must be the same as that of the - currently focused window. + Compare value against the window class. Can be a regular expression. If value + is _focused_, then the window class must be the same as that of the currently + focused window. **con_id**:: Compare against the internal container ID, which you can find via IPC. @@ -505,38 +500,20 @@ Currently supported attributes: Matches against floating windows. **id**:: - Compare value against the X11 window id. Must be numeric. - -**instance**:: - Compare value against the window instance. Can be a regular expression. If - value is __focused__, then the window instance must be the same as that of - the currently focused window. - -**tiling**:: - Matches against tiling windows. + Compare value against the app id. Can be a regular expression. **title**:: Compare against the window title. Can be a regular expression. If value is - __focused__, then the window title must be the same as that of the currently + _focused_ then the window title must be the same as that of the currently focused window. -**urgent**:: - Compares the urgent state of the window. Can be "latest" or "oldest". - -**window_role**:: - Compare against the window role (WM_WINDOW_ROLE). Can be a regular - expression. If value is __focused__, then the window role must be the same - as that of the currently focused window. - -**window_type**:: - Compare against the window type (_NET_WM_WINDOW_TYPE). Possible values are - normal, dialog, utility, toolbar, splash, menu, dropdown_menu, popup_menu, - tooltip and notification. +**tiling**:: + Matches against tiling windows. **workspace**:: - Compare against the workspace name for this view. Can be a regular - expression. If the value is __focused__, then all the views on the currently - focused workspace matches. + Compare against the workspace name for this view. Can be a regular expression. + If the value is _focused_, then all the views on the currently focused workspace + matches. See Also -------- diff --git a/sway/tree/view.c b/sway/tree/view.c index 7431ac06..424c1084 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -3,7 +3,6 @@ #include #include #include -#include "list.h" #include "log.h" #include "sway/criteria.h" #include "sway/commands.h" @@ -20,7 +19,6 @@ 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); } @@ -33,8 +31,6 @@ void view_destroy(struct sway_view *view) { view_unmap(view); } - list_free(view->executed_criteria); - container_destroy(view->swayc); if (view->impl->destroy) { @@ -45,47 +41,33 @@ void view_destroy(struct sway_view *view) { } const char *view_get_title(struct sway_view *view) { - if (view->impl->get_string_prop) { - return view->impl->get_string_prop(view, VIEW_PROP_TITLE); + if (view->impl->get_prop) { + return view->impl->get_prop(view, VIEW_PROP_TITLE); } return NULL; } const char *view_get_app_id(struct sway_view *view) { - if (view->impl->get_string_prop) { - return view->impl->get_string_prop(view, VIEW_PROP_APP_ID); + if (view->impl->get_prop) { + return view->impl->get_prop(view, VIEW_PROP_APP_ID); } return NULL; } const char *view_get_class(struct sway_view *view) { - if (view->impl->get_string_prop) { - return view->impl->get_string_prop(view, VIEW_PROP_CLASS); + if (view->impl->get_prop) { + return view->impl->get_prop(view, VIEW_PROP_CLASS); } return NULL; } const char *view_get_instance(struct sway_view *view) { - if (view->impl->get_string_prop) { - return view->impl->get_string_prop(view, VIEW_PROP_INSTANCE); + if (view->impl->get_prop) { + return view->impl->get_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; -} - const char *view_get_type(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_WL_SHELL: @@ -302,36 +284,19 @@ static void view_handle_container_reparent(struct wl_listener *listener, } } -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) { +static void view_execute_criteria(struct sway_view *view) { + if (!sway_assert(view->swayc, "cannot run criteria for unmapped view")) { return; } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *prior_workspace = container_parent(view->swayc, C_WORKSPACE); - list_t *criterias = criteria_for_view(view, CT_COMMAND); - for (int i = 0; i < criterias->length; i++) { - struct criteria *criteria = criterias->items[i]; - wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); - if (view_has_executed_criteria(view, criteria)) { - wlr_log(L_DEBUG, "Criteria already executed"); - continue; - } - wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", - criteria->raw, view, criteria->cmdlist); - list_add(view->executed_criteria, criteria); - struct cmd_results *res = execute_command(criteria->cmdlist, NULL); + 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'", + crit->crit_raw, view, crit->cmdlist); + 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); } @@ -340,7 +305,7 @@ void view_execute_criteria(struct sway_view *view) { // so always refocus in-between command lists seat_set_focus(seat, view->swayc); } - list_free(criterias); + list_free(criteria); seat_set_focus(seat, seat_get_focus_inactive(seat, prior_workspace)); } @@ -350,26 +315,9 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { } struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focus(seat); - struct sway_container *cont = NULL; - - // Check if there's any `assign` criteria for the view - list_t *criterias = criteria_for_view(view, - CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); - if (criterias->length) { - struct criteria *criteria = criterias->items[0]; - if (criteria->type == CT_ASSIGN_WORKSPACE) { - struct sway_container *workspace = workspace_by_name(criteria->target); - if (!workspace) { - workspace = workspace_create(NULL, criteria->target); - } - focus = seat_get_focus_inactive(seat, workspace); - } else { - // TODO: CT_ASSIGN_OUTPUT - } - } - free(criterias); - cont = container_view_create(focus, view); + struct sway_container *focus = seat_get_focus_inactive(seat, + &root_container); + struct sway_container *cont = container_view_create(focus, view); view->surface = wlr_surface; view->swayc = cont; @@ -387,11 +335,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { arrange_children_of(cont->parent); input_manager_set_focus(input_manager, cont); - view_update_title(view, false); - view_execute_criteria(view); - container_damage_whole(cont); view_handle_container_reparent(&view->container_reparent, NULL); + + view_execute_criteria(view); } void view_unmap(struct sway_view *view) { -- cgit v1.2.3