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; | |||
152 | sway_cmd cmd_swap; | 152 | sway_cmd cmd_swap; |
153 | sway_cmd cmd_title_format; | 153 | sway_cmd cmd_title_format; |
154 | sway_cmd cmd_unmark; | 154 | sway_cmd cmd_unmark; |
155 | sway_cmd cmd_urgent; | ||
155 | sway_cmd cmd_workspace; | 156 | sway_cmd cmd_workspace; |
156 | sway_cmd cmd_ws_auto_back_and_forth; | 157 | sway_cmd cmd_ws_auto_back_and_forth; |
157 | sway_cmd cmd_workspace_layout; | 158 | 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 @@ | |||
6 | #include "tree/view.h" | 6 | #include "tree/view.h" |
7 | 7 | ||
8 | enum criteria_type { | 8 | enum criteria_type { |
9 | CT_COMMAND = 1 << 0, | 9 | CT_COMMAND = 1 << 0, |
10 | CT_ASSIGN_OUTPUT = 1 << 1, | 10 | CT_ASSIGN_OUTPUT = 1 << 1, |
11 | CT_ASSIGN_WORKSPACE = 1 << 2, | 11 | CT_ASSIGN_WORKSPACE = 1 << 2, |
12 | CT_NO_FOCUS = 1 << 3, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | struct criteria { | 15 | 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, | |||
316 | */ | 316 | */ |
317 | void container_set_dirty(struct sway_container *container); | 317 | void container_set_dirty(struct sway_container *container); |
318 | 318 | ||
319 | bool container_has_urgent_child(struct sway_container *container); | ||
320 | |||
319 | #endif | 321 | #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 { | |||
69 | bool border_bottom; | 69 | bool border_bottom; |
70 | bool border_left; | 70 | bool border_left; |
71 | bool border_right; | 71 | bool border_right; |
72 | bool using_csd; | ||
73 | |||
74 | struct timespec urgent; | ||
75 | bool allow_request_urgent; | ||
76 | struct wl_event_source *urgent_timer; | ||
72 | 77 | ||
73 | bool destroying; | 78 | bool destroying; |
74 | 79 | ||
@@ -305,4 +310,8 @@ void view_update_marks_textures(struct sway_view *view); | |||
305 | */ | 310 | */ |
306 | bool view_is_visible(struct sway_view *view); | 311 | bool view_is_visible(struct sway_view *view); |
307 | 312 | ||
313 | void view_set_urgent(struct sway_view *view, bool enable); | ||
314 | |||
315 | bool view_is_urgent(struct sway_view *view); | ||
316 | |||
308 | #endif | 317 | #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 { | |||
10 | struct sway_view *fullscreen; | 10 | struct sway_view *fullscreen; |
11 | struct sway_container *floating; | 11 | struct sway_container *floating; |
12 | list_t *output_priority; | 12 | list_t *output_priority; |
13 | bool urgent; | ||
13 | }; | 14 | }; |
14 | 15 | ||
15 | extern char *prev_workspace_name; | 16 | extern char *prev_workspace_name; |
@@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace, | |||
42 | 43 | ||
43 | struct sway_container *workspace_output_get_highest_available( | 44 | struct sway_container *workspace_output_get_highest_available( |
44 | struct sway_container *ws, struct sway_container *exclude); | 45 | struct sway_container *ws, struct sway_container *exclude); |
46 | |||
47 | void workspace_detect_urgent(struct sway_container *workspace); | ||
48 | |||
45 | #endif | 49 | #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[] = { | |||
98 | { "client.unfocused", cmd_client_unfocused }, | 98 | { "client.unfocused", cmd_client_unfocused }, |
99 | { "client.urgent", cmd_client_urgent }, | 99 | { "client.urgent", cmd_client_urgent }, |
100 | { "default_border", cmd_default_border }, | 100 | { "default_border", cmd_default_border }, |
101 | { "default_floating_border", cmd_default_floating_border }, | ||
101 | { "exec", cmd_exec }, | 102 | { "exec", cmd_exec }, |
102 | { "exec_always", cmd_exec_always }, | 103 | { "exec_always", cmd_exec_always }, |
103 | { "floating_maximum_size", cmd_floating_maximum_size }, | 104 | { "floating_maximum_size", cmd_floating_maximum_size }, |
@@ -114,6 +115,7 @@ static struct cmd_handler handlers[] = { | |||
114 | { "input", cmd_input }, | 115 | { "input", cmd_input }, |
115 | { "mode", cmd_mode }, | 116 | { "mode", cmd_mode }, |
116 | { "mouse_warping", cmd_mouse_warping }, | 117 | { "mouse_warping", cmd_mouse_warping }, |
118 | { "no_focus", cmd_no_focus }, | ||
117 | { "output", cmd_output }, | 119 | { "output", cmd_output }, |
118 | { "seat", cmd_seat }, | 120 | { "seat", cmd_seat }, |
119 | { "set", cmd_set }, | 121 | { "set", cmd_set }, |
@@ -153,6 +155,7 @@ static struct cmd_handler command_handlers[] = { | |||
153 | { "swap", cmd_swap }, | 155 | { "swap", cmd_swap }, |
154 | { "title_format", cmd_title_format }, | 156 | { "title_format", cmd_title_format }, |
155 | { "unmark", cmd_unmark }, | 157 | { "unmark", cmd_unmark }, |
158 | { "urgent", cmd_urgent }, | ||
156 | }; | 159 | }; |
157 | 160 | ||
158 | static int handler_compare(const void *_a, const void *_b) { | 161 | 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 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/tree/container.h" | ||
5 | |||
6 | struct cmd_results *cmd_default_floating_border(int argc, char **argv) { | ||
7 | struct cmd_results *error = NULL; | ||
8 | if ((error = checkarg(argc, "default_floating_border", | ||
9 | EXPECTED_AT_LEAST, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | |||
13 | if (strcmp(argv[0], "none") == 0) { | ||
14 | config->floating_border = B_NONE; | ||
15 | } else if (strcmp(argv[0], "normal") == 0) { | ||
16 | config->floating_border = B_NORMAL; | ||
17 | } else if (strcmp(argv[0], "pixel") == 0) { | ||
18 | config->floating_border = B_PIXEL; | ||
19 | } else { | ||
20 | return cmd_results_new(CMD_INVALID, "default_floating_border", | ||
21 | "Expected 'default_floating_border <none|normal|pixel>' " | ||
22 | "or 'default_floating_border <normal|pixel> <px>'"); | ||
23 | } | ||
24 | if (argc == 2) { | ||
25 | config->floating_border_thickness = atoi(argv[1]); | ||
26 | } | ||
27 | |||
28 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
29 | } | ||
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 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <string.h> | ||
3 | #include "sway/commands.h" | ||
4 | #include "sway/criteria.h" | ||
5 | #include "list.h" | ||
6 | #include "log.h" | ||
7 | |||
8 | struct cmd_results *cmd_no_focus(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | |||
14 | char *err_str = NULL; | ||
15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | ||
16 | if (!criteria) { | ||
17 | error = cmd_results_new(CMD_INVALID, "no_focus", err_str); | ||
18 | free(err_str); | ||
19 | return error; | ||
20 | } | ||
21 | |||
22 | criteria->type = CT_NO_FOCUS; | ||
23 | list_add(config->criteria, criteria); | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
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 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/tree/arrange.h" | ||
5 | #include "sway/tree/container.h" | ||
6 | #include "sway/tree/view.h" | ||
7 | #include "sway/tree/layout.h" | ||
8 | |||
9 | struct cmd_results *cmd_urgent(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct sway_container *container = | ||
15 | config->handler_context.current_container; | ||
16 | if (container->type != C_VIEW) { | ||
17 | return cmd_results_new(CMD_INVALID, "urgent", | ||
18 | "Only views can be urgent"); | ||
19 | } | ||
20 | struct sway_view *view = container->sway_view; | ||
21 | |||
22 | if (strcmp(argv[0], "enable") == 0) { | ||
23 | view_set_urgent(view, true); | ||
24 | } else if (strcmp(argv[0], "disable") == 0) { | ||
25 | view_set_urgent(view, false); | ||
26 | } else if (strcmp(argv[0], "allow") == 0) { | ||
27 | view->allow_request_urgent = true; | ||
28 | } else if (strcmp(argv[0], "deny") == 0) { | ||
29 | view->allow_request_urgent = false; | ||
30 | } else { | ||
31 | return cmd_results_new(CMD_INVALID, "urgent", | ||
32 | "Expected 'urgent <enable|disable|allow|deny>'"); | ||
33 | } | ||
34 | |||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
36 | } | ||
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 @@ | |||
24 | #include "sway/input/seat.h" | 24 | #include "sway/input/seat.h" |
25 | #include "sway/commands.h" | 25 | #include "sway/commands.h" |
26 | #include "sway/config.h" | 26 | #include "sway/config.h" |
27 | #include "sway/criteria.h" | ||
27 | #include "sway/tree/arrange.h" | 28 | #include "sway/tree/arrange.h" |
28 | #include "sway/tree/layout.h" | 29 | #include "sway/tree/layout.h" |
29 | #include "sway/tree/workspace.h" | 30 | #include "sway/tree/workspace.h" |
@@ -105,7 +106,12 @@ void free_config(struct sway_config *config) { | |||
105 | } | 106 | } |
106 | list_free(config->seat_configs); | 107 | list_free(config->seat_configs); |
107 | } | 108 | } |
108 | list_free(config->criteria); | 109 | if (config->criteria) { |
110 | for (int i = 0; i < config->criteria->length; ++i) { | ||
111 | criteria_destroy(config->criteria->items[i]); | ||
112 | } | ||
113 | list_free(config->criteria); | ||
114 | } | ||
109 | list_free(config->no_focus); | 115 | list_free(config->no_focus); |
110 | list_free(config->active_bar_modifiers); | 116 | list_free(config->active_bar_modifiers); |
111 | list_free(config->config_chain); | 117 | list_free(config->config_chain); |
@@ -474,7 +480,6 @@ static bool load_include_config(const char *path, const char *parent_dir, | |||
474 | list_del(config->config_chain, index); | 480 | list_del(config->config_chain, index); |
475 | return false; | 481 | return false; |
476 | } | 482 | } |
477 | free(real_path); | ||
478 | 483 | ||
479 | // restore current_config_path | 484 | // restore current_config_path |
480 | config->current_config_path = parent_config; | 485 | 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) { | |||
37 | pcre_free(criteria->con_mark); | 37 | pcre_free(criteria->con_mark); |
38 | pcre_free(criteria->window_role); | 38 | pcre_free(criteria->window_role); |
39 | free(criteria->workspace); | 39 | free(criteria->workspace); |
40 | 40 | free(criteria->cmdlist); | |
41 | free(criteria->raw); | 41 | free(criteria->raw); |
42 | free(criteria); | 42 | free(criteria); |
43 | } | 43 | } |
@@ -46,6 +46,31 @@ static int regex_cmp(const char *item, const pcre *regex) { | |||
46 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); | 46 | return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); |
47 | } | 47 | } |
48 | 48 | ||
49 | static int cmp_urgent(const void *_a, const void *_b) { | ||
50 | struct sway_view *a = *(void **)_a; | ||
51 | struct sway_view *b = *(void **)_b; | ||
52 | |||
53 | if (a->urgent.tv_sec < b->urgent.tv_sec) { | ||
54 | return -1; | ||
55 | } else if (a->urgent.tv_sec > b->urgent.tv_sec) { | ||
56 | return 1; | ||
57 | } | ||
58 | if (a->urgent.tv_nsec < b->urgent.tv_nsec) { | ||
59 | return -1; | ||
60 | } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { | ||
61 | return 1; | ||
62 | } | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void find_urgent_iterator(struct sway_container *swayc, void *data) { | ||
67 | if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { | ||
68 | return; | ||
69 | } | ||
70 | list_t *urgent_views = data; | ||
71 | list_add(urgent_views, swayc->sway_view); | ||
72 | } | ||
73 | |||
49 | static bool criteria_matches_view(struct criteria *criteria, | 74 | static bool criteria_matches_view(struct criteria *criteria, |
50 | struct sway_view *view) { | 75 | struct sway_view *view) { |
51 | if (criteria->title) { | 76 | if (criteria->title) { |
@@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
133 | } | 158 | } |
134 | 159 | ||
135 | if (criteria->urgent) { | 160 | if (criteria->urgent) { |
136 | // TODO | 161 | if (!view_is_urgent(view)) { |
137 | return false; | 162 | return false; |
163 | } | ||
164 | list_t *urgent_views = create_list(); | ||
165 | container_for_each_descendant_dfs(&root_container, | ||
166 | find_urgent_iterator, urgent_views); | ||
167 | list_stable_sort(urgent_views, cmp_urgent); | ||
168 | struct sway_view *target; | ||
169 | if (criteria->urgent == 'o') { // oldest | ||
170 | target = urgent_views->items[0]; | ||
171 | } else { // latest | ||
172 | target = urgent_views->items[urgent_views->length - 1]; | ||
173 | } | ||
174 | list_free(urgent_views); | ||
175 | if (view != target) { | ||
176 | return false; | ||
177 | } | ||
138 | } | 178 | } |
139 | 179 | ||
140 | if (criteria->workspace) { | 180 | 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( | |||
67 | 67 | ||
68 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); | 68 | manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); |
69 | if (!manager->wlr_manager) { | 69 | if (!manager->wlr_manager) { |
70 | free(manager); | ||
70 | return NULL; | 71 | return NULL; |
71 | } | 72 | } |
72 | manager->idle = idle; | 73 | 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) { | |||
325 | layer_surface->client_pending.margin.bottom, | 325 | layer_surface->client_pending.margin.bottom, |
326 | layer_surface->client_pending.margin.left); | 326 | layer_surface->client_pending.margin.left); |
327 | 327 | ||
328 | struct sway_layer_surface *sway_layer = | ||
329 | calloc(1, sizeof(struct sway_layer_surface)); | ||
330 | if (!sway_layer) { | ||
331 | return; | ||
332 | } | ||
333 | |||
334 | if (!layer_surface->output) { | 328 | if (!layer_surface->output) { |
335 | // Assign last active output | 329 | // Assign last active output |
336 | struct sway_container *output = NULL; | 330 | struct sway_container *output = NULL; |
@@ -352,6 +346,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { | |||
352 | layer_surface->output = output->sway_output->wlr_output; | 346 | layer_surface->output = output->sway_output->wlr_output; |
353 | } | 347 | } |
354 | 348 | ||
349 | struct sway_layer_surface *sway_layer = | ||
350 | calloc(1, sizeof(struct sway_layer_surface)); | ||
351 | if (!sway_layer) { | ||
352 | return; | ||
353 | } | ||
354 | |||
355 | sway_layer->surface_commit.notify = handle_surface_commit; | 355 | sway_layer->surface_commit.notify = handle_surface_commit; |
356 | wl_signal_add(&layer_surface->surface->events.commit, | 356 | wl_signal_add(&layer_surface->surface->events.commit, |
357 | &sway_layer->surface_commit); | 357 | &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, | |||
256 | render_view_surfaces(view, output, damage, view->swayc->alpha); | 256 | render_view_surfaces(view, output, damage, view->swayc->alpha); |
257 | } | 257 | } |
258 | 258 | ||
259 | if (view->using_csd) { | ||
260 | return; | ||
261 | } | ||
262 | |||
259 | struct wlr_box box; | 263 | struct wlr_box box; |
260 | float output_scale = output->wlr_output->scale; | 264 | float output_scale = output->wlr_output->scale; |
261 | float color[4]; | 265 | float color[4]; |
@@ -553,7 +557,11 @@ static void render_container_simple(struct sway_output *output, | |||
553 | struct wlr_texture *marks_texture; | 557 | struct wlr_texture *marks_texture; |
554 | struct sway_container_state *state = &child->current; | 558 | struct sway_container_state *state = &child->current; |
555 | 559 | ||
556 | if (state->focused || parent_focused) { | 560 | if (view_is_urgent(view)) { |
561 | colors = &config->border_colors.urgent; | ||
562 | title_texture = child->title_urgent; | ||
563 | marks_texture = view->marks_urgent; | ||
564 | } else if (state->focused || parent_focused) { | ||
557 | colors = &config->border_colors.focused; | 565 | colors = &config->border_colors.focused; |
558 | title_texture = child->title_focused; | 566 | title_texture = child->title_focused; |
559 | marks_texture = view->marks_focused; | 567 | marks_texture = view->marks_focused; |
@@ -567,12 +575,14 @@ static void render_container_simple(struct sway_output *output, | |||
567 | marks_texture = view->marks_unfocused; | 575 | marks_texture = view->marks_unfocused; |
568 | } | 576 | } |
569 | 577 | ||
570 | if (state->border == B_NORMAL) { | 578 | if (!view->using_csd) { |
571 | render_titlebar(output, damage, child, state->swayc_x, | 579 | if (state->border == B_NORMAL) { |
572 | state->swayc_y, state->swayc_width, colors, | 580 | render_titlebar(output, damage, child, state->swayc_x, |
573 | title_texture, marks_texture); | 581 | state->swayc_y, state->swayc_width, colors, |
574 | } else { | 582 | title_texture, marks_texture); |
575 | render_top_border(output, damage, child, colors); | 583 | } else { |
584 | render_top_border(output, damage, child, colors); | ||
585 | } | ||
576 | } | 586 | } |
577 | render_view(output, damage, child, colors); | 587 | render_view(output, damage, child, colors); |
578 | } else { | 588 | } else { |
@@ -607,8 +617,14 @@ static void render_container_tabbed(struct sway_output *output, | |||
607 | struct border_colors *colors; | 617 | struct border_colors *colors; |
608 | struct wlr_texture *title_texture; | 618 | struct wlr_texture *title_texture; |
609 | struct wlr_texture *marks_texture; | 619 | struct wlr_texture *marks_texture; |
610 | 620 | bool urgent = view ? | |
611 | if (cstate->focused || parent_focused) { | 621 | view_is_urgent(view) : container_has_urgent_child(child); |
622 | |||
623 | if (urgent) { | ||
624 | colors = &config->border_colors.urgent; | ||
625 | title_texture = child->title_urgent; | ||
626 | marks_texture = view ? view->marks_urgent : NULL; | ||
627 | } else if (cstate->focused || parent_focused) { | ||
612 | colors = &config->border_colors.focused; | 628 | colors = &config->border_colors.focused; |
613 | title_texture = child->title_focused; | 629 | title_texture = child->title_focused; |
614 | marks_texture = view ? view->marks_focused : NULL; | 630 | marks_texture = view ? view->marks_focused : NULL; |
@@ -670,8 +686,14 @@ static void render_container_stacked(struct sway_output *output, | |||
670 | struct border_colors *colors; | 686 | struct border_colors *colors; |
671 | struct wlr_texture *title_texture; | 687 | struct wlr_texture *title_texture; |
672 | struct wlr_texture *marks_texture; | 688 | struct wlr_texture *marks_texture; |
673 | 689 | bool urgent = view ? | |
674 | if (cstate->focused || parent_focused) { | 690 | view_is_urgent(view) : container_has_urgent_child(child); |
691 | |||
692 | if (urgent) { | ||
693 | colors = &config->border_colors.urgent; | ||
694 | title_texture = child->title_urgent; | ||
695 | marks_texture = view ? view->marks_urgent : NULL; | ||
696 | } else if (cstate->focused || parent_focused) { | ||
675 | colors = &config->border_colors.focused; | 697 | colors = &config->border_colors.focused; |
676 | title_texture = child->title_focused; | 698 | title_texture = child->title_focused; |
677 | marks_texture = view ? view->marks_focused : NULL; | 699 | marks_texture = view ? view->marks_focused : NULL; |
@@ -731,7 +753,11 @@ static void render_floating_container(struct sway_output *soutput, | |||
731 | struct wlr_texture *title_texture; | 753 | struct wlr_texture *title_texture; |
732 | struct wlr_texture *marks_texture; | 754 | struct wlr_texture *marks_texture; |
733 | 755 | ||
734 | if (con->current.focused) { | 756 | if (view_is_urgent(view)) { |
757 | colors = &config->border_colors.urgent; | ||
758 | title_texture = con->title_urgent; | ||
759 | marks_texture = view->marks_urgent; | ||
760 | } else if (con->current.focused) { | ||
735 | colors = &config->border_colors.focused; | 761 | colors = &config->border_colors.focused; |
736 | title_texture = con->title_focused; | 762 | title_texture = con->title_focused; |
737 | marks_texture = view->marks_focused; | 763 | marks_texture = view->marks_focused; |
@@ -741,12 +767,14 @@ static void render_floating_container(struct sway_output *soutput, | |||
741 | marks_texture = view->marks_unfocused; | 767 | marks_texture = view->marks_unfocused; |
742 | } | 768 | } |
743 | 769 | ||
744 | if (con->current.border == B_NORMAL) { | 770 | if (!view->using_csd) { |
745 | render_titlebar(soutput, damage, con, con->current.swayc_x, | 771 | if (con->current.border == B_NORMAL) { |
746 | con->current.swayc_y, con->current.swayc_width, colors, | 772 | render_titlebar(soutput, damage, con, con->current.swayc_x, |
747 | title_texture, marks_texture); | 773 | con->current.swayc_y, con->current.swayc_width, colors, |
748 | } else if (con->current.border != B_NONE) { | 774 | title_texture, marks_texture); |
749 | render_top_border(soutput, damage, con, colors); | 775 | } else if (con->current.border != B_NONE) { |
776 | render_top_border(soutput, damage, con, colors); | ||
777 | } | ||
750 | } | 778 | } |
751 | render_view(soutput, damage, con, colors); | 779 | render_view(soutput, damage, con, colors); |
752 | } else { | 780 | } 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) { | |||
297 | } | 297 | } |
298 | 298 | ||
299 | view_damage_from(view); | 299 | view_damage_from(view); |
300 | |||
301 | if (view->allow_request_urgent) { | ||
302 | view_set_urgent(view, (bool)xsurface->hints_urgency); | ||
303 | } | ||
300 | } | 304 | } |
301 | 305 | ||
302 | static void handle_unmap(struct wl_listener *listener, void *data) { | 306 | 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, | |||
594 | } | 594 | } |
595 | } | 595 | } |
596 | 596 | ||
597 | static int handle_urgent_timeout(void *data) { | ||
598 | struct sway_view *view = data; | ||
599 | view_set_urgent(view, false); | ||
600 | return 0; | ||
601 | } | ||
602 | |||
597 | void seat_set_focus_warp(struct sway_seat *seat, | 603 | void seat_set_focus_warp(struct sway_seat *seat, |
598 | struct sway_container *container, bool warp) { | 604 | struct sway_container *container, bool warp) { |
599 | if (seat->focused_layer) { | 605 | if (seat->focused_layer) { |
@@ -649,6 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
649 | while (parent) { | 655 | while (parent) { |
650 | wl_list_remove(&parent->link); | 656 | wl_list_remove(&parent->link); |
651 | wl_list_insert(&seat->focus_stack, &parent->link); | 657 | wl_list_insert(&seat->focus_stack, &parent->link); |
658 | container_set_dirty(parent->container); | ||
652 | 659 | ||
653 | parent = | 660 | parent = |
654 | seat_container_from_container(seat, | 661 | seat_container_from_container(seat, |
@@ -670,6 +677,16 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
670 | } | 677 | } |
671 | } | 678 | } |
672 | 679 | ||
680 | // If urgent, start a timer to unset it | ||
681 | if (container && container->type == C_VIEW && | ||
682 | view_is_urgent(container->sway_view) && | ||
683 | !container->sway_view->urgent_timer) { | ||
684 | struct sway_view *view = container->sway_view; | ||
685 | view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, | ||
686 | handle_urgent_timeout, view); | ||
687 | wl_event_source_timer_update(view->urgent_timer, 1000); | ||
688 | } | ||
689 | |||
673 | // If we've focused a floating container, bring it to the front. | 690 | // If we've focused a floating container, bring it to the front. |
674 | // We do this by putting it at the end of the floating list. | 691 | // We do this by putting it at the end of the floating list. |
675 | // This must happen for both the pending and current children lists. | 692 | // 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, | |||
170 | json_object_object_add(object, "output", workspace->parent ? | 170 | json_object_object_add(object, "output", workspace->parent ? |
171 | json_object_new_string(workspace->parent->name) : NULL); | 171 | json_object_new_string(workspace->parent->name) : NULL); |
172 | json_object_object_add(object, "type", json_object_new_string("workspace")); | 172 | json_object_object_add(object, "type", json_object_new_string("workspace")); |
173 | json_object_object_add(object, "urgent", json_object_new_boolean(false)); | 173 | json_object_object_add(object, "urgent", |
174 | json_object_new_boolean(workspace->sway_workspace->urgent)); | ||
174 | json_object_object_add(object, "representation", workspace->formatted_title ? | 175 | json_object_object_add(object, "representation", workspace->formatted_title ? |
175 | json_object_new_string(workspace->formatted_title) : NULL); | 176 | json_object_new_string(workspace->formatted_title) : NULL); |
176 | 177 | ||
@@ -196,6 +197,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
196 | json_object_object_add(object, "layout", | 197 | json_object_object_add(object, "layout", |
197 | json_object_new_string(ipc_json_layout_description(c->layout))); | 198 | json_object_new_string(ipc_json_layout_description(c->layout))); |
198 | } | 199 | } |
200 | |||
201 | bool urgent = c->type == C_VIEW ? | ||
202 | view_is_urgent(c->sway_view) : container_has_urgent_child(c); | ||
203 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | ||
199 | } | 204 | } |
200 | 205 | ||
201 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { | 206 | 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( | |||
35 | 'commands/border.c', | 35 | 'commands/border.c', |
36 | 'commands/client.c', | 36 | 'commands/client.c', |
37 | 'commands/default_border.c', | 37 | 'commands/default_border.c', |
38 | 'commands/default_floating_border.c', | ||
38 | 'commands/default_orientation.c', | 39 | 'commands/default_orientation.c', |
39 | 'commands/exit.c', | 40 | 'commands/exit.c', |
40 | 'commands/exec.c', | 41 | 'commands/exec.c', |
@@ -59,6 +60,7 @@ sway_sources = files( | |||
59 | 'commands/mode.c', | 60 | 'commands/mode.c', |
60 | 'commands/mouse_warping.c', | 61 | 'commands/mouse_warping.c', |
61 | 'commands/move.c', | 62 | 'commands/move.c', |
63 | 'commands/no_focus.c', | ||
62 | 'commands/output.c', | 64 | 'commands/output.c', |
63 | 'commands/reload.c', | 65 | 'commands/reload.c', |
64 | 'commands/rename.c', | 66 | 'commands/rename.c', |
@@ -76,6 +78,7 @@ sway_sources = files( | |||
76 | 'commands/swap.c', | 78 | 'commands/swap.c', |
77 | 'commands/title_format.c', | 79 | 'commands/title_format.c', |
78 | 'commands/unmark.c', | 80 | 'commands/unmark.c', |
81 | 'commands/urgent.c', | ||
79 | 'commands/workspace.c', | 82 | 'commands/workspace.c', |
80 | 'commands/workspace_layout.c', | 83 | 'commands/workspace_layout.c', |
81 | 'commands/ws_auto_back_and_forth.c', | 84 | '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. | |||
499 | *unmark* will remove _identifier_ from the list of current marks on a | 499 | *unmark* will remove _identifier_ from the list of current marks on a |
500 | window. If _identifier_ is omitted, all marks are removed. | 500 | window. If _identifier_ is omitted, all marks are removed. |
501 | 501 | ||
502 | *urgent* enable|disable|allow|deny | ||
503 | Using _enable_ or _disable_ manually sets or unsets the window's urgent | ||
504 | state. Using _allow_ or _deny_ controls the window's ability to set itself | ||
505 | as urgent. By default, windows are allowed to set their own urgency. | ||
506 | |||
502 | *workspace* [number] <name> | 507 | *workspace* [number] <name> |
503 | Switches to the specified workspace. The string "number" is optional and is | 508 | Switches to the specified workspace. The string "number" is optional and is |
504 | used to sort workspaces. | 509 | 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, | |||
674 | void container_for_each_descendant_dfs(struct sway_container *container, | 674 | void container_for_each_descendant_dfs(struct sway_container *container, |
675 | void (*f)(struct sway_container *container, void *data), | 675 | void (*f)(struct sway_container *container, void *data), |
676 | void *data) { | 676 | void *data) { |
677 | if (container) { | 677 | if (!container) { |
678 | if (container->children) { | 678 | return; |
679 | for (int i = 0; i < container->children->length; ++i) { | 679 | } |
680 | struct sway_container *child = | 680 | if (container->children) { |
681 | container->children->items[i]; | 681 | for (int i = 0; i < container->children->length; ++i) { |
682 | container_for_each_descendant_dfs(child, f, data); | 682 | struct sway_container *child = container->children->items[i]; |
683 | } | 683 | container_for_each_descendant_dfs(child, f, data); |
684 | } | 684 | } |
685 | f(container, data); | ||
686 | } | 685 | } |
686 | if (container->type == C_WORKSPACE) { | ||
687 | struct sway_container *floating = container->sway_workspace->floating; | ||
688 | for (int i = 0; i < floating->children->length; ++i) { | ||
689 | struct sway_container *child = floating->children->items[i]; | ||
690 | container_for_each_descendant_dfs(child, f, data); | ||
691 | } | ||
692 | } | ||
693 | f(container, data); | ||
687 | } | 694 | } |
688 | 695 | ||
689 | void container_for_each_descendant_bfs(struct sway_container *con, | 696 | 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) { | |||
960 | return; | 967 | return; |
961 | } | 968 | } |
962 | struct sway_view *view = con->sway_view; | 969 | struct sway_view *view = con->sway_view; |
963 | size_t border_width = view->border_thickness * (view->border != B_NONE); | 970 | size_t border_width = 0; |
964 | size_t top = | 971 | size_t top = 0; |
965 | view->border == B_NORMAL ? container_titlebar_height() : border_width; | 972 | |
973 | if (!view->using_csd) { | ||
974 | border_width = view->border_thickness * (view->border != B_NONE); | ||
975 | top = view->border == B_NORMAL ? | ||
976 | container_titlebar_height() : border_width; | ||
977 | } | ||
966 | 978 | ||
967 | con->x = view->x - border_width; | 979 | con->x = view->x - border_width; |
968 | con->y = view->y - top; | 980 | con->y = view->y - top; |
@@ -1063,6 +1075,8 @@ void container_floating_move_to(struct sway_container *con, | |||
1063 | container_add_child(new_workspace->sway_workspace->floating, con); | 1075 | container_add_child(new_workspace->sway_workspace->floating, con); |
1064 | arrange_windows(old_workspace); | 1076 | arrange_windows(old_workspace); |
1065 | arrange_windows(new_workspace); | 1077 | arrange_windows(new_workspace); |
1078 | workspace_detect_urgent(old_workspace); | ||
1079 | workspace_detect_urgent(new_workspace); | ||
1066 | } | 1080 | } |
1067 | } | 1081 | } |
1068 | 1082 | ||
@@ -1073,3 +1087,12 @@ void container_set_dirty(struct sway_container *container) { | |||
1073 | container->dirty = true; | 1087 | container->dirty = true; |
1074 | list_add(server.dirty_containers, container); | 1088 | list_add(server.dirty_containers, container); |
1075 | } | 1089 | } |
1090 | |||
1091 | static bool find_urgent_iterator(struct sway_container *con, | ||
1092 | void *data) { | ||
1093 | return con->type == C_VIEW && view_is_urgent(con->sway_view); | ||
1094 | } | ||
1095 | |||
1096 | bool container_has_urgent_child(struct sway_container *container) { | ||
1097 | return container_find(container, find_urgent_iterator, NULL); | ||
1098 | } | ||
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, | |||
225 | } | 225 | } |
226 | } | 226 | } |
227 | } | 227 | } |
228 | // Update workspace urgent state | ||
229 | struct sway_container *old_workspace = old_parent; | ||
230 | if (old_workspace->type != C_WORKSPACE) { | ||
231 | old_workspace = container_parent(old_workspace, C_WORKSPACE); | ||
232 | } | ||
233 | if (new_workspace != old_workspace) { | ||
234 | workspace_detect_urgent(new_workspace); | ||
235 | workspace_detect_urgent(old_workspace); | ||
236 | } | ||
228 | } | 237 | } |
229 | 238 | ||
230 | static bool sway_dir_to_wlr(enum movement_direction dir, | 239 | static bool sway_dir_to_wlr(enum movement_direction dir, |
@@ -548,6 +557,8 @@ void container_move(struct sway_container *container, | |||
548 | } | 557 | } |
549 | if (last_ws && next_ws && last_ws != next_ws) { | 558 | if (last_ws && next_ws && last_ws != next_ws) { |
550 | ipc_event_workspace(last_ws, container, "focus"); | 559 | ipc_event_workspace(last_ws, container, "focus"); |
560 | workspace_detect_urgent(last_ws); | ||
561 | workspace_detect_urgent(next_ws); | ||
551 | } | 562 | } |
552 | } | 563 | } |
553 | 564 | ||
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, | |||
25 | view->impl = impl; | 25 | view->impl = impl; |
26 | view->executed_criteria = create_list(); | 26 | view->executed_criteria = create_list(); |
27 | view->marks = create_list(); | 27 | view->marks = create_list(); |
28 | view->allow_request_urgent = true; | ||
28 | wl_signal_init(&view->events.unmap); | 29 | wl_signal_init(&view->events.unmap); |
29 | } | 30 | } |
30 | 31 | ||
@@ -315,11 +316,15 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
315 | } | 316 | } |
316 | 317 | ||
317 | void view_set_tiled(struct sway_view *view, bool tiled) { | 318 | void view_set_tiled(struct sway_view *view, bool tiled) { |
318 | bool csd = true; | 319 | if (!tiled) { |
319 | if (view->impl->has_client_side_decorations) { | 320 | view->using_csd = true; |
320 | csd = view->impl->has_client_side_decorations(view); | 321 | if (view->impl->has_client_side_decorations) { |
322 | view->using_csd = view->impl->has_client_side_decorations(view); | ||
323 | } | ||
324 | } else { | ||
325 | view->using_csd = false; | ||
321 | } | 326 | } |
322 | view->border = tiled || !csd ? config->border : B_NONE; | 327 | |
323 | if (view->impl->set_tiled) { | 328 | if (view->impl->set_tiled) { |
324 | view->impl->set_tiled(view, tiled); | 329 | view->impl->set_tiled(view, tiled); |
325 | } | 330 | } |
@@ -504,20 +509,38 @@ void view_execute_criteria(struct sway_view *view) { | |||
504 | } | 509 | } |
505 | wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", | 510 | wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", |
506 | criteria->raw, view, criteria->cmdlist); | 511 | criteria->raw, view, criteria->cmdlist); |
512 | seat_set_focus(seat, view->swayc); | ||
507 | list_add(view->executed_criteria, criteria); | 513 | list_add(view->executed_criteria, criteria); |
508 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); | 514 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); |
509 | if (res->status != CMD_SUCCESS) { | 515 | if (res->status != CMD_SUCCESS) { |
510 | wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); | 516 | wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); |
511 | } | 517 | } |
512 | free_cmd_results(res); | 518 | free_cmd_results(res); |
513 | // view must be focused for commands to affect it, | ||
514 | // so always refocus in-between command lists | ||
515 | seat_set_focus(seat, view->swayc); | ||
516 | } | 519 | } |
517 | list_free(criterias); | 520 | list_free(criterias); |
518 | seat_set_focus(seat, prior_focus); | 521 | seat_set_focus(seat, prior_focus); |
519 | } | 522 | } |
520 | 523 | ||
524 | static bool should_focus(struct sway_view *view) { | ||
525 | // If the view is the only one in the focused workspace, it'll get focus | ||
526 | // regardless of any no_focus criteria. | ||
527 | struct sway_container *parent = view->swayc->parent; | ||
528 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
529 | if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { | ||
530 | size_t num_children = parent->children->length + | ||
531 | parent->sway_workspace->floating->children->length; | ||
532 | if (num_children == 1) { | ||
533 | return true; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | // Check no_focus criteria | ||
538 | list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); | ||
539 | size_t len = criterias->length; | ||
540 | list_free(criterias); | ||
541 | return len == 0; | ||
542 | } | ||
543 | |||
521 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | 544 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { |
522 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | 545 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { |
523 | return; | 546 | return; |
@@ -554,8 +577,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
554 | 577 | ||
555 | view->surface = wlr_surface; | 578 | view->surface = wlr_surface; |
556 | view->swayc = cont; | 579 | view->swayc = cont; |
557 | view->border = config->border; | ||
558 | view->border_thickness = config->border_thickness; | ||
559 | 580 | ||
560 | view_init_subsurfaces(view, wlr_surface); | 581 | view_init_subsurfaces(view, wlr_surface); |
561 | wl_signal_add(&wlr_surface->events.new_subsurface, | 582 | wl_signal_add(&wlr_surface->events.new_subsurface, |
@@ -566,14 +587,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
566 | view->container_reparent.notify = view_handle_container_reparent; | 587 | view->container_reparent.notify = view_handle_container_reparent; |
567 | 588 | ||
568 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 589 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
590 | view->border = config->floating_border; | ||
591 | view->border_thickness = config->floating_border_thickness; | ||
569 | container_set_floating(view->swayc, true); | 592 | container_set_floating(view->swayc, true); |
570 | } else { | 593 | } else { |
594 | view->border = config->border; | ||
595 | view->border_thickness = config->border_thickness; | ||
571 | view_set_tiled(view, true); | 596 | view_set_tiled(view, true); |
572 | } | 597 | } |
573 | 598 | ||
574 | input_manager_set_focus(input_manager, cont); | 599 | if (should_focus(view)) { |
575 | if (workspace) { | 600 | input_manager_set_focus(input_manager, cont); |
576 | workspace_switch(workspace); | 601 | if (workspace) { |
602 | workspace_switch(workspace); | ||
603 | } | ||
577 | } | 604 | } |
578 | 605 | ||
579 | view_update_title(view, false); | 606 | view_update_title(view, false); |
@@ -589,16 +616,26 @@ void view_unmap(struct sway_view *view) { | |||
589 | wl_list_remove(&view->surface_new_subsurface.link); | 616 | wl_list_remove(&view->surface_new_subsurface.link); |
590 | wl_list_remove(&view->container_reparent.link); | 617 | wl_list_remove(&view->container_reparent.link); |
591 | 618 | ||
619 | if (view->urgent_timer) { | ||
620 | wl_event_source_remove(view->urgent_timer); | ||
621 | view->urgent_timer = NULL; | ||
622 | } | ||
623 | |||
624 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
625 | |||
626 | struct sway_container *parent; | ||
592 | if (view->is_fullscreen) { | 627 | if (view->is_fullscreen) { |
593 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
594 | ws->sway_workspace->fullscreen = NULL; | 628 | ws->sway_workspace->fullscreen = NULL; |
595 | container_destroy(view->swayc); | 629 | parent = container_destroy(view->swayc); |
596 | 630 | ||
597 | arrange_windows(ws->parent); | 631 | arrange_windows(ws->parent); |
598 | } else { | 632 | } else { |
599 | struct sway_container *parent = container_destroy(view->swayc); | 633 | parent = container_destroy(view->swayc); |
600 | arrange_windows(parent); | 634 | arrange_windows(parent); |
601 | } | 635 | } |
636 | if (parent->type >= C_WORKSPACE) { // if the workspace still exists | ||
637 | workspace_detect_urgent(ws); | ||
638 | } | ||
602 | transaction_commit_dirty(); | 639 | transaction_commit_dirty(); |
603 | view->surface = NULL; | 640 | view->surface = NULL; |
604 | } | 641 | } |
@@ -1047,3 +1084,32 @@ bool view_is_visible(struct sway_view *view) { | |||
1047 | } | 1084 | } |
1048 | return true; | 1085 | return true; |
1049 | } | 1086 | } |
1087 | |||
1088 | void view_set_urgent(struct sway_view *view, bool enable) { | ||
1089 | if (view_is_urgent(view) == enable) { | ||
1090 | return; | ||
1091 | } | ||
1092 | if (enable) { | ||
1093 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
1094 | if (seat_get_focus(seat) == view->swayc) { | ||
1095 | return; | ||
1096 | } | ||
1097 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | ||
1098 | } else { | ||
1099 | view->urgent = (struct timespec){ 0 }; | ||
1100 | if (view->urgent_timer) { | ||
1101 | wl_event_source_remove(view->urgent_timer); | ||
1102 | view->urgent_timer = NULL; | ||
1103 | } | ||
1104 | } | ||
1105 | container_damage_whole(view->swayc); | ||
1106 | |||
1107 | ipc_event_window(view->swayc, "urgent"); | ||
1108 | |||
1109 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
1110 | workspace_detect_urgent(ws); | ||
1111 | } | ||
1112 | |||
1113 | bool view_is_urgent(struct sway_view *view) { | ||
1114 | return view->urgent.tv_sec || view->urgent.tv_nsec; | ||
1115 | } | ||
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 @@ | |||
11 | #include "sway/ipc-server.h" | 11 | #include "sway/ipc-server.h" |
12 | #include "sway/tree/arrange.h" | 12 | #include "sway/tree/arrange.h" |
13 | #include "sway/tree/container.h" | 13 | #include "sway/tree/container.h" |
14 | #include "sway/tree/view.h" | ||
14 | #include "sway/tree/workspace.h" | 15 | #include "sway/tree/workspace.h" |
15 | #include "list.h" | 16 | #include "list.h" |
16 | #include "log.h" | 17 | #include "log.h" |
@@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available( | |||
518 | 519 | ||
519 | return NULL; | 520 | return NULL; |
520 | } | 521 | } |
522 | |||
523 | void workspace_detect_urgent(struct sway_container *workspace) { | ||
524 | bool new_urgent = container_has_urgent_child(workspace); | ||
525 | |||
526 | if (workspace->sway_workspace->urgent != new_urgent) { | ||
527 | workspace->sway_workspace->urgent = new_urgent; | ||
528 | ipc_event_workspace(NULL, workspace, "urgent"); | ||
529 | container_damage_whole(workspace); | ||
530 | } | ||
531 | } | ||
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( | |||
115 | config->colors.inactive_workspace.text = parse_color( | 115 | config->colors.inactive_workspace.text = parse_color( |
116 | json_object_get_string(inactive_workspace_text)); | 116 | json_object_get_string(inactive_workspace_text)); |
117 | } | 117 | } |
118 | if (urgent_workspace_border) { | ||
119 | config->colors.urgent_workspace.border = parse_color( | ||
120 | json_object_get_string(urgent_workspace_border)); | ||
121 | } | ||
122 | if (urgent_workspace_bg) { | ||
123 | config->colors.urgent_workspace.background = parse_color( | ||
124 | json_object_get_string(urgent_workspace_bg)); | ||
125 | } | ||
126 | if (urgent_workspace_text) { | ||
127 | config->colors.urgent_workspace.text = parse_color( | ||
128 | json_object_get_string(urgent_workspace_text)); | ||
129 | } | ||
118 | if (binding_mode_border) { | 130 | if (binding_mode_border) { |
119 | config->colors.binding_mode.border = parse_color( | 131 | config->colors.binding_mode.border = parse_color( |
120 | json_object_get_string(binding_mode_border)); | 132 | 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 @@ | |||
1 | #define _XOPEN_SOURCE 500 | 1 | #define _XOPEN_SOURCE 500 |
2 | #include <errno.h> | ||
2 | #include <getopt.h> | 3 | #include <getopt.h> |
3 | #include <signal.h> | ||
4 | #include <pthread.h> | 4 | #include <pthread.h> |
5 | #include <signal.h> | ||
5 | #include <stdio.h> | 6 | #include <stdio.h> |
6 | #include <stdlib.h> | 7 | #include <stdlib.h> |
7 | #include <errno.h> | ||
8 | #include <string.h> | 8 | #include <string.h> |
9 | #include <sys/wait.h> | 9 | #include <sys/wait.h> |
10 | #include <unistd.h> | 10 | #include <unistd.h> |
11 | #include <wayland-client-protocol.h> | 11 | #include <wayland-client-protocol.h> |
12 | #include <wayland-client.h> | 12 | #include <wayland-client.h> |
13 | #include <wayland-server.h> | ||
13 | #include <wayland-util.h> | 14 | #include <wayland-util.h> |
14 | #include <wlr/config.h> | 15 | #include <wlr/config.h> |
15 | #include <wlr/util/log.h> | 16 | #include <wlr/util/log.h> |
16 | #include <wlr/types/wlr_output_layout.h> | ||
17 | #include <wlr/types/wlr_output.h> | ||
18 | #include "idle-client-protocol.h" | ||
19 | #include "config.h" | 17 | #include "config.h" |
18 | #include "idle-client-protocol.h" | ||
20 | #include "list.h" | 19 | #include "list.h" |
21 | #ifdef SWAY_IDLE_HAS_SYSTEMD | 20 | #ifdef SWAY_IDLE_HAS_SYSTEMD |
22 | #include <systemd/sd-bus.h> | 21 | #include <systemd/sd-bus.h> |
@@ -36,7 +35,6 @@ struct swayidle_state { | |||
36 | struct wl_display *display; | 35 | struct wl_display *display; |
37 | struct org_kde_kwin_idle_timeout *idle_timer; | 36 | struct org_kde_kwin_idle_timeout *idle_timer; |
38 | struct org_kde_kwin_idle_timeout *lock_timer; | 37 | struct org_kde_kwin_idle_timeout *lock_timer; |
39 | struct wlr_output_layout *layout; | ||
40 | struct wl_event_loop *event_loop; | 38 | struct wl_event_loop *event_loop; |
41 | list_t *timeout_cmds; | 39 | list_t *timeout_cmds; |
42 | } state; | 40 | } state; |
@@ -165,7 +163,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) { | |||
165 | 163 | ||
166 | void setup_sleep_listener() { | 164 | void setup_sleep_listener() { |
167 | struct sd_bus *bus; | 165 | struct sd_bus *bus; |
168 | 166 | ||
169 | int ret = sd_bus_default_system(&bus); | 167 | int ret = sd_bus_default_system(&bus); |
170 | if (ret < 0) { | 168 | if (ret < 0) { |
171 | wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", | 169 | 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) { | |||
360 | if (wl_display_dispatch(state.display) < 0) { | 358 | if (wl_display_dispatch(state.display) < 0) { |
361 | wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting"); | 359 | wlr_log_errno(WLR_ERROR, "wl_display_dispatch failed, exiting"); |
362 | sway_terminate(0); | 360 | sway_terminate(0); |
363 | }; | 361 | } |
364 | return 0; | 362 | return 0; |
365 | } | 363 | } |
366 | 364 | ||
@@ -397,7 +395,6 @@ int main(int argc, char *argv[]) { | |||
397 | struct wl_registry *registry = wl_display_get_registry(state.display); | 395 | struct wl_registry *registry = wl_display_get_registry(state.display); |
398 | wl_registry_add_listener(registry, ®istry_listener, NULL); | 396 | wl_registry_add_listener(registry, ®istry_listener, NULL); |
399 | wl_display_roundtrip(state.display); | 397 | wl_display_roundtrip(state.display); |
400 | state.layout = wlr_output_layout_create(); | ||
401 | state.event_loop = wl_event_loop_create(); | 398 | state.event_loop = wl_event_loop_create(); |
402 | 399 | ||
403 | if (idle_manager == NULL) { | 400 | 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 @@ | |||
21 | #include "pool-buffer.h" | 21 | #include "pool-buffer.h" |
22 | #include "cairo.h" | 22 | #include "cairo.h" |
23 | #include "log.h" | 23 | #include "log.h" |
24 | #include "readline.h" | ||
24 | #include "stringop.h" | 25 | #include "stringop.h" |
25 | #include "util.h" | 26 | #include "util.h" |
26 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" | 27 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" |
@@ -412,15 +413,14 @@ static void set_default_colors(struct swaylock_colors *colors) { | |||
412 | }; | 413 | }; |
413 | } | 414 | } |
414 | 415 | ||
415 | static struct swaylock_state state; | 416 | enum line_mode { |
416 | 417 | LM_LINE, | |
417 | int main(int argc, char **argv) { | 418 | LM_INSIDE, |
418 | enum line_mode { | 419 | LM_RING, |
419 | LM_LINE, | 420 | }; |
420 | LM_INSIDE, | ||
421 | LM_RING, | ||
422 | }; | ||
423 | 421 | ||
422 | static int parse_options(int argc, char **argv, struct swaylock_state *state, | ||
423 | enum line_mode *line_mode) { | ||
424 | enum long_option_codes { | 424 | enum long_option_codes { |
425 | LO_BS_HL_COLOR = 256, | 425 | LO_BS_HL_COLOR = 256, |
426 | LO_FONT, | 426 | LO_FONT, |
@@ -447,6 +447,7 @@ int main(int argc, char **argv) { | |||
447 | }; | 447 | }; |
448 | 448 | ||
449 | static struct option long_options[] = { | 449 | static struct option long_options[] = { |
450 | {"config", required_argument, NULL, 'C'}, | ||
450 | {"color", required_argument, NULL, 'c'}, | 451 | {"color", required_argument, NULL, 'c'}, |
451 | {"ignore-empty-password", no_argument, NULL, 'e'}, | 452 | {"ignore-empty-password", no_argument, NULL, 'e'}, |
452 | {"daemonize", no_argument, NULL, 'f'}, | 453 | {"daemonize", no_argument, NULL, 'f'}, |
@@ -487,6 +488,8 @@ int main(int argc, char **argv) { | |||
487 | const char usage[] = | 488 | const char usage[] = |
488 | "Usage: swaylock [options...]\n" | 489 | "Usage: swaylock [options...]\n" |
489 | "\n" | 490 | "\n" |
491 | " -C, --config <config_file> " | ||
492 | "Path to the config file.\n" | ||
490 | " -c, --color <color> " | 493 | " -c, --color <color> " |
491 | "Turn the screen into the given color instead of white.\n" | 494 | "Turn the screen into the given color instead of white.\n" |
492 | " -e, --ignore-empty-password " | 495 | " -e, --ignore-empty-password " |
@@ -559,58 +562,48 @@ int main(int argc, char **argv) { | |||
559 | "\n" | 562 | "\n" |
560 | "All <color> options are of the form <rrggbb[aa]>.\n"; | 563 | "All <color> options are of the form <rrggbb[aa]>.\n"; |
561 | 564 | ||
562 | enum line_mode line_mode = LM_LINE; | ||
563 | state.args = (struct swaylock_args){ | ||
564 | .mode = BACKGROUND_MODE_SOLID_COLOR, | ||
565 | .font = strdup("sans-serif"), | ||
566 | .radius = 50, | ||
567 | .thickness = 10, | ||
568 | .ignore_empty = false, | ||
569 | .show_indicator = true, | ||
570 | }; | ||
571 | wl_list_init(&state.images); | ||
572 | set_default_colors(&state.args.colors); | ||
573 | |||
574 | wlr_log_init(WLR_DEBUG, NULL); | ||
575 | |||
576 | int c; | 565 | int c; |
566 | optind = 1; | ||
577 | while (1) { | 567 | while (1) { |
578 | int opt_idx = 0; | 568 | int opt_idx = 0; |
579 | c = getopt_long(argc, argv, "c:efhi:nrs:tuv", long_options, &opt_idx); | 569 | c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx); |
580 | if (c == -1) { | 570 | if (c == -1) { |
581 | break; | 571 | break; |
582 | } | 572 | } |
583 | switch (c) { | 573 | switch (c) { |
574 | case 'C': | ||
575 | // Config file. This will have already been handled so just ignore. | ||
576 | break; | ||
584 | case 'c': | 577 | case 'c': |
585 | state.args.colors.background = parse_color(optarg); | 578 | state->args.colors.background = parse_color(optarg); |
586 | state.args.mode = BACKGROUND_MODE_SOLID_COLOR; | 579 | state->args.mode = BACKGROUND_MODE_SOLID_COLOR; |
587 | break; | 580 | break; |
588 | case 'e': | 581 | case 'e': |
589 | state.args.ignore_empty = true; | 582 | state->args.ignore_empty = true; |
590 | break; | 583 | break; |
591 | case 'f': | 584 | case 'f': |
592 | state.args.daemonize = true; | 585 | state->args.daemonize = true; |
593 | break; | 586 | break; |
594 | case 'i': | 587 | case 'i': |
595 | load_image(optarg, &state); | 588 | load_image(optarg, state); |
596 | break; | 589 | break; |
597 | case 'n': | 590 | case 'n': |
598 | line_mode = LM_INSIDE; | 591 | *line_mode = LM_INSIDE; |
599 | break; | 592 | break; |
600 | case 'r': | 593 | case 'r': |
601 | line_mode = LM_RING; | 594 | *line_mode = LM_RING; |
602 | break; | 595 | break; |
603 | case 's': | 596 | case 's': |
604 | state.args.mode = parse_background_mode(optarg); | 597 | state->args.mode = parse_background_mode(optarg); |
605 | if (state.args.mode == BACKGROUND_MODE_INVALID) { | 598 | if (state->args.mode == BACKGROUND_MODE_INVALID) { |
606 | return 1; | 599 | return 1; |
607 | } | 600 | } |
608 | break; | 601 | break; |
609 | case 't': | 602 | case 't': |
610 | state.args.mode = BACKGROUND_MODE_TILE; | 603 | state->args.mode = BACKGROUND_MODE_TILE; |
611 | break; | 604 | break; |
612 | case 'u': | 605 | case 'u': |
613 | state.args.show_indicator = false; | 606 | state->args.show_indicator = false; |
614 | break; | 607 | break; |
615 | case 'v': | 608 | case 'v': |
616 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE | 609 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE |
@@ -621,71 +614,71 @@ int main(int argc, char **argv) { | |||
621 | #endif | 614 | #endif |
622 | return 0; | 615 | return 0; |
623 | case LO_BS_HL_COLOR: | 616 | case LO_BS_HL_COLOR: |
624 | state.args.colors.bs_highlight = parse_color(optarg); | 617 | state->args.colors.bs_highlight = parse_color(optarg); |
625 | break; | 618 | break; |
626 | case LO_FONT: | 619 | case LO_FONT: |
627 | free(state.args.font); | 620 | free(state->args.font); |
628 | state.args.font = strdup(optarg); | 621 | state->args.font = strdup(optarg); |
629 | break; | 622 | break; |
630 | case LO_IND_RADIUS: | 623 | case LO_IND_RADIUS: |
631 | state.args.radius = strtol(optarg, NULL, 0); | 624 | state->args.radius = strtol(optarg, NULL, 0); |
632 | break; | 625 | break; |
633 | case LO_IND_THICKNESS: | 626 | case LO_IND_THICKNESS: |
634 | state.args.thickness = strtol(optarg, NULL, 0); | 627 | state->args.thickness = strtol(optarg, NULL, 0); |
635 | break; | 628 | break; |
636 | case LO_INSIDE_COLOR: | 629 | case LO_INSIDE_COLOR: |
637 | state.args.colors.inside.input = parse_color(optarg); | 630 | state->args.colors.inside.input = parse_color(optarg); |
638 | break; | 631 | break; |
639 | case LO_INSIDE_CLEAR_COLOR: | 632 | case LO_INSIDE_CLEAR_COLOR: |
640 | state.args.colors.inside.cleared = parse_color(optarg); | 633 | state->args.colors.inside.cleared = parse_color(optarg); |
641 | break; | 634 | break; |
642 | case LO_INSIDE_VER_COLOR: | 635 | case LO_INSIDE_VER_COLOR: |
643 | state.args.colors.inside.verifying = parse_color(optarg); | 636 | state->args.colors.inside.verifying = parse_color(optarg); |
644 | break; | 637 | break; |
645 | case LO_INSIDE_WRONG_COLOR: | 638 | case LO_INSIDE_WRONG_COLOR: |
646 | state.args.colors.inside.wrong = parse_color(optarg); | 639 | state->args.colors.inside.wrong = parse_color(optarg); |
647 | break; | 640 | break; |
648 | case LO_KEY_HL_COLOR: | 641 | case LO_KEY_HL_COLOR: |
649 | state.args.colors.key_highlight = parse_color(optarg); | 642 | state->args.colors.key_highlight = parse_color(optarg); |
650 | break; | 643 | break; |
651 | case LO_LINE_COLOR: | 644 | case LO_LINE_COLOR: |
652 | state.args.colors.line.input = parse_color(optarg); | 645 | state->args.colors.line.input = parse_color(optarg); |
653 | break; | 646 | break; |
654 | case LO_LINE_CLEAR_COLOR: | 647 | case LO_LINE_CLEAR_COLOR: |
655 | state.args.colors.line.cleared = parse_color(optarg); | 648 | state->args.colors.line.cleared = parse_color(optarg); |
656 | break; | 649 | break; |
657 | case LO_LINE_VER_COLOR: | 650 | case LO_LINE_VER_COLOR: |
658 | state.args.colors.line.verifying = parse_color(optarg); | 651 | state->args.colors.line.verifying = parse_color(optarg); |
659 | break; | 652 | break; |
660 | case LO_LINE_WRONG_COLOR: | 653 | case LO_LINE_WRONG_COLOR: |
661 | state.args.colors.line.wrong = parse_color(optarg); | 654 | state->args.colors.line.wrong = parse_color(optarg); |
662 | break; | 655 | break; |
663 | case LO_RING_COLOR: | 656 | case LO_RING_COLOR: |
664 | state.args.colors.ring.input = parse_color(optarg); | 657 | state->args.colors.ring.input = parse_color(optarg); |
665 | break; | 658 | break; |
666 | case LO_RING_CLEAR_COLOR: | 659 | case LO_RING_CLEAR_COLOR: |
667 | state.args.colors.ring.cleared = parse_color(optarg); | 660 | state->args.colors.ring.cleared = parse_color(optarg); |
668 | break; | 661 | break; |
669 | case LO_RING_VER_COLOR: | 662 | case LO_RING_VER_COLOR: |
670 | state.args.colors.ring.verifying = parse_color(optarg); | 663 | state->args.colors.ring.verifying = parse_color(optarg); |
671 | break; | 664 | break; |
672 | case LO_RING_WRONG_COLOR: | 665 | case LO_RING_WRONG_COLOR: |
673 | state.args.colors.ring.wrong = parse_color(optarg); | 666 | state->args.colors.ring.wrong = parse_color(optarg); |
674 | break; | 667 | break; |
675 | case LO_SEP_COLOR: | 668 | case LO_SEP_COLOR: |
676 | state.args.colors.separator = parse_color(optarg); | 669 | state->args.colors.separator = parse_color(optarg); |
677 | break; | 670 | break; |
678 | case LO_TEXT_COLOR: | 671 | case LO_TEXT_COLOR: |
679 | state.args.colors.text.input = parse_color(optarg); | 672 | state->args.colors.text.input = parse_color(optarg); |
680 | break; | 673 | break; |
681 | case LO_TEXT_CLEAR_COLOR: | 674 | case LO_TEXT_CLEAR_COLOR: |
682 | state.args.colors.text.cleared = parse_color(optarg); | 675 | state->args.colors.text.cleared = parse_color(optarg); |
683 | break; | 676 | break; |
684 | case LO_TEXT_VER_COLOR: | 677 | case LO_TEXT_VER_COLOR: |
685 | state.args.colors.text.verifying = parse_color(optarg); | 678 | state->args.colors.text.verifying = parse_color(optarg); |
686 | break; | 679 | break; |
687 | case LO_TEXT_WRONG_COLOR: | 680 | case LO_TEXT_WRONG_COLOR: |
688 | state.args.colors.text.wrong = parse_color(optarg); | 681 | state->args.colors.text.wrong = parse_color(optarg); |
689 | break; | 682 | break; |
690 | default: | 683 | default: |
691 | fprintf(stderr, "%s", usage); | 684 | fprintf(stderr, "%s", usage); |
@@ -693,6 +686,143 @@ int main(int argc, char **argv) { | |||
693 | } | 686 | } |
694 | } | 687 | } |
695 | 688 | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | static bool file_exists(const char *path) { | ||
693 | return path && access(path, R_OK) != -1; | ||
694 | } | ||
695 | |||
696 | static char *get_config_path(void) { | ||
697 | static const char *config_paths[] = { | ||
698 | "$HOME/.swaylock/config", | ||
699 | "$XDG_CONFIG_HOME/swaylock/config", | ||
700 | SYSCONFDIR "/swaylock/config", | ||
701 | }; | ||
702 | |||
703 | if (!getenv("XDG_CONFIG_HOME")) { | ||
704 | char *home = getenv("HOME"); | ||
705 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); | ||
706 | if (!config_home) { | ||
707 | wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); | ||
708 | } else { | ||
709 | strcpy(config_home, home); | ||
710 | strcat(config_home, "/.config"); | ||
711 | setenv("XDG_CONFIG_HOME", config_home, 1); | ||
712 | wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); | ||
713 | free(config_home); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | wordexp_t p; | ||
718 | char *path; | ||
719 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | ||
720 | if (wordexp(config_paths[i], &p, 0) == 0) { | ||
721 | path = strdup(p.we_wordv[0]); | ||
722 | wordfree(&p); | ||
723 | if (file_exists(path)) { | ||
724 | return path; | ||
725 | } | ||
726 | free(path); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | return NULL; | ||
731 | } | ||
732 | |||
733 | static int load_config(char *path, struct swaylock_state *state, | ||
734 | enum line_mode *line_mode) { | ||
735 | FILE *config = fopen(path, "r"); | ||
736 | if (!config) { | ||
737 | wlr_log(WLR_ERROR, "Failed to read config. Running without it."); | ||
738 | return 0; | ||
739 | } | ||
740 | char *line; | ||
741 | int line_number = 0; | ||
742 | while (!feof(config)) { | ||
743 | line = read_line(config); | ||
744 | if (!line) { | ||
745 | continue; | ||
746 | } | ||
747 | |||
748 | line_number++; | ||
749 | if (line[0] == '#') { | ||
750 | free(line); | ||
751 | continue; | ||
752 | } | ||
753 | if (strlen(line) == 0) { | ||
754 | free(line); | ||
755 | continue; | ||
756 | } | ||
757 | |||
758 | wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); | ||
759 | char flag[strlen(line) + 3]; | ||
760 | sprintf(flag, "--%s", line); | ||
761 | char *argv[] = {"swaylock", flag}; | ||
762 | int result = parse_options(2, argv, state, line_mode); | ||
763 | if (result != 0) { | ||
764 | free(line); | ||
765 | fclose(config); | ||
766 | return result; | ||
767 | } | ||
768 | free(line); | ||
769 | } | ||
770 | fclose(config); | ||
771 | return 0; | ||
772 | } | ||
773 | |||
774 | static struct swaylock_state state; | ||
775 | |||
776 | int main(int argc, char **argv) { | ||
777 | enum line_mode line_mode = LM_LINE; | ||
778 | state.args = (struct swaylock_args){ | ||
779 | .mode = BACKGROUND_MODE_SOLID_COLOR, | ||
780 | .font = strdup("sans-serif"), | ||
781 | .radius = 50, | ||
782 | .thickness = 10, | ||
783 | .ignore_empty = false, | ||
784 | .show_indicator = true, | ||
785 | }; | ||
786 | wl_list_init(&state.images); | ||
787 | set_default_colors(&state.args.colors); | ||
788 | |||
789 | wlr_log_init(WLR_DEBUG, NULL); | ||
790 | |||
791 | char *config_path = NULL; | ||
792 | static struct option long_options[] = { | ||
793 | {"config", required_argument, NULL, 'C'}, | ||
794 | {0, 0, 0, 0}, | ||
795 | }; | ||
796 | while (1) { | ||
797 | int c = getopt_long(argc, argv, "C:", long_options, NULL); | ||
798 | if (c == -1) { | ||
799 | break; | ||
800 | } else if (c == 'C') { | ||
801 | config_path = strdup(optarg); | ||
802 | break; | ||
803 | } | ||
804 | } | ||
805 | if (!config_path) { | ||
806 | config_path = get_config_path(); | ||
807 | } | ||
808 | |||
809 | if (config_path) { | ||
810 | wlr_log(WLR_DEBUG, "Found config at %s", config_path); | ||
811 | int config_status = load_config(config_path, &state, &line_mode); | ||
812 | free(config_path); | ||
813 | if (config_status != 0) { | ||
814 | return config_status; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | if (argc > 1) { | ||
819 | wlr_log(WLR_DEBUG, "Parsing CLI Args"); | ||
820 | int result = parse_options(argc, argv, &state, &line_mode); | ||
821 | if (result != 0) { | ||
822 | return result; | ||
823 | } | ||
824 | } | ||
825 | |||
696 | if (line_mode == LM_INSIDE) { | 826 | if (line_mode == LM_INSIDE) { |
697 | state.args.colors.line = state.args.colors.inside; | 827 | state.args.colors.line = state.args.colors.inside; |
698 | } else if (line_mode == LM_RING) { | 828 | } 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. | |||
12 | 12 | ||
13 | # OPTIONS | 13 | # OPTIONS |
14 | 14 | ||
15 | *-C, --config* <path> | ||
16 | The config file to use. By default, the following paths are checked: | ||
17 | _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and | ||
18 | _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid | ||
19 | options in the configuration file using the format _long-option=value_. | ||
20 | For options such as _ignore-empty-password_, just supply the _long-option_. | ||
21 | All leading dashes should be omitted and the equals sign is required for | ||
22 | flags that take an argument. | ||
23 | |||
15 | *-c, --color* <rrggbb[aa]> | 24 | *-c, --color* <rrggbb[aa]> |
16 | Turn the screen into the given color. If -i is used, this sets the | 25 | Turn the screen into the given color. If -i is used, this sets the |
17 | background of the image to the given color. Defaults to white (FFFFFF), or | 26 | background of the image to the given color. Defaults to white (FFFFFF), or |