summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/criteria.h5
-rw-r--r--include/sway/debug.h8
-rw-r--r--include/sway/desktop/transaction.h33
-rw-r--r--include/sway/input/seat.h11
-rw-r--r--include/sway/server.h5
-rw-r--r--include/sway/tree/arrange.h22
-rw-r--r--include/sway/tree/container.h15
-rw-r--r--include/sway/tree/view.h8
-rw-r--r--include/sway/tree/workspace.h4
-rw-r--r--sway/commands.c2
-rw-r--r--sway/commands/bar/position.c1
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/floating.c2
-rw-r--r--sway/commands/fullscreen.c2
-rw-r--r--sway/commands/gaps.c8
-rw-r--r--sway/commands/layout.c2
-rw-r--r--sway/commands/move.c25
-rw-r--r--sway/commands/no_focus.c26
-rw-r--r--sway/commands/reload.c2
-rw-r--r--sway/commands/resize.c6
-rw-r--r--sway/commands/smart_gaps.c2
-rw-r--r--sway/commands/split.c2
-rw-r--r--sway/commands/swap.c9
-rw-r--r--sway/commands/urgent.c36
-rw-r--r--sway/config.c43
-rw-r--r--sway/config/output.c2
-rw-r--r--sway/criteria.c44
-rw-r--r--sway/desktop/layer_shell.c3
-rw-r--r--sway/desktop/output.c14
-rw-r--r--sway/desktop/render.c89
-rw-r--r--sway/desktop/transaction.c65
-rw-r--r--sway/desktop/xdg_shell.c8
-rw-r--r--sway/desktop/xdg_shell_v6.c8
-rw-r--r--sway/desktop/xwayland.c12
-rw-r--r--sway/input/cursor.c3
-rw-r--r--sway/input/keyboard.c2
-rw-r--r--sway/input/seat.c41
-rw-r--r--sway/ipc-json.c7
-rw-r--r--sway/ipc-server.c2
-rw-r--r--sway/main.c18
-rw-r--r--sway/meson.build2
-rw-r--r--sway/server.c7
-rw-r--r--sway/sway.5.scd5
-rw-r--r--sway/tree/arrange.c78
-rw-r--r--sway/tree/container.c58
-rw-r--r--sway/tree/layout.c14
-rw-r--r--sway/tree/view.c78
-rw-r--r--sway/tree/workspace.c13
-rw-r--r--swaybar/ipc.c12
-rw-r--r--swaylock/main.c244
-rw-r--r--swaylock/swaylock.1.scd9
52 files changed, 754 insertions, 366 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;
152sway_cmd cmd_swap; 152sway_cmd cmd_swap;
153sway_cmd cmd_title_format; 153sway_cmd cmd_title_format;
154sway_cmd cmd_unmark; 154sway_cmd cmd_unmark;
155sway_cmd cmd_urgent;
155sway_cmd cmd_workspace; 156sway_cmd cmd_workspace;
156sway_cmd cmd_ws_auto_back_and_forth; 157sway_cmd cmd_ws_auto_back_and_forth;
157sway_cmd cmd_workspace_layout; 158sway_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
8enum criteria_type { 8enum 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
14struct criteria { 15struct criteria {
diff --git a/include/sway/debug.h b/include/sway/debug.h
index 2430d319..38d4eccd 100644
--- a/include/sway/debug.h
+++ b/include/sway/debug.h
@@ -1,7 +1,15 @@
1#ifndef SWAY_DEBUG_H 1#ifndef SWAY_DEBUG_H
2#define SWAY_DEBUG_H 2#define SWAY_DEBUG_H
3 3
4// Tree
4extern bool enable_debug_tree; 5extern bool enable_debug_tree;
5void update_debug_tree(); 6void update_debug_tree();
6 7
8// Damage
9extern const char *damage_debug;
10
11// Transactions
12extern int txn_timeout_ms;
13extern bool txn_debug;
14
7#endif 15#endif
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h
index 7ab80eb8..cee4afed 100644
--- a/include/sway/desktop/transaction.h
+++ b/include/sway/desktop/transaction.h
@@ -6,34 +6,25 @@
6/** 6/**
7 * Transactions enable us to perform atomic layout updates. 7 * Transactions enable us to perform atomic layout updates.
8 * 8 *
9 * When we want to make adjustments to the layout, we create a transaction. 9 * A transaction contains a list of containers and their new state.
10 * A transaction contains a list of affected containers and their new state.
11 * A state might contain a new size, or new border settings, or new parent/child 10 * A state might contain a new size, or new border settings, or new parent/child
12 * relationships. 11 * relationships.
13 * 12 *
14 * Calling transaction_commit() makes sway notify of all the affected clients 13 * Committing a transaction makes sway notify of all the affected clients with
15 * with their new sizes. We then wait for all the views to respond with their 14 * their new sizes. We then wait for all the views to respond with their new
16 * new surface sizes. When all are ready, or when a timeout has passed, we apply 15 * surface sizes. When all are ready, or when a timeout has passed, we apply the
17 * the updates all at the same time. 16 * updates all at the same time.
18 */ 17 *
19 18 * When we want to make adjustments to the layout, we change the pending state
20struct sway_transaction; 19 * in containers, mark them as dirty and call transaction_commit_dirty(). This
21 20 * create and commits a transaction from the dirty containers.
22/**
23 * Create a new transaction.
24 */
25struct sway_transaction *transaction_create(void);
26
27/**
28 * Add a container's pending state to the transaction.
29 */ 21 */
30void transaction_add_container(struct sway_transaction *transaction,
31 struct sway_container *container);
32 22
33/** 23/**
34 * Submit a transaction to the client views for configuration. 24 * Find all dirty containers, create and commit a transaction containing them,
25 * and unmark them as dirty.
35 */ 26 */
36void transaction_commit(struct sway_transaction *transaction); 27void transaction_commit_dirty(void);
37 28
38/** 29/**
39 * Notify the transaction system that a view is ready for the new layout. 30 * Notify the transaction system that a view is ready for the new layout.
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 0e440701..1f7792ba 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -119,17 +119,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
119 struct sway_container *container); 119 struct sway_container *container);
120 120
121/** 121/**
122 * Return the immediate child of container which was most recently focused, with
123 * fallback to selecting the child in the parent's `current` (rendered) children
124 * list.
125 *
126 * This is useful for when a tabbed container and its children are destroyed but
127 * still being rendered, and we have to render an appropriate child.
128 */
129struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
130 struct sway_container *container);
131
132/**
133 * Iterate over the focus-inactive children of the container calling the 122 * Iterate over the focus-inactive children of the container calling the
134 * function on each. 123 * function on each.
135 */ 124 */
diff --git a/include/sway/server.h b/include/sway/server.h
index a3e32898..a017d1c4 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -47,10 +47,7 @@ struct sway_server {
47 bool debug_txn_timings; 47 bool debug_txn_timings;
48 48
49 list_t *transactions; 49 list_t *transactions;
50 50 list_t *dirty_containers;
51 // When a view is being destroyed and is waiting for a transaction to
52 // complete it will be stored here.
53 list_t *destroying_containers;
54}; 51};
55 52
56struct sway_server server; 53struct sway_server server;
diff --git a/include/sway/tree/arrange.h b/include/sway/tree/arrange.h
index 58235642..d6abcc81 100644
--- a/include/sway/tree/arrange.h
+++ b/include/sway/tree/arrange.h
@@ -11,26 +11,8 @@ void remove_gaps(struct sway_container *c);
11void add_gaps(struct sway_container *c); 11void add_gaps(struct sway_container *c);
12 12
13/** 13/**
14 * Arrange layout for all the children of the given container, and add them to 14 * Arrange layout for all the children of the given container.
15 * the given transaction.
16 *
17 * Use this function if you need to arrange multiple sections of the tree in one
18 * transaction.
19 *
20 * You must set the desired state of the container before calling
21 * arrange_windows, then don't change any state-tracked properties in the
22 * container until you've called transaction_commit.
23 */ 15 */
24void arrange_windows(struct sway_container *container, 16void arrange_windows(struct sway_container *container);
25 struct sway_transaction *transaction);
26
27/**
28 * Arrange layout for the given container and commit the transaction.
29 *
30 * This function is a wrapper around arrange_windows, and handles creating and
31 * committing the transaction for you. Use this function if you're only doing
32 * one arrange operation.
33 */
34void arrange_and_commit(struct sway_container *container);
35 17
36#endif 18#endif
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index a69da9db..ca7a3288 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -68,6 +68,9 @@ struct sway_container_state {
68 struct sway_container *parent; 68 struct sway_container *parent;
69 list_t *children; 69 list_t *children;
70 70
71 struct sway_container *focused_inactive_child;
72 bool focused;
73
71 // View properties 74 // View properties
72 double view_x, view_y; 75 double view_x, view_y;
73 double view_width, view_height; 76 double view_width, view_height;
@@ -144,6 +147,10 @@ struct sway_container {
144 147
145 bool destroying; 148 bool destroying;
146 149
150 // If true, indicates that the container has pending state that differs from
151 // the current.
152 bool dirty;
153
147 struct { 154 struct {
148 struct wl_signal destroy; 155 struct wl_signal destroy;
149 // Raised after the tree updates, but before arrange_windows 156 // Raised after the tree updates, but before arrange_windows
@@ -303,4 +310,12 @@ void container_get_box(struct sway_container *container, struct wlr_box *box);
303void container_floating_move_to(struct sway_container *con, 310void container_floating_move_to(struct sway_container *con,
304 double lx, double ly); 311 double lx, double ly);
305 312
313/**
314 * Mark a container as dirty if it isn't already. Dirty containers will be
315 * included in the next transaction then unmarked as dirty.
316 */
317void container_set_dirty(struct sway_container *container);
318
319bool container_has_urgent_child(struct sway_container *container);
320
306#endif 321#endif
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 21d6403e..9022f7a6 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -70,6 +70,10 @@ struct sway_view {
70 bool border_left; 70 bool border_left;
71 bool border_right; 71 bool border_right;
72 72
73 struct timespec urgent;
74 bool allow_request_urgent;
75 struct wl_event_source *urgent_timer;
76
73 bool destroying; 77 bool destroying;
74 78
75 list_t *executed_criteria; // struct criteria * 79 list_t *executed_criteria; // struct criteria *
@@ -305,4 +309,8 @@ void view_update_marks_textures(struct sway_view *view);
305 */ 309 */
306bool view_is_visible(struct sway_view *view); 310bool view_is_visible(struct sway_view *view);
307 311
312void view_set_urgent(struct sway_view *view, bool enable);
313
314bool view_is_urgent(struct sway_view *view);
315
308#endif 316#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
15extern char *prev_workspace_name; 16extern char *prev_workspace_name;
@@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace,
42 43
43struct sway_container *workspace_output_get_highest_available( 44struct 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
47void workspace_detect_urgent(struct sway_container *workspace);
48
45#endif 49#endif
diff --git a/sway/commands.c b/sway/commands.c
index addd64a6..27329602 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -114,6 +114,7 @@ static struct cmd_handler handlers[] = {
114 { "input", cmd_input }, 114 { "input", cmd_input },
115 { "mode", cmd_mode }, 115 { "mode", cmd_mode },
116 { "mouse_warping", cmd_mouse_warping }, 116 { "mouse_warping", cmd_mouse_warping },
117 { "no_focus", cmd_no_focus },
117 { "output", cmd_output }, 118 { "output", cmd_output },
118 { "seat", cmd_seat }, 119 { "seat", cmd_seat },
119 { "set", cmd_set }, 120 { "set", cmd_set },
@@ -153,6 +154,7 @@ static struct cmd_handler command_handlers[] = {
153 { "swap", cmd_swap }, 154 { "swap", cmd_swap },
154 { "title_format", cmd_title_format }, 155 { "title_format", cmd_title_format },
155 { "unmark", cmd_unmark }, 156 { "unmark", cmd_unmark },
157 { "urgent", cmd_urgent },
156}; 158};
157 159
158static int handler_compare(const void *_a, const void *_b) { 160static int handler_compare(const void *_a, const void *_b) {
diff --git a/sway/commands/bar/position.c b/sway/commands/bar/position.c
index 48e7ddbd..44bb4ae3 100644
--- a/sway/commands/bar/position.c
+++ b/sway/commands/bar/position.c
@@ -17,6 +17,7 @@ struct cmd_results *bar_cmd_position(int argc, char **argv) {
17 if (strcasecmp(valid[i], argv[0]) == 0) { 17 if (strcasecmp(valid[i], argv[0]) == 0) {
18 wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s", 18 wlr_log(WLR_DEBUG, "Setting bar position '%s' for bar: %s",
19 argv[0], config->current_bar->id); 19 argv[0], config->current_bar->id);
20 free(config->current_bar->position);
20 config->current_bar->position = strdup(argv[0]); 21 config->current_bar->position = strdup(argv[0]);
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 22 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22 } 23 }
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 6db85395..9c19e20a 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -42,7 +42,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
42 container_set_geometry_from_floating_view(view->swayc); 42 container_set_geometry_from_floating_view(view->swayc);
43 } 43 }
44 44
45 arrange_and_commit(view->swayc); 45 arrange_windows(view->swayc);
46 46
47 struct sway_seat *seat = input_manager_current_seat(input_manager); 47 struct sway_seat *seat = input_manager_current_seat(input_manager);
48 if (seat->cursor) { 48 if (seat->cursor) {
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index e6003521..6ab56c3b 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -37,7 +37,7 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
37 container_set_floating(container, wants_floating); 37 container_set_floating(container, wants_floating);
38 38
39 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 39 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
40 arrange_and_commit(workspace); 40 arrange_windows(workspace);
41 41
42 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 42 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
43} 43}
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c
index 1a4d8b41..0b5beaa2 100644
--- a/sway/commands/fullscreen.c
+++ b/sway/commands/fullscreen.c
@@ -34,7 +34,7 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) {
34 view_set_fullscreen(view, wants_fullscreen); 34 view_set_fullscreen(view, wants_fullscreen);
35 35
36 struct sway_container *workspace = container_parent(container, C_WORKSPACE); 36 struct sway_container *workspace = container_parent(container, C_WORKSPACE);
37 arrange_and_commit(workspace->parent); 37 arrange_windows(workspace->parent);
38 38
39 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 39 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
40} 40}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 801fb179..3906eb70 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -43,7 +43,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
43 return cmd_results_new(CMD_INVALID, "gaps", 43 return cmd_results_new(CMD_INVALID, "gaps",
44 "gaps edge_gaps on|off|toggle"); 44 "gaps edge_gaps on|off|toggle");
45 } 45 }
46 arrange_and_commit(&root_container); 46 arrange_windows(&root_container);
47 } else { 47 } else {
48 int amount_idx = 0; // the current index in argv 48 int amount_idx = 0; // the current index in argv
49 enum gaps_op op = GAPS_OP_SET; 49 enum gaps_op op = GAPS_OP_SET;
@@ -124,7 +124,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
124 if (amount_idx == 0) { // gaps <amount> 124 if (amount_idx == 0) { // gaps <amount>
125 config->gaps_inner = val; 125 config->gaps_inner = val;
126 config->gaps_outer = val; 126 config->gaps_outer = val;
127 arrange_and_commit(&root_container); 127 arrange_windows(&root_container);
128 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 128 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
129 } 129 }
130 // Other variants. The middle-length variant (gaps inner|outer <amount>) 130 // Other variants. The middle-length variant (gaps inner|outer <amount>)
@@ -155,7 +155,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
155 } else { 155 } else {
156 config->gaps_outer = total; 156 config->gaps_outer = total;
157 } 157 }
158 arrange_and_commit(&root_container); 158 arrange_windows(&root_container);
159 } else { 159 } else {
160 struct sway_container *c = 160 struct sway_container *c =
161 config->handler_context.current_container; 161 config->handler_context.current_container;
@@ -169,7 +169,7 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
169 c->gaps_outer = total; 169 c->gaps_outer = total;
170 } 170 }
171 171
172 arrange_and_commit(c->parent ? c->parent : &root_container); 172 arrange_windows(c->parent ? c->parent : &root_container);
173 } 173 }
174 } 174 }
175 175
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index 9945fa5c..c446f1f9 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -49,7 +49,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
49 } 49 }
50 50
51 container_notify_subtree_changed(parent); 51 container_notify_subtree_changed(parent);
52 arrange_and_commit(parent); 52 arrange_windows(parent);
53 53
54 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 54 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
55} 55}
diff --git a/sway/commands/move.c b/sway/commands/move.c
index a1c1e018..6ec050a8 100644
--- a/sway/commands/move.c
+++ b/sway/commands/move.c
@@ -6,7 +6,6 @@
6#include <wlr/types/wlr_output_layout.h> 6#include <wlr/types/wlr_output_layout.h>
7#include <wlr/util/log.h> 7#include <wlr/util/log.h>
8#include "sway/commands.h" 8#include "sway/commands.h"
9#include "sway/desktop/transaction.h"
10#include "sway/input/cursor.h" 9#include "sway/input/cursor.h"
11#include "sway/input/seat.h" 10#include "sway/input/seat.h"
12#include "sway/output.h" 11#include "sway/output.h"
@@ -105,10 +104,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
105 // TODO: Ideally we would arrange the surviving parent after reaping, 104 // TODO: Ideally we would arrange the surviving parent after reaping,
106 // but container_reap_empty does not return it, so we arrange the 105 // but container_reap_empty does not return it, so we arrange the
107 // workspace instead. 106 // workspace instead.
108 struct sway_transaction *txn = transaction_create(); 107 arrange_windows(old_ws);
109 arrange_windows(old_ws, txn); 108 arrange_windows(destination->parent);
110 arrange_windows(destination->parent, txn);
111 transaction_commit(txn);
112 109
113 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 110 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
114 } else if (strcasecmp(argv[1], "to") == 0 111 } else if (strcasecmp(argv[1], "to") == 0
@@ -144,10 +141,8 @@ static struct cmd_results *cmd_move_container(struct sway_container *current,
144 // TODO: Ideally we would arrange the surviving parent after reaping, 141 // TODO: Ideally we would arrange the surviving parent after reaping,
145 // but container_reap_empty does not return it, so we arrange the 142 // but container_reap_empty does not return it, so we arrange the
146 // workspace instead. 143 // workspace instead.
147 struct sway_transaction *txn = transaction_create(); 144 arrange_windows(old_ws);
148 arrange_windows(old_ws, txn); 145 arrange_windows(focus->parent);
149 arrange_windows(focus->parent, txn);
150 transaction_commit(txn);
151 146
152 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 147 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
153 } 148 }
@@ -177,10 +172,8 @@ static struct cmd_results *cmd_move_workspace(struct sway_container *current,
177 } 172 }
178 container_move_to(current, destination); 173 container_move_to(current, destination);
179 174
180 struct sway_transaction *txn = transaction_create(); 175 arrange_windows(source);
181 arrange_windows(source, txn); 176 arrange_windows(destination);
182 arrange_windows(destination, txn);
183 transaction_commit(txn);
184 177
185 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 178 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
186} 179}
@@ -238,12 +231,10 @@ static struct cmd_results *move_in_direction(struct sway_container *container,
238 container_move(container, direction, move_amt); 231 container_move(container, direction, move_amt);
239 struct sway_container *new_ws = container_parent(container, C_WORKSPACE); 232 struct sway_container *new_ws = container_parent(container, C_WORKSPACE);
240 233
241 struct sway_transaction *txn = transaction_create(); 234 arrange_windows(old_ws);
242 arrange_windows(old_ws, txn);
243 if (new_ws != old_ws) { 235 if (new_ws != old_ws) {
244 arrange_windows(new_ws, txn); 236 arrange_windows(new_ws);
245 } 237 }
246 transaction_commit(txn);
247 238
248 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 239 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
249} 240}
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
8struct 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/reload.c b/sway/commands/reload.c
index c6715f9c..cea6a94b 100644
--- a/sway/commands/reload.c
+++ b/sway/commands/reload.c
@@ -12,6 +12,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
12 } 12 }
13 13
14 load_swaybars(); 14 load_swaybars();
15 arrange_and_commit(&root_container); 15 arrange_windows(&root_container);
16 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 16 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
17} 17}
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 2cf811d8..e657864c 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -268,7 +268,7 @@ static void resize_tiled(int amount, enum resize_axis axis) {
268 } 268 }
269 } 269 }
270 270
271 arrange_and_commit(parent->parent); 271 arrange_windows(parent->parent);
272} 272}
273 273
274/** 274/**
@@ -338,7 +338,7 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
338 view->height += grow_height; 338 view->height += grow_height;
339 } 339 }
340 340
341 arrange_and_commit(con); 341 arrange_windows(con);
342 342
343 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 343 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
344} 344}
@@ -410,7 +410,7 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
410 view->height += grow_height; 410 view->height += grow_height;
411 } 411 }
412 412
413 arrange_and_commit(con); 413 arrange_windows(con);
414 414
415 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 415 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
416} 416}
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index f687e78e..7d27e571 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -23,7 +23,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
23 "Expected 'smart_gaps <on|off>' "); 23 "Expected 'smart_gaps <on|off>' ");
24 } 24 }
25 25
26 arrange_and_commit(&root_container); 26 arrange_windows(&root_container);
27 27
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 28 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 29}
diff --git a/sway/commands/split.c b/sway/commands/split.c
index c40f4d9f..313799da 100644
--- a/sway/commands/split.c
+++ b/sway/commands/split.c
@@ -16,7 +16,7 @@ static struct cmd_results *do_split(int layout) {
16 } 16 }
17 struct sway_container *parent = container_split(con, layout); 17 struct sway_container *parent = container_split(con, layout);
18 container_create_notify(parent); 18 container_create_notify(parent);
19 arrange_and_commit(parent->parent); 19 arrange_windows(parent->parent);
20 20
21 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
22} 22}
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index e052058f..2fc88308 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -1,7 +1,6 @@
1#include <strings.h> 1#include <strings.h>
2#include <wlr/util/log.h> 2#include <wlr/util/log.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "sway/desktop/transaction.h"
5#include "sway/tree/arrange.h" 4#include "sway/tree/arrange.h"
6#include "sway/tree/layout.h" 5#include "sway/tree/layout.h"
7#include "sway/tree/view.h" 6#include "sway/tree/view.h"
@@ -79,14 +78,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
79 78
80 container_swap(current, other); 79 container_swap(current, other);
81 80
82 struct sway_transaction *txn = transaction_create(); 81 arrange_windows(current->parent);
83 arrange_windows(current->parent, txn);
84
85 if (other->parent != current->parent) { 82 if (other->parent != current->parent) {
86 arrange_windows(other->parent, txn); 83 arrange_windows(other->parent);
87 } 84 }
88 85
89 transaction_commit(txn);
90
91 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 86 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
92} 87}
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
9struct 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 d2386f46..f63835bf 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -474,6 +474,7 @@ static bool load_include_config(const char *path, const char *parent_dir,
474 list_del(config->config_chain, index); 474 list_del(config->config_chain, index);
475 return false; 475 return false;
476 } 476 }
477 free(real_path);
477 478
478 // restore current_config_path 479 // restore current_config_path
479 config->current_config_path = parent_config; 480 config->current_config_path = parent_config;
@@ -560,16 +561,21 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {
560 561
561bool read_config(FILE *file, struct sway_config *config) { 562bool read_config(FILE *file, struct sway_config *config) {
562 bool reading_main_config = false; 563 bool reading_main_config = false;
563 char *this_config = NULL, *config_pos; 564 char *this_config = NULL;
564 long config_size = 0; 565 size_t config_size = 0;
565 if (config->current_config == NULL) { 566 if (config->current_config == NULL) {
566 reading_main_config = true; 567 reading_main_config = true;
567 568
568 fseek(file, 0, SEEK_END); 569 int ret_seek = fseek(file, 0, SEEK_END);
569 config_size = ftell(file); 570 long ret_tell = ftell(file);
571 if (ret_seek == -1 || ret_tell == -1) {
572 wlr_log(WLR_ERROR, "Unable to get size of config file");
573 return false;
574 }
575 config_size = ret_tell;
570 rewind(file); 576 rewind(file);
571 577
572 config_pos = this_config = malloc(config_size + 1); 578 config->current_config = this_config = calloc(1, config_size + 1);
573 if (this_config == NULL) { 579 if (this_config == NULL) {
574 wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents"); 580 wlr_log(WLR_ERROR, "Unable to allocate buffer for config contents");
575 return false; 581 return false;
@@ -580,6 +586,7 @@ bool read_config(FILE *file, struct sway_config *config) {
580 int line_number = 0; 586 int line_number = 0;
581 char *line; 587 char *line;
582 list_t *stack = create_list(); 588 list_t *stack = create_list();
589 size_t read = 0;
583 while (!feof(file)) { 590 while (!feof(file)) {
584 char *block = stack->length ? stack->items[0] : NULL; 591 char *block = stack->length ? stack->items[0] : NULL;
585 line = read_line(file); 592 line = read_line(file);
@@ -590,10 +597,21 @@ bool read_config(FILE *file, struct sway_config *config) {
590 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line); 597 wlr_log(WLR_DEBUG, "Read line %d: %s", line_number, line);
591 598
592 if (reading_main_config) { 599 if (reading_main_config) {
593 size_t l = strlen(line); 600 size_t length = strlen(line);
594 memcpy(config_pos, line, l); // don't copy terminating character 601
595 config_pos += l; 602 if (read + length > config_size) {
596 *config_pos++ = '\n'; 603 wlr_log(WLR_ERROR, "Config file changed during reading");
604 list_foreach(stack, free);
605 list_free(stack);
606 free(line);
607 return false;
608 }
609
610 strcpy(this_config + read, line);
611 if (line_number != 1) {
612 this_config[read - 1] = '\n';
613 }
614 read += length + 1;
597 } 615 }
598 616
599 line = strip_whitespace(line); 617 line = strip_whitespace(line);
@@ -616,7 +634,6 @@ bool read_config(FILE *file, struct sway_config *config) {
616 list_foreach(stack, free); 634 list_foreach(stack, free);
617 list_free(stack); 635 list_free(stack);
618 free(line); 636 free(line);
619 free(this_config);
620 return false; 637 return false;
621 } 638 }
622 wlr_log(WLR_DEBUG, "Expanded line: %s", expanded); 639 wlr_log(WLR_DEBUG, "Expanded line: %s", expanded);
@@ -677,10 +694,6 @@ bool read_config(FILE *file, struct sway_config *config) {
677 list_foreach(stack, free); 694 list_foreach(stack, free);
678 list_free(stack); 695 list_free(stack);
679 696
680 if (reading_main_config) {
681 this_config[config_size - 1] = '\0';
682 config->current_config = this_config;
683 }
684 return success; 697 return success;
685} 698}
686 699
@@ -773,6 +786,6 @@ void config_update_font_height(bool recalculate) {
773 } 786 }
774 787
775 if (config->font_height != prev_max_height) { 788 if (config->font_height != prev_max_height) {
776 arrange_and_commit(&root_container); 789 arrange_windows(&root_container);
777 } 790 }
778} 791}
diff --git a/sway/config/output.c b/sway/config/output.c
index 205e2633..1bf9e5f1 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -207,6 +207,8 @@ void apply_output_config(struct output_config *oc, struct sway_container *output
207 output->sway_output->bg_pid = fork(); 207 output->sway_output->bg_pid = fork();
208 if (output->sway_output->bg_pid == 0) { 208 if (output->sway_output->bg_pid == 0) {
209 execvp(cmd[0], cmd); 209 execvp(cmd[0], cmd);
210 } else {
211 free(command);
210 } 212 }
211 } 213 }
212 if (oc && oc->dpms_state != DPMS_IGNORE) { 214 if (oc && oc->dpms_state != DPMS_IGNORE) {
diff --git a/sway/criteria.c b/sway/criteria.c
index 29a3668b..c999d248 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -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
49static 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
66static 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
49static bool criteria_matches_view(struct criteria *criteria, 74static 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/layer_shell.c b/sway/desktop/layer_shell.c
index 16910c7e..91baa6f8 100644
--- a/sway/desktop/layer_shell.c
+++ b/sway/desktop/layer_shell.c
@@ -12,7 +12,6 @@
12#include "sway/layers.h" 12#include "sway/layers.h"
13#include "sway/output.h" 13#include "sway/output.h"
14#include "sway/server.h" 14#include "sway/server.h"
15#include "sway/tree/arrange.h"
16#include "sway/tree/layout.h" 15#include "sway/tree/layout.h"
17#include "log.h" 16#include "log.h"
18 17
@@ -176,7 +175,7 @@ void arrange_layers(struct sway_output *output) {
176 sizeof(struct wlr_box)) != 0) { 175 sizeof(struct wlr_box)) != 0) {
177 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); 176 wlr_log(WLR_DEBUG, "Usable area changed, rearranging output");
178 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); 177 memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
179 arrange_and_commit(output->swayc); 178 container_set_dirty(output->swayc);
180 } 179 }
181 180
182 // Arrange non-exlusive surfaces from top->bottom 181 // Arrange non-exlusive surfaces from top->bottom
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index a2720885..a9808406 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -492,19 +492,21 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
492 output->wlr_output->data = NULL; 492 output->wlr_output->data = NULL;
493 free(output); 493 free(output);
494 494
495 arrange_and_commit(&root_container); 495 arrange_windows(&root_container);
496} 496}
497 497
498static void handle_mode(struct wl_listener *listener, void *data) { 498static void handle_mode(struct wl_listener *listener, void *data) {
499 struct sway_output *output = wl_container_of(listener, output, mode); 499 struct sway_output *output = wl_container_of(listener, output, mode);
500 arrange_layers(output); 500 arrange_layers(output);
501 arrange_and_commit(output->swayc); 501 arrange_windows(output->swayc);
502 transaction_commit_dirty();
502} 503}
503 504
504static void handle_transform(struct wl_listener *listener, void *data) { 505static void handle_transform(struct wl_listener *listener, void *data) {
505 struct sway_output *output = wl_container_of(listener, output, transform); 506 struct sway_output *output = wl_container_of(listener, output, transform);
506 arrange_layers(output); 507 arrange_layers(output);
507 arrange_and_commit(output->swayc); 508 arrange_windows(output->swayc);
509 transaction_commit_dirty();
508} 510}
509 511
510static void handle_scale_iterator(struct sway_container *view, void *data) { 512static void handle_scale_iterator(struct sway_container *view, void *data) {
@@ -515,7 +517,8 @@ static void handle_scale(struct wl_listener *listener, void *data) {
515 struct sway_output *output = wl_container_of(listener, output, scale); 517 struct sway_output *output = wl_container_of(listener, output, scale);
516 arrange_layers(output); 518 arrange_layers(output);
517 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); 519 container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL);
518 arrange_and_commit(output->swayc); 520 arrange_windows(output->swayc);
521 transaction_commit_dirty();
519} 522}
520 523
521struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { 524struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) {
@@ -584,5 +587,6 @@ void output_enable(struct sway_output *output) {
584 output->damage_destroy.notify = damage_handle_destroy; 587 output->damage_destroy.notify = damage_handle_destroy;
585 588
586 arrange_layers(output); 589 arrange_layers(output);
587 arrange_and_commit(&root_container); 590 arrange_windows(&root_container);
591 transaction_commit_dirty();
588} 592}
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index b370f8a2..cb995215 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -15,6 +15,7 @@
15#include <wlr/util/region.h> 15#include <wlr/util/region.h>
16#include "log.h" 16#include "log.h"
17#include "sway/config.h" 17#include "sway/config.h"
18#include "sway/debug.h"
18#include "sway/input/input-manager.h" 19#include "sway/input/input-manager.h"
19#include "sway/input/seat.h" 20#include "sway/input/seat.h"
20#include "sway/layers.h" 21#include "sway/layers.h"
@@ -542,9 +543,6 @@ static void render_container(struct sway_output *output,
542static void render_container_simple(struct sway_output *output, 543static void render_container_simple(struct sway_output *output,
543 pixman_region32_t *damage, struct sway_container *con, 544 pixman_region32_t *damage, struct sway_container *con,
544 bool parent_focused) { 545 bool parent_focused) {
545 struct sway_seat *seat = input_manager_current_seat(input_manager);
546 struct sway_container *focus = seat_get_focus(seat);
547
548 for (int i = 0; i < con->current.children->length; ++i) { 546 for (int i = 0; i < con->current.children->length; ++i) {
549 struct sway_container *child = con->current.children->items[i]; 547 struct sway_container *child = con->current.children->items[i];
550 548
@@ -555,11 +553,15 @@ static void render_container_simple(struct sway_output *output,
555 struct wlr_texture *marks_texture; 553 struct wlr_texture *marks_texture;
556 struct sway_container_state *state = &child->current; 554 struct sway_container_state *state = &child->current;
557 555
558 if (focus == child || parent_focused) { 556 if (view_is_urgent(view)) {
557 colors = &config->border_colors.urgent;
558 title_texture = child->title_urgent;
559 marks_texture = view->marks_urgent;
560 } else if (state->focused || parent_focused) {
559 colors = &config->border_colors.focused; 561 colors = &config->border_colors.focused;
560 title_texture = child->title_focused; 562 title_texture = child->title_focused;
561 marks_texture = view->marks_focused; 563 marks_texture = view->marks_focused;
562 } else if (seat_get_focus_inactive(seat, con) == child) { 564 } else if (con->current.focused_inactive_child == child) {
563 colors = &config->border_colors.focused_inactive; 565 colors = &config->border_colors.focused_inactive;
564 title_texture = child->title_focused_inactive; 566 title_texture = child->title_focused_inactive;
565 marks_texture = view->marks_focused_inactive; 567 marks_texture = view->marks_focused_inactive;
@@ -579,7 +581,7 @@ static void render_container_simple(struct sway_output *output,
579 render_view(output, damage, child, colors); 581 render_view(output, damage, child, colors);
580 } else { 582 } else {
581 render_container(output, damage, child, 583 render_container(output, damage, child,
582 parent_focused || focus == child); 584 parent_focused || child->current.focused);
583 } 585 }
584 } 586 }
585} 587}
@@ -593,11 +595,9 @@ static void render_container_tabbed(struct sway_output *output,
593 if (!con->current.children->length) { 595 if (!con->current.children->length) {
594 return; 596 return;
595 } 597 }
596 struct sway_seat *seat = input_manager_current_seat(input_manager);
597 struct sway_container *focus = seat_get_focus(seat);
598 struct sway_container *current = seat_get_active_current_child(seat, con);
599 struct border_colors *current_colors = &config->border_colors.unfocused;
600 struct sway_container_state *pstate = &con->current; 598 struct sway_container_state *pstate = &con->current;
599 struct sway_container *current = pstate->focused_inactive_child;
600 struct border_colors *current_colors = &config->border_colors.unfocused;
601 601
602 double width_gap_adjustment = 2 * pstate->current_gaps; 602 double width_gap_adjustment = 2 * pstate->current_gaps;
603 int tab_width = 603 int tab_width =
@@ -611,12 +611,18 @@ static void render_container_tabbed(struct sway_output *output,
611 struct border_colors *colors; 611 struct border_colors *colors;
612 struct wlr_texture *title_texture; 612 struct wlr_texture *title_texture;
613 struct wlr_texture *marks_texture; 613 struct wlr_texture *marks_texture;
614 614 bool urgent = view ?
615 if (focus == child || parent_focused) { 615 view_is_urgent(view) : container_has_urgent_child(child);
616
617 if (urgent) {
618 colors = &config->border_colors.urgent;
619 title_texture = child->title_urgent;
620 marks_texture = view ? view->marks_urgent : NULL;
621 } else if (cstate->focused || parent_focused) {
616 colors = &config->border_colors.focused; 622 colors = &config->border_colors.focused;
617 title_texture = child->title_focused; 623 title_texture = child->title_focused;
618 marks_texture = view ? view->marks_focused : NULL; 624 marks_texture = view ? view->marks_focused : NULL;
619 } else if (child == current) { 625 } else if (child == pstate->focused_inactive_child) {
620 colors = &config->border_colors.focused_inactive; 626 colors = &config->border_colors.focused_inactive;
621 title_texture = child->title_focused_inactive; 627 title_texture = child->title_focused_inactive;
622 marks_texture = view ? view->marks_focused_inactive : NULL; 628 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -643,13 +649,11 @@ static void render_container_tabbed(struct sway_output *output,
643 } 649 }
644 650
645 // Render surface and left/right/bottom borders 651 // Render surface and left/right/bottom borders
646 if (current) { 652 if (current->type == C_VIEW) {
647 if (current->type == C_VIEW) { 653 render_view(output, damage, current, current_colors);
648 render_view(output, damage, current, current_colors); 654 } else {
649 } else { 655 render_container(output, damage, current,
650 render_container(output, damage, current, 656 parent_focused || current->current.focused);
651 parent_focused || current == focus);
652 }
653 } 657 }
654} 658}
655 659
@@ -662,11 +666,9 @@ static void render_container_stacked(struct sway_output *output,
662 if (!con->current.children->length) { 666 if (!con->current.children->length) {
663 return; 667 return;
664 } 668 }
665 struct sway_seat *seat = input_manager_current_seat(input_manager);
666 struct sway_container *focus = seat_get_focus(seat);
667 struct sway_container *current = seat_get_active_current_child(seat, con);
668 struct border_colors *current_colors = &config->border_colors.unfocused;
669 struct sway_container_state *pstate = &con->current; 669 struct sway_container_state *pstate = &con->current;
670 struct sway_container *current = pstate->focused_inactive_child;
671 struct border_colors *current_colors = &config->border_colors.unfocused;
670 672
671 size_t titlebar_height = container_titlebar_height(); 673 size_t titlebar_height = container_titlebar_height();
672 674
@@ -678,12 +680,18 @@ static void render_container_stacked(struct sway_output *output,
678 struct border_colors *colors; 680 struct border_colors *colors;
679 struct wlr_texture *title_texture; 681 struct wlr_texture *title_texture;
680 struct wlr_texture *marks_texture; 682 struct wlr_texture *marks_texture;
681 683 bool urgent = view ?
682 if (focus == child || parent_focused) { 684 view_is_urgent(view) : container_has_urgent_child(child);
685
686 if (urgent) {
687 colors = &config->border_colors.urgent;
688 title_texture = child->title_urgent;
689 marks_texture = view ? view->marks_urgent : NULL;
690 } else if (cstate->focused || parent_focused) {
683 colors = &config->border_colors.focused; 691 colors = &config->border_colors.focused;
684 title_texture = child->title_focused; 692 title_texture = child->title_focused;
685 marks_texture = view ? view->marks_focused : NULL; 693 marks_texture = view ? view->marks_focused : NULL;
686 } else if (child == current) { 694 } else if (child == pstate->focused_inactive_child) {
687 colors = &config->border_colors.focused_inactive; 695 colors = &config->border_colors.focused_inactive;
688 title_texture = child->title_focused_inactive; 696 title_texture = child->title_focused_inactive;
689 marks_texture = view ? view->marks_focused_inactive : NULL; 697 marks_texture = view ? view->marks_focused_inactive : NULL;
@@ -703,13 +711,11 @@ static void render_container_stacked(struct sway_output *output,
703 } 711 }
704 712
705 // Render surface and left/right/bottom borders 713 // Render surface and left/right/bottom borders
706 if (current) { 714 if (current->type == C_VIEW) {
707 if (current->type == C_VIEW) { 715 render_view(output, damage, current, current_colors);
708 render_view(output, damage, current, current_colors); 716 } else {
709 } else { 717 render_container(output, damage, current,
710 render_container(output, damage, current, 718 parent_focused || current->current.focused);
711 parent_focused || current == focus);
712 }
713 } 719 }
714} 720}
715 721
@@ -737,13 +743,15 @@ static void render_floating_container(struct sway_output *soutput,
737 pixman_region32_t *damage, struct sway_container *con) { 743 pixman_region32_t *damage, struct sway_container *con) {
738 if (con->type == C_VIEW) { 744 if (con->type == C_VIEW) {
739 struct sway_view *view = con->sway_view; 745 struct sway_view *view = con->sway_view;
740 struct sway_seat *seat = input_manager_current_seat(input_manager);
741 struct sway_container *focus = seat_get_focus(seat);
742 struct border_colors *colors; 746 struct border_colors *colors;
743 struct wlr_texture *title_texture; 747 struct wlr_texture *title_texture;
744 struct wlr_texture *marks_texture; 748 struct wlr_texture *marks_texture;
745 749
746 if (focus == con) { 750 if (view_is_urgent(view)) {
751 colors = &config->border_colors.urgent;
752 title_texture = con->title_urgent;
753 marks_texture = view->marks_urgent;
754 } else if (con->current.focused) {
747 colors = &config->border_colors.focused; 755 colors = &config->border_colors.focused;
748 title_texture = con->title_focused; 756 title_texture = con->title_focused;
749 marks_texture = view->marks_focused; 757 marks_texture = view->marks_focused;
@@ -786,6 +794,8 @@ static void render_floating(struct sway_output *soutput,
786 } 794 }
787} 795}
788 796
797const char *damage_debug = NULL;
798
789void output_render(struct sway_output *output, struct timespec *when, 799void output_render(struct sway_output *output, struct timespec *when,
790 pixman_region32_t *damage) { 800 pixman_region32_t *damage) {
791 struct wlr_output *wlr_output = output->wlr_output; 801 struct wlr_output *wlr_output = output->wlr_output;
@@ -805,7 +815,6 @@ void output_render(struct sway_output *output, struct timespec *when,
805 goto renderer_end; 815 goto renderer_end;
806 } 816 }
807 817
808 const char *damage_debug = getenv("SWAY_DAMAGE_DEBUG");
809 if (damage_debug != NULL) { 818 if (damage_debug != NULL) {
810 if (strcmp(damage_debug, "highlight") == 0) { 819 if (strcmp(damage_debug, "highlight") == 0) {
811 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1}); 820 wlr_renderer_clear(renderer, (float[]){1, 1, 0, 1});
@@ -869,9 +878,7 @@ void output_render(struct sway_output *output, struct timespec *when,
869 render_layer(output, damage, 878 render_layer(output, damage,
870 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); 879 &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
871 880
872 struct sway_seat *seat = input_manager_current_seat(input_manager); 881 render_container(output, damage, workspace, workspace->current.focused);
873 struct sway_container *focus = seat_get_focus(seat);
874 render_container(output, damage, workspace, focus == workspace);
875 render_floating(output, damage); 882 render_floating(output, damage);
876 883
877 render_unmanaged(output, damage, 884 render_unmanaged(output, damage,
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 2b3f87c3..fcfb0b51 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -19,14 +19,14 @@
19 * How long we should wait for views to respond to the configure before giving 19 * How long we should wait for views to respond to the configure before giving
20 * up and applying the transaction anyway. 20 * up and applying the transaction anyway.
21 */ 21 */
22#define TIMEOUT_MS 200 22int txn_timeout_ms = 200;
23 23
24/** 24/**
25 * If enabled, sway will always wait for the transaction timeout before 25 * If enabled, sway will always wait for the transaction timeout before
26 * applying it, rather than applying it when the views are ready. This allows us 26 * applying it, rather than applying it when the views are ready. This allows us
27 * to observe the rendered state while a transaction is in progress. 27 * to observe the rendered state while a transaction is in progress.
28 */ 28 */
29#define TRANSACTION_DEBUG false 29bool txn_debug = false;
30 30
31struct sway_transaction { 31struct sway_transaction {
32 struct wl_event_source *timer; 32 struct wl_event_source *timer;
@@ -47,7 +47,7 @@ struct sway_transaction_instruction {
47 bool ready; 47 bool ready;
48}; 48};
49 49
50struct sway_transaction *transaction_create() { 50static struct sway_transaction *transaction_create() {
51 struct sway_transaction *transaction = 51 struct sway_transaction *transaction =
52 calloc(1, sizeof(struct sway_transaction)); 52 calloc(1, sizeof(struct sway_transaction));
53 transaction->instructions = create_list(); 53 transaction->instructions = create_list();
@@ -139,25 +139,18 @@ static void copy_pending_state(struct sway_container *container,
139 state->children = create_list(); 139 state->children = create_list();
140 list_cat(state->children, container->children); 140 list_cat(state->children, container->children);
141 } 141 }
142}
143 142
144static bool transaction_has_container(struct sway_transaction *transaction, 143 struct sway_seat *seat = input_manager_current_seat(input_manager);
145 struct sway_container *container) { 144 state->focused = seat_get_focus(seat) == container;
146 for (int i = 0; i < transaction->instructions->length; ++i) { 145
147 struct sway_transaction_instruction *instruction = 146 if (container->type != C_VIEW) {
148 transaction->instructions->items[i]; 147 state->focused_inactive_child =
149 if (instruction->container == container) { 148 seat_get_active_child(seat, container);
150 return true;
151 }
152 } 149 }
153 return false;
154} 150}
155 151
156void transaction_add_container(struct sway_transaction *transaction, 152static void transaction_add_container(struct sway_transaction *transaction,
157 struct sway_container *container) { 153 struct sway_container *container) {
158 if (transaction_has_container(transaction, container)) {
159 return;
160 }
161 struct sway_transaction_instruction *instruction = 154 struct sway_transaction_instruction *instruction =
162 calloc(1, sizeof(struct sway_transaction_instruction)); 155 calloc(1, sizeof(struct sway_transaction_instruction));
163 instruction->transaction = transaction; 156 instruction->transaction = transaction;
@@ -210,10 +203,12 @@ static void transaction_apply(struct sway_transaction *transaction) {
210 .width = instruction->state.swayc_width, 203 .width = instruction->state.swayc_width,
211 .height = instruction->state.swayc_height, 204 .height = instruction->state.swayc_height,
212 }; 205 };
213 for (int j = 0; j < root_container.children->length; ++j) { 206 for (int j = 0; j < root_container.current.children->length; ++j) {
214 struct sway_container *output = root_container.children->items[j]; 207 struct sway_container *output = root_container.current.children->items[j];
215 output_damage_box(output->sway_output, &old_box); 208 if (output->sway_output) {
216 output_damage_box(output->sway_output, &new_box); 209 output_damage_box(output->sway_output, &old_box);
210 output_damage_box(output->sway_output, &new_box);
211 }
217 } 212 }
218 213
219 // There are separate children lists for each instruction state, the 214 // There are separate children lists for each instruction state, the
@@ -285,7 +280,7 @@ static bool should_configure(struct sway_container *con,
285 return true; 280 return true;
286} 281}
287 282
288void transaction_commit(struct sway_transaction *transaction) { 283static void transaction_commit(struct sway_transaction *transaction) {
289 wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions", 284 wlr_log(WLR_DEBUG, "Transaction %p committing with %i instructions",
290 transaction, transaction->instructions->length); 285 transaction, transaction->instructions->length);
291 transaction->num_waiting = 0; 286 transaction->num_waiting = 0;
@@ -330,7 +325,7 @@ void transaction_commit(struct sway_transaction *transaction) {
330 // Set up a timer which the views must respond within 325 // Set up a timer which the views must respond within
331 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, 326 transaction->timer = wl_event_loop_add_timer(server.wl_event_loop,
332 handle_timeout, transaction); 327 handle_timeout, transaction);
333 wl_event_source_timer_update(transaction->timer, TIMEOUT_MS); 328 wl_event_source_timer_update(transaction->timer, txn_timeout_ms);
334 } 329 }
335 330
336 // The debug tree shows the pending/live tree. Here is a good place to 331 // The debug tree shows the pending/live tree. Here is a good place to
@@ -361,11 +356,11 @@ static void set_instruction_ready(
361 // If all views are ready, apply the transaction. 356 // If all views are ready, apply the transaction.
362 // If the transaction has timed out then its num_waiting will be 0 already. 357 // If the transaction has timed out then its num_waiting will be 0 already.
363 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { 358 if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) {
364#if !TRANSACTION_DEBUG 359 if (!txn_debug) {
365 wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); 360 wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction);
366 wl_event_source_timer_update(transaction->timer, 0); 361 wl_event_source_timer_update(transaction->timer, 0);
367 transaction_progress_queue(); 362 transaction_progress_queue();
368#endif 363 }
369 } 364 }
370} 365}
371 366
@@ -418,3 +413,17 @@ struct wlr_texture *transaction_get_saved_texture(struct sway_view *view,
418 *height = instruction->saved_buffer_height; 413 *height = instruction->saved_buffer_height;
419 return instruction->saved_buffer->texture; 414 return instruction->saved_buffer->texture;
420} 415}
416
417void transaction_commit_dirty(void) {
418 if (!server.dirty_containers->length) {
419 return;
420 }
421 struct sway_transaction *transaction = transaction_create();
422 for (int i = 0; i < server.dirty_containers->length; ++i) {
423 struct sway_container *container = server.dirty_containers->items[i];
424 transaction_add_container(transaction, container);
425 container->dirty = false;
426 }
427 server.dirty_containers->length = 0;
428 transaction_commit(transaction);
429}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index fbeeb2e3..98c16faf 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -244,7 +244,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
244 view_set_fullscreen(view, e->fullscreen); 244 view_set_fullscreen(view, e->fullscreen);
245 245
246 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 246 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
247 arrange_and_commit(output); 247 arrange_windows(output);
248 transaction_commit_dirty();
248} 249}
249 250
250static void handle_unmap(struct wl_listener *listener, void *data) { 251static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -281,10 +282,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
281 if (xdg_surface->toplevel->client_pending.fullscreen) { 282 if (xdg_surface->toplevel->client_pending.fullscreen) {
282 view_set_fullscreen(view, true); 283 view_set_fullscreen(view, true);
283 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 284 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
284 arrange_and_commit(ws); 285 arrange_windows(ws);
285 } else { 286 } else {
286 arrange_and_commit(view->swayc->parent); 287 arrange_windows(view->swayc->parent);
287 } 288 }
289 transaction_commit_dirty();
288 290
289 xdg_shell_view->commit.notify = handle_commit; 291 xdg_shell_view->commit.notify = handle_commit;
290 wl_signal_add(&xdg_surface->surface->events.commit, 292 wl_signal_add(&xdg_surface->surface->events.commit,
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 88d9bb94..4d76f0a7 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -239,7 +239,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
239 view_set_fullscreen(view, e->fullscreen); 239 view_set_fullscreen(view, e->fullscreen);
240 240
241 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 241 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
242 arrange_and_commit(output); 242 arrange_windows(output);
243 transaction_commit_dirty();
243} 244}
244 245
245static void handle_unmap(struct wl_listener *listener, void *data) { 246static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -276,10 +277,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
276 if (xdg_surface->toplevel->client_pending.fullscreen) { 277 if (xdg_surface->toplevel->client_pending.fullscreen) {
277 view_set_fullscreen(view, true); 278 view_set_fullscreen(view, true);
278 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 279 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
279 arrange_and_commit(ws); 280 arrange_windows(ws);
280 } else { 281 } else {
281 arrange_and_commit(view->swayc->parent); 282 arrange_windows(view->swayc->parent);
282 } 283 }
284 transaction_commit_dirty();
283 285
284 xdg_shell_v6_view->commit.notify = handle_commit; 286 xdg_shell_v6_view->commit.notify = handle_commit;
285 wl_signal_add(&xdg_surface->surface->events.commit, 287 wl_signal_add(&xdg_surface->surface->events.commit,
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 460d1cc8..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
302static void handle_unmap(struct wl_listener *listener, void *data) { 306static void handle_unmap(struct wl_listener *listener, void *data) {
@@ -333,10 +337,11 @@ static void handle_map(struct wl_listener *listener, void *data) {
333 if (xsurface->fullscreen) { 337 if (xsurface->fullscreen) {
334 view_set_fullscreen(view, true); 338 view_set_fullscreen(view, true);
335 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 339 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
336 arrange_and_commit(ws); 340 arrange_windows(ws);
337 } else { 341 } else {
338 arrange_and_commit(view->swayc->parent); 342 arrange_windows(view->swayc->parent);
339 } 343 }
344 transaction_commit_dirty();
340} 345}
341 346
342static void handle_destroy(struct wl_listener *listener, void *data) { 347static void handle_destroy(struct wl_listener *listener, void *data) {
@@ -392,7 +397,8 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
392 view_set_fullscreen(view, xsurface->fullscreen); 397 view_set_fullscreen(view, xsurface->fullscreen);
393 398
394 struct sway_container *output = container_parent(view->swayc, C_OUTPUT); 399 struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
395 arrange_and_commit(output); 400 arrange_windows(output);
401 transaction_commit_dirty();
396} 402}
397 403
398static void handle_set_title(struct wl_listener *listener, void *data) { 404static void handle_set_title(struct wl_listener *listener, void *data) {
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 307eedd4..7a9f3ed7 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -10,6 +10,7 @@
10#include <wlr/types/wlr_idle.h> 10#include <wlr/types/wlr_idle.h>
11#include "list.h" 11#include "list.h"
12#include "log.h" 12#include "log.h"
13#include "sway/desktop/transaction.h"
13#include "sway/input/cursor.h" 14#include "sway/input/cursor.h"
14#include "sway/layers.h" 15#include "sway/layers.h"
15#include "sway/output.h" 16#include "sway/output.h"
@@ -219,6 +220,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
219 struct sway_drag_icon *drag_icon = wlr_drag_icon->data; 220 struct sway_drag_icon *drag_icon = wlr_drag_icon->data;
220 drag_icon_update_position(drag_icon); 221 drag_icon_update_position(drag_icon);
221 } 222 }
223 transaction_commit_dirty();
222} 224}
223 225
224static void handle_cursor_motion(struct wl_listener *listener, void *data) { 226static void handle_cursor_motion(struct wl_listener *listener, void *data) {
@@ -278,6 +280,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
278 280
279 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, 281 wlr_seat_pointer_notify_button(cursor->seat->wlr_seat,
280 time_msec, button, state); 282 time_msec, button, state);
283 transaction_commit_dirty();
281} 284}
282 285
283static void handle_cursor_button(struct wl_listener *listener, void *data) { 286static void handle_cursor_button(struct wl_listener *listener, void *data) {
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c
index 580c0d4b..ede38519 100644
--- a/sway/input/keyboard.c
+++ b/sway/input/keyboard.c
@@ -3,6 +3,7 @@
3#include <wlr/backend/multi.h> 3#include <wlr/backend/multi.h>
4#include <wlr/backend/session.h> 4#include <wlr/backend/session.h>
5#include <wlr/types/wlr_idle.h> 5#include <wlr/types/wlr_idle.h>
6#include "sway/desktop/transaction.h"
6#include "sway/input/seat.h" 7#include "sway/input/seat.h"
7#include "sway/input/keyboard.h" 8#include "sway/input/keyboard.h"
8#include "sway/input/input-manager.h" 9#include "sway/input/input-manager.h"
@@ -126,6 +127,7 @@ static void keyboard_execute_command(struct sway_keyboard *keyboard,
126 binding->command); 127 binding->command);
127 config->handler_context.seat = keyboard->seat_device->sway_seat; 128 config->handler_context.seat = keyboard->seat_device->sway_seat;
128 struct cmd_results *results = execute_command(binding->command, NULL); 129 struct cmd_results *results = execute_command(binding->command, NULL);
130 transaction_commit_dirty();
129 if (results->status != CMD_SUCCESS) { 131 if (results->status != CMD_SUCCESS) {
130 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", 132 wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)",
131 binding->command, results->error); 133 binding->command, results->error);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 5e65ca70..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
597static int handle_urgent_timeout(void *data) {
598 struct sway_view *view = data;
599 view_set_urgent(view, false);
600 return 0;
601}
602
597void seat_set_focus_warp(struct sway_seat *seat, 603void 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,
@@ -661,9 +668,23 @@ void seat_set_focus_warp(struct sway_seat *seat,
661 if (last_focus) { 668 if (last_focus) {
662 seat_send_unfocus(last_focus, seat); 669 seat_send_unfocus(last_focus, seat);
663 } 670 }
664
665 seat_send_focus(container, seat); 671 seat_send_focus(container, seat);
666 container_damage_whole(container->parent); 672
673 container_set_dirty(container);
674 container_set_dirty(container->parent); // for focused_inactive_child
675 if (last_focus) {
676 container_set_dirty(last_focus);
677 }
678 }
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);
667 } 688 }
668 689
669 // 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.
@@ -717,10 +738,6 @@ void seat_set_focus_warp(struct sway_seat *seat,
717 } 738 }
718 } 739 }
719 740
720 if (last_focus) {
721 container_damage_whole(last_focus);
722 }
723
724 if (last_workspace && last_workspace != new_workspace) { 741 if (last_workspace && last_workspace != new_workspace) {
725 cursor_send_pointer_motion(seat->cursor, 0, true); 742 cursor_send_pointer_motion(seat->cursor, 0, true);
726 } 743 }
@@ -840,18 +857,6 @@ struct sway_container *seat_get_active_child(struct sway_seat *seat,
840 return NULL; 857 return NULL;
841} 858}
842 859
843struct sway_container *seat_get_active_current_child(struct sway_seat *seat,
844 struct sway_container *container) {
845 struct sway_seat_container *current = NULL;
846 wl_list_for_each(current, &seat->focus_stack, link) {
847 if (current->container->current.parent == container &&
848 current->container->current.layout != L_FLOATING) {
849 return current->container;
850 }
851 }
852 return NULL;
853}
854
855struct sway_container *seat_get_focus(struct sway_seat *seat) { 860struct sway_container *seat_get_focus(struct sway_seat *seat) {
856 if (!seat->has_focus) { 861 if (!seat->has_focus) {
857 return NULL; 862 return NULL;
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
201static void focus_inactive_children_iterator(struct sway_container *c, void *data) { 206static void focus_inactive_children_iterator(struct sway_container *c, void *data) {
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index c5161a6b..be703915 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -18,6 +18,7 @@
18#include <wayland-server.h> 18#include <wayland-server.h>
19#include "sway/commands.h" 19#include "sway/commands.h"
20#include "sway/config.h" 20#include "sway/config.h"
21#include "sway/desktop/transaction.h"
21#include "sway/ipc-json.h" 22#include "sway/ipc-json.h"
22#include "sway/ipc-server.h" 23#include "sway/ipc-server.h"
23#include "sway/output.h" 24#include "sway/output.h"
@@ -484,6 +485,7 @@ void ipc_client_handle_command(struct ipc_client *client) {
484 case IPC_COMMAND: 485 case IPC_COMMAND:
485 { 486 {
486 struct cmd_results *results = execute_command(buf, NULL); 487 struct cmd_results *results = execute_command(buf, NULL);
488 transaction_commit_dirty();
487 char *json = cmd_results_to_json(results); 489 char *json = cmd_results_to_json(results);
488 int length = strlen(json); 490 int length = strlen(json);
489 client_valid = ipc_send_reply(client, json, (uint32_t)length); 491 client_valid = ipc_send_reply(client, json, (uint32_t)length);
diff --git a/sway/main.c b/sway/main.c
index c6453226..1a55b519 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -20,6 +20,7 @@
20#include "sway/commands.h" 20#include "sway/commands.h"
21#include "sway/config.h" 21#include "sway/config.h"
22#include "sway/debug.h" 22#include "sway/debug.h"
23#include "sway/desktop/transaction.h"
23#include "sway/server.h" 24#include "sway/server.h"
24#include "sway/tree/layout.h" 25#include "sway/tree/layout.h"
25#include "sway/ipc-server.h" 26#include "sway/ipc-server.h"
@@ -251,6 +252,18 @@ static void drop_permissions(bool keep_caps) {
251#endif 252#endif
252} 253}
253 254
255void enable_debug_flag(const char *flag) {
256 if (strcmp(flag, "render-tree") == 0) {
257 enable_debug_tree = true;
258 } else if (strncmp(flag, "damage=", 7) == 0) {
259 damage_debug = &flag[7];
260 } else if (strcmp(flag, "txn-debug") == 0) {
261 txn_debug = true;
262 } else if (strncmp(flag, "txn-timeout=", 12) == 0) {
263 txn_timeout_ms = atoi(&flag[12]);
264 }
265}
266
254int main(int argc, char **argv) { 267int main(int argc, char **argv) {
255 static int verbose = 0, debug = 0, validate = 0; 268 static int verbose = 0, debug = 0, validate = 0;
256 269
@@ -290,7 +303,7 @@ int main(int argc, char **argv) {
290 int c; 303 int c;
291 while (1) { 304 while (1) {
292 int option_index = 0; 305 int option_index = 0;
293 c = getopt_long(argc, argv, "hCdDvVc:", long_options, &option_index); 306 c = getopt_long(argc, argv, "hCdD:vVc:", long_options, &option_index);
294 if (c == -1) { 307 if (c == -1) {
295 break; 308 break;
296 } 309 }
@@ -309,7 +322,7 @@ int main(int argc, char **argv) {
309 debug = 1; 322 debug = 1;
310 break; 323 break;
311 case 'D': // extended debug options 324 case 'D': // extended debug options
312 enable_debug_tree = true; 325 enable_debug_flag(optarg);
313 break; 326 break;
314 case 'v': // version 327 case 'v': // version
315 fprintf(stdout, "sway version " SWAY_VERSION "\n"); 328 fprintf(stdout, "sway version " SWAY_VERSION "\n");
@@ -429,6 +442,7 @@ int main(int argc, char **argv) {
429 free(line); 442 free(line);
430 list_del(config->cmd_queue, 0); 443 list_del(config->cmd_queue, 0);
431 } 444 }
445 transaction_commit_dirty();
432 446
433 if (!terminate_request) { 447 if (!terminate_request) {
434 server_run(&server); 448 server_run(&server);
diff --git a/sway/meson.build b/sway/meson.build
index f878450d..23e54a62 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -59,6 +59,7 @@ sway_sources = files(
59 'commands/mode.c', 59 'commands/mode.c',
60 'commands/mouse_warping.c', 60 'commands/mouse_warping.c',
61 'commands/move.c', 61 'commands/move.c',
62 'commands/no_focus.c',
62 'commands/output.c', 63 'commands/output.c',
63 'commands/reload.c', 64 'commands/reload.c',
64 'commands/rename.c', 65 'commands/rename.c',
@@ -76,6 +77,7 @@ sway_sources = files(
76 'commands/swap.c', 77 'commands/swap.c',
77 'commands/title_format.c', 78 'commands/title_format.c',
78 'commands/unmark.c', 79 'commands/unmark.c',
80 'commands/urgent.c',
79 'commands/workspace.c', 81 'commands/workspace.c',
80 'commands/workspace_layout.c', 82 'commands/workspace_layout.c',
81 'commands/ws_auto_back_and_forth.c', 83 'commands/ws_auto_back_and_forth.c',
diff --git a/sway/server.c b/sway/server.c
index 1d8eb964..f904b177 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -14,7 +14,6 @@
14#include <wlr/types/wlr_linux_dmabuf.h> 14#include <wlr/types/wlr_linux_dmabuf.h>
15#include <wlr/types/wlr_primary_selection.h> 15#include <wlr/types/wlr_primary_selection.h>
16#include <wlr/types/wlr_screencopy_v1.h> 16#include <wlr/types/wlr_screencopy_v1.h>
17#include <wlr/types/wlr_screenshooter.h>
18#include <wlr/types/wlr_server_decoration.h> 17#include <wlr/types/wlr_server_decoration.h>
19#include <wlr/types/wlr_xcursor_manager.h> 18#include <wlr/types/wlr_xcursor_manager.h>
20#include <wlr/types/wlr_xdg_output.h> 19#include <wlr/types/wlr_xdg_output.h>
@@ -53,7 +52,6 @@ bool server_init(struct sway_server *server) {
53 server->data_device_manager = 52 server->data_device_manager =
54 wlr_data_device_manager_create(server->wl_display); 53 wlr_data_device_manager_create(server->wl_display);
55 54
56 wlr_screenshooter_create(server->wl_display);
57 wlr_gamma_control_manager_create(server->wl_display); 55 wlr_gamma_control_manager_create(server->wl_display);
58 wlr_primary_selection_device_manager_create(server->wl_display); 56 wlr_primary_selection_device_manager_create(server->wl_display);
59 57
@@ -125,8 +123,7 @@ bool server_init(struct sway_server *server) {
125 if (debug != NULL && strcmp(debug, "txn_timings") == 0) { 123 if (debug != NULL && strcmp(debug, "txn_timings") == 0) {
126 server->debug_txn_timings = true; 124 server->debug_txn_timings = true;
127 } 125 }
128 server->destroying_containers = create_list(); 126 server->dirty_containers = create_list();
129
130 server->transactions = create_list(); 127 server->transactions = create_list();
131 128
132 input_manager = input_manager_create(server); 129 input_manager = input_manager_create(server);
@@ -136,7 +133,7 @@ bool server_init(struct sway_server *server) {
136void server_fini(struct sway_server *server) { 133void server_fini(struct sway_server *server) {
137 // TODO: free sway-specific resources 134 // TODO: free sway-specific resources
138 wl_display_destroy(server->wl_display); 135 wl_display_destroy(server->wl_display);
139 list_free(server->destroying_containers); 136 list_free(server->dirty_containers);
140 list_free(server->transactions); 137 list_free(server->transactions);
141} 138}
142 139
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/arrange.c b/sway/tree/arrange.c
index bcc3ee9a..533cf71c 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -144,38 +144,22 @@ static void apply_tabbed_or_stacked_layout(struct sway_container *parent) {
144 } 144 }
145} 145}
146 146
147/** 147static void arrange_children_of(struct sway_container *parent);
148 * If a container has been deleted from the pending tree state, we must add it
149 * to the transaction so it can be freed afterwards. To do this, we iterate the
150 * server's destroying_containers list and add all of them. We may add more than
151 * what we need to, but this is easy and has no negative consequences.
152 */
153static void add_deleted_containers(struct sway_transaction *transaction) {
154 for (int i = 0; i < server.destroying_containers->length; ++i) {
155 struct sway_container *child = server.destroying_containers->items[i];
156 transaction_add_container(transaction, child);
157 }
158}
159
160static void arrange_children_of(struct sway_container *parent,
161 struct sway_transaction *transaction);
162 148
163static void arrange_floating(struct sway_container *floating, 149static void arrange_floating(struct sway_container *floating) {
164 struct sway_transaction *transaction) {
165 for (int i = 0; i < floating->children->length; ++i) { 150 for (int i = 0; i < floating->children->length; ++i) {
166 struct sway_container *floater = floating->children->items[i]; 151 struct sway_container *floater = floating->children->items[i];
167 if (floater->type == C_VIEW) { 152 if (floater->type == C_VIEW) {
168 view_autoconfigure(floater->sway_view); 153 view_autoconfigure(floater->sway_view);
169 } else { 154 } else {
170 arrange_children_of(floater, transaction); 155 arrange_children_of(floater);
171 } 156 }
172 transaction_add_container(transaction, floater); 157 container_set_dirty(floater);
173 } 158 }
174 transaction_add_container(transaction, floating); 159 container_set_dirty(floating);
175} 160}
176 161
177static void arrange_children_of(struct sway_container *parent, 162static void arrange_children_of(struct sway_container *parent) {
178 struct sway_transaction *transaction) {
179 if (config->reloading) { 163 if (config->reloading) {
180 return; 164 return;
181 } 165 }
@@ -198,7 +182,7 @@ static void arrange_children_of(struct sway_container *parent,
198 apply_horiz_layout(parent); 182 apply_horiz_layout(parent);
199 break; 183 break;
200 case L_FLOATING: 184 case L_FLOATING:
201 arrange_floating(parent, transaction); 185 arrange_floating(parent);
202 break; 186 break;
203 } 187 }
204 188
@@ -213,14 +197,13 @@ static void arrange_children_of(struct sway_container *parent,
213 if (child->type == C_VIEW) { 197 if (child->type == C_VIEW) {
214 view_autoconfigure(child->sway_view); 198 view_autoconfigure(child->sway_view);
215 } else { 199 } else {
216 arrange_children_of(child, transaction); 200 arrange_children_of(child);
217 } 201 }
218 transaction_add_container(transaction, child); 202 container_set_dirty(child);
219 } 203 }
220} 204}
221 205
222static void arrange_workspace(struct sway_container *workspace, 206static void arrange_workspace(struct sway_container *workspace) {
223 struct sway_transaction *transaction) {
224 if (config->reloading) { 207 if (config->reloading) {
225 return; 208 return;
226 } 209 }
@@ -234,15 +217,14 @@ static void arrange_workspace(struct sway_container *workspace,
234 workspace->x = output->x + area->x; 217 workspace->x = output->x + area->x;
235 workspace->y = output->y + area->y; 218 workspace->y = output->y + area->y;
236 add_gaps(workspace); 219 add_gaps(workspace);
237 transaction_add_container(transaction, workspace); 220 container_set_dirty(workspace);
238 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, 221 wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name,
239 workspace->x, workspace->y); 222 workspace->x, workspace->y);
240 arrange_floating(workspace->sway_workspace->floating, transaction); 223 arrange_floating(workspace->sway_workspace->floating);
241 arrange_children_of(workspace, transaction); 224 arrange_children_of(workspace);
242} 225}
243 226
244static void arrange_output(struct sway_container *output, 227static void arrange_output(struct sway_container *output) {
245 struct sway_transaction *transaction) {
246 if (config->reloading) { 228 if (config->reloading) {
247 return; 229 return;
248 } 230 }
@@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output,
253 output->y = output_box->y; 235 output->y = output_box->y;
254 output->width = output_box->width; 236 output->width = output_box->width;
255 output->height = output_box->height; 237 output->height = output_box->height;
256 transaction_add_container(transaction, output); 238 container_set_dirty(output);
257 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f", 239 wlr_log(WLR_DEBUG, "Arranging output '%s' at %f,%f",
258 output->name, output->x, output->y); 240 output->name, output->x, output->y);
259 for (int i = 0; i < output->children->length; ++i) { 241 for (int i = 0; i < output->children->length; ++i) {
260 struct sway_container *workspace = output->children->items[i]; 242 struct sway_container *workspace = output->children->items[i];
261 arrange_workspace(workspace, transaction); 243 arrange_workspace(workspace);
262 } 244 }
263} 245}
264 246
265static void arrange_root(struct sway_transaction *transaction) { 247static void arrange_root() {
266 if (config->reloading) { 248 if (config->reloading) {
267 return; 249 return;
268 } 250 }
@@ -274,43 +256,35 @@ static void arrange_root(struct sway_transaction *transaction) {
274 root_container.y = layout_box->y; 256 root_container.y = layout_box->y;
275 root_container.width = layout_box->width; 257 root_container.width = layout_box->width;
276 root_container.height = layout_box->height; 258 root_container.height = layout_box->height;
277 transaction_add_container(transaction, &root_container); 259 container_set_dirty(&root_container);
278 for (int i = 0; i < root_container.children->length; ++i) { 260 for (int i = 0; i < root_container.children->length; ++i) {
279 struct sway_container *output = root_container.children->items[i]; 261 struct sway_container *output = root_container.children->items[i];
280 arrange_output(output, transaction); 262 arrange_output(output);
281 } 263 }
282} 264}
283 265
284void arrange_windows(struct sway_container *container, 266void arrange_windows(struct sway_container *container) {
285 struct sway_transaction *transaction) {
286 switch (container->type) { 267 switch (container->type) {
287 case C_ROOT: 268 case C_ROOT:
288 arrange_root(transaction); 269 arrange_root();
289 break; 270 break;
290 case C_OUTPUT: 271 case C_OUTPUT:
291 arrange_output(container, transaction); 272 arrange_output(container);
292 break; 273 break;
293 case C_WORKSPACE: 274 case C_WORKSPACE:
294 arrange_workspace(container, transaction); 275 arrange_workspace(container);
295 break; 276 break;
296 case C_CONTAINER: 277 case C_CONTAINER:
297 arrange_children_of(container, transaction); 278 arrange_children_of(container);
298 transaction_add_container(transaction, container); 279 container_set_dirty(container);
299 break; 280 break;
300 case C_VIEW: 281 case C_VIEW:
301 view_autoconfigure(container->sway_view); 282 view_autoconfigure(container->sway_view);
302 transaction_add_container(transaction, container); 283 container_set_dirty(container);
303 break; 284 break;
304 case C_TYPES: 285 case C_TYPES:
305 break; 286 break;
306 } 287 }
307 add_deleted_containers(transaction);
308}
309
310void arrange_and_commit(struct sway_container *container) {
311 struct sway_transaction *transaction = transaction_create();
312 arrange_windows(container, transaction);
313 transaction_commit(transaction);
314} 288}
315 289
316void remove_gaps(struct sway_container *c) { 290void remove_gaps(struct sway_container *c) {
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 58852717..6d52c38c 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -159,14 +159,6 @@ void container_free(struct sway_container *cont) {
159 wlr_texture_destroy(cont->title_focused_inactive); 159 wlr_texture_destroy(cont->title_focused_inactive);
160 wlr_texture_destroy(cont->title_unfocused); 160 wlr_texture_destroy(cont->title_unfocused);
161 wlr_texture_destroy(cont->title_urgent); 161 wlr_texture_destroy(cont->title_urgent);
162
163 for (int i = 0; i < server.destroying_containers->length; ++i) {
164 if (server.destroying_containers->items[i] == cont) {
165 list_del(server.destroying_containers, i);
166 break;
167 }
168 }
169
170 list_free(cont->instructions); 162 list_free(cont->instructions);
171 list_free(cont->children); 163 list_free(cont->children);
172 list_free(cont->current.children); 164 list_free(cont->current.children);
@@ -325,7 +317,7 @@ static struct sway_container *container_destroy_noreaping(
325 } 317 }
326 318
327 con->destroying = true; 319 con->destroying = true;
328 list_add(server.destroying_containers, con); 320 container_set_dirty(con);
329 321
330 if (!con->parent) { 322 if (!con->parent) {
331 return NULL; 323 return NULL;
@@ -682,16 +674,23 @@ struct sway_container *floating_container_at(double lx, double ly,
682void container_for_each_descendant_dfs(struct sway_container *container, 674void container_for_each_descendant_dfs(struct sway_container *container,
683 void (*f)(struct sway_container *container, void *data), 675 void (*f)(struct sway_container *container, void *data),
684 void *data) { 676 void *data) {
685 if (container) { 677 if (!container) {
686 if (container->children) { 678 return;
687 for (int i = 0; i < container->children->length; ++i) { 679 }
688 struct sway_container *child = 680 if (container->children) {
689 container->children->items[i]; 681 for (int i = 0; i < container->children->length; ++i) {
690 container_for_each_descendant_dfs(child, f, data); 682 struct sway_container *child = container->children->items[i];
691 } 683 container_for_each_descendant_dfs(child, f, data);
684 }
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);
692 } 691 }
693 f(container, data);
694 } 692 }
693 f(container, data);
695} 694}
696 695
697void container_for_each_descendant_bfs(struct sway_container *con, 696void container_for_each_descendant_bfs(struct sway_container *con,
@@ -1069,9 +1068,26 @@ void container_floating_move_to(struct sway_container *con,
1069 if (old_workspace != new_workspace) { 1068 if (old_workspace != new_workspace) {
1070 container_remove_child(con); 1069 container_remove_child(con);
1071 container_add_child(new_workspace->sway_workspace->floating, con); 1070 container_add_child(new_workspace->sway_workspace->floating, con);
1072 struct sway_transaction *transaction = transaction_create(); 1071 arrange_windows(old_workspace);
1073 arrange_windows(old_workspace, transaction); 1072 arrange_windows(new_workspace);
1074 arrange_windows(new_workspace, transaction); 1073 workspace_detect_urgent(old_workspace);
1075 transaction_commit(transaction); 1074 workspace_detect_urgent(new_workspace);
1076 } 1075 }
1077} 1076}
1077
1078void container_set_dirty(struct sway_container *container) {
1079 if (container->dirty) {
1080 return;
1081 }
1082 container->dirty = true;
1083 list_add(server.dirty_containers, container);
1084}
1085
1086static bool find_urgent_iterator(struct sway_container *con,
1087 void *data) {
1088 return con->type == C_VIEW && view_is_urgent(con->sway_view);
1089}
1090
1091bool container_has_urgent_child(struct sway_container *container) {
1092 return container_find(container, find_urgent_iterator, NULL);
1093}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ba234e89..197a2fc8 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -22,7 +22,8 @@ struct sway_container root_container;
22 22
23static void output_layout_handle_change(struct wl_listener *listener, 23static void output_layout_handle_change(struct wl_listener *listener,
24 void *data) { 24 void *data) {
25 arrange_and_commit(&root_container); 25 arrange_windows(&root_container);
26 transaction_commit_dirty();
26} 27}
27 28
28void layout_init(void) { 29void layout_init(void) {
@@ -224,6 +225,15 @@ void container_move_to(struct sway_container *container,
224 } 225 }
225 } 226 }
226 } 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 }
227} 237}
228 238
229static bool sway_dir_to_wlr(enum movement_direction dir, 239static bool sway_dir_to_wlr(enum movement_direction dir,
@@ -547,6 +557,8 @@ void container_move(struct sway_container *container,
547 } 557 }
548 if (last_ws && next_ws && last_ws != next_ws) { 558 if (last_ws && next_ws && last_ws != next_ws) {
549 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);
550 } 562 }
551} 563}
552 564
diff --git a/sway/tree/view.c b/sway/tree/view.c
index b356183c..76e0f42c 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
@@ -504,20 +505,38 @@ void view_execute_criteria(struct sway_view *view) {
504 } 505 }
505 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", 506 wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'",
506 criteria->raw, view, criteria->cmdlist); 507 criteria->raw, view, criteria->cmdlist);
508 seat_set_focus(seat, view->swayc);
507 list_add(view->executed_criteria, criteria); 509 list_add(view->executed_criteria, criteria);
508 struct cmd_results *res = execute_command(criteria->cmdlist, NULL); 510 struct cmd_results *res = execute_command(criteria->cmdlist, NULL);
509 if (res->status != CMD_SUCCESS) { 511 if (res->status != CMD_SUCCESS) {
510 wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); 512 wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error);
511 } 513 }
512 free_cmd_results(res); 514 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 } 515 }
517 list_free(criterias); 516 list_free(criterias);
518 seat_set_focus(seat, prior_focus); 517 seat_set_focus(seat, prior_focus);
519} 518}
520 519
520static bool should_focus(struct sway_view *view) {
521 // If the view is the only one in the focused workspace, it'll get focus
522 // regardless of any no_focus criteria.
523 struct sway_container *parent = view->swayc->parent;
524 struct sway_seat *seat = input_manager_current_seat(input_manager);
525 if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) {
526 size_t num_children = parent->children->length +
527 parent->sway_workspace->floating->children->length;
528 if (num_children == 1) {
529 return true;
530 }
531 }
532
533 // Check no_focus criteria
534 list_t *criterias = criteria_for_view(view, CT_NO_FOCUS);
535 size_t len = criterias->length;
536 list_free(criterias);
537 return len == 0;
538}
539
521void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { 540void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
522 if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { 541 if (!sway_assert(view->surface == NULL, "cannot map mapped view")) {
523 return; 542 return;
@@ -571,9 +590,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
571 view_set_tiled(view, true); 590 view_set_tiled(view, true);
572 } 591 }
573 592
574 input_manager_set_focus(input_manager, cont); 593 if (should_focus(view)) {
575 if (workspace) { 594 input_manager_set_focus(input_manager, cont);
576 workspace_switch(workspace); 595 if (workspace) {
596 workspace_switch(workspace);
597 }
577 } 598 }
578 599
579 view_update_title(view, false); 600 view_update_title(view, false);
@@ -589,16 +610,27 @@ void view_unmap(struct sway_view *view) {
589 wl_list_remove(&view->surface_new_subsurface.link); 610 wl_list_remove(&view->surface_new_subsurface.link);
590 wl_list_remove(&view->container_reparent.link); 611 wl_list_remove(&view->container_reparent.link);
591 612
613 if (view->urgent_timer) {
614 wl_event_source_remove(view->urgent_timer);
615 view->urgent_timer = NULL;
616 }
617
618 struct sway_container *parent;
619 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
620
592 if (view->is_fullscreen) { 621 if (view->is_fullscreen) {
593 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
594 ws->sway_workspace->fullscreen = NULL; 622 ws->sway_workspace->fullscreen = NULL;
595 container_destroy(view->swayc); 623 parent = container_destroy(view->swayc);
596 624
597 arrange_and_commit(ws->parent); 625 arrange_windows(ws->parent);
598 } else { 626 } else {
599 struct sway_container *parent = container_destroy(view->swayc); 627 struct sway_container *parent = container_destroy(view->swayc);
600 arrange_and_commit(parent); 628 arrange_windows(parent);
601 } 629 }
630 if (parent->type >= C_WORKSPACE) { // if the workspace still exists
631 workspace_detect_urgent(ws);
632 }
633 transaction_commit_dirty();
602 view->surface = NULL; 634 view->surface = NULL;
603} 635}
604 636
@@ -1046,3 +1078,29 @@ bool view_is_visible(struct sway_view *view) {
1046 } 1078 }
1047 return true; 1079 return true;
1048} 1080}
1081
1082void view_set_urgent(struct sway_view *view, bool enable) {
1083 if (enable) {
1084 struct sway_seat *seat = input_manager_current_seat(input_manager);
1085 if (seat_get_focus(seat) == view->swayc) {
1086 return;
1087 }
1088 clock_gettime(CLOCK_MONOTONIC, &view->urgent);
1089 } else {
1090 view->urgent = (struct timespec){ 0 };
1091 if (view->urgent_timer) {
1092 wl_event_source_remove(view->urgent_timer);
1093 view->urgent_timer = NULL;
1094 }
1095 }
1096 container_damage_whole(view->swayc);
1097
1098 ipc_event_window(view->swayc, "urgent");
1099
1100 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
1101 workspace_detect_urgent(ws);
1102}
1103
1104bool view_is_urgent(struct sway_view *view) {
1105 return view->urgent.tv_sec || view->urgent.tv_nsec;
1106}
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 50f9400a..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"
@@ -427,7 +428,7 @@ bool workspace_switch(struct sway_container *workspace) {
427 } 428 }
428 seat_set_focus(seat, next); 429 seat_set_focus(seat, next);
429 struct sway_container *output = container_parent(workspace, C_OUTPUT); 430 struct sway_container *output = container_parent(workspace, C_OUTPUT);
430 arrange_and_commit(output); 431 arrange_windows(output);
431 return true; 432 return true;
432} 433}
433 434
@@ -518,3 +519,13 @@ struct sway_container *workspace_output_get_highest_available(
518 519
519 return NULL; 520 return NULL;
520} 521}
522
523void 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/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
415static struct swaylock_state state; 416enum line_mode {
416 417 LM_LINE,
417int 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
422static 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
692static bool file_exists(const char *path) {
693 return path && access(path, R_OK) != -1;
694}
695
696static 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
733static 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
774static struct swaylock_state state;
775
776int 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