diff --git a/include/sway/criteria.h b/include/sway/criteria.h index ad8610cd7..165207f5f 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -24,6 +24,18 @@ struct pattern { pcre *regex; }; +enum criteria_idle_inhibitor { + // implicit: C_IDLE_INHIBITOR_UNSPEC = 0, + C_IDLE_INHIBITOR_NONE = 1, + C_IDLE_INHIBITOR_APPLICATION, + C_IDLE_INHIBITOR_USER, + C_IDLE_INHIBITOR_FOCUS, + C_IDLE_INHIBITOR_FULLSCREEN, + C_IDLE_INHIBITOR_OPEN, + C_IDLE_INHIBITOR_VISIBLE, + C_IDLE_INHIBITOR_ACTIVE, +}; + struct criteria { enum criteria_type type; char *raw; // entire criteria string (for logging) @@ -47,6 +59,7 @@ struct criteria { char urgent; // 'l' for latest or 'o' for oldest struct pattern *workspace; pid_t pid; + enum criteria_idle_inhibitor idle_inhibitor; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 4d4e59b0b..29c9e94d6 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -29,12 +29,18 @@ struct sway_idle_inhibitor_v1 { struct wl_listener destroy; }; +bool sway_idle_inhibit_v1_inhibitor_check_active( + struct sway_idle_inhibitor_v1 *inhibitor); + void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibit_manager_v1 *manager); void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode); +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_inhibitor_for_view( + struct sway_view *view); + struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( struct sway_view *view); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 4d3532d27..8a967a812 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -341,4 +341,9 @@ void view_save_buffer(struct sway_view *view); bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); +void view_schedule_criteria_execution_from_wlr_surface( + struct wlr_surface *wlr_surface); + +void view_schedule_criteria_execution_from_view(struct sway_view *view); + #endif diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c index aebc2bf9f..94ea3df8d 100644 --- a/sway/commands/inhibit_idle.c +++ b/sway/commands/inhibit_idle.c @@ -47,5 +47,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); } + view_execute_criteria(con->view); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/criteria.c b/sway/criteria.c index 02b04fc89..12c8fad75 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -5,6 +5,7 @@ #include #include #include "sway/criteria.h" +#include "sway/desktop/idle_inhibit_v1.h" #include "sway/tree/container.h" #include "sway/config.h" #include "sway/tree/root.h" @@ -32,7 +33,8 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->tiling && !criteria->urgent && !criteria->workspace - && !criteria->pid; + && !criteria->pid + && !criteria->idle_inhibitor; } // The error pointer is used for parsing functions, and saves having to pass it @@ -377,6 +379,61 @@ static bool criteria_matches_view(struct criteria *criteria, } } + if (criteria->idle_inhibitor) { + struct sway_idle_inhibitor_v1 *sway_inhibitor = + sway_idle_inhibit_v1_inhibitor_for_view(view); + switch (criteria->idle_inhibitor) { + case C_IDLE_INHIBITOR_NONE: + if (sway_inhibitor) { + return false; + } + break; + case C_IDLE_INHIBITOR_APPLICATION: + if (!sway_inhibitor || sway_inhibitor->mode != + INHIBIT_IDLE_APPLICATION) { + return false; + } + break; + case C_IDLE_INHIBITOR_USER: + if (!sway_inhibitor || sway_inhibitor->mode == + INHIBIT_IDLE_APPLICATION) { + return false; + } + break; + case C_IDLE_INHIBITOR_FOCUS: + if (!sway_inhibitor || sway_inhibitor->mode != + INHIBIT_IDLE_FOCUS) { + return false; + } + break; + case C_IDLE_INHIBITOR_FULLSCREEN: + if (!sway_inhibitor || sway_inhibitor->mode != + INHIBIT_IDLE_FULLSCREEN) { + return false; + } + break; + case C_IDLE_INHIBITOR_OPEN: + if (!sway_inhibitor || sway_inhibitor->mode != + INHIBIT_IDLE_OPEN) { + return false; + } + break; + case C_IDLE_INHIBITOR_VISIBLE: + if (!sway_inhibitor || sway_inhibitor->mode != + INHIBIT_IDLE_VISIBLE) { + return false; + } + break; + case C_IDLE_INHIBITOR_ACTIVE: + if (!sway_inhibitor || + !sway_idle_inhibit_v1_inhibitor_check_active( + sway_inhibitor)) { + return false; + } + break; + } + } + return true; } @@ -466,6 +523,7 @@ enum criteria_token { T_URGENT, T_WORKSPACE, T_PID, + T_IDLE_INHIBITOR, T_INVALID, }; @@ -503,6 +561,8 @@ static enum criteria_token token_from_name(char *name) { return T_FLOATING; } else if (strcmp(name, "pid") == 0) { return T_PID; + } else if (strcmp(name, "idle_inhibitor") == 0) { + return T_IDLE_INHIBITOR; } return T_INVALID; } @@ -603,6 +663,30 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { error = strdup("The value for 'pid' should be numeric"); } break; + case T_IDLE_INHIBITOR: + if (strcmp(value, "none") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_NONE; + } else if (strcmp(value, "application") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_APPLICATION; + } else if (strcmp(value, "user") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_USER; + } else if (strcmp(value, "focus") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_FOCUS; + } else if (strcmp(value, "fullscreen ") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_FULLSCREEN; + } else if (strcmp(value, "open") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_OPEN; + } else if (strcmp(value, "visible") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_VISIBLE; + } else if (strcmp(value, "active") == 0) { + criteria->idle_inhibitor = C_IDLE_INHIBITOR_ACTIVE; + } else { + error = strdup("The value for 'idle_inhibitor' must be " + "'none', 'application', 'user', 'focus', " + "'fullscreen', 'open', 'visible' or " + "'active'"); + } + break; case T_INVALID: break; } diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 73e46a8f8..b51c5e1d3 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -19,6 +19,10 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, destroy); sway_log(SWAY_DEBUG, "Sway idle inhibitor destroyed"); + + // avoid use-after free of view by scheduling deferred execution of + // criteria + view_schedule_criteria_execution_from_view(inhibitor->view); destroy_inhibitor(inhibitor); } @@ -42,6 +46,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); + view_execute_criteria(inhibitor->view); sway_idle_inhibit_v1_check_active(manager); } @@ -64,19 +69,28 @@ void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, sway_idle_inhibit_v1_check_active(inhibitor->manager); } -struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_inhibitor_for_view( struct sway_view *view) { struct sway_idle_inhibitor_v1 *inhibitor; wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, link) { - if (inhibitor->view == view && - inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + if (inhibitor->view == view) { return inhibitor; } } return NULL; } +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view) { + struct sway_idle_inhibitor_v1 *inhibitor = + sway_idle_inhibit_v1_inhibitor_for_view(view); + if (inhibitor && inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + return inhibitor; + } + return NULL; +} + void sway_idle_inhibit_v1_user_inhibitor_destroy( struct sway_idle_inhibitor_v1 *inhibitor) { if (!inhibitor) { @@ -89,7 +103,7 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy( destroy_inhibitor(inhibitor); } -static bool check_active(struct sway_idle_inhibitor_v1 *inhibitor) { +bool sway_idle_inhibit_v1_inhibitor_check_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION: // If there is no view associated with the inhibitor, assume visible @@ -122,7 +136,7 @@ void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibitor_v1 *inhibitor; bool inhibited = false; wl_list_for_each(inhibitor, &manager->inhibitors, link) { - if ((inhibited = check_active(inhibitor))) { + if ((inhibited = sway_idle_inhibit_v1_inhibitor_check_active(inhibitor))) { break; } } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index bbcc94e23..26a9f3d6c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -870,6 +870,14 @@ The following attributes may be matched with: *id* Compare value against the X11 window ID. Must be numeric. +*idle_inhibitor* + Matches the state of idle inhibitors on windows. Values can be "none", + "application", "user", "focus", "fullscreen", "open", "none", "visible" + and "active" and will match windows where no, an application or a user + inhibitor is present with the "focus" to "visible" keywords describing + the type of user inhibtor to match more closely. "active" matches any + inhibitor that is currently preventing idle regardless of type. + *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 diff --git a/sway/tree/view.c b/sway/tree/view.c index de1e936aa..65f2c8f2b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1175,3 +1175,23 @@ bool view_is_transient_for(struct sway_view *child, return child->impl->is_transient_for && child->impl->is_transient_for(child, ancestor); } + +static void view_criteria_execution_view_iterator( + struct sway_container *container, void *data) { + struct sway_view *view = data; + + if (container->view == view) { + view_execute_criteria(container->view); + } +} + +static void view_execute_criteria_from_view(void *data) { + // here we intentionally go looking for a view matching our argument + // because that view might have been freed since + root_for_each_container(view_criteria_execution_view_iterator, data); +} + +void view_schedule_criteria_execution_from_view(struct sway_view *view) { + wl_event_loop_add_idle(server.wl_event_loop, + view_execute_criteria_from_view, view); +}