diff options
Diffstat (limited to 'sway/tree')
-rw-r--r-- | sway/tree/arrange.c | 102 | ||||
-rw-r--r-- | sway/tree/container.c | 209 | ||||
-rw-r--r-- | sway/tree/layout.c | 97 | ||||
-rw-r--r-- | sway/tree/output.c | 6 | ||||
-rw-r--r-- | sway/tree/view.c | 191 | ||||
-rw-r--r-- | sway/tree/workspace.c | 57 |
6 files changed, 472 insertions, 190 deletions
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 582b2891..533cf71c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -47,11 +47,11 @@ static void apply_horiz_layout(struct sway_container *parent) { | |||
47 | double scale = parent->width / total_width; | 47 | double scale = parent->width / total_width; |
48 | 48 | ||
49 | // Resize windows | 49 | // Resize windows |
50 | wlr_log(L_DEBUG, "Arranging %p horizontally", parent); | 50 | wlr_log(WLR_DEBUG, "Arranging %p horizontally", parent); |
51 | double child_x = parent->x; | 51 | double child_x = parent->x; |
52 | for (size_t i = 0; i < num_children; ++i) { | 52 | for (size_t i = 0; i < num_children; ++i) { |
53 | struct sway_container *child = parent->children->items[i]; | 53 | struct sway_container *child = parent->children->items[i]; |
54 | wlr_log(L_DEBUG, | 54 | wlr_log(WLR_DEBUG, |
55 | "Calculating arrangement for %p:%d (will scale %f by %f)", | 55 | "Calculating arrangement for %p:%d (will scale %f by %f)", |
56 | child, child->type, child->width, scale); | 56 | child, child->type, child->width, scale); |
57 | child->x = child_x; | 57 | child->x = child_x; |
@@ -99,11 +99,11 @@ static void apply_vert_layout(struct sway_container *parent) { | |||
99 | double scale = parent_height / total_height; | 99 | double scale = parent_height / total_height; |
100 | 100 | ||
101 | // Resize | 101 | // Resize |
102 | wlr_log(L_DEBUG, "Arranging %p vertically", parent); | 102 | wlr_log(WLR_DEBUG, "Arranging %p vertically", parent); |
103 | double child_y = parent->y + parent_offset; | 103 | double child_y = parent->y + parent_offset; |
104 | for (size_t i = 0; i < num_children; ++i) { | 104 | for (size_t i = 0; i < num_children; ++i) { |
105 | struct sway_container *child = parent->children->items[i]; | 105 | struct sway_container *child = parent->children->items[i]; |
106 | wlr_log(L_DEBUG, | 106 | wlr_log(WLR_DEBUG, |
107 | "Calculating arrangement for %p:%d (will scale %f by %f)", | 107 | "Calculating arrangement for %p:%d (will scale %f by %f)", |
108 | child, child->type, child->height, scale); | 108 | child, child->type, child->height, scale); |
109 | child->x = parent->x; | 109 | child->x = parent->x; |
@@ -144,42 +144,26 @@ 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 | } |
182 | wlr_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, | 166 | wlr_log(WLR_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", parent, |
183 | parent->name, parent->width, parent->height, parent->x, parent->y); | 167 | parent->name, parent->width, parent->height, parent->x, parent->y); |
184 | 168 | ||
185 | // Calculate x, y, width and height of children | 169 | // Calculate x, y, width and height of children |
@@ -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,20 +197,19 @@ 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 | } |
227 | struct sway_container *output = workspace->parent; | 210 | struct sway_container *output = workspace->parent; |
228 | struct wlr_box *area = &output->sway_output->usable_area; | 211 | struct wlr_box *area = &output->sway_output->usable_area; |
229 | wlr_log(L_DEBUG, "Usable area for ws: %dx%d@%d,%d", | 212 | wlr_log(WLR_DEBUG, "Usable area for ws: %dx%d@%d,%d", |
230 | area->width, area->height, area->x, area->y); | 213 | area->width, area->height, area->x, area->y); |
231 | remove_gaps(workspace); | 214 | remove_gaps(workspace); |
232 | workspace->width = area->width; | 215 | workspace->width = area->width; |
@@ -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(L_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(L_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,48 +256,40 @@ 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) { |
317 | if (c->current_gaps == 0) { | 291 | if (c->current_gaps == 0) { |
318 | wlr_log(L_DEBUG, "Removing gaps: not gapped: %p", c); | 292 | wlr_log(WLR_DEBUG, "Removing gaps: not gapped: %p", c); |
319 | return; | 293 | return; |
320 | } | 294 | } |
321 | 295 | ||
@@ -326,12 +300,12 @@ void remove_gaps(struct sway_container *c) { | |||
326 | 300 | ||
327 | c->current_gaps = 0; | 301 | c->current_gaps = 0; |
328 | 302 | ||
329 | wlr_log(L_DEBUG, "Removing gaps %p", c); | 303 | wlr_log(WLR_DEBUG, "Removing gaps %p", c); |
330 | } | 304 | } |
331 | 305 | ||
332 | void add_gaps(struct sway_container *c) { | 306 | void add_gaps(struct sway_container *c) { |
333 | if (c->current_gaps > 0 || c->type == C_CONTAINER) { | 307 | if (c->current_gaps > 0 || c->type == C_CONTAINER) { |
334 | wlr_log(L_DEBUG, "Not adding gaps: %p", c); | 308 | wlr_log(WLR_DEBUG, "Not adding gaps: %p", c); |
335 | return; | 309 | return; |
336 | } | 310 | } |
337 | 311 | ||
@@ -348,5 +322,5 @@ void add_gaps(struct sway_container *c) { | |||
348 | c->height -= 2 * gaps; | 322 | c->height -= 2 * gaps; |
349 | c->current_gaps = gaps; | 323 | c->current_gaps = gaps; |
350 | 324 | ||
351 | wlr_log(L_DEBUG, "Adding gaps: %p", c); | 325 | wlr_log(WLR_DEBUG, "Adding gaps: %p", c); |
352 | } | 326 | } |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 6f6137c4..4f743c40 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -11,11 +11,15 @@ | |||
11 | #include "cairo.h" | 11 | #include "cairo.h" |
12 | #include "pango.h" | 12 | #include "pango.h" |
13 | #include "sway/config.h" | 13 | #include "sway/config.h" |
14 | #include "sway/desktop.h" | ||
15 | #include "sway/desktop/transaction.h" | ||
14 | #include "sway/input/input-manager.h" | 16 | #include "sway/input/input-manager.h" |
15 | #include "sway/input/seat.h" | 17 | #include "sway/input/seat.h" |
16 | #include "sway/ipc-server.h" | 18 | #include "sway/ipc-server.h" |
17 | #include "sway/output.h" | 19 | #include "sway/output.h" |
20 | #include "sway/scratchpad.h" | ||
18 | #include "sway/server.h" | 21 | #include "sway/server.h" |
22 | #include "sway/tree/arrange.h" | ||
19 | #include "sway/tree/layout.h" | 23 | #include "sway/tree/layout.h" |
20 | #include "sway/tree/view.h" | 24 | #include "sway/tree/view.h" |
21 | #include "sway/tree/workspace.h" | 25 | #include "sway/tree/workspace.h" |
@@ -28,7 +32,7 @@ static list_t *get_bfs_queue() { | |||
28 | if (!bfs_queue) { | 32 | if (!bfs_queue) { |
29 | bfs_queue = create_list(); | 33 | bfs_queue = create_list(); |
30 | if (!bfs_queue) { | 34 | if (!bfs_queue) { |
31 | wlr_log(L_ERROR, "could not allocate list for bfs queue"); | 35 | wlr_log(WLR_ERROR, "could not allocate list for bfs queue"); |
32 | return NULL; | 36 | return NULL; |
33 | } | 37 | } |
34 | } | 38 | } |
@@ -151,18 +155,11 @@ void container_free(struct sway_container *cont) { | |||
151 | return; | 155 | return; |
152 | } | 156 | } |
153 | free(cont->name); | 157 | free(cont->name); |
158 | free(cont->formatted_title); | ||
154 | wlr_texture_destroy(cont->title_focused); | 159 | wlr_texture_destroy(cont->title_focused); |
155 | wlr_texture_destroy(cont->title_focused_inactive); | 160 | wlr_texture_destroy(cont->title_focused_inactive); |
156 | wlr_texture_destroy(cont->title_unfocused); | 161 | wlr_texture_destroy(cont->title_unfocused); |
157 | wlr_texture_destroy(cont->title_urgent); | 162 | wlr_texture_destroy(cont->title_urgent); |
158 | |||
159 | for (int i = 0; i < server.destroying_containers->length; ++i) { | ||
160 | if (server.destroying_containers->items[i] == cont) { | ||
161 | list_del(server.destroying_containers, i); | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | list_free(cont->instructions); | 163 | list_free(cont->instructions); |
167 | list_free(cont->children); | 164 | list_free(cont->children); |
168 | list_free(cont->current.children); | 165 | list_free(cont->current.children); |
@@ -203,16 +200,23 @@ static struct sway_container *container_workspace_destroy( | |||
203 | return NULL; | 200 | return NULL; |
204 | } | 201 | } |
205 | 202 | ||
206 | // Do not destroy this if it's the last workspace on this output | ||
207 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | 203 | struct sway_container *output = container_parent(workspace, C_OUTPUT); |
208 | if (output && output->children->length == 1) { | 204 | |
205 | // If we're destroying the output, it will be NULL here. Return the root so | ||
206 | // that it doesn't appear that the workspace has refused to be destoyed, | ||
207 | // which would leave it in a broken state with no parent. | ||
208 | if (output == NULL) { | ||
209 | return &root_container; | ||
210 | } | ||
211 | |||
212 | // Do not destroy this if it's the last workspace on this output | ||
213 | if (output->children->length == 1) { | ||
209 | return NULL; | 214 | return NULL; |
210 | } | 215 | } |
211 | 216 | ||
212 | wlr_log(L_DEBUG, "destroying workspace '%s'", workspace->name); | 217 | wlr_log(WLR_DEBUG, "destroying workspace '%s'", workspace->name); |
213 | 218 | ||
214 | struct sway_container *parent = workspace->parent; | 219 | if (!workspace_is_empty(workspace)) { |
215 | if (!workspace_is_empty(workspace) && output) { | ||
216 | // Move children to a different workspace on this output | 220 | // Move children to a different workspace on this output |
217 | struct sway_container *new_workspace = NULL; | 221 | struct sway_container *new_workspace = NULL; |
218 | for (int i = 0; i < output->children->length; i++) { | 222 | for (int i = 0; i < output->children->length; i++) { |
@@ -222,7 +226,7 @@ static struct sway_container *container_workspace_destroy( | |||
222 | } | 226 | } |
223 | } | 227 | } |
224 | 228 | ||
225 | wlr_log(L_DEBUG, "moving children to different workspace '%s' -> '%s'", | 229 | wlr_log(WLR_DEBUG, "moving children to different workspace '%s' -> '%s'", |
226 | workspace->name, new_workspace->name); | 230 | workspace->name, new_workspace->name); |
227 | for (int i = 0; i < workspace->children->length; i++) { | 231 | for (int i = 0; i < workspace->children->length; i++) { |
228 | container_move_to(workspace->children->items[i], new_workspace); | 232 | container_move_to(workspace->children->items[i], new_workspace); |
@@ -234,7 +238,7 @@ static struct sway_container *container_workspace_destroy( | |||
234 | } | 238 | } |
235 | } | 239 | } |
236 | 240 | ||
237 | return parent; | 241 | return output; |
238 | } | 242 | } |
239 | 243 | ||
240 | static struct sway_container *container_output_destroy( | 244 | static struct sway_container *container_output_destroy( |
@@ -288,7 +292,7 @@ static struct sway_container *container_output_destroy( | |||
288 | output->sway_output->swayc = NULL; | 292 | output->sway_output->swayc = NULL; |
289 | output->sway_output = NULL; | 293 | output->sway_output = NULL; |
290 | 294 | ||
291 | wlr_log(L_DEBUG, "OUTPUT: Destroying output '%s'", output->name); | 295 | wlr_log(WLR_DEBUG, "OUTPUT: Destroying output '%s'", output->name); |
292 | 296 | ||
293 | return &root_container; | 297 | return &root_container; |
294 | } | 298 | } |
@@ -315,13 +319,19 @@ static struct sway_container *container_destroy_noreaping( | |||
315 | // Workspaces will refuse to be destroyed if they're the last workspace | 319 | // Workspaces will refuse to be destroyed if they're the last workspace |
316 | // on their output. | 320 | // on their output. |
317 | if (!container_workspace_destroy(con)) { | 321 | if (!container_workspace_destroy(con)) { |
318 | wlr_log(L_ERROR, "workspace doesn't want to destroy"); | 322 | wlr_log(WLR_ERROR, "workspace doesn't want to destroy"); |
319 | return NULL; | 323 | return NULL; |
320 | } | 324 | } |
321 | } | 325 | } |
322 | 326 | ||
327 | container_end_mouse_operation(con); | ||
328 | |||
323 | con->destroying = true; | 329 | con->destroying = true; |
324 | list_add(server.destroying_containers, con); | 330 | container_set_dirty(con); |
331 | |||
332 | if (con->scratchpad) { | ||
333 | scratchpad_remove_container(con); | ||
334 | } | ||
325 | 335 | ||
326 | if (!con->parent) { | 336 | if (!con->parent) { |
327 | return NULL; | 337 | return NULL; |
@@ -342,7 +352,7 @@ bool container_reap_empty(struct sway_container *con) { | |||
342 | break; | 352 | break; |
343 | case C_WORKSPACE: | 353 | case C_WORKSPACE: |
344 | if (!workspace_is_visible(con) && workspace_is_empty(con)) { | 354 | if (!workspace_is_visible(con) && workspace_is_empty(con)) { |
345 | wlr_log(L_DEBUG, "Destroying workspace via reaper"); | 355 | wlr_log(WLR_DEBUG, "Destroying workspace via reaper"); |
346 | container_destroy_noreaping(con); | 356 | container_destroy_noreaping(con); |
347 | return true; | 357 | return true; |
348 | } | 358 | } |
@@ -435,7 +445,7 @@ struct sway_container *container_view_create(struct sway_container *sibling, | |||
435 | } | 445 | } |
436 | const char *title = view_get_title(sway_view); | 446 | const char *title = view_get_title(sway_view); |
437 | struct sway_container *swayc = container_create(C_VIEW); | 447 | struct sway_container *swayc = container_create(C_VIEW); |
438 | wlr_log(L_DEBUG, "Adding new view %p:%s to container %p %d %s", | 448 | wlr_log(WLR_DEBUG, "Adding new view %p:%s to container %p %d %s", |
439 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); | 449 | swayc, title, sibling, sibling ? sibling->type : 0, sibling->name); |
440 | // Setup values | 450 | // Setup values |
441 | swayc->sway_view = sway_view; | 451 | swayc->sway_view = sway_view; |
@@ -657,7 +667,9 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
657 | if (!workspace_is_visible(workspace)) { | 667 | if (!workspace_is_visible(workspace)) { |
658 | continue; | 668 | continue; |
659 | } | 669 | } |
660 | for (int k = 0; k < ws->floating->children->length; ++k) { | 670 | // Items at the end of the list are on top, so iterate the list in |
671 | // reverse. | ||
672 | for (int k = ws->floating->children->length - 1; k >= 0; --k) { | ||
661 | struct sway_container *floater = | 673 | struct sway_container *floater = |
662 | ws->floating->children->items[k]; | 674 | ws->floating->children->items[k]; |
663 | struct wlr_box box = { | 675 | struct wlr_box box = { |
@@ -678,16 +690,23 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
678 | void container_for_each_descendant_dfs(struct sway_container *container, | 690 | void container_for_each_descendant_dfs(struct sway_container *container, |
679 | void (*f)(struct sway_container *container, void *data), | 691 | void (*f)(struct sway_container *container, void *data), |
680 | void *data) { | 692 | void *data) { |
681 | if (container) { | 693 | if (!container) { |
682 | if (container->children) { | 694 | return; |
683 | for (int i = 0; i < container->children->length; ++i) { | 695 | } |
684 | struct sway_container *child = | 696 | if (container->children) { |
685 | container->children->items[i]; | 697 | for (int i = 0; i < container->children->length; ++i) { |
686 | container_for_each_descendant_dfs(child, f, data); | 698 | struct sway_container *child = container->children->items[i]; |
687 | } | 699 | container_for_each_descendant_dfs(child, f, data); |
688 | } | 700 | } |
689 | f(container, data); | ||
690 | } | 701 | } |
702 | if (container->type == C_WORKSPACE) { | ||
703 | struct sway_container *floating = container->sway_workspace->floating; | ||
704 | for (int i = 0; i < floating->children->length; ++i) { | ||
705 | struct sway_container *child = floating->children->items[i]; | ||
706 | container_for_each_descendant_dfs(child, f, data); | ||
707 | } | ||
708 | } | ||
709 | f(container, data); | ||
691 | } | 710 | } |
692 | 711 | ||
693 | void container_for_each_descendant_bfs(struct sway_container *con, | 712 | void container_for_each_descendant_bfs(struct sway_container *con, |
@@ -698,7 +717,7 @@ void container_for_each_descendant_bfs(struct sway_container *con, | |||
698 | } | 717 | } |
699 | 718 | ||
700 | if (queue == NULL) { | 719 | if (queue == NULL) { |
701 | wlr_log(L_ERROR, "could not allocate list"); | 720 | wlr_log(WLR_ERROR, "could not allocate list"); |
702 | return; | 721 | return; |
703 | } | 722 | } |
704 | 723 | ||
@@ -782,7 +801,7 @@ static void update_title_texture(struct sway_container *con, | |||
782 | 801 | ||
783 | double scale = output->sway_output->wlr_output->scale; | 802 | double scale = output->sway_output->wlr_output->scale; |
784 | int width = 0; | 803 | int width = 0; |
785 | int height = config->font_height * scale; | 804 | int height = con->title_height * scale; |
786 | 805 | ||
787 | cairo_t *c = cairo_create(NULL); | 806 | cairo_t *c = cairo_create(NULL); |
788 | get_text_size(c, config->font, &width, NULL, scale, config->pango_markup, | 807 | get_text_size(c, config->font, &width, NULL, scale, config->pango_markup, |
@@ -935,11 +954,15 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
935 | container_add_child(workspace->sway_workspace->floating, container); | 954 | container_add_child(workspace->sway_workspace->floating, container); |
936 | if (container->type == C_VIEW) { | 955 | if (container->type == C_VIEW) { |
937 | view_init_floating(container->sway_view); | 956 | view_init_floating(container->sway_view); |
957 | view_set_tiled(container->sway_view, false); | ||
938 | } | 958 | } |
939 | seat_set_focus(seat, seat_get_focus_inactive(seat, container)); | 959 | seat_set_focus(seat, seat_get_focus_inactive(seat, container)); |
940 | container_reap_empty_recursive(workspace); | 960 | container_reap_empty_recursive(workspace); |
941 | } else { | 961 | } else { |
942 | // Returning to tiled | 962 | // Returning to tiled |
963 | if (container->scratchpad) { | ||
964 | scratchpad_remove_container(container); | ||
965 | } | ||
943 | container_remove_child(container); | 966 | container_remove_child(container); |
944 | container_add_child(workspace, container); | 967 | container_add_child(workspace, container); |
945 | container->width = container->parent->width; | 968 | container->width = container->parent->width; |
@@ -951,6 +974,8 @@ void container_set_floating(struct sway_container *container, bool enable) { | |||
951 | container_reap_empty_recursive(workspace->sway_workspace->floating); | 974 | container_reap_empty_recursive(workspace->sway_workspace->floating); |
952 | } | 975 | } |
953 | 976 | ||
977 | container_end_mouse_operation(container); | ||
978 | |||
954 | ipc_event_window(container, "floating"); | 979 | ipc_event_window(container, "floating"); |
955 | } | 980 | } |
956 | 981 | ||
@@ -963,9 +988,14 @@ void container_set_geometry_from_floating_view(struct sway_container *con) { | |||
963 | return; | 988 | return; |
964 | } | 989 | } |
965 | struct sway_view *view = con->sway_view; | 990 | struct sway_view *view = con->sway_view; |
966 | size_t border_width = view->border_thickness * (view->border != B_NONE); | 991 | size_t border_width = 0; |
967 | size_t top = | 992 | size_t top = 0; |
968 | view->border == B_NORMAL ? container_titlebar_height() : border_width; | 993 | |
994 | if (!view->using_csd) { | ||
995 | border_width = view->border_thickness * (view->border != B_NONE); | ||
996 | top = view->border == B_NORMAL ? | ||
997 | container_titlebar_height() : border_width; | ||
998 | } | ||
969 | 999 | ||
970 | con->x = view->x - border_width; | 1000 | con->x = view->x - border_width; |
971 | con->y = view->y - top; | 1001 | con->y = view->y - top; |
@@ -987,3 +1017,112 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
987 | box->width = container->width; | 1017 | box->width = container->width; |
988 | box->height = container->height; | 1018 | box->height = container->height; |
989 | } | 1019 | } |
1020 | |||
1021 | /** | ||
1022 | * Translate the container's position as well as all children. | ||
1023 | */ | ||
1024 | void container_floating_translate(struct sway_container *con, | ||
1025 | double x_amount, double y_amount) { | ||
1026 | con->x += x_amount; | ||
1027 | con->y += y_amount; | ||
1028 | con->current.swayc_x += x_amount; | ||
1029 | con->current.swayc_y += y_amount; | ||
1030 | if (con->type == C_VIEW) { | ||
1031 | con->sway_view->x += x_amount; | ||
1032 | con->sway_view->y += y_amount; | ||
1033 | con->current.view_x += x_amount; | ||
1034 | con->current.view_y += y_amount; | ||
1035 | } else { | ||
1036 | for (int i = 0; i < con->children->length; ++i) { | ||
1037 | struct sway_container *child = con->children->items[i]; | ||
1038 | container_floating_translate(child, x_amount, y_amount); | ||
1039 | } | ||
1040 | } | ||
1041 | } | ||
1042 | |||
1043 | /** | ||
1044 | * Choose an output for the floating container's new position. | ||
1045 | * | ||
1046 | * If the center of the container intersects an output then we'll choose that | ||
1047 | * one, otherwise we'll choose whichever output is closest to the container's | ||
1048 | * center. | ||
1049 | */ | ||
1050 | static struct sway_container *container_floating_find_output( | ||
1051 | struct sway_container *con) { | ||
1052 | double center_x = con->x + con->width / 2; | ||
1053 | double center_y = con->y + con->height / 2; | ||
1054 | struct sway_container *closest_output = NULL; | ||
1055 | double closest_distance = DBL_MAX; | ||
1056 | for (int i = 0; i < root_container.children->length; ++i) { | ||
1057 | struct sway_container *output = root_container.children->items[i]; | ||
1058 | struct wlr_box output_box; | ||
1059 | double closest_x, closest_y; | ||
1060 | container_get_box(output, &output_box); | ||
1061 | wlr_box_closest_point(&output_box, center_x, center_y, | ||
1062 | &closest_x, &closest_y); | ||
1063 | if (center_x == closest_x && center_y == closest_y) { | ||
1064 | // The center of the floating container is on this output | ||
1065 | return output; | ||
1066 | } | ||
1067 | double x_dist = closest_x - center_x; | ||
1068 | double y_dist = closest_y - center_y; | ||
1069 | double distance = x_dist * x_dist + y_dist * y_dist; | ||
1070 | if (distance < closest_distance) { | ||
1071 | closest_output = output; | ||
1072 | closest_distance = distance; | ||
1073 | } | ||
1074 | } | ||
1075 | return closest_output; | ||
1076 | } | ||
1077 | |||
1078 | void container_floating_move_to(struct sway_container *con, | ||
1079 | double lx, double ly) { | ||
1080 | if (!sway_assert(container_is_floating(con), | ||
1081 | "Expected a floating container")) { | ||
1082 | return; | ||
1083 | } | ||
1084 | desktop_damage_whole_container(con); | ||
1085 | container_floating_translate(con, lx - con->x, ly - con->y); | ||
1086 | desktop_damage_whole_container(con); | ||
1087 | struct sway_container *old_workspace = container_parent(con, C_WORKSPACE); | ||
1088 | struct sway_container *new_output = container_floating_find_output(con); | ||
1089 | if (!sway_assert(new_output, "Unable to find any output")) { | ||
1090 | return; | ||
1091 | } | ||
1092 | struct sway_container *new_workspace = | ||
1093 | output_get_active_workspace(new_output->sway_output); | ||
1094 | if (old_workspace != new_workspace) { | ||
1095 | container_remove_child(con); | ||
1096 | container_add_child(new_workspace->sway_workspace->floating, con); | ||
1097 | arrange_windows(old_workspace); | ||
1098 | arrange_windows(new_workspace); | ||
1099 | workspace_detect_urgent(old_workspace); | ||
1100 | workspace_detect_urgent(new_workspace); | ||
1101 | } | ||
1102 | } | ||
1103 | |||
1104 | void container_set_dirty(struct sway_container *container) { | ||
1105 | if (container->dirty) { | ||
1106 | return; | ||
1107 | } | ||
1108 | container->dirty = true; | ||
1109 | list_add(server.dirty_containers, container); | ||
1110 | } | ||
1111 | |||
1112 | static bool find_urgent_iterator(struct sway_container *con, | ||
1113 | void *data) { | ||
1114 | return con->type == C_VIEW && view_is_urgent(con->sway_view); | ||
1115 | } | ||
1116 | |||
1117 | bool container_has_urgent_child(struct sway_container *container) { | ||
1118 | return container_find(container, find_urgent_iterator, NULL); | ||
1119 | } | ||
1120 | |||
1121 | void container_end_mouse_operation(struct sway_container *container) { | ||
1122 | struct sway_seat *seat; | ||
1123 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
1124 | if (seat->op_container == container) { | ||
1125 | seat_end_mouse_operation(seat); | ||
1126 | } | ||
1127 | } | ||
1128 | } | ||
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 14631ad4..a2be0ef3 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) { |
@@ -41,6 +42,7 @@ void layout_init(void) { | |||
41 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); | 42 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); |
42 | wl_list_init(&root_container.sway_root->drag_icons); | 43 | wl_list_init(&root_container.sway_root->drag_icons); |
43 | wl_signal_init(&root_container.sway_root->events.new_container); | 44 | wl_signal_init(&root_container.sway_root->events.new_container); |
45 | root_container.sway_root->scratchpad = create_list(); | ||
44 | 46 | ||
45 | root_container.sway_root->output_layout_change.notify = | 47 | root_container.sway_root->output_layout_change.notify = |
46 | output_layout_handle_change; | 48 | output_layout_handle_change; |
@@ -101,7 +103,7 @@ void container_insert_child(struct sway_container *parent, | |||
101 | if (old_parent) { | 103 | if (old_parent) { |
102 | container_remove_child(child); | 104 | container_remove_child(child); |
103 | } | 105 | } |
104 | wlr_log(L_DEBUG, "Inserting id:%zd at index %d", child->id, i); | 106 | wlr_log(WLR_DEBUG, "Inserting id:%zd at index %d", child->id, i); |
105 | list_insert(parent->children, i, child); | 107 | list_insert(parent->children, i, child); |
106 | child->parent = parent; | 108 | child->parent = parent; |
107 | container_handle_fullscreen_reparent(child, old_parent); | 109 | container_handle_fullscreen_reparent(child, old_parent); |
@@ -127,13 +129,17 @@ struct sway_container *container_add_sibling(struct sway_container *fixed, | |||
127 | 129 | ||
128 | void container_add_child(struct sway_container *parent, | 130 | void container_add_child(struct sway_container *parent, |
129 | struct sway_container *child) { | 131 | struct sway_container *child) { |
130 | wlr_log(L_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", | 132 | wlr_log(WLR_DEBUG, "Adding %p (%d, %fx%f) to %p (%d, %fx%f)", |
131 | child, child->type, child->width, child->height, | 133 | child, child->type, child->width, child->height, |
132 | parent, parent->type, parent->width, parent->height); | 134 | parent, parent->type, parent->width, parent->height); |
133 | struct sway_container *old_parent = child->parent; | 135 | struct sway_container *old_parent = child->parent; |
134 | list_add(parent->children, child); | 136 | list_add(parent->children, child); |
135 | child->parent = parent; | 137 | child->parent = parent; |
136 | container_handle_fullscreen_reparent(child, old_parent); | 138 | container_handle_fullscreen_reparent(child, old_parent); |
139 | if (old_parent) { | ||
140 | container_set_dirty(old_parent); | ||
141 | } | ||
142 | container_set_dirty(child); | ||
137 | } | 143 | } |
138 | 144 | ||
139 | struct sway_container *container_remove_child(struct sway_container *child) { | 145 | struct sway_container *container_remove_child(struct sway_container *child) { |
@@ -152,6 +158,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { | |||
152 | child->parent = NULL; | 158 | child->parent = NULL; |
153 | container_notify_subtree_changed(parent); | 159 | container_notify_subtree_changed(parent); |
154 | 160 | ||
161 | container_set_dirty(parent); | ||
162 | container_set_dirty(child); | ||
163 | |||
155 | return parent; | 164 | return parent; |
156 | } | 165 | } |
157 | 166 | ||
@@ -168,7 +177,12 @@ void container_move_to(struct sway_container *container, | |||
168 | struct sway_container *old_parent = container_remove_child(container); | 177 | struct sway_container *old_parent = container_remove_child(container); |
169 | container->width = container->height = 0; | 178 | container->width = container->height = 0; |
170 | container->saved_width = container->saved_height = 0; | 179 | container->saved_width = container->saved_height = 0; |
171 | struct sway_container *new_parent; | 180 | |
181 | struct sway_container *new_parent, *new_parent_focus; | ||
182 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
183 | |||
184 | // Get the focus of the destination before we change it. | ||
185 | new_parent_focus = seat_get_focus_inactive(seat, destination); | ||
172 | if (destination->type == C_VIEW) { | 186 | if (destination->type == C_VIEW) { |
173 | new_parent = container_add_sibling(destination, container); | 187 | new_parent = container_add_sibling(destination, container); |
174 | } else { | 188 | } else { |
@@ -176,17 +190,20 @@ void container_move_to(struct sway_container *container, | |||
176 | container_add_child(destination, container); | 190 | container_add_child(destination, container); |
177 | } | 191 | } |
178 | wl_signal_emit(&container->events.reparent, old_parent); | 192 | wl_signal_emit(&container->events.reparent, old_parent); |
193 | |||
179 | if (container->type == C_WORKSPACE) { | 194 | if (container->type == C_WORKSPACE) { |
180 | // If moving a workspace to a new output, maybe create a new workspace | 195 | // If moving a workspace to a new output, maybe create a new workspace |
181 | // on the previous output | 196 | // on the previous output |
182 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
183 | if (old_parent->children->length == 0) { | 197 | if (old_parent->children->length == 0) { |
184 | char *ws_name = workspace_next_name(old_parent->name); | 198 | char *ws_name = workspace_next_name(old_parent->name); |
185 | struct sway_container *ws = | 199 | struct sway_container *ws = workspace_create(old_parent, ws_name); |
186 | workspace_create(old_parent, ws_name); | ||
187 | free(ws_name); | 200 | free(ws_name); |
188 | seat_set_focus(seat, ws); | 201 | seat_set_focus(seat, ws); |
189 | } | 202 | } |
203 | |||
204 | // Try to remove an empty workspace from the destination output. | ||
205 | container_reap_empty_recursive(new_parent_focus); | ||
206 | |||
190 | container_sort_workspaces(new_parent); | 207 | container_sort_workspaces(new_parent); |
191 | seat_set_focus(seat, new_parent); | 208 | seat_set_focus(seat, new_parent); |
192 | workspace_output_raise_priority(container, old_parent, new_parent); | 209 | workspace_output_raise_priority(container, old_parent, new_parent); |
@@ -216,6 +233,17 @@ void container_move_to(struct sway_container *container, | |||
216 | } | 233 | } |
217 | } | 234 | } |
218 | } | 235 | } |
236 | // Update workspace urgent state | ||
237 | struct sway_container *old_workspace = old_parent; | ||
238 | if (old_workspace->type != C_WORKSPACE) { | ||
239 | old_workspace = container_parent(old_workspace, C_WORKSPACE); | ||
240 | } | ||
241 | if (new_workspace != old_workspace) { | ||
242 | workspace_detect_urgent(new_workspace); | ||
243 | if (old_workspace) { | ||
244 | workspace_detect_urgent(old_workspace); | ||
245 | } | ||
246 | } | ||
219 | } | 247 | } |
220 | 248 | ||
221 | static bool sway_dir_to_wlr(enum movement_direction dir, | 249 | static bool sway_dir_to_wlr(enum movement_direction dir, |
@@ -311,13 +339,13 @@ static void move_out_of_tabs_stacks(struct sway_container *container, | |||
311 | int offs) { | 339 | int offs) { |
312 | if (container->parent == current->parent | 340 | if (container->parent == current->parent |
313 | && current->parent->children->length == 1) { | 341 | && current->parent->children->length == 1) { |
314 | wlr_log(L_DEBUG, "Changing layout of %zd", current->parent->id); | 342 | wlr_log(WLR_DEBUG, "Changing layout of %zd", current->parent->id); |
315 | current->parent->layout = move_dir == | 343 | current->parent->layout = move_dir == |
316 | MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; | 344 | MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT; |
317 | return; | 345 | return; |
318 | } | 346 | } |
319 | 347 | ||
320 | wlr_log(L_DEBUG, "Moving out of tab/stack into a split"); | 348 | wlr_log(WLR_DEBUG, "Moving out of tab/stack into a split"); |
321 | bool is_workspace = current->parent->type == C_WORKSPACE; | 349 | bool is_workspace = current->parent->type == C_WORKSPACE; |
322 | struct sway_container *new_parent = container_split(current->parent, | 350 | struct sway_container *new_parent = container_split(current->parent, |
323 | move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); | 351 | move_dir == MOVE_LEFT || move_dir == MOVE_RIGHT ? L_HORIZ : L_VERT); |
@@ -362,7 +390,7 @@ void container_move(struct sway_container *container, | |||
362 | } | 390 | } |
363 | 391 | ||
364 | parent = current->parent; | 392 | parent = current->parent; |
365 | wlr_log(L_DEBUG, "Visiting %p %s '%s'", current, | 393 | wlr_log(WLR_DEBUG, "Visiting %p %s '%s'", current, |
366 | container_type_to_str(current->type), current->name); | 394 | container_type_to_str(current->type), current->name); |
367 | 395 | ||
368 | int index = index_child(current); | 396 | int index = index_child(current); |
@@ -380,12 +408,12 @@ void container_move(struct sway_container *container, | |||
380 | root_container.sway_root->output_layout, wlr_dir, | 408 | root_container.sway_root->output_layout, wlr_dir, |
381 | current->sway_output->wlr_output, ref_lx, ref_ly); | 409 | current->sway_output->wlr_output, ref_lx, ref_ly); |
382 | if (!next) { | 410 | if (!next) { |
383 | wlr_log(L_DEBUG, "Hit edge of output, nowhere else to go"); | 411 | wlr_log(WLR_DEBUG, "Hit edge of output, nowhere else to go"); |
384 | return; | 412 | return; |
385 | } | 413 | } |
386 | struct sway_output *next_output = next->data; | 414 | struct sway_output *next_output = next->data; |
387 | current = next_output->swayc; | 415 | current = next_output->swayc; |
388 | wlr_log(L_DEBUG, "Selected next output (%s)", current->name); | 416 | wlr_log(WLR_DEBUG, "Selected next output (%s)", current->name); |
389 | // Select workspace and get outta here | 417 | // Select workspace and get outta here |
390 | current = seat_get_focus_inactive( | 418 | current = seat_get_focus_inactive( |
391 | config->handler_context.seat, current); | 419 | config->handler_context.seat, current); |
@@ -398,20 +426,20 @@ void container_move(struct sway_container *container, | |||
398 | case C_WORKSPACE: | 426 | case C_WORKSPACE: |
399 | if (!is_parallel(current->layout, move_dir)) { | 427 | if (!is_parallel(current->layout, move_dir)) { |
400 | if (current->children->length >= 2) { | 428 | if (current->children->length >= 2) { |
401 | wlr_log(L_DEBUG, "Rejiggering the workspace (%d kiddos)", | 429 | wlr_log(WLR_DEBUG, "Rejiggering the workspace (%d kiddos)", |
402 | current->children->length); | 430 | current->children->length); |
403 | workspace_rejigger(current, container, move_dir); | 431 | workspace_rejigger(current, container, move_dir); |
404 | return; | 432 | return; |
405 | } else { | 433 | } else { |
406 | wlr_log(L_DEBUG, "Selecting output"); | 434 | wlr_log(WLR_DEBUG, "Selecting output"); |
407 | current = current->parent; | 435 | current = current->parent; |
408 | } | 436 | } |
409 | } else if (current->layout == L_TABBED | 437 | } else if (current->layout == L_TABBED |
410 | || current->layout == L_STACKED) { | 438 | || current->layout == L_STACKED) { |
411 | wlr_log(L_DEBUG, "Rejiggering out of tabs/stacks"); | 439 | wlr_log(WLR_DEBUG, "Rejiggering out of tabs/stacks"); |
412 | workspace_rejigger(current, container, move_dir); | 440 | workspace_rejigger(current, container, move_dir); |
413 | } else { | 441 | } else { |
414 | wlr_log(L_DEBUG, "Selecting output"); | 442 | wlr_log(WLR_DEBUG, "Selecting output"); |
415 | current = current->parent; | 443 | current = current->parent; |
416 | } | 444 | } |
417 | break; | 445 | break; |
@@ -427,11 +455,11 @@ void container_move(struct sway_container *container, | |||
427 | move_dir, offs); | 455 | move_dir, offs); |
428 | return; | 456 | return; |
429 | } else { | 457 | } else { |
430 | wlr_log(L_DEBUG, "Hit limit, selecting parent"); | 458 | wlr_log(WLR_DEBUG, "Hit limit, selecting parent"); |
431 | current = current->parent; | 459 | current = current->parent; |
432 | } | 460 | } |
433 | } else { | 461 | } else { |
434 | wlr_log(L_DEBUG, "Hit limit, " | 462 | wlr_log(WLR_DEBUG, "Hit limit, " |
435 | "promoting descendant to sibling"); | 463 | "promoting descendant to sibling"); |
436 | // Special case | 464 | // Special case |
437 | container_insert_child(current->parent, container, | 465 | container_insert_child(current->parent, container, |
@@ -441,14 +469,14 @@ void container_move(struct sway_container *container, | |||
441 | } | 469 | } |
442 | } else { | 470 | } else { |
443 | sibling = parent->children->items[index + offs]; | 471 | sibling = parent->children->items[index + offs]; |
444 | wlr_log(L_DEBUG, "Selecting sibling id:%zd", sibling->id); | 472 | wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); |
445 | } | 473 | } |
446 | } else if (parent->layout == L_TABBED | 474 | } else if (parent->layout == L_TABBED |
447 | || parent->layout == L_STACKED) { | 475 | || parent->layout == L_STACKED) { |
448 | move_out_of_tabs_stacks(container, current, move_dir, offs); | 476 | move_out_of_tabs_stacks(container, current, move_dir, offs); |
449 | return; | 477 | return; |
450 | } else { | 478 | } else { |
451 | wlr_log(L_DEBUG, "Moving up to find a parallel container"); | 479 | wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); |
452 | current = current->parent; | 480 | current = current->parent; |
453 | } | 481 | } |
454 | break; | 482 | break; |
@@ -467,11 +495,11 @@ void container_move(struct sway_container *container, | |||
467 | switch (sibling->type) { | 495 | switch (sibling->type) { |
468 | case C_VIEW: | 496 | case C_VIEW: |
469 | if (sibling->parent == container->parent) { | 497 | if (sibling->parent == container->parent) { |
470 | wlr_log(L_DEBUG, "Swapping siblings"); | 498 | wlr_log(WLR_DEBUG, "Swapping siblings"); |
471 | sibling->parent->children->items[index + offs] = container; | 499 | sibling->parent->children->items[index + offs] = container; |
472 | sibling->parent->children->items[index] = sibling; | 500 | sibling->parent->children->items[index] = sibling; |
473 | } else { | 501 | } else { |
474 | wlr_log(L_DEBUG, "Promoting to sibling of cousin"); | 502 | wlr_log(WLR_DEBUG, "Promoting to sibling of cousin"); |
475 | container_insert_child(sibling->parent, container, | 503 | container_insert_child(sibling->parent, container, |
476 | index_child(sibling) + (offs > 0 ? 0 : 1)); | 504 | index_child(sibling) + (offs > 0 ? 0 : 1)); |
477 | container->width = container->height = 0; | 505 | container->width = container->height = 0; |
@@ -482,31 +510,31 @@ void container_move(struct sway_container *container, | |||
482 | case C_CONTAINER: | 510 | case C_CONTAINER: |
483 | if (is_parallel(sibling->layout, move_dir)) { | 511 | if (is_parallel(sibling->layout, move_dir)) { |
484 | int limit = container_limit(sibling, invert_movement(move_dir)); | 512 | int limit = container_limit(sibling, invert_movement(move_dir)); |
485 | wlr_log(L_DEBUG, "limit: %d", limit); | 513 | wlr_log(WLR_DEBUG, "limit: %d", limit); |
486 | wlr_log(L_DEBUG, | 514 | wlr_log(WLR_DEBUG, |
487 | "Reparenting container (parallel) to index %d " | 515 | "Reparenting container (parallel) to index %d " |
488 | "(move dir: %d)", limit, move_dir); | 516 | "(move dir: %d)", limit, move_dir); |
489 | container_insert_child(sibling, container, limit); | 517 | container_insert_child(sibling, container, limit); |
490 | container->width = container->height = 0; | 518 | container->width = container->height = 0; |
491 | sibling = NULL; | 519 | sibling = NULL; |
492 | } else { | 520 | } else { |
493 | wlr_log(L_DEBUG, "Reparenting container (perpendicular)"); | 521 | wlr_log(WLR_DEBUG, "Reparenting container (perpendicular)"); |
494 | struct sway_container *focus_inactive = seat_get_focus_inactive( | 522 | struct sway_container *focus_inactive = seat_get_focus_inactive( |
495 | config->handler_context.seat, sibling); | 523 | config->handler_context.seat, sibling); |
496 | if (focus_inactive && focus_inactive != sibling) { | 524 | if (focus_inactive && focus_inactive != sibling) { |
497 | while (focus_inactive->parent != sibling) { | 525 | while (focus_inactive->parent != sibling) { |
498 | focus_inactive = focus_inactive->parent; | 526 | focus_inactive = focus_inactive->parent; |
499 | } | 527 | } |
500 | wlr_log(L_DEBUG, "Focus inactive: id:%zd", | 528 | wlr_log(WLR_DEBUG, "Focus inactive: id:%zd", |
501 | focus_inactive->id); | 529 | focus_inactive->id); |
502 | sibling = focus_inactive; | 530 | sibling = focus_inactive; |
503 | continue; | 531 | continue; |
504 | } else if (sibling->children->length) { | 532 | } else if (sibling->children->length) { |
505 | wlr_log(L_DEBUG, "No focus-inactive, adding arbitrarily"); | 533 | wlr_log(WLR_DEBUG, "No focus-inactive, adding arbitrarily"); |
506 | container_remove_child(container); | 534 | container_remove_child(container); |
507 | container_add_sibling(sibling->children->items[0], container); | 535 | container_add_sibling(sibling->children->items[0], container); |
508 | } else { | 536 | } else { |
509 | wlr_log(L_DEBUG, "No kiddos, adding container alone"); | 537 | wlr_log(WLR_DEBUG, "No kiddos, adding container alone"); |
510 | container_remove_child(container); | 538 | container_remove_child(container); |
511 | container_add_child(sibling, container); | 539 | container_add_child(sibling, container); |
512 | } | 540 | } |
@@ -539,7 +567,10 @@ void container_move(struct sway_container *container, | |||
539 | } | 567 | } |
540 | if (last_ws && next_ws && last_ws != next_ws) { | 568 | if (last_ws && next_ws && last_ws != next_ws) { |
541 | ipc_event_workspace(last_ws, container, "focus"); | 569 | ipc_event_workspace(last_ws, container, "focus"); |
570 | workspace_detect_urgent(last_ws); | ||
571 | workspace_detect_urgent(next_ws); | ||
542 | } | 572 | } |
573 | container_end_mouse_operation(container); | ||
543 | } | 574 | } |
544 | 575 | ||
545 | enum sway_container_layout container_get_default_layout( | 576 | enum sway_container_layout container_get_default_layout( |
@@ -603,7 +634,7 @@ static struct sway_container *get_swayc_in_output_direction( | |||
603 | } | 634 | } |
604 | 635 | ||
605 | if (ws == NULL) { | 636 | if (ws == NULL) { |
606 | wlr_log(L_ERROR, "got an output without a workspace"); | 637 | wlr_log(WLR_ERROR, "got an output without a workspace"); |
607 | return NULL; | 638 | return NULL; |
608 | } | 639 | } |
609 | 640 | ||
@@ -775,7 +806,7 @@ struct sway_container *container_get_in_direction( | |||
775 | } else { | 806 | } else { |
776 | struct sway_container *desired_con = | 807 | struct sway_container *desired_con = |
777 | parent->children->items[desired]; | 808 | parent->children->items[desired]; |
778 | wlr_log(L_DEBUG, | 809 | wlr_log(WLR_DEBUG, |
779 | "cont %d-%p dir %i sibling %d: %p", idx, | 810 | "cont %d-%p dir %i sibling %d: %p", idx, |
780 | container, dir, desired, desired_con); | 811 | container, dir, desired, desired_con); |
781 | return seat_get_focus_inactive_view(seat, desired_con); | 812 | return seat_get_focus_inactive_view(seat, desired_con); |
@@ -840,7 +871,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
840 | 871 | ||
841 | struct sway_container *cont = container_create(C_CONTAINER); | 872 | struct sway_container *cont = container_create(C_CONTAINER); |
842 | 873 | ||
843 | wlr_log(L_DEBUG, "creating container %p around %p", cont, child); | 874 | wlr_log(WLR_DEBUG, "creating container %p around %p", cont, child); |
844 | 875 | ||
845 | remove_gaps(child); | 876 | remove_gaps(child); |
846 | 877 | ||
@@ -888,7 +919,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
888 | void container_recursive_resize(struct sway_container *container, | 919 | void container_recursive_resize(struct sway_container *container, |
889 | double amount, enum resize_edge edge) { | 920 | double amount, enum resize_edge edge) { |
890 | bool layout_match = true; | 921 | bool layout_match = true; |
891 | wlr_log(L_DEBUG, "Resizing %p with amount: %f", container, amount); | 922 | wlr_log(WLR_DEBUG, "Resizing %p with amount: %f", container, amount); |
892 | if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { | 923 | if (edge == RESIZE_EDGE_LEFT || edge == RESIZE_EDGE_RIGHT) { |
893 | container->width += amount; | 924 | container->width += amount; |
894 | layout_match = container->layout == L_HORIZ; | 925 | layout_match = container->layout == L_HORIZ; |
@@ -978,7 +1009,7 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
978 | return; | 1009 | return; |
979 | } | 1010 | } |
980 | 1011 | ||
981 | wlr_log(L_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); | 1012 | wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); |
982 | 1013 | ||
983 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; | 1014 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; |
984 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; | 1015 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; |
diff --git a/sway/tree/output.c b/sway/tree/output.c index e2927cdb..da535c18 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -43,11 +43,11 @@ struct sway_container *output_create( | |||
43 | 43 | ||
44 | if (strcasecmp(name, cur->name) == 0 || | 44 | if (strcasecmp(name, cur->name) == 0 || |
45 | strcasecmp(identifier, cur->name) == 0) { | 45 | strcasecmp(identifier, cur->name) == 0) { |
46 | wlr_log(L_DEBUG, "Matched output config for %s", name); | 46 | wlr_log(WLR_DEBUG, "Matched output config for %s", name); |
47 | oc = cur; | 47 | oc = cur; |
48 | } | 48 | } |
49 | if (strcasecmp("*", cur->name) == 0) { | 49 | if (strcasecmp("*", cur->name) == 0) { |
50 | wlr_log(L_DEBUG, "Matched wildcard output config for %s", name); | 50 | wlr_log(WLR_DEBUG, "Matched wildcard output config for %s", name); |
51 | all = cur; | 51 | all = cur; |
52 | } | 52 | } |
53 | 53 | ||
@@ -86,7 +86,7 @@ struct sway_container *output_create( | |||
86 | if (!output->children->length) { | 86 | if (!output->children->length) { |
87 | // Create workspace | 87 | // Create workspace |
88 | char *ws_name = workspace_next_name(output->name); | 88 | char *ws_name = workspace_next_name(output->name); |
89 | wlr_log(L_DEBUG, "Creating default workspace %s", ws_name); | 89 | wlr_log(WLR_DEBUG, "Creating default workspace %s", ws_name); |
90 | struct sway_container *ws = workspace_create(output, ws_name); | 90 | struct sway_container *ws = workspace_create(output, ws_name); |
91 | // Set each seat's focus if not already set | 91 | // Set each seat's focus if not already set |
92 | struct sway_seat *seat = NULL; | 92 | struct sway_seat *seat = NULL; |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 24fb6864..a55c8a29 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -26,6 +26,7 @@ void view_init(struct sway_view *view, enum sway_view_type type, | |||
26 | view->impl = impl; | 26 | view->impl = impl; |
27 | view->executed_criteria = create_list(); | 27 | view->executed_criteria = create_list(); |
28 | view->marks = create_list(); | 28 | view->marks = create_list(); |
29 | view->allow_request_urgent = true; | ||
29 | wl_signal_init(&view->events.unmap); | 30 | wl_signal_init(&view->events.unmap); |
30 | } | 31 | } |
31 | 32 | ||
@@ -141,6 +142,19 @@ const char *view_get_shell(struct sway_view *view) { | |||
141 | return "unknown"; | 142 | return "unknown"; |
142 | } | 143 | } |
143 | 144 | ||
145 | void view_get_constraints(struct sway_view *view, double *min_width, | ||
146 | double *max_width, double *min_height, double *max_height) { | ||
147 | if (view->impl->get_constraints) { | ||
148 | view->impl->get_constraints(view, | ||
149 | min_width, max_width, min_height, max_height); | ||
150 | } else { | ||
151 | *min_width = DBL_MIN; | ||
152 | *max_width = DBL_MAX; | ||
153 | *min_height = DBL_MIN; | ||
154 | *max_height = DBL_MAX; | ||
155 | } | ||
156 | } | ||
157 | |||
144 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | 158 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, |
145 | int height) { | 159 | int height) { |
146 | if (view->impl->configure) { | 160 | if (view->impl->configure) { |
@@ -151,12 +165,43 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
151 | 165 | ||
152 | void view_init_floating(struct sway_view *view) { | 166 | void view_init_floating(struct sway_view *view) { |
153 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 167 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
154 | int max_width = ws->width * 0.6666; | 168 | int min_width, min_height; |
155 | int max_height = ws->height * 0.6666; | 169 | int max_width, max_height; |
156 | view->width = | 170 | |
157 | view->natural_width > max_width ? max_width : view->natural_width; | 171 | if (config->floating_minimum_width == -1) { // no minimum |
158 | view->height = | 172 | min_width = 0; |
159 | view->natural_height > max_height ? max_height : view->natural_height; | 173 | } else if (config->floating_minimum_width == 0) { // automatic |
174 | min_width = 75; | ||
175 | } else { | ||
176 | min_width = config->floating_minimum_width; | ||
177 | } | ||
178 | |||
179 | if (config->floating_minimum_height == -1) { // no minimum | ||
180 | min_height = 0; | ||
181 | } else if (config->floating_minimum_height == 0) { // automatic | ||
182 | min_height = 50; | ||
183 | } else { | ||
184 | min_height = config->floating_minimum_height; | ||
185 | } | ||
186 | |||
187 | if (config->floating_maximum_width == -1) { // no maximum | ||
188 | max_width = INT_MAX; | ||
189 | } else if (config->floating_maximum_width == 0) { // automatic | ||
190 | max_width = ws->width * 0.6666; | ||
191 | } else { | ||
192 | max_width = config->floating_maximum_width; | ||
193 | } | ||
194 | |||
195 | if (config->floating_maximum_height == -1) { // no maximum | ||
196 | max_height = INT_MAX; | ||
197 | } else if (config->floating_maximum_height == 0) { // automatic | ||
198 | max_height = ws->height * 0.6666; | ||
199 | } else { | ||
200 | max_height = config->floating_maximum_height; | ||
201 | } | ||
202 | |||
203 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
204 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
160 | view->x = ws->x + (ws->width - view->width) / 2; | 205 | view->x = ws->x + (ws->width - view->width) / 2; |
161 | view->y = ws->y + (ws->height - view->height) / 2; | 206 | view->y = ws->y + (ws->height - view->height) / 2; |
162 | 207 | ||
@@ -165,9 +210,6 @@ void view_init_floating(struct sway_view *view) { | |||
165 | view->border_left = view->border_right = true; | 210 | view->border_left = view->border_right = true; |
166 | 211 | ||
167 | container_set_geometry_from_floating_view(view->swayc); | 212 | container_set_geometry_from_floating_view(view->swayc); |
168 | |||
169 | // Don't maximize floating windows | ||
170 | view_set_tiled(view, false); | ||
171 | } | 213 | } |
172 | 214 | ||
173 | void view_autoconfigure(struct sway_view *view) { | 215 | void view_autoconfigure(struct sway_view *view) { |
@@ -279,7 +321,6 @@ void view_autoconfigure(struct sway_view *view) { | |||
279 | view->y = y; | 321 | view->y = y; |
280 | view->width = width; | 322 | view->width = width; |
281 | view->height = height; | 323 | view->height = height; |
282 | view_set_tiled(view, true); | ||
283 | } | 324 | } |
284 | 325 | ||
285 | void view_set_activated(struct sway_view *view, bool activated) { | 326 | void view_set_activated(struct sway_view *view, bool activated) { |
@@ -289,7 +330,15 @@ void view_set_activated(struct sway_view *view, bool activated) { | |||
289 | } | 330 | } |
290 | 331 | ||
291 | void view_set_tiled(struct sway_view *view, bool tiled) { | 332 | void view_set_tiled(struct sway_view *view, bool tiled) { |
292 | view->border = tiled ? config->border : B_NONE; | 333 | if (!tiled) { |
334 | view->using_csd = true; | ||
335 | if (view->impl->has_client_side_decorations) { | ||
336 | view->using_csd = view->impl->has_client_side_decorations(view); | ||
337 | } | ||
338 | } else { | ||
339 | view->using_csd = false; | ||
340 | } | ||
341 | |||
293 | if (view->impl->set_tiled) { | 342 | if (view->impl->set_tiled) { |
294 | view->impl->set_tiled(view, tiled); | 343 | view->impl->set_tiled(view, tiled); |
295 | } | 344 | } |
@@ -352,6 +401,8 @@ void view_set_fullscreen(struct sway_view *view, bool fullscreen) { | |||
352 | } | 401 | } |
353 | } | 402 | } |
354 | 403 | ||
404 | container_end_mouse_operation(view->swayc); | ||
405 | |||
355 | ipc_event_window(view->swayc, "fullscreen_mode"); | 406 | ipc_event_window(view->swayc, "fullscreen_mode"); |
356 | } | 407 | } |
357 | 408 | ||
@@ -467,27 +518,45 @@ void view_execute_criteria(struct sway_view *view) { | |||
467 | list_t *criterias = criteria_for_view(view, CT_COMMAND); | 518 | list_t *criterias = criteria_for_view(view, CT_COMMAND); |
468 | for (int i = 0; i < criterias->length; i++) { | 519 | for (int i = 0; i < criterias->length; i++) { |
469 | struct criteria *criteria = criterias->items[i]; | 520 | struct criteria *criteria = criterias->items[i]; |
470 | wlr_log(L_DEBUG, "Checking criteria %s", criteria->raw); | 521 | wlr_log(WLR_DEBUG, "Checking criteria %s", criteria->raw); |
471 | if (view_has_executed_criteria(view, criteria)) { | 522 | if (view_has_executed_criteria(view, criteria)) { |
472 | wlr_log(L_DEBUG, "Criteria already executed"); | 523 | wlr_log(WLR_DEBUG, "Criteria already executed"); |
473 | continue; | 524 | continue; |
474 | } | 525 | } |
475 | wlr_log(L_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", | 526 | wlr_log(WLR_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", |
476 | criteria->raw, view, criteria->cmdlist); | 527 | criteria->raw, view, criteria->cmdlist); |
528 | seat_set_focus(seat, view->swayc); | ||
477 | list_add(view->executed_criteria, criteria); | 529 | list_add(view->executed_criteria, criteria); |
478 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); | 530 | struct cmd_results *res = execute_command(criteria->cmdlist, NULL); |
479 | if (res->status != CMD_SUCCESS) { | 531 | if (res->status != CMD_SUCCESS) { |
480 | wlr_log(L_ERROR, "Command '%s' failed: %s", res->input, res->error); | 532 | wlr_log(WLR_ERROR, "Command '%s' failed: %s", res->input, res->error); |
481 | } | 533 | } |
482 | free_cmd_results(res); | 534 | free_cmd_results(res); |
483 | // view must be focused for commands to affect it, | ||
484 | // so always refocus in-between command lists | ||
485 | seat_set_focus(seat, view->swayc); | ||
486 | } | 535 | } |
487 | list_free(criterias); | 536 | list_free(criterias); |
488 | seat_set_focus(seat, prior_focus); | 537 | seat_set_focus(seat, prior_focus); |
489 | } | 538 | } |
490 | 539 | ||
540 | static bool should_focus(struct sway_view *view) { | ||
541 | // If the view is the only one in the focused workspace, it'll get focus | ||
542 | // regardless of any no_focus criteria. | ||
543 | struct sway_container *parent = view->swayc->parent; | ||
544 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
545 | if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { | ||
546 | size_t num_children = parent->children->length + | ||
547 | parent->sway_workspace->floating->children->length; | ||
548 | if (num_children == 1) { | ||
549 | return true; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | // Check no_focus criteria | ||
554 | list_t *criterias = criteria_for_view(view, CT_NO_FOCUS); | ||
555 | size_t len = criterias->length; | ||
556 | list_free(criterias); | ||
557 | return len == 0; | ||
558 | } | ||
559 | |||
491 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | 560 | void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { |
492 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | 561 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { |
493 | return; | 562 | return; |
@@ -524,7 +593,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
524 | prev_focus = target_sibling; | 593 | prev_focus = target_sibling; |
525 | target_sibling = seat_get_focus_inactive(seat, workspace); | 594 | target_sibling = seat_get_focus_inactive(seat, workspace); |
526 | } else { | 595 | } else { |
527 | // TODO: CT_ASSIGN_OUTPUT | 596 | // CT_ASSIGN_OUTPUT |
597 | struct sway_container *output = output_by_name(criteria->target); | ||
598 | if (output) { | ||
599 | prev_focus = seat_get_focus_inactive(seat, output); | ||
600 | } | ||
528 | } | 601 | } |
529 | } | 602 | } |
530 | list_free(criterias); | 603 | list_free(criterias); |
@@ -549,8 +622,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
549 | 622 | ||
550 | view->surface = wlr_surface; | 623 | view->surface = wlr_surface; |
551 | view->swayc = cont; | 624 | view->swayc = cont; |
552 | view->border = config->border; | ||
553 | view->border_thickness = config->border_thickness; | ||
554 | 625 | ||
555 | view_init_subsurfaces(view, wlr_surface); | 626 | view_init_subsurfaces(view, wlr_surface); |
556 | wl_signal_add(&wlr_surface->events.new_subsurface, | 627 | wl_signal_add(&wlr_surface->events.new_subsurface, |
@@ -561,11 +632,20 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
561 | view->container_reparent.notify = view_handle_container_reparent; | 632 | view->container_reparent.notify = view_handle_container_reparent; |
562 | 633 | ||
563 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { | 634 | if (view->impl->wants_floating && view->impl->wants_floating(view)) { |
635 | view->border = config->floating_border; | ||
636 | view->border_thickness = config->floating_border_thickness; | ||
564 | container_set_floating(view->swayc, true); | 637 | container_set_floating(view->swayc, true); |
638 | } else { | ||
639 | view->border = config->border; | ||
640 | view->border_thickness = config->border_thickness; | ||
641 | view_set_tiled(view, true); | ||
565 | } | 642 | } |
566 | 643 | ||
567 | if (prev_focus == target_sibling) { | 644 | if (should_focus(view) && prev_focus == target_sibling) { |
568 | input_manager_set_focus(input_manager, cont); | 645 | input_manager_set_focus(input_manager, cont); |
646 | if (workspace) { | ||
647 | workspace_switch(workspace); | ||
648 | } | ||
569 | } | 649 | } |
570 | 650 | ||
571 | view_update_title(view, false); | 651 | view_update_title(view, false); |
@@ -581,16 +661,27 @@ void view_unmap(struct sway_view *view) { | |||
581 | wl_list_remove(&view->surface_new_subsurface.link); | 661 | wl_list_remove(&view->surface_new_subsurface.link); |
582 | wl_list_remove(&view->container_reparent.link); | 662 | wl_list_remove(&view->container_reparent.link); |
583 | 663 | ||
664 | if (view->urgent_timer) { | ||
665 | wl_event_source_remove(view->urgent_timer); | ||
666 | view->urgent_timer = NULL; | ||
667 | } | ||
668 | |||
669 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
670 | |||
671 | struct sway_container *parent; | ||
584 | if (view->is_fullscreen) { | 672 | if (view->is_fullscreen) { |
585 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
586 | ws->sway_workspace->fullscreen = NULL; | 673 | ws->sway_workspace->fullscreen = NULL; |
587 | container_destroy(view->swayc); | 674 | parent = container_destroy(view->swayc); |
588 | 675 | ||
589 | arrange_and_commit(ws->parent); | 676 | arrange_windows(ws->parent); |
590 | } else { | 677 | } else { |
591 | struct sway_container *parent = container_destroy(view->swayc); | 678 | parent = container_destroy(view->swayc); |
592 | arrange_and_commit(parent); | 679 | arrange_windows(parent); |
680 | } | ||
681 | if (parent->type >= C_WORKSPACE) { // if the workspace still exists | ||
682 | workspace_detect_urgent(ws); | ||
593 | } | 683 | } |
684 | transaction_commit_dirty(); | ||
594 | view->surface = NULL; | 685 | view->surface = NULL; |
595 | } | 686 | } |
596 | 687 | ||
@@ -601,6 +692,8 @@ void view_update_position(struct sway_view *view, double lx, double ly) { | |||
601 | container_damage_whole(view->swayc); | 692 | container_damage_whole(view->swayc); |
602 | view->x = lx; | 693 | view->x = lx; |
603 | view->y = ly; | 694 | view->y = ly; |
695 | view->swayc->current.view_x = lx; | ||
696 | view->swayc->current.view_y = ly; | ||
604 | if (container_is_floating(view->swayc)) { | 697 | if (container_is_floating(view->swayc)) { |
605 | container_set_geometry_from_floating_view(view->swayc); | 698 | container_set_geometry_from_floating_view(view->swayc); |
606 | } | 699 | } |
@@ -614,6 +707,8 @@ void view_update_size(struct sway_view *view, int width, int height) { | |||
614 | container_damage_whole(view->swayc); | 707 | container_damage_whole(view->swayc); |
615 | view->width = width; | 708 | view->width = width; |
616 | view->height = height; | 709 | view->height = height; |
710 | view->swayc->current.view_width = width; | ||
711 | view->swayc->current.view_height = height; | ||
617 | if (container_is_floating(view->swayc)) { | 712 | if (container_is_floating(view->swayc)) { |
618 | container_set_geometry_from_floating_view(view->swayc); | 713 | container_set_geometry_from_floating_view(view->swayc); |
619 | } | 714 | } |
@@ -624,7 +719,7 @@ static void view_subsurface_create(struct sway_view *view, | |||
624 | struct wlr_subsurface *subsurface) { | 719 | struct wlr_subsurface *subsurface) { |
625 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); | 720 | struct sway_view_child *child = calloc(1, sizeof(struct sway_view_child)); |
626 | if (child == NULL) { | 721 | if (child == NULL) { |
627 | wlr_log(L_ERROR, "Allocation failed"); | 722 | wlr_log(WLR_ERROR, "Allocation failed"); |
628 | return; | 723 | return; |
629 | } | 724 | } |
630 | view_child_init(child, NULL, view, subsurface->surface); | 725 | view_child_init(child, NULL, view, subsurface->surface); |
@@ -744,8 +839,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | |||
744 | return NULL; | 839 | return NULL; |
745 | } | 840 | } |
746 | 841 | ||
747 | wlr_log(L_DEBUG, "Surface of unknown type (role %s): %p", | 842 | const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; |
748 | wlr_surface->role, wlr_surface); | 843 | wlr_log(WLR_DEBUG, "Surface of unknown type (role %s): %p", |
844 | role, wlr_surface); | ||
749 | return NULL; | 845 | return NULL; |
750 | } | 846 | } |
751 | 847 | ||
@@ -812,7 +908,7 @@ static char *escape_title(char *buffer) { | |||
812 | char *escaped_title = calloc(length + 1, sizeof(char)); | 908 | char *escaped_title = calloc(length + 1, sizeof(char)); |
813 | int result = escape_markup_text(buffer, escaped_title, length); | 909 | int result = escape_markup_text(buffer, escaped_title, length); |
814 | if (result != length) { | 910 | if (result != length) { |
815 | wlr_log(L_ERROR, "Could not escape title: %s", buffer); | 911 | wlr_log(WLR_ERROR, "Could not escape title: %s", buffer); |
816 | free(escaped_title); | 912 | free(escaped_title); |
817 | return buffer; | 913 | return buffer; |
818 | } | 914 | } |
@@ -946,7 +1042,7 @@ static void update_marks_texture(struct sway_view *view, | |||
946 | 1042 | ||
947 | double scale = output->sway_output->wlr_output->scale; | 1043 | double scale = output->sway_output->wlr_output->scale; |
948 | int width = 0; | 1044 | int width = 0; |
949 | int height = config->font_height * scale; | 1045 | int height = view->swayc->title_height * scale; |
950 | 1046 | ||
951 | cairo_t *c = cairo_create(NULL); | 1047 | cairo_t *c = cairo_create(NULL); |
952 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); | 1048 | get_text_size(c, config->font, &width, NULL, scale, false, "%s", buffer); |
@@ -995,7 +1091,7 @@ void view_update_marks_textures(struct sway_view *view) { | |||
995 | } | 1091 | } |
996 | 1092 | ||
997 | bool view_is_visible(struct sway_view *view) { | 1093 | bool view_is_visible(struct sway_view *view) { |
998 | if (!view->swayc || view->swayc->destroying) { | 1094 | if (!view->swayc || view->swayc->destroying || !view->swayc->parent) { |
999 | return false; | 1095 | return false; |
1000 | } | 1096 | } |
1001 | struct sway_container *workspace = | 1097 | struct sway_container *workspace = |
@@ -1033,3 +1129,32 @@ bool view_is_visible(struct sway_view *view) { | |||
1033 | } | 1129 | } |
1034 | return true; | 1130 | return true; |
1035 | } | 1131 | } |
1132 | |||
1133 | void view_set_urgent(struct sway_view *view, bool enable) { | ||
1134 | if (view_is_urgent(view) == enable) { | ||
1135 | return; | ||
1136 | } | ||
1137 | if (enable) { | ||
1138 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
1139 | if (seat_get_focus(seat) == view->swayc) { | ||
1140 | return; | ||
1141 | } | ||
1142 | clock_gettime(CLOCK_MONOTONIC, &view->urgent); | ||
1143 | } else { | ||
1144 | view->urgent = (struct timespec){ 0 }; | ||
1145 | if (view->urgent_timer) { | ||
1146 | wl_event_source_remove(view->urgent_timer); | ||
1147 | view->urgent_timer = NULL; | ||
1148 | } | ||
1149 | } | ||
1150 | container_damage_whole(view->swayc); | ||
1151 | |||
1152 | ipc_event_window(view->swayc, "urgent"); | ||
1153 | |||
1154 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
1155 | workspace_detect_urgent(ws); | ||
1156 | } | ||
1157 | |||
1158 | bool view_is_urgent(struct sway_view *view) { | ||
1159 | return view->urgent.tv_sec || view->urgent.tv_nsec; | ||
1160 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 651cc011..a93d9f44 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "sway/output.h" | 12 | #include "sway/output.h" |
13 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
14 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
15 | #include "sway/tree/view.h" | ||
15 | #include "sway/tree/workspace.h" | 16 | #include "sway/tree/workspace.h" |
16 | #include "list.h" | 17 | #include "list.h" |
17 | #include "log.h" | 18 | #include "log.h" |
@@ -50,7 +51,7 @@ struct sway_container *workspace_create(struct sway_container *output, | |||
50 | output = get_workspace_initial_output(name); | 51 | output = get_workspace_initial_output(name); |
51 | } | 52 | } |
52 | 53 | ||
53 | wlr_log(L_DEBUG, "Added workspace %s for output %s", name, output->name); | 54 | wlr_log(WLR_DEBUG, "Added workspace %s for output %s", name, output->name); |
54 | struct sway_container *workspace = container_create(C_WORKSPACE); | 55 | struct sway_container *workspace = container_create(C_WORKSPACE); |
55 | 56 | ||
56 | workspace->x = output->x; | 57 | workspace->x = output->x; |
@@ -108,9 +109,8 @@ static bool workspace_valid_on_output(const char *output_name, | |||
108 | } | 109 | } |
109 | 110 | ||
110 | char *workspace_next_name(const char *output_name) { | 111 | char *workspace_next_name(const char *output_name) { |
111 | wlr_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", | 112 | wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", |
112 | output_name); | 113 | output_name); |
113 | int l = 1; | ||
114 | // Scan all workspace bindings to find the next available workspace name, | 114 | // Scan all workspace bindings to find the next available workspace name, |
115 | // if none are found/available then default to a number | 115 | // if none are found/available then default to a number |
116 | struct sway_mode *mode = config->current_mode; | 116 | struct sway_mode *mode = config->current_mode; |
@@ -137,7 +137,7 @@ char *workspace_next_name(const char *output_name) { | |||
137 | while (isspace(*_target)) { | 137 | while (isspace(*_target)) { |
138 | memmove(_target, _target+1, strlen(_target+1)); | 138 | memmove(_target, _target+1, strlen(_target+1)); |
139 | } | 139 | } |
140 | wlr_log(L_DEBUG, "Got valid workspace command for target: '%s'", | 140 | wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", |
141 | _target); | 141 | _target); |
142 | 142 | ||
143 | // Make sure that the command references an actual workspace | 143 | // Make sure that the command references an actual workspace |
@@ -163,7 +163,7 @@ char *workspace_next_name(const char *output_name) { | |||
163 | temp[length - 1] = '\0'; | 163 | temp[length - 1] = '\0'; |
164 | free(_target); | 164 | free(_target); |
165 | _target = temp; | 165 | _target = temp; |
166 | wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); | 166 | wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); |
167 | 167 | ||
168 | // Make sure the workspace number doesn't already exist | 168 | // Make sure the workspace number doesn't already exist |
169 | if (workspace_by_number(_target)) { | 169 | if (workspace_by_number(_target)) { |
@@ -192,7 +192,9 @@ char *workspace_next_name(const char *output_name) { | |||
192 | order = binding->order; | 192 | order = binding->order; |
193 | free(target); | 193 | free(target); |
194 | target = _target; | 194 | target = _target; |
195 | wlr_log(L_DEBUG, "Workspace: Found free name %s", _target); | 195 | wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); |
196 | } else { | ||
197 | free(_target); | ||
196 | } | 198 | } |
197 | } | 199 | } |
198 | free(dup); | 200 | free(dup); |
@@ -203,14 +205,9 @@ char *workspace_next_name(const char *output_name) { | |||
203 | // As a fall back, get the current number of active workspaces | 205 | // As a fall back, get the current number of active workspaces |
204 | // and return that + 1 for the next workspace's name | 206 | // and return that + 1 for the next workspace's name |
205 | int ws_num = root_container.children->length; | 207 | int ws_num = root_container.children->length; |
206 | if (ws_num >= 10) { | 208 | int l = snprintf(NULL, 0, "%d", ws_num); |
207 | l = 2; | ||
208 | } else if (ws_num >= 100) { | ||
209 | l = 3; | ||
210 | } | ||
211 | char *name = malloc(l + 1); | 209 | char *name = malloc(l + 1); |
212 | if (!name) { | 210 | if (!sway_assert(name, "Cloud not allocate workspace name")) { |
213 | wlr_log(L_ERROR, "Could not allocate workspace name"); | ||
214 | return NULL; | 211 | return NULL; |
215 | } | 212 | } |
216 | sprintf(name, "%d", ws_num++); | 213 | sprintf(name, "%d", ws_num++); |
@@ -272,6 +269,9 @@ struct sway_container *workspace_by_name(const char *name) { | |||
272 | */ | 269 | */ |
273 | struct sway_container *workspace_output_prev_next_impl( | 270 | struct sway_container *workspace_output_prev_next_impl( |
274 | struct sway_container *output, bool next) { | 271 | struct sway_container *output, bool next) { |
272 | if (!output) { | ||
273 | return NULL; | ||
274 | } | ||
275 | if (!sway_assert(output->type == C_OUTPUT, | 275 | if (!sway_assert(output->type == C_OUTPUT, |
276 | "Argument must be an output, is %d", output->type)) { | 276 | "Argument must be an output, is %d", output->type)) { |
277 | return NULL; | 277 | return NULL; |
@@ -304,6 +304,9 @@ struct sway_container *workspace_output_prev_next_impl( | |||
304 | */ | 304 | */ |
305 | struct sway_container *workspace_prev_next_impl( | 305 | struct sway_container *workspace_prev_next_impl( |
306 | struct sway_container *workspace, bool next) { | 306 | struct sway_container *workspace, bool next) { |
307 | if (!workspace) { | ||
308 | return NULL; | ||
309 | } | ||
307 | if (!sway_assert(workspace->type == C_WORKSPACE, | 310 | if (!sway_assert(workspace->type == C_WORKSPACE, |
308 | "Argument must be a workspace, is %d", workspace->type)) { | 311 | "Argument must be a workspace, is %d", workspace->type)) { |
309 | return NULL; | 312 | return NULL; |
@@ -386,7 +389,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
386 | free(prev_workspace_name); | 389 | free(prev_workspace_name); |
387 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); | 390 | prev_workspace_name = malloc(strlen(active_ws->name) + 1); |
388 | if (!prev_workspace_name) { | 391 | if (!prev_workspace_name) { |
389 | wlr_log(L_ERROR, "Unable to allocate previous workspace name"); | 392 | wlr_log(WLR_ERROR, "Unable to allocate previous workspace name"); |
390 | return false; | 393 | return false; |
391 | } | 394 | } |
392 | strcpy(prev_workspace_name, active_ws->name); | 395 | strcpy(prev_workspace_name, active_ws->name); |
@@ -408,7 +411,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
408 | } | 411 | } |
409 | } | 412 | } |
410 | 413 | ||
411 | wlr_log(L_DEBUG, "Switching to workspace %p:%s", | 414 | wlr_log(WLR_DEBUG, "Switching to workspace %p:%s", |
412 | workspace, workspace->name); | 415 | workspace, workspace->name); |
413 | struct sway_container *next = seat_get_focus_inactive(seat, workspace); | 416 | struct sway_container *next = seat_get_focus_inactive(seat, workspace); |
414 | if (next == NULL) { | 417 | if (next == NULL) { |
@@ -426,7 +429,7 @@ bool workspace_switch(struct sway_container *workspace) { | |||
426 | } | 429 | } |
427 | seat_set_focus(seat, next); | 430 | seat_set_focus(seat, next); |
428 | struct sway_container *output = container_parent(workspace, C_OUTPUT); | 431 | struct sway_container *output = container_parent(workspace, C_OUTPUT); |
429 | arrange_and_commit(output); | 432 | arrange_windows(output); |
430 | return true; | 433 | return true; |
431 | } | 434 | } |
432 | 435 | ||
@@ -518,6 +521,16 @@ struct sway_container *workspace_output_get_highest_available( | |||
518 | return NULL; | 521 | return NULL; |
519 | } | 522 | } |
520 | 523 | ||
524 | void workspace_detect_urgent(struct sway_container *workspace) { | ||
525 | bool new_urgent = container_has_urgent_child(workspace); | ||
526 | |||
527 | if (workspace->sway_workspace->urgent != new_urgent) { | ||
528 | workspace->sway_workspace->urgent = new_urgent; | ||
529 | ipc_event_workspace(NULL, workspace, "urgent"); | ||
530 | container_damage_whole(workspace); | ||
531 | } | ||
532 | } | ||
533 | |||
521 | struct pid_workspace { | 534 | struct pid_workspace { |
522 | pid_t pid; | 535 | pid_t pid; |
523 | char *workspace; | 536 | char *workspace; |
@@ -540,14 +553,14 @@ struct sway_container *workspace_for_pid(pid_t pid) { | |||
540 | struct sway_container *ws = NULL; | 553 | struct sway_container *ws = NULL; |
541 | struct pid_workspace *pw = NULL; | 554 | struct pid_workspace *pw = NULL; |
542 | 555 | ||
543 | wlr_log(L_DEBUG, "Looking up workspace for pid %d", pid); | 556 | wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); |
544 | 557 | ||
545 | do { | 558 | do { |
546 | struct pid_workspace *_pw = NULL; | 559 | struct pid_workspace *_pw = NULL; |
547 | wl_list_for_each(_pw, &pid_workspaces, link) { | 560 | wl_list_for_each(_pw, &pid_workspaces, link) { |
548 | if (pid == _pw->pid) { | 561 | if (pid == _pw->pid) { |
549 | pw = _pw; | 562 | pw = _pw; |
550 | wlr_log(L_DEBUG, | 563 | wlr_log(WLR_DEBUG, |
551 | "found pid_workspace for pid %d, workspace %s", | 564 | "found pid_workspace for pid %d, workspace %s", |
552 | pid, pw->workspace); | 565 | pid, pw->workspace); |
553 | goto found; | 566 | goto found; |
@@ -561,7 +574,7 @@ found: | |||
561 | ws = workspace_by_name(pw->workspace); | 574 | ws = workspace_by_name(pw->workspace); |
562 | 575 | ||
563 | if (!ws) { | 576 | if (!ws) { |
564 | wlr_log(L_DEBUG, | 577 | wlr_log(WLR_DEBUG, |
565 | "Creating workspace %s for pid %d because it disappeared", | 578 | "Creating workspace %s for pid %d because it disappeared", |
566 | pw->workspace, pid); | 579 | pw->workspace, pid); |
567 | ws = workspace_create(pw->output, pw->workspace); | 580 | ws = workspace_create(pw->output, pw->workspace); |
@@ -582,7 +595,7 @@ static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | |||
582 | } | 595 | } |
583 | 596 | ||
584 | void workspace_record_pid(pid_t pid) { | 597 | void workspace_record_pid(pid_t pid) { |
585 | wlr_log(L_DEBUG, "Recording workspace for process %d", pid); | 598 | wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); |
586 | if (!pid_workspaces.prev && !pid_workspaces.next) { | 599 | if (!pid_workspaces.prev && !pid_workspaces.next) { |
587 | wl_list_init(&pid_workspaces); | 600 | wl_list_init(&pid_workspaces); |
588 | } | 601 | } |
@@ -594,12 +607,12 @@ void workspace_record_pid(pid_t pid) { | |||
594 | ws = container_parent(ws, C_WORKSPACE); | 607 | ws = container_parent(ws, C_WORKSPACE); |
595 | } | 608 | } |
596 | if (!ws) { | 609 | if (!ws) { |
597 | wlr_log(L_DEBUG, "Bailing out, no workspace"); | 610 | wlr_log(WLR_DEBUG, "Bailing out, no workspace"); |
598 | return; | 611 | return; |
599 | } | 612 | } |
600 | struct sway_container *output = ws->parent; | 613 | struct sway_container *output = ws->parent; |
601 | if (!output) { | 614 | if (!output) { |
602 | wlr_log(L_DEBUG, "Bailing out, no output"); | 615 | wlr_log(WLR_DEBUG, "Bailing out, no output"); |
603 | return; | 616 | return; |
604 | } | 617 | } |
605 | 618 | ||