diff options
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; | |||
152 | sway_cmd cmd_swap; | 152 | sway_cmd cmd_swap; |
153 | sway_cmd cmd_title_format; | 153 | sway_cmd cmd_title_format; |
154 | sway_cmd cmd_unmark; | 154 | sway_cmd cmd_unmark; |
155 | sway_cmd cmd_urgent; | ||
155 | sway_cmd cmd_workspace; | 156 | sway_cmd cmd_workspace; |
156 | sway_cmd cmd_ws_auto_back_and_forth; | 157 | sway_cmd cmd_ws_auto_back_and_forth; |
157 | sway_cmd cmd_workspace_layout; | 158 | sway_cmd cmd_workspace_layout; |
diff --git a/include/sway/criteria.h b/include/sway/criteria.h index bd3ca0ac..6a8337c5 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h | |||
@@ -6,9 +6,10 @@ | |||
6 | #include "tree/view.h" | 6 | #include "tree/view.h" |
7 | 7 | ||
8 | enum criteria_type { | 8 | enum criteria_type { |
9 | CT_COMMAND = 1 << 0, | 9 | CT_COMMAND = 1 << 0, |
10 | CT_ASSIGN_OUTPUT = 1 << 1, | 10 | CT_ASSIGN_OUTPUT = 1 << 1, |
11 | CT_ASSIGN_WORKSPACE = 1 << 2, | 11 | CT_ASSIGN_WORKSPACE = 1 << 2, |
12 | CT_NO_FOCUS = 1 << 3, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | struct criteria { | 15 | struct criteria { |
diff --git a/include/sway/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 | ||
4 | extern bool enable_debug_tree; | 5 | extern bool enable_debug_tree; |
5 | void update_debug_tree(); | 6 | void update_debug_tree(); |
6 | 7 | ||
8 | // Damage | ||
9 | extern const char *damage_debug; | ||
10 | |||
11 | // Transactions | ||
12 | extern int txn_timeout_ms; | ||
13 | extern 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 | |
20 | struct 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 | */ | ||
25 | struct sway_transaction *transaction_create(void); | ||
26 | |||
27 | /** | ||
28 | * Add a container's pending state to the transaction. | ||
29 | */ | 21 | */ |
30 | void 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 | */ |
36 | void transaction_commit(struct sway_transaction *transaction); | 27 | void 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 | */ | ||
129 | struct 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 | ||
56 | struct sway_server server; | 53 | struct 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); | |||
11 | void add_gaps(struct sway_container *c); | 11 | void 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 | */ |
24 | void arrange_windows(struct sway_container *container, | 16 | void 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 | */ | ||
34 | void 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); | |||
303 | void container_floating_move_to(struct sway_container *con, | 310 | void 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 | */ | ||
317 | void container_set_dirty(struct sway_container *container); | ||
318 | |||
319 | bool 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 | */ |
306 | bool view_is_visible(struct sway_view *view); | 310 | bool view_is_visible(struct sway_view *view); |
307 | 311 | ||
312 | void view_set_urgent(struct sway_view *view, bool enable); | ||
313 | |||
314 | bool 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 | ||
15 | extern char *prev_workspace_name; | 16 | extern char *prev_workspace_name; |
@@ -42,4 +43,7 @@ void workspace_output_add_priority(struct sway_container *workspace, | |||
42 | 43 | ||
43 | struct sway_container *workspace_output_get_highest_available( | 44 | struct sway_container *workspace_output_get_highest_available( |
44 | struct sway_container *ws, struct sway_container *exclude); | 45 | struct sway_container *ws, struct sway_container *exclude); |
46 | |||
47 | void workspace_detect_urgent(struct sway_container *workspace); | ||
48 | |||
45 | #endif | 49 | #endif |
diff --git a/sway/commands.c b/sway/commands.c index addd64a6..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 | ||
158 | static int handler_compare(const void *_a, const void *_b) { | 160 | static 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 | |||
8 | struct cmd_results *cmd_no_focus(int argc, char **argv) { | ||
9 | struct cmd_results *error = NULL; | ||
10 | if ((error = checkarg(argc, "no_focus", EXPECTED_AT_LEAST, 1))) { | ||
11 | return error; | ||
12 | } | ||
13 | |||
14 | char *err_str = NULL; | ||
15 | struct criteria *criteria = criteria_parse(argv[0], &err_str); | ||
16 | if (!criteria) { | ||
17 | error = cmd_results_new(CMD_INVALID, "no_focus", err_str); | ||
18 | free(err_str); | ||
19 | return error; | ||
20 | } | ||
21 | |||
22 | criteria->type = CT_NO_FOCUS; | ||
23 | list_add(config->criteria, criteria); | ||
24 | |||
25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
26 | } | ||
diff --git a/sway/commands/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 | |||
9 | struct cmd_results *cmd_urgent(int argc, char **argv) { | ||
10 | struct cmd_results *error = NULL; | ||
11 | if ((error = checkarg(argc, "urgent", EXPECTED_EQUAL_TO, 1))) { | ||
12 | return error; | ||
13 | } | ||
14 | struct sway_container *container = | ||
15 | config->handler_context.current_container; | ||
16 | if (container->type != C_VIEW) { | ||
17 | return cmd_results_new(CMD_INVALID, "urgent", | ||
18 | "Only views can be urgent"); | ||
19 | } | ||
20 | struct sway_view *view = container->sway_view; | ||
21 | |||
22 | if (strcmp(argv[0], "enable") == 0) { | ||
23 | view_set_urgent(view, true); | ||
24 | } else if (strcmp(argv[0], "disable") == 0) { | ||
25 | view_set_urgent(view, false); | ||
26 | } else if (strcmp(argv[0], "allow") == 0) { | ||
27 | view->allow_request_urgent = true; | ||
28 | } else if (strcmp(argv[0], "deny") == 0) { | ||
29 | view->allow_request_urgent = false; | ||
30 | } else { | ||
31 | return cmd_results_new(CMD_INVALID, "urgent", | ||
32 | "Expected 'urgent <enable|disable|allow|deny>'"); | ||
33 | } | ||
34 | |||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
36 | } | ||
diff --git a/sway/config.c b/sway/config.c index 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 | ||
561 | bool read_config(FILE *file, struct sway_config *config) { | 562 | bool 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 | ||
49 | static int cmp_urgent(const void *_a, const void *_b) { | ||
50 | struct sway_view *a = *(void **)_a; | ||
51 | struct sway_view *b = *(void **)_b; | ||
52 | |||
53 | if (a->urgent.tv_sec < b->urgent.tv_sec) { | ||
54 | return -1; | ||
55 | } else if (a->urgent.tv_sec > b->urgent.tv_sec) { | ||
56 | return 1; | ||
57 | } | ||
58 | if (a->urgent.tv_nsec < b->urgent.tv_nsec) { | ||
59 | return -1; | ||
60 | } else if (a->urgent.tv_nsec > b->urgent.tv_nsec) { | ||
61 | return 1; | ||
62 | } | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void find_urgent_iterator(struct sway_container *swayc, void *data) { | ||
67 | if (swayc->type != C_VIEW || !view_is_urgent(swayc->sway_view)) { | ||
68 | return; | ||
69 | } | ||
70 | list_t *urgent_views = data; | ||
71 | list_add(urgent_views, swayc->sway_view); | ||
72 | } | ||
73 | |||
49 | static bool criteria_matches_view(struct criteria *criteria, | 74 | static bool criteria_matches_view(struct criteria *criteria, |
50 | struct sway_view *view) { | 75 | struct sway_view *view) { |
51 | if (criteria->title) { | 76 | if (criteria->title) { |
@@ -133,8 +158,23 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
133 | } | 158 | } |
134 | 159 | ||
135 | if (criteria->urgent) { | 160 | if (criteria->urgent) { |
136 | // TODO | 161 | if (!view_is_urgent(view)) { |
137 | return false; | 162 | return false; |
163 | } | ||
164 | list_t *urgent_views = create_list(); | ||
165 | container_for_each_descendant_dfs(&root_container, | ||
166 | find_urgent_iterator, urgent_views); | ||
167 | list_stable_sort(urgent_views, cmp_urgent); | ||
168 | struct sway_view *target; | ||
169 | if (criteria->urgent == 'o') { // oldest | ||
170 | target = urgent_views->items[0]; | ||
171 | } else { // latest | ||
172 | target = urgent_views->items[urgent_views->length - 1]; | ||
173 | } | ||
174 | list_free(urgent_views); | ||
175 | if (view != target) { | ||
176 | return false; | ||
177 | } | ||
138 | } | 178 | } |
139 | 179 | ||
140 | if (criteria->workspace) { | 180 | if (criteria->workspace) { |
diff --git a/sway/desktop/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 | ||
498 | static void handle_mode(struct wl_listener *listener, void *data) { | 498 | static 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 | ||
504 | static void handle_transform(struct wl_listener *listener, void *data) { | 505 | static 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 | ||
510 | static void handle_scale_iterator(struct sway_container *view, void *data) { | 512 | static 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 | ||
521 | struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { | 524 | struct 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, | |||
542 | static void render_container_simple(struct sway_output *output, | 543 | static 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 | ||
797 | const char *damage_debug = NULL; | ||
798 | |||
789 | void output_render(struct sway_output *output, struct timespec *when, | 799 | void 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 | 22 | int 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 | 29 | bool txn_debug = false; |
30 | 30 | ||
31 | struct sway_transaction { | 31 | struct 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 | ||
50 | struct sway_transaction *transaction_create() { | 50 | static 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 | ||
144 | static 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 | ||
156 | void transaction_add_container(struct sway_transaction *transaction, | 152 | static 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 | ||
288 | void transaction_commit(struct sway_transaction *transaction) { | 283 | static 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 | |||
417 | void 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 | ||
250 | static void handle_unmap(struct wl_listener *listener, void *data) { | 251 | static 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 | ||
245 | static void handle_unmap(struct wl_listener *listener, void *data) { | 246 | static 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 | ||
302 | static void handle_unmap(struct wl_listener *listener, void *data) { | 306 | static 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 | ||
342 | static void handle_destroy(struct wl_listener *listener, void *data) { | 347 | static 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 | ||
398 | static void handle_set_title(struct wl_listener *listener, void *data) { | 404 | static 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 | ||
224 | static void handle_cursor_motion(struct wl_listener *listener, void *data) { | 226 | static 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 | ||
283 | static void handle_cursor_button(struct wl_listener *listener, void *data) { | 286 | static 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 | ||
597 | static int handle_urgent_timeout(void *data) { | ||
598 | struct sway_view *view = data; | ||
599 | view_set_urgent(view, false); | ||
600 | return 0; | ||
601 | } | ||
602 | |||
597 | void seat_set_focus_warp(struct sway_seat *seat, | 603 | void seat_set_focus_warp(struct sway_seat *seat, |
598 | struct sway_container *container, bool warp) { | 604 | struct sway_container *container, bool warp) { |
599 | if (seat->focused_layer) { | 605 | if (seat->focused_layer) { |
@@ -649,6 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
649 | while (parent) { | 655 | while (parent) { |
650 | wl_list_remove(&parent->link); | 656 | wl_list_remove(&parent->link); |
651 | wl_list_insert(&seat->focus_stack, &parent->link); | 657 | wl_list_insert(&seat->focus_stack, &parent->link); |
658 | container_set_dirty(parent->container); | ||
652 | 659 | ||
653 | parent = | 660 | parent = |
654 | seat_container_from_container(seat, | 661 | seat_container_from_container(seat, |
@@ -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 | ||
843 | struct 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 | |||
855 | struct sway_container *seat_get_focus(struct sway_seat *seat) { | 860 | struct 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 | ||
201 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { | 206 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { |
diff --git a/sway/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 | ||
255 | void 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 | |||
254 | int main(int argc, char **argv) { | 267 | int 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) { | |||
136 | void server_fini(struct sway_server *server) { | 133 | void 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 | /** | 147 | static 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 | */ | ||
153 | static 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 | |||
160 | static void arrange_children_of(struct sway_container *parent, | ||
161 | struct sway_transaction *transaction); | ||
162 | 148 | ||
163 | static void arrange_floating(struct sway_container *floating, | 149 | static 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 | ||
177 | static void arrange_children_of(struct sway_container *parent, | 162 | static 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 | ||
222 | static void arrange_workspace(struct sway_container *workspace, | 206 | static 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 | ||
244 | static void arrange_output(struct sway_container *output, | 227 | static 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 | ||
265 | static void arrange_root(struct sway_transaction *transaction) { | 247 | static 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 | ||
284 | void arrange_windows(struct sway_container *container, | 266 | void 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 | |||
310 | void 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 | ||
316 | void remove_gaps(struct sway_container *c) { | 290 | void 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, | |||
682 | void container_for_each_descendant_dfs(struct sway_container *container, | 674 | void 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 | ||
697 | void container_for_each_descendant_bfs(struct sway_container *con, | 696 | void 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 | |||
1078 | void 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 | |||
1086 | static 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 | |||
1091 | bool 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 | ||
23 | static void output_layout_handle_change(struct wl_listener *listener, | 23 | static 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 | ||
28 | void layout_init(void) { | 29 | void 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 | ||
229 | static bool sway_dir_to_wlr(enum movement_direction dir, | 239 | static 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 | ||
520 | static 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 | |||
521 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | 540 | void 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 | |||
1082 | void 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 | |||
1104 | bool 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 | |||
523 | void workspace_detect_urgent(struct sway_container *workspace) { | ||
524 | bool new_urgent = container_has_urgent_child(workspace); | ||
525 | |||
526 | if (workspace->sway_workspace->urgent != new_urgent) { | ||
527 | workspace->sway_workspace->urgent = new_urgent; | ||
528 | ipc_event_workspace(NULL, workspace, "urgent"); | ||
529 | container_damage_whole(workspace); | ||
530 | } | ||
531 | } | ||
diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 08531f2a..c2d05920 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c | |||
@@ -115,6 +115,18 @@ static void ipc_parse_colors( | |||
115 | config->colors.inactive_workspace.text = parse_color( | 115 | config->colors.inactive_workspace.text = parse_color( |
116 | json_object_get_string(inactive_workspace_text)); | 116 | json_object_get_string(inactive_workspace_text)); |
117 | } | 117 | } |
118 | if (urgent_workspace_border) { | ||
119 | config->colors.urgent_workspace.border = parse_color( | ||
120 | json_object_get_string(urgent_workspace_border)); | ||
121 | } | ||
122 | if (urgent_workspace_bg) { | ||
123 | config->colors.urgent_workspace.background = parse_color( | ||
124 | json_object_get_string(urgent_workspace_bg)); | ||
125 | } | ||
126 | if (urgent_workspace_text) { | ||
127 | config->colors.urgent_workspace.text = parse_color( | ||
128 | json_object_get_string(urgent_workspace_text)); | ||
129 | } | ||
118 | if (binding_mode_border) { | 130 | if (binding_mode_border) { |
119 | config->colors.binding_mode.border = parse_color( | 131 | config->colors.binding_mode.border = parse_color( |
120 | json_object_get_string(binding_mode_border)); | 132 | json_object_get_string(binding_mode_border)); |
diff --git a/swaylock/main.c b/swaylock/main.c index faebc757..ae5b86b9 100644 --- a/swaylock/main.c +++ b/swaylock/main.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "pool-buffer.h" | 21 | #include "pool-buffer.h" |
22 | #include "cairo.h" | 22 | #include "cairo.h" |
23 | #include "log.h" | 23 | #include "log.h" |
24 | #include "readline.h" | ||
24 | #include "stringop.h" | 25 | #include "stringop.h" |
25 | #include "util.h" | 26 | #include "util.h" |
26 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" | 27 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" |
@@ -412,15 +413,14 @@ static void set_default_colors(struct swaylock_colors *colors) { | |||
412 | }; | 413 | }; |
413 | } | 414 | } |
414 | 415 | ||
415 | static struct swaylock_state state; | 416 | enum line_mode { |
416 | 417 | LM_LINE, | |
417 | int main(int argc, char **argv) { | 418 | LM_INSIDE, |
418 | enum line_mode { | 419 | LM_RING, |
419 | LM_LINE, | 420 | }; |
420 | LM_INSIDE, | ||
421 | LM_RING, | ||
422 | }; | ||
423 | 421 | ||
422 | static int parse_options(int argc, char **argv, struct swaylock_state *state, | ||
423 | enum line_mode *line_mode) { | ||
424 | enum long_option_codes { | 424 | enum long_option_codes { |
425 | LO_BS_HL_COLOR = 256, | 425 | LO_BS_HL_COLOR = 256, |
426 | LO_FONT, | 426 | LO_FONT, |
@@ -447,6 +447,7 @@ int main(int argc, char **argv) { | |||
447 | }; | 447 | }; |
448 | 448 | ||
449 | static struct option long_options[] = { | 449 | static struct option long_options[] = { |
450 | {"config", required_argument, NULL, 'C'}, | ||
450 | {"color", required_argument, NULL, 'c'}, | 451 | {"color", required_argument, NULL, 'c'}, |
451 | {"ignore-empty-password", no_argument, NULL, 'e'}, | 452 | {"ignore-empty-password", no_argument, NULL, 'e'}, |
452 | {"daemonize", no_argument, NULL, 'f'}, | 453 | {"daemonize", no_argument, NULL, 'f'}, |
@@ -487,6 +488,8 @@ int main(int argc, char **argv) { | |||
487 | const char usage[] = | 488 | const char usage[] = |
488 | "Usage: swaylock [options...]\n" | 489 | "Usage: swaylock [options...]\n" |
489 | "\n" | 490 | "\n" |
491 | " -C, --config <config_file> " | ||
492 | "Path to the config file.\n" | ||
490 | " -c, --color <color> " | 493 | " -c, --color <color> " |
491 | "Turn the screen into the given color instead of white.\n" | 494 | "Turn the screen into the given color instead of white.\n" |
492 | " -e, --ignore-empty-password " | 495 | " -e, --ignore-empty-password " |
@@ -559,58 +562,48 @@ int main(int argc, char **argv) { | |||
559 | "\n" | 562 | "\n" |
560 | "All <color> options are of the form <rrggbb[aa]>.\n"; | 563 | "All <color> options are of the form <rrggbb[aa]>.\n"; |
561 | 564 | ||
562 | enum line_mode line_mode = LM_LINE; | ||
563 | state.args = (struct swaylock_args){ | ||
564 | .mode = BACKGROUND_MODE_SOLID_COLOR, | ||
565 | .font = strdup("sans-serif"), | ||
566 | .radius = 50, | ||
567 | .thickness = 10, | ||
568 | .ignore_empty = false, | ||
569 | .show_indicator = true, | ||
570 | }; | ||
571 | wl_list_init(&state.images); | ||
572 | set_default_colors(&state.args.colors); | ||
573 | |||
574 | wlr_log_init(WLR_DEBUG, NULL); | ||
575 | |||
576 | int c; | 565 | int c; |
566 | optind = 1; | ||
577 | while (1) { | 567 | while (1) { |
578 | int opt_idx = 0; | 568 | int opt_idx = 0; |
579 | c = getopt_long(argc, argv, "c:efhi:nrs:tuv", long_options, &opt_idx); | 569 | c = getopt_long(argc, argv, "c:efhi:nrs:tuvC:", long_options, &opt_idx); |
580 | if (c == -1) { | 570 | if (c == -1) { |
581 | break; | 571 | break; |
582 | } | 572 | } |
583 | switch (c) { | 573 | switch (c) { |
574 | case 'C': | ||
575 | // Config file. This will have already been handled so just ignore. | ||
576 | break; | ||
584 | case 'c': | 577 | case 'c': |
585 | state.args.colors.background = parse_color(optarg); | 578 | state->args.colors.background = parse_color(optarg); |
586 | state.args.mode = BACKGROUND_MODE_SOLID_COLOR; | 579 | state->args.mode = BACKGROUND_MODE_SOLID_COLOR; |
587 | break; | 580 | break; |
588 | case 'e': | 581 | case 'e': |
589 | state.args.ignore_empty = true; | 582 | state->args.ignore_empty = true; |
590 | break; | 583 | break; |
591 | case 'f': | 584 | case 'f': |
592 | state.args.daemonize = true; | 585 | state->args.daemonize = true; |
593 | break; | 586 | break; |
594 | case 'i': | 587 | case 'i': |
595 | load_image(optarg, &state); | 588 | load_image(optarg, state); |
596 | break; | 589 | break; |
597 | case 'n': | 590 | case 'n': |
598 | line_mode = LM_INSIDE; | 591 | *line_mode = LM_INSIDE; |
599 | break; | 592 | break; |
600 | case 'r': | 593 | case 'r': |
601 | line_mode = LM_RING; | 594 | *line_mode = LM_RING; |
602 | break; | 595 | break; |
603 | case 's': | 596 | case 's': |
604 | state.args.mode = parse_background_mode(optarg); | 597 | state->args.mode = parse_background_mode(optarg); |
605 | if (state.args.mode == BACKGROUND_MODE_INVALID) { | 598 | if (state->args.mode == BACKGROUND_MODE_INVALID) { |
606 | return 1; | 599 | return 1; |
607 | } | 600 | } |
608 | break; | 601 | break; |
609 | case 't': | 602 | case 't': |
610 | state.args.mode = BACKGROUND_MODE_TILE; | 603 | state->args.mode = BACKGROUND_MODE_TILE; |
611 | break; | 604 | break; |
612 | case 'u': | 605 | case 'u': |
613 | state.args.show_indicator = false; | 606 | state->args.show_indicator = false; |
614 | break; | 607 | break; |
615 | case 'v': | 608 | case 'v': |
616 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE | 609 | #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE |
@@ -621,71 +614,71 @@ int main(int argc, char **argv) { | |||
621 | #endif | 614 | #endif |
622 | return 0; | 615 | return 0; |
623 | case LO_BS_HL_COLOR: | 616 | case LO_BS_HL_COLOR: |
624 | state.args.colors.bs_highlight = parse_color(optarg); | 617 | state->args.colors.bs_highlight = parse_color(optarg); |
625 | break; | 618 | break; |
626 | case LO_FONT: | 619 | case LO_FONT: |
627 | free(state.args.font); | 620 | free(state->args.font); |
628 | state.args.font = strdup(optarg); | 621 | state->args.font = strdup(optarg); |
629 | break; | 622 | break; |
630 | case LO_IND_RADIUS: | 623 | case LO_IND_RADIUS: |
631 | state.args.radius = strtol(optarg, NULL, 0); | 624 | state->args.radius = strtol(optarg, NULL, 0); |
632 | break; | 625 | break; |
633 | case LO_IND_THICKNESS: | 626 | case LO_IND_THICKNESS: |
634 | state.args.thickness = strtol(optarg, NULL, 0); | 627 | state->args.thickness = strtol(optarg, NULL, 0); |
635 | break; | 628 | break; |
636 | case LO_INSIDE_COLOR: | 629 | case LO_INSIDE_COLOR: |
637 | state.args.colors.inside.input = parse_color(optarg); | 630 | state->args.colors.inside.input = parse_color(optarg); |
638 | break; | 631 | break; |
639 | case LO_INSIDE_CLEAR_COLOR: | 632 | case LO_INSIDE_CLEAR_COLOR: |
640 | state.args.colors.inside.cleared = parse_color(optarg); | 633 | state->args.colors.inside.cleared = parse_color(optarg); |
641 | break; | 634 | break; |
642 | case LO_INSIDE_VER_COLOR: | 635 | case LO_INSIDE_VER_COLOR: |
643 | state.args.colors.inside.verifying = parse_color(optarg); | 636 | state->args.colors.inside.verifying = parse_color(optarg); |
644 | break; | 637 | break; |
645 | case LO_INSIDE_WRONG_COLOR: | 638 | case LO_INSIDE_WRONG_COLOR: |
646 | state.args.colors.inside.wrong = parse_color(optarg); | 639 | state->args.colors.inside.wrong = parse_color(optarg); |
647 | break; | 640 | break; |
648 | case LO_KEY_HL_COLOR: | 641 | case LO_KEY_HL_COLOR: |
649 | state.args.colors.key_highlight = parse_color(optarg); | 642 | state->args.colors.key_highlight = parse_color(optarg); |
650 | break; | 643 | break; |
651 | case LO_LINE_COLOR: | 644 | case LO_LINE_COLOR: |
652 | state.args.colors.line.input = parse_color(optarg); | 645 | state->args.colors.line.input = parse_color(optarg); |
653 | break; | 646 | break; |
654 | case LO_LINE_CLEAR_COLOR: | 647 | case LO_LINE_CLEAR_COLOR: |
655 | state.args.colors.line.cleared = parse_color(optarg); | 648 | state->args.colors.line.cleared = parse_color(optarg); |
656 | break; | 649 | break; |
657 | case LO_LINE_VER_COLOR: | 650 | case LO_LINE_VER_COLOR: |
658 | state.args.colors.line.verifying = parse_color(optarg); | 651 | state->args.colors.line.verifying = parse_color(optarg); |
659 | break; | 652 | break; |
660 | case LO_LINE_WRONG_COLOR: | 653 | case LO_LINE_WRONG_COLOR: |
661 | state.args.colors.line.wrong = parse_color(optarg); | 654 | state->args.colors.line.wrong = parse_color(optarg); |
662 | break; | 655 | break; |
663 | case LO_RING_COLOR: | 656 | case LO_RING_COLOR: |
664 | state.args.colors.ring.input = parse_color(optarg); | 657 | state->args.colors.ring.input = parse_color(optarg); |
665 | break; | 658 | break; |
666 | case LO_RING_CLEAR_COLOR: | 659 | case LO_RING_CLEAR_COLOR: |
667 | state.args.colors.ring.cleared = parse_color(optarg); | 660 | state->args.colors.ring.cleared = parse_color(optarg); |
668 | break; | 661 | break; |
669 | case LO_RING_VER_COLOR: | 662 | case LO_RING_VER_COLOR: |
670 | state.args.colors.ring.verifying = parse_color(optarg); | 663 | state->args.colors.ring.verifying = parse_color(optarg); |
671 | break; | 664 | break; |
672 | case LO_RING_WRONG_COLOR: | 665 | case LO_RING_WRONG_COLOR: |
673 | state.args.colors.ring.wrong = parse_color(optarg); | 666 | state->args.colors.ring.wrong = parse_color(optarg); |
674 | break; | 667 | break; |
675 | case LO_SEP_COLOR: | 668 | case LO_SEP_COLOR: |
676 | state.args.colors.separator = parse_color(optarg); | 669 | state->args.colors.separator = parse_color(optarg); |
677 | break; | 670 | break; |
678 | case LO_TEXT_COLOR: | 671 | case LO_TEXT_COLOR: |
679 | state.args.colors.text.input = parse_color(optarg); | 672 | state->args.colors.text.input = parse_color(optarg); |
680 | break; | 673 | break; |
681 | case LO_TEXT_CLEAR_COLOR: | 674 | case LO_TEXT_CLEAR_COLOR: |
682 | state.args.colors.text.cleared = parse_color(optarg); | 675 | state->args.colors.text.cleared = parse_color(optarg); |
683 | break; | 676 | break; |
684 | case LO_TEXT_VER_COLOR: | 677 | case LO_TEXT_VER_COLOR: |
685 | state.args.colors.text.verifying = parse_color(optarg); | 678 | state->args.colors.text.verifying = parse_color(optarg); |
686 | break; | 679 | break; |
687 | case LO_TEXT_WRONG_COLOR: | 680 | case LO_TEXT_WRONG_COLOR: |
688 | state.args.colors.text.wrong = parse_color(optarg); | 681 | state->args.colors.text.wrong = parse_color(optarg); |
689 | break; | 682 | break; |
690 | default: | 683 | default: |
691 | fprintf(stderr, "%s", usage); | 684 | fprintf(stderr, "%s", usage); |
@@ -693,6 +686,143 @@ int main(int argc, char **argv) { | |||
693 | } | 686 | } |
694 | } | 687 | } |
695 | 688 | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | static bool file_exists(const char *path) { | ||
693 | return path && access(path, R_OK) != -1; | ||
694 | } | ||
695 | |||
696 | static char *get_config_path(void) { | ||
697 | static const char *config_paths[] = { | ||
698 | "$HOME/.swaylock/config", | ||
699 | "$XDG_CONFIG_HOME/swaylock/config", | ||
700 | SYSCONFDIR "/swaylock/config", | ||
701 | }; | ||
702 | |||
703 | if (!getenv("XDG_CONFIG_HOME")) { | ||
704 | char *home = getenv("HOME"); | ||
705 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); | ||
706 | if (!config_home) { | ||
707 | wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); | ||
708 | } else { | ||
709 | strcpy(config_home, home); | ||
710 | strcat(config_home, "/.config"); | ||
711 | setenv("XDG_CONFIG_HOME", config_home, 1); | ||
712 | wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); | ||
713 | free(config_home); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | wordexp_t p; | ||
718 | char *path; | ||
719 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | ||
720 | if (wordexp(config_paths[i], &p, 0) == 0) { | ||
721 | path = strdup(p.we_wordv[0]); | ||
722 | wordfree(&p); | ||
723 | if (file_exists(path)) { | ||
724 | return path; | ||
725 | } | ||
726 | free(path); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | return NULL; | ||
731 | } | ||
732 | |||
733 | static int load_config(char *path, struct swaylock_state *state, | ||
734 | enum line_mode *line_mode) { | ||
735 | FILE *config = fopen(path, "r"); | ||
736 | if (!config) { | ||
737 | wlr_log(WLR_ERROR, "Failed to read config. Running without it."); | ||
738 | return 0; | ||
739 | } | ||
740 | char *line; | ||
741 | int line_number = 0; | ||
742 | while (!feof(config)) { | ||
743 | line = read_line(config); | ||
744 | if (!line) { | ||
745 | continue; | ||
746 | } | ||
747 | |||
748 | line_number++; | ||
749 | if (line[0] == '#') { | ||
750 | free(line); | ||
751 | continue; | ||
752 | } | ||
753 | if (strlen(line) == 0) { | ||
754 | free(line); | ||
755 | continue; | ||
756 | } | ||
757 | |||
758 | wlr_log(WLR_DEBUG, "Config Line #%d: %s", line_number, line); | ||
759 | char flag[strlen(line) + 3]; | ||
760 | sprintf(flag, "--%s", line); | ||
761 | char *argv[] = {"swaylock", flag}; | ||
762 | int result = parse_options(2, argv, state, line_mode); | ||
763 | if (result != 0) { | ||
764 | free(line); | ||
765 | fclose(config); | ||
766 | return result; | ||
767 | } | ||
768 | free(line); | ||
769 | } | ||
770 | fclose(config); | ||
771 | return 0; | ||
772 | } | ||
773 | |||
774 | static struct swaylock_state state; | ||
775 | |||
776 | int main(int argc, char **argv) { | ||
777 | enum line_mode line_mode = LM_LINE; | ||
778 | state.args = (struct swaylock_args){ | ||
779 | .mode = BACKGROUND_MODE_SOLID_COLOR, | ||
780 | .font = strdup("sans-serif"), | ||
781 | .radius = 50, | ||
782 | .thickness = 10, | ||
783 | .ignore_empty = false, | ||
784 | .show_indicator = true, | ||
785 | }; | ||
786 | wl_list_init(&state.images); | ||
787 | set_default_colors(&state.args.colors); | ||
788 | |||
789 | wlr_log_init(WLR_DEBUG, NULL); | ||
790 | |||
791 | char *config_path = NULL; | ||
792 | static struct option long_options[] = { | ||
793 | {"config", required_argument, NULL, 'C'}, | ||
794 | {0, 0, 0, 0}, | ||
795 | }; | ||
796 | while (1) { | ||
797 | int c = getopt_long(argc, argv, "C:", long_options, NULL); | ||
798 | if (c == -1) { | ||
799 | break; | ||
800 | } else if (c == 'C') { | ||
801 | config_path = strdup(optarg); | ||
802 | break; | ||
803 | } | ||
804 | } | ||
805 | if (!config_path) { | ||
806 | config_path = get_config_path(); | ||
807 | } | ||
808 | |||
809 | if (config_path) { | ||
810 | wlr_log(WLR_DEBUG, "Found config at %s", config_path); | ||
811 | int config_status = load_config(config_path, &state, &line_mode); | ||
812 | free(config_path); | ||
813 | if (config_status != 0) { | ||
814 | return config_status; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | if (argc > 1) { | ||
819 | wlr_log(WLR_DEBUG, "Parsing CLI Args"); | ||
820 | int result = parse_options(argc, argv, &state, &line_mode); | ||
821 | if (result != 0) { | ||
822 | return result; | ||
823 | } | ||
824 | } | ||
825 | |||
696 | if (line_mode == LM_INSIDE) { | 826 | if (line_mode == LM_INSIDE) { |
697 | state.args.colors.line = state.args.colors.inside; | 827 | state.args.colors.line = state.args.colors.inside; |
698 | } else if (line_mode == LM_RING) { | 828 | } else if (line_mode == LM_RING) { |
diff --git a/swaylock/swaylock.1.scd b/swaylock/swaylock.1.scd index eea62c2a..3107124f 100644 --- a/swaylock/swaylock.1.scd +++ b/swaylock/swaylock.1.scd | |||
@@ -12,6 +12,15 @@ Locks your Wayland session. | |||
12 | 12 | ||
13 | # OPTIONS | 13 | # OPTIONS |
14 | 14 | ||
15 | *-C, --config* <path> | ||
16 | The config file to use. By default, the following paths are checked: | ||
17 | _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and | ||
18 | _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid | ||
19 | options in the configuration file using the format _long-option=value_. | ||
20 | For options such as _ignore-empty-password_, just supply the _long-option_. | ||
21 | All leading dashes should be omitted and the equals sign is required for | ||
22 | flags that take an argument. | ||
23 | |||
15 | *-c, --color* <rrggbb[aa]> | 24 | *-c, --color* <rrggbb[aa]> |
16 | Turn the screen into the given color. If -i is used, this sets the | 25 | Turn the screen into the given color. If -i is used, this sets the |
17 | background of the image to the given color. Defaults to white (FFFFFF), or | 26 | background of the image to the given color. Defaults to white (FFFFFF), or |