diff options
-rw-r--r-- | include/sway/commands.h | 1 | ||||
-rw-r--r-- | include/sway/criteria.h | 5 | ||||
-rw-r--r-- | include/sway/tree/container.h | 2 | ||||
-rw-r--r-- | include/sway/tree/view.h | 9 | ||||
-rw-r--r-- | include/sway/tree/workspace.h | 4 | ||||
-rw-r--r-- | sway/commands.c | 3 | ||||
-rw-r--r-- | sway/commands/default_floating_border.c | 29 | ||||
-rw-r--r-- | sway/commands/no_focus.c | 26 | ||||
-rw-r--r-- | sway/commands/urgent.c | 36 | ||||
-rw-r--r-- | sway/config.c | 9 | ||||
-rw-r--r-- | sway/criteria.c | 46 | ||||
-rw-r--r-- | sway/desktop/idle_inhibit_v1.c | 1 | ||||
-rw-r--r-- | sway/desktop/layer_shell.c | 12 | ||||
-rw-r--r-- | sway/desktop/render.c | 64 | ||||
-rw-r--r-- | sway/desktop/xwayland.c | 4 | ||||
-rw-r--r-- | sway/input/seat.c | 17 | ||||
-rw-r--r-- | sway/ipc-json.c | 7 | ||||
-rw-r--r-- | sway/meson.build | 3 | ||||
-rw-r--r-- | sway/sway.5.scd | 5 | ||||
-rw-r--r-- | sway/tree/container.c | 45 | ||||
-rw-r--r-- | sway/tree/layout.c | 11 | ||||
-rw-r--r-- | sway/tree/view.c | 96 | ||||
-rw-r--r-- | sway/tree/workspace.c | 11 | ||||
-rw-r--r-- | swaybar/ipc.c | 12 | ||||
-rw-r--r-- | swayidle/main.c | 15 | ||||
-rw-r--r-- | swaylock/main.c | 244 | ||||
-rw-r--r-- | swaylock/swaylock.1.scd | 9 |
27 files changed, 602 insertions, 124 deletions
diff --git a/include/sway/commands.h b/include/sway/commands.h index 3ebd0002..1e93e2a3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -152,6 +152,7 @@ sway_cmd cmd_swaybg_command; sway_cmd cmd_swap; sway_cmd cmd_title_format; sway_cmd cmd_unmark; +sway_cmd cmd_urgent; sway_cmd cmd_workspace; sway_cmd cmd_ws_auto_back_and_forth; sway_cmd cmd_workspace_layout; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index bd3ca0ac..6a8337c5 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -6,9 +6,10 @@ #include "tree/view.h" enum criteria_type { - CT_COMMAND = 1 << 0, - CT_ASSIGN_OUTPUT = 1 << 1, + CT_COMMAND = 1 << 0, + CT_ASSIGN_OUTPUT = 1 << 1, CT_ASSIGN_WORKSPACE = 1 << 2, + CT_NO_FOCUS = 1 << 3, }; struct criteria { diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 04e50fc6..ca7a3288 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -316,4 +316,6 @@ void container_floating_move_to(struct sway_container *con, */ void container_set_dirty(struct sway_container *container); +bool container_has_urgent_child(struct sway_container *container); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 21d6403e..e270f851 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -69,6 +69,11 @@ struct sway_view { bool border_bottom; bool border_left; bool border_right; + bool using_csd; + + struct timespec urgent; + bool allow_request_urgent; + struct wl_event_source *urgent_timer; bool destroying; @@ -305,4 +310,8 @@ void view_update_marks_textures(struct sway_view *view); */ bool view_is_visible(struct sway_view *view); +void view_set_urgent(struct sway_view *view, bool enable); + +bool view_is_urgent(struct sway_view *view); + #endif diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index c72a4ac0..bc95317a 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -10,6 +10,7 @@ struct sway_workspace { struct sway_view *fullscreen; struct sway_container *floating; list_t *output_priority; + bool urgent; }; extern char *prev_workspace_name; @@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace, struct sway_container *workspace_output_get_highest_available( struct sway_container *ws, struct sway_container *exclude); + +void workspace_detect_urgent(struct sway_container *workspace); + #endif diff --git a/sway/commands.c b/sway/commands.c index addd64a6..a3e6a500 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -98,6 +98,7 @@ static struct cmd_handler handlers[] = { { "client.unfocused", cmd_client_unfocused }, { "client.urgent", cmd_client_urgent }, { "default_border", cmd_default_border }, + { "default_floating_border", cmd_default_floating_border }, { "exec", cmd_exec }, { "exec_always", cmd_exec_always }, { "floating_maximum_size", cmd_floating_maximum_size }, @@ -114,6 +115,7 @@ static struct cmd_handler handlers[] = { { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, + { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "seat", cmd_seat }, { "set", cmd_set }, @@ -153,6 +155,7 @@ static struct cmd_handler command_handlers[] = { { "swap", cmd_swap }, { "title_format", cmd_title_format }, { "unmark", cmd_unmark }, + { "urgent", cmd_urgent }, }; static int handler_compare(const void *_a, const void *_b) { diff --git a/sway/commands/default_floating_border.c b/sway/commands/default_floating_border.c new file mode 100644 index 00000000..1bfc24af --- /dev/null +++ b/sway/commands/default_floating_border.c @@ -0,0 +1,29 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/container.h" + +struct cmd_results *cmd_default_floating_border(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "default_floating_border", + EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (strcmp(argv[0], "none") == 0) { + config->floating_border = B_NONE; + } else if (strcmp(argv[0], "normal") == 0) { + config->floating_border = B_NORMAL; + } else if (strcmp(argv[0], "pixel") == 0) { + config->floating_border = B_PIXEL; + } else { + return cmd_results_new(CMD_INVALID, "default_floating_border", + "Expected 'default_floating_border <none|normal|pixel>' " + "or 'default_floating_border <normal|pixel> <px>'"); + } + if (argc == 2) { + config->floating_border_thickness = atoi(argv[1]); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c new file mode 100644 index 00000000..61a8de7e --- /dev/null +++ b/sway/commands/no_focus.c @@ -0,0 +1,26 @@ +#define _XOPEN_SOURCE 500 +#include <string.h> +#include "sway/commands.h" +#include "sway/criteria.h" +#include "list.h" +#include "log.h" + +struct cmd_results *cmd_no_focus(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { + return error; + } + + char *err_str = NULL; + struct criteria *criteria = criteria_parse(argv[0], &err_str); + if (!criteria) { + error = cmd_results_new(CMD_INVALID, "no_focus", err_str); + free(err_str); + return error; + } + + criteria->type = CT_NO_FOCUS; + list_add(config->criteria, criteria); + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c new file mode 100644 index 00000000..d199858a --- /dev/null +++ b/sway/commands/urgent.c @@ -0,0 +1,36 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/tree/arrange.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "sway/tree/layout.h" + +struct cmd_results *cmd_urgent(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { + return error; + } + struct sway_container *container = + config->handler_context.current_container; + if (container->type != C_VIEW) { + return cmd_results_new(CMD_INVALID, "urgent", + "Only views can be urgent"); + } + struct sway_view *view = container->sway_view; + + if (strcmp(argv[0], "enable") == 0) { + view_set_urgent(view, true); + } else if (strcmp(argv[0], "disable") == 0) { + view_set_urgent(view, false); + } else if (strcmp(argv[0], "allow") == 0) { + view->allow_request_urgent = true; + } else if (strcmp(argv[0], "deny") == 0) { + view->allow_request_urgent = false; + } else { + return cmd_results_new(CMD_INVALID, "urgent", + "Expected 'urgent <enable|disable|allow|deny>'"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index f63835bf..c620e4c7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -24,6 +24,7 @@ #include "sway/input/seat.h" #include "sway/commands.h" #include "sway/config.h" +#include "sway/criteria.h" #include "sway/tree/arrange.h" #include "sway/tree/layout.h" #include "sway/tree/workspace.h" @@ -105,7 +106,12 @@ void free_config(struct sway_config *config) { } list_free(config->seat_configs); } - list_free(config->criteria); + if (config->criteria) { + for (int i = 0; i < config->criteria->length; ++i) { + criteria_destroy(config->criteria->items[i]); + } + list_free(config->criteria); + } list_free(config->no_focus); list_free(config->active_bar_modifiers); list_free(config->config_chain); @@ -474,7 +480,6 @@ static bool load_include_config(const char *path, const char *parent_dir, list_del(config->config_chain, index); return false; } - free(real_path); // restore current_config_path config->current_config_path = parent_config; diff --git a/sway/criteria.c b/sway/criteria.c index 29a3668b..e2b248de 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -37,7 +37,7 @@ void criteria_destroy(struct criteria *criteria) { pcre_free(criteria->con_mark); pcre_free(criteria->window_role); free(criteria->workspace); - + free(criteria->cmdlist); free(criteria->raw); free(criteria); } @@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) { return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } +static int cmp_urgent(const void *_a, const void *_b) { + struct sway_view *a = *(void **)_a; + struct sway_view *b = *(void **)_b; + + if (a->urgent.tv_sec < b->urgent.tv_sec) { + return -1; + } else if (a->urgent.tv_sec > b->urgent.tv_sec) { + return 1; + } + if (a->urgent.tv_nsec < b->urgent.tv_nsec) { + return -1; + } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { + return 1; + } + return 0; +} + +static void find_urgent_iterator(struct sway_container *swayc, void *data) { + if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { + return; + } + list_t *urgent_views = data; + list_add(urgent_views, swayc->sway_view); +} + static bool criteria_matches_view(struct criteria *criteria, struct sway_view *view) { if (criteria->title) { @@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria, } if (criteria->urgent) { - // TODO - return false; + if (!view_is_urgent(view)) { + return false; + } + list_t *urgent_views = create_list(); + container_for_each_descendant_dfs(&root_container, + find_urgent_iterator, urgent_views); + list_stable_sort(urgent_views, cmp_urgent); + struct sway_view *target; + if (criteria->urgent == 'o') { // oldest + target = urgent_views->items[0]; + } else { // latest + target = urgent_views->items[urgent_views->length - 1]; + } + list_free(urgent_views); + if (view != target) { + return false; + } } if (criteria->workspace) { diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 108a8417..da17d0f2 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -67,6 +67,7 @@ struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); if (!manager->wlr_manager) { + free(manager); return NULL; } manager->idle = idle; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 91baa6f8..a7d96717 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -325,12 +325,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->client_pending.margin.bottom, layer_surface->client_pending.margin.left); - struct sway_layer_surface *sway_layer = - calloc(1, sizeof(struct sway_layer_surface)); - if (!sway_layer) { - return; - } - if (!layer_surface->output) { // Assign last active output struct sway_container *output = NULL; @@ -352,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->output = output->sway_output->wlr_output; } + struct sway_layer_surface *sway_layer = + calloc(1, sizeof(struct sway_layer_surface)); + if (!sway_layer) { + return; + } + sway_layer->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, &sway_layer->surface_commit); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 17fe823a..4c85e516 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -256,6 +256,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, render_view_surfaces(view, output, damage, view->swayc->alpha); } + if (view->using_csd) { + return; + } + struct wlr_box box; float output_scale = output->wlr_output->scale; float color[4]; @@ -553,7 +557,11 @@ static void render_container_simple(struct sway_output *output, struct wlr_texture *marks_texture; struct sway_container_state *state = &child->current; - if (state->focused || parent_focused) { + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view->marks_urgent; + } else if (state->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view->marks_focused; @@ -567,12 +575,14 @@ static void render_container_simple(struct sway_output *output, marks_texture = view->marks_unfocused; } - if (state->border == B_NORMAL) { - render_titlebar(output, damage, child, state->swayc_x, - state->swayc_y, state->swayc_width, colors, - title_texture, marks_texture); - } else { - render_top_border(output, damage, child, colors); + if (!view->using_csd) { + if (state->border == B_NORMAL) { + render_titlebar(output, damage, child, state->swayc_x, + state->swayc_y, state->swayc_width, colors, + title_texture, marks_texture); + } else { + render_top_border(output, damage, child, colors); + } } render_view(output, damage, child, colors); } else { @@ -607,8 +617,14 @@ static void render_container_tabbed(struct sway_output *output, struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - - if (cstate->focused || parent_focused) { + bool urgent = view ? + view_is_urgent(view) : container_has_urgent_child(child); + + if (urgent) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view ? view->marks_urgent : NULL; + } else if (cstate->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; @@ -670,8 +686,14 @@ static void render_container_stacked(struct sway_output *output, struct border_colors *colors; struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - - if (cstate->focused || parent_focused) { + bool urgent = view ? + view_is_urgent(view) : container_has_urgent_child(child); + + if (urgent) { + colors = &config->border_colors.urgent; + title_texture = child->title_urgent; + marks_texture = view ? view->marks_urgent : NULL; + } else if (cstate->focused || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; marks_texture = view ? view->marks_focused : NULL; @@ -731,7 +753,11 @@ static void render_floating_container(struct sway_output *soutput, struct wlr_texture *title_texture; struct wlr_texture *marks_texture; - if (con->current.focused) { + if (view_is_urgent(view)) { + colors = &config->border_colors.urgent; + title_texture = con->title_urgent; + marks_texture = view->marks_urgent; + } else if (con->current.focused) { colors = &config->border_colors.focused; title_texture = con->title_focused; marks_texture = view->marks_focused; @@ -741,12 +767,14 @@ static void render_floating_container(struct sway_output *soutput, marks_texture = view->marks_unfocused; } - if (con->current.border == B_NORMAL) { - render_titlebar(soutput, damage, con, con->current.swayc_x, - con->current.swayc_y, con->current.swayc_width, colors, - title_texture, marks_texture); - } else if (con->current.border != B_NONE) { - render_top_border(soutput, damage, con, colors); + if (!view->using_csd) { + if (con->current.border == B_NORMAL) { + render_titlebar(soutput, damage, con, con->current.swayc_x, + con->current.swayc_y, con->current.swayc_width, colors, + title_texture, marks_texture); + } else if (con->current.border != B_NONE) { + render_top_border(soutput, damage, con, colors); + } } render_view(soutput, damage, con, colors); } else { diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 11516673..9df7977d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -297,6 +297,10 @@ static void handle_commit(struct wl_listener *listener, void *data) { } view_damage_from(view); + + if (view->allow_request_urgent) { + view_set_urgent(view, (bool)xsurface->hints_urgency); + } } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 74f1375e..12b1fab5 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -594,6 +594,12 @@ static void seat_send_unfocus(struct sway_container *container, } } +static int handle_urgent_timeout(void *data) { + struct sway_view *view = data; + view_set_urgent(view, false); + return 0; +} + void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp) { if (seat->focused_layer) { @@ -649,6 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat, while (parent) { wl_list_remove(&parent->link); wl_list_insert(&seat->focus_stack, &parent->link); + container_set_dirty(parent->container); parent = seat_container_from_container(seat, @@ -670,6 +677,16 @@ void seat_set_focus_warp(struct sway_seat *seat, } } + // If urgent, start a timer to unset it + if (container && container->type == C_VIEW && + view_is_urgent(container->sway_view) && + !container->sway_view->urgent_timer) { + struct sway_view *view = container->sway_view; + view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, + handle_urgent_timeout, view); + wl_event_source_timer_update(view->urgent_timer, 1000); + } + // If we've focused a floating container, bring it to the front. // We do this by putting it at the end of the floating list. // This must happen for both the pending and current children lists. diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 3d0e88f0..c49ea47e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -170,7 +170,8 @@ static void ipc_json_describe_workspace(struct sway_container *workspace, json_object_object_add(object, "output", workspace->parent ? json_object_new_string(workspace->parent->name) : NULL); json_object_object_add(object, "type", json_object_new_string("workspace")); - json_object_object_add(object, "urgent", json_object_new_boolean(false)); + json_object_object_add(object, "urgent", + json_object_new_boolean(workspace->sway_workspace->urgent)); json_object_object_add(object, "representation", workspace->formatted_title ? json_object_new_string(workspace->formatted_title) : NULL); @@ -196,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "layout", json_object_new_string(ipc_json_layout_description(c->layout))); } + + bool urgent = c->type == C_VIEW ? + view_is_urgent(c->sway_view) : container_has_urgent_child(c); + json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); } static void focus_inactive_children_iterator(struct sway_container *c, void *data) { diff --git a/sway/meson.build b/sway/meson.build index f878450d..c58d3470 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -35,6 +35,7 @@ sway_sources = files( 'commands/border.c', 'commands/client.c', 'commands/default_border.c', + 'commands/default_floating_border.c', 'commands/default_orientation.c', 'commands/exit.c', 'commands/exec.c', @@ -59,6 +60,7 @@ sway_sources = files( 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', + 'commands/no_focus.c', 'commands/output.c', 'commands/reload.c', 'commands/rename.c', @@ -76,6 +78,7 @@ sway_sources = files( 'commands/swap.c', 'commands/title_format.c', 'commands/unmark.c', + 'commands/urgent.c', 'commands/workspace.c', 'commands/workspace_layout.c', 'commands/ws_auto_back_and_forth.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index c6eb5e6d..d369d7b6 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -499,6 +499,11 @@ config after the others, or it will be matched instead of the others. *unmark* will remove _identifier_ from the list of current marks on a window. If _identifier_ is omitted, all marks are removed. +*urgent* enable|disable|allow|deny + Using _enable_ or _disable_ manually sets or unsets the window's urgent + state. Using _allow_ or _deny_ controls the window's ability to set itself + as urgent. By default, windows are allowed to set their own urgency. + *workspace* [number] <name> Switches to the specified workspace. The string "number" is optional and is used to sort workspaces. diff --git a/sway/tree/container.c b/sway/tree/container.c index 35f67cce..3f9d701a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -674,16 +674,23 @@ struct sway_container *floating_container_at(double lx, double ly, void container_for_each_descendant_dfs(struct sway_container *container, void (*f)(struct sway_container *container, void *data), void *data) { - if (container) { - if (container->children) { - for (int i = 0; i < container->children->length; ++i) { - struct sway_container *child = - container->children->items[i]; - container_for_each_descendant_dfs(child, f, data); - } + if (!container) { + return; + } + if (container->children) { + for (int i = 0; i < container->children->length; ++i) { + struct sway_container *child = container->children->items[i]; + container_for_each_descendant_dfs(child, f, data); } - f(container, data); } + if (container->type == C_WORKSPACE) { + struct sway_container *floating = container->sway_workspace->floating; + for (int i = 0; i < floating->children->length; ++i) { + struct sway_container *child = floating->children->items[i]; + container_for_each_descendant_dfs(child, f, data); + } + } + f(container, data); } void container_for_each_descendant_bfs(struct sway_container *con, @@ -960,9 +967,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { return; } struct sway_view *view = con->sway_view; - size_t border_width = view->border_thickness * (view->border != B_NONE); - size_t top = - view->border == B_NORMAL ? container_titlebar_height() : border_width; + size_t border_width = 0; + size_t top = 0; + + if (!view->using_csd) { + border_width = view->border_thickness * (view->border != B_NONE); + top = view->border == B_NORMAL ? + container_titlebar_height() : border_width; + } con->x = view->x - border_width; con->y = view->y - top; @@ -1063,6 +1075,8 @@ void container_floating_move_to(struct sway_container *con, container_add_child(new_workspace->sway_workspace->floating, con); arrange_windows(old_workspace); arrange_windows(new_workspace); + workspace_detect_urgent(old_workspace); + workspace_detect_urgent(new_workspace); } } @@ -1073,3 +1087,12 @@ void container_set_dirty(struct sway_container *container) { container->dirty = true; list_add(server.dirty_containers, container); } + +static bool find_urgent_iterator(struct sway_container *con, + void *data) { + return con->type == C_VIEW && view_is_urgent(con->sway_view); +} + +bool container_has_urgent_child(struct sway_container *container) { + return container_find(container, find_urgent_iterator, NULL); +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 54ddb3f9..197a2fc8 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -225,6 +225,15 @@ void container_move_to(struct sway_container *container, } } } + // Update workspace urgent state + struct sway_container *old_workspace = old_parent; + if (old_workspace->type != C_WORKSPACE) { + old_workspace = container_parent(old_workspace, C_WORKSPACE); + } + if (new_workspace != old_workspace) { + workspace_detect_urgent(new_workspace); + workspace_detect_urgent(old_workspace); + } } static bool sway_dir_to_wlr(enum movement_direction dir, @@ -548,6 +557,8 @@ void container_move(struct sway_container *container, } if (last_ws && next_ws && last_ws != next_ws) { ipc_event_workspace(last_ws, container, "focus"); + workspace_detect_urgent(last_ws); + workspace_detect_urgent(next_ws); } } diff --git a/sway/tree/view.c b/sway/tree/view.c index bf380d98..fc31699c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,6 +25,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, view->impl = impl; view->executed_criteria = create_list(); view->marks = create_list(); + view->allow_request_urgent = true; wl_signal_init(&view->events.unmap); } @@ -315,11 +316,15 @@ void view_set_activated(struct sway_view *view, bool activated) { } void view_set_tiled(struct sway_view *view, bool tiled) { - bool csd = true; - if (view->impl->has_client_side_decorations) { - csd = view->impl->has_client_side_decorations(view); + if (!tiled) { + view->using_csd = true; + if (view->impl->has_client_side_decorations) { + view->using_csd = view->impl->has_client_side_decorations(view); + } + } else { + view->using_csd = false; } - view->border = tiled || !csd ? config->border : B_NONE; + if (view->impl->set_tiled) { view->impl->set_tiled(view, tiled); } @@ -504,20 +509,38 @@ void view_execute_criteria(struct sway_view *view) { } wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", criteria->raw, view, criteria->cmdlist); + seat_set_focus(seat, view->swayc); list_add(view->executed_criteria, criteria); struct cmd_results *res = execute_command(criteria->cmdlist, NULL); if (res->status != CMD_SUCCESS) { wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); } free_cmd_results(res); - // view must be focused for commands to affect it, - // so always refocus in-between command lists - seat_set_focus(seat, view->swayc); } list_free(criterias); seat_set_focus(seat, prior_focus); } +static bool should_focus(struct sway_view *view) { + // If the view is the only one in the focused workspace, it'll get focus + // regardless of any no_focus criteria. + struct sway_container *parent = view->swayc->parent; + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { + size_t num_children = parent->children->length + + parent->sway_workspace->floating->children->length; + if (num_children == 1) { + return true; + } + } + + // Check no_focus criteria + list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); + size_t len = criterias->length; + list_free(criterias); + return len == 0; +} + void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; @@ -554,8 +577,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->surface = wlr_surface; view->swayc = cont; - view->border = config->border; - view->border_thickness = config->border_thickness; view_init_subsurfaces(view, wlr_surface); wl_signal_add(&wlr_surface->events.new_subsurface, @@ -566,14 +587,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { view->container_reparent.notify = view_handle_container_reparent; if (view->impl->wants_floating && view->impl->wants_floating(view)) { + view->border = config->floating_border; + view->border_thickness = config->floating_border_thickness; container_set_floating(view->swayc, true); } else { + view->border = config->border; + view->border_thickness = config->border_thickness; view_set_tiled(view, true); } - input_manager_set_focus(input_manager, cont); - if (workspace) { - workspace_switch(workspace); + if (should_focus(view)) { + input_manager_set_focus(input_manager, cont); + if (workspace) { + workspace_switch(workspace); + } } view_update_title(view, false); @@ -589,16 +616,26 @@ void view_unmap(struct sway_view *view) { wl_list_remove(&view->surface_new_subsurface.link); wl_list_remove(&view->container_reparent.link); + if (view->urgent_timer) { + wl_event_source_remove(view->urgent_timer); + view->urgent_timer = NULL; + } + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + + struct sway_container *parent; if (view->is_fullscreen) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); ws->sway_workspace->fullscreen = NULL; - container_destroy(view->swayc); + parent = container_destroy(view->swayc); arrange_windows(ws->parent); } else { - struct sway_container *parent = container_destroy(view->swayc); + parent = container_destroy(view->swayc); arrange_windows(parent); } + if (parent->type >= C_WORKSPACE) { // if the workspace still exists + workspace_detect_urgent(ws); + } transaction_commit_dirty(); view->surface = NULL; } @@ -1047,3 +1084,32 @@ bool view_is_visible(struct sway_view *view) { } return true; } + +void view_set_urgent(struct sway_view *view, bool enable) { + if (view_is_urgent(view) == enable) { + return; + } + if (enable) { + struct sway_seat *seat = input_manager_current_seat(input_manager); + if (seat_get_focus(seat) == view->swayc) { + return; + } + clock_gettime(CLOCK_MONOTONIC, &view->urgent); + } else { + view->urgent = (struct timespec){ 0 }; + if (view->urgent_timer) { + wl_event_source_remove(view->urgent_timer); + view->urgent_timer = NULL; + } + } + container_damage_whole(view->swayc); + + ipc_event_window(view->swayc, "urgent"); + + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + workspace_detect_urgent(ws); +} + +bool view_is_urgent(struct sway_view *view) { + return view->urgent.tv_sec || view->urgent.tv_nsec; +} diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 2a2d834a..622f01ec 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -11,6 +11,7 @@ #include "sway/ipc-server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" #include "log.h" @@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available( return NULL; } + +void workspace_detect_urgent(struct sway_container *workspace) { + bool new_urgent = container_has_urgent_child(workspace); + + if (workspace->sway_workspace->urgent != new_urgent) { + workspace->sway_workspace->urgent = new_urgent; + ipc_event_workspace(NULL, workspace, "urgent"); + container_damage_whole(workspace); + } +} diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 08531f2a..c2d05920 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -115,6 +115,18 @@ static void ipc_parse_colors( config->colors.inactive_workspace.text = parse_color( json_object_get_string(inactive_workspace_text)); } + if (urgent_workspace_border) { + config->colors.urgent_workspace.border = parse_color( + json_object_get_string(urgent_workspace_border)); + } + if (urgent_workspace_bg) { + config->colors.urgent_workspace.background = parse_color( + json_object_get_string(urgent_workspace_bg)); + } + if (urgent_workspace_text) { + config->colors.urgent_workspace.text = parse_color( + json_object_get_string(urgent_workspace_text)); + } if (binding_mode_border) { config->colors.binding_mode.border = parse_color( json_object_get_string(binding_mode_border)); diff --git a/swayidle/main.c b/swayidle/main.c index 64e45036..678d622f 100644 --- a/swayidle/main.c +++ b/swayidle/main.c @@ -1,22 +1,21 @@ #define _XOPEN_SOURCE 500 +#include <errno.h> #include <getopt.h> -#include <signal.h> #include <pthread.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> -#include <errno.h> #include <string.h> #include <sys/wait.h> #include <unistd.h> #include <wayland-client-protocol.h> #include <wayland-client.h> +#include <wayland-server.h> #include <wayland-util.h> #include <wlr/config.h> #include <wlr/util/log.h> -#include <wlr/types/wlr_output_layout.h> -#include <wlr/types/wlr_output.h> -#include "idle-client-protocol.h" #include "config.h" +#include "idle-client-protocol.h" #include "list.h" #ifdef SWAY_IDLE_HAS_SYSTEMD #include <systemd/sd-bus.h> @@ -36,7 +35,6 @@ struct swayidle_state { struct wl_display *display; struct org_kde_kwin_idle_timeout *idle_timer; struct org_kde_kwin_idle_timeout *lock_timer; - struct wlr_output_layout *layout; struct wl_event_loop *event_loop; list_t *timeout_cmds; } state; @@ -165,7 +163,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) { void setup_sleep_listener() { struct sd_bus *bus; - + int ret = sd_bus_default_system(&bus); if (ret < 0) { wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", @@ -360,7 +358,7 @@ static int display_event(int fd, uint32_t mask, void *data) { if (wl_display_dispatch(state.display) < 0) { wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting"); sway_terminate(0); - }; + } return 0; } @@ -397,7 +395,6 @@ int main(int argc, char *argv[]) { struct wl_registry *registry = wl_display_get_registry(state.display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(state.display); - state.layout = wlr_output_layout_create(); state.event_loop = wl_event_loop_create(); if (idle_manager == NULL) { diff --git a/swaylock/main.c b/swaylock/main.c index faebc757..ae5b86b9 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -21,6 +21,7 @@ #include "pool-buffer.h" #include "cairo.h" #include "log.h" +#include "readline.h" #include "stringop.h" #include "util.h" #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" @@ -412,15 +413,14 @@ static void set_default_colors(struct swaylock_colors *colors) { }; } -static struct swaylock_state state; - -int main(int argc, char **argv) { - enum line_mode { - LM_LINE, - LM_INSIDE, - LM_RING, - }; +enum line_mode { + LM_LINE, + LM_INSIDE, + LM_RING, +}; +static int parse_options(int argc, char **argv, struct swaylock_state *state, + enum line_mode *line_mode) { enum long_option_codes { LO_BS_HL_COLOR = 256, LO_FONT, @@ -447,6 +447,7 @@ int main(int argc, char **argv) { }; static struct option long_options[] = { + {"config", required_argument, NULL, 'C'}, {"color", required_argument, NULL, 'c'}, {"ignore-empty-password", no_argument, NULL, 'e'}, {"daemonize", no_argument, NULL, 'f'}, @@ -487,6 +488,8 @@ int main(int argc, char **argv) { const char usage[] = "Usage: swaylock [options...]\n" "\n" + " -C, --config <config_file> " + "Path to the config file.\n" " -c, --color <color> " "Turn the screen into the given color instead of white.\n" " -e, --ignore-empty-password " @@ -559,58 +562,48 @@ int main(int argc, char **argv) { "\n" "All <color> options are of the form <rrggbb[aa]>.\n"; - enum line_mode line_mode = LM_LINE; - state.args = (struct swaylock_args){ - .mode = BACKGROUND_MODE_SOLID_COLOR, - .font = strdup("sans-serif"), - .radius = 50, - .thickness = 10, - .ignore_empty = false, - .show_indicator = true, - }; - wl_list_init(&state.images); - set_default_colors(&state.args.colors); - - wlr_log_init(WLR_DEBUG, NULL); - int c; + optind = 1; while (1) { int opt_idx = 0; - c = getopt_long(argc, argv, "c:efhi:nrs:tuv", long_options, &opt_idx); + c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx); if (c == -1) { break; } switch (c) { + case 'C': + // Config file. This will have already been handled so just ignore. + break; case 'c': - state.args.colors.background = parse_color(optarg); - state.args.mode = BACKGROUND_MODE_SOLID_COLOR; + state->args.colors.background = parse_color(optarg); + state->args.mode = BACKGROUND_MODE_SOLID_COLOR; break; case 'e': - state.args.ignore_empty = true; + state->args.ignore_empty = true; break; case 'f': - state.args.daemonize = true; + state->args.daemonize = true; break; case 'i': - load_image(optarg, &state); + load_image(optarg, state); break; case 'n': - line_mode = LM_INSIDE; + *line_mode = LM_INSIDE; break; case 'r': - line_mode = LM_RING; + *line_mode = LM_RING; break; case 's': - state.args.mode = parse_background_mode(optarg); - if (state.args.mode == BACKGROUND_MODE_INVALID) { + state->args.mode = parse_background_mode(optarg); + if (state->args.mode == BACKGROUND_MODE_INVALID) { return 1; } break; case 't': - state.args.mode = BACKGROUND_MODE_TILE; + state->args.mode = BACKGROUND_MODE_TILE; break; case 'u': - state.args.show_indicator = false; + state->args.show_indicator = false; break; case 'v': #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE @@ -621,71 +614,71 @@ int main(int argc, char **argv) { #endif return 0; case LO_BS_HL_COLOR: - state.args.colors.bs_highlight = parse_color(optarg); + state->args.colors.bs_highlight = parse_color(optarg); break; case LO_FONT: - free(state.args.font); - state.args.font = strdup(optarg); + free(state->args.font); + state->args.font = strdup(optarg); break; case LO_IND_RADIUS: - state.args.radius = strtol(optarg, NULL, 0); + state->args.radius = strtol(optarg, NULL, 0); break; case LO_IND_THICKNESS: - state.args.thickness = strtol(optarg, NULL, 0); + state->args.thickness = strtol(optarg, NULL, 0); break; case LO_INSIDE_COLOR: - state.args.colors.inside.input = parse_color(optarg); + state->args.colors.inside.input = parse_color(optarg); break; case LO_INSIDE_CLEAR_COLOR: - state.args.colors.inside.cleared = parse_color(optarg); + state->args.colors.inside.cleared = parse_color(optarg); break; case LO_INSIDE_VER_COLOR: - state.args.colors.inside.verifying = parse_color(optarg); + state->args.colors.inside.verifying = parse_color(optarg); break; case LO_INSIDE_WRONG_COLOR: - state.args.colors.inside.wrong = parse_color(optarg); + state->args.colors.inside.wrong = parse_color(optarg); break; case LO_KEY_HL_COLOR: - state.args.colors.key_highlight = parse_color(optarg); + state->args.colors.key_highlight = parse_color(optarg); break; case LO_LINE_COLOR: - state.args.colors.line.input = parse_color(optarg); + state->args.colors.line.input = parse_color(optarg); break; case LO_LINE_CLEAR_COLOR: - state.args.colors.line.cleared = parse_color(optarg); + state->args.colors.line.cleared = parse_color(optarg); break; case LO_LINE_VER_COLOR: - state.args.colors.line.verifying = parse_color(optarg); + state->args.colors.line.verifying = parse_color(optarg); break; case LO_LINE_WRONG_COLOR: - state.args.colors.line.wrong = parse_color(optarg); + state->args.colors.line.wrong = parse_color(optarg); break; case LO_RING_COLOR: - state.args.colors.ring.input = parse_color(optarg); + state->args.colors.ring.input = parse_color(optarg); break; case LO_RING_CLEAR_COLOR: - state.args.colors.ring.cleared = parse_color(optarg); + state->args.colors.ring.cleared = parse_color(optarg); break; case LO_RING_VER_COLOR: - state.args.colors.ring.verifying = parse_color(optarg); + state->args.colors.ring.verifying = parse_color(optarg); break; case LO_RING_WRONG_COLOR: - state.args.colors.ring.wrong = parse_color(optarg); + state->args.colors.ring.wrong = parse_color(optarg); break; case LO_SEP_COLOR: - state.args.colors.separator = parse_color(optarg); + state->args.colors.separator = parse_color(optarg); break; case LO_TEXT_COLOR: - state.args.colors.text.input = parse_color(optarg); + state->args.colors.text.input = parse_color(optarg); break; case LO_TEXT_CLEAR_COLOR: - state.args.colors.text.cleared = parse_color(optarg); + state->args.colors.text.cleared = parse_color(optarg); break; case LO_TEXT_VER_COLOR: - state.args.colors.text.verifying = parse_color(optarg); + state->args.colors.text.verifying = parse_color(optarg); break; case LO_TEXT_WRONG_COLOR: - state.args.colors.text.wrong = parse_color(optarg); + state->args.colors.text.wrong = parse_color(optarg); break; default: fprintf(stderr, "%s", usage); @@ -693,6 +686,143 @@ int main(int argc, char **argv) { } } + return 0; +} + +static bool file_exists(const char *path) { + return path && access(path, R_OK) != -1; +} + +static char *get_config_path(void) { + static const char *config_paths[] = { + "$HOME/.swaylock/config", + "$XDG_CONFIG_HOME/swaylock/config", + SYSCONFDIR "/swaylock/config", + }; + + if (!getenv("XDG_CONFIG_HOME")) { + char *home = getenv("HOME"); + char *config_home = malloc(strlen(home) + strlen("/.config") + 1); + if (!config_home) { + wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); + } else { + strcpy(config_home, home); + strcat(config_home, "/.config"); + setenv("XDG_CONFIG_HOME", config_home, 1); + wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); + free(config_home); + } + } + + wordexp_t p; + char *path; + for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { + if (wordexp(config_paths[i], &p, 0) == 0) { + path = strdup(p.we_wordv[0]); + wordfree(&p); + if (file_exists(path)) { + return path; + } + free(path); + } + } + + return NULL; +} + +static int load_config(char *path, struct swaylock_state *state, + enum line_mode *line_mode) { + FILE *config = fopen(path, "r"); + if (!config) { + wlr_log(WLR_ERROR, "Failed to read config. Running without it."); + return 0; + } + char *line; + int line_number = 0; + while (!feof(config)) { + line = read_line(config); + if (!line) { + continue; + } + + line_number++; + if (line[0] == '#') { + free(line); + continue; + } + if (strlen(line) == 0) { + free(line); + continue; + } + + wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); + char flag[strlen(line) + 3]; + sprintf(flag, "--%s", line); + char *argv[] = {"swaylock", flag}; + int result = parse_options(2, argv, state, line_mode); + if (result != 0) { + free(line); + fclose(config); + return result; + } + free(line); + } + fclose(config); + return 0; +} + +static struct swaylock_state state; + +int main(int argc, char **argv) { + enum line_mode line_mode = LM_LINE; + state.args = (struct swaylock_args){ + .mode = BACKGROUND_MODE_SOLID_COLOR, + .font = strdup("sans-serif"), + .radius = 50, + .thickness = 10, + .ignore_empty = false, + .show_indicator = true, + }; + wl_list_init(&state.images); + set_default_colors(&state.args.colors); + + wlr_log_init(WLR_DEBUG, NULL); + + char *config_path = NULL; + static struct option long_options[] = { + {"config", required_argument, NULL, 'C'}, + {0, 0, 0, 0}, + }; + while (1) { + int c = getopt_long(argc, argv, "C:", long_options, NULL); + if (c == -1) { + break; + } else if (c == 'C') { + config_path = strdup(optarg); + break; + } + } + if (!config_path) { + config_path = get_config_path(); + } + + if (config_path) { + wlr_log(WLR_DEBUG, "Found config at %s", config_path); + int config_status = load_config(config_path, &state, &line_mode); + free(config_path); + if (config_status != 0) { + return config_status; + } + } + + if (argc > 1) { + wlr_log(WLR_DEBUG, "Parsing CLI Args"); + int result = parse_options(argc, argv, &state, &line_mode); + if (result != 0) { + return result; + } + } + if (line_mode == LM_INSIDE) { state.args.colors.line = state.args.colors.inside; } else if (line_mode == LM_RING) { diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd index eea62c2a..3107124f 100644 --- a/swaylock/swaylock.1.scd +++ b/swaylock/swaylock.1.scd @@ -12,6 +12,15 @@ Locks your Wayland session. # OPTIONS +*-C, --config* <path> + The config file to use. By default, the following paths are checked: + _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and + _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid + options in the configuration file using the format _long-option=value_. + For options such as _ignore-empty-password_, just supply the _long-option_. + All leading dashes should be omitted and the equals sign is required for + flags that take an argument. + *-c, --color* <rrggbb[aa]> Turn the screen into the given color. If -i is used, this sets the background of the image to the given color. Defaults to white (FFFFFF), or |