aboutsummaryrefslogtreecommitdiffstats
path: root/sway/tree
diff options
context:
space:
mode:
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/arrange.c102
-rw-r--r--sway/tree/container.c209
-rw-r--r--sway/tree/layout.c97
-rw-r--r--sway/tree/output.c6
-rw-r--r--sway/tree/view.c191
-rw-r--r--sway/tree/workspace.c57
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/** 147static void arrange_children_of(struct sway_container *parent);
148 * If a container has been deleted from the pending tree state, we must add it
149 * to the transaction so it can be freed afterwards. To do this, we iterate the
150 * server's destroying_containers list and add all of them. We may add more than
151 * what we need to, but this is easy and has no negative consequences.
152 */
153static void add_deleted_containers(struct sway_transaction *transaction) {
154 for (int i = 0; i < server.destroying_containers->length; ++i) {
155 struct sway_container *child = server.destroying_containers->items[i];
156 transaction_add_container(transaction, child);
157 }
158}
159
160static void arrange_children_of(struct sway_container *parent,
161 struct sway_transaction *transaction);
162 148
163static void arrange_floating(struct sway_container *floating, 149static void arrange_floating(struct sway_container *floating) {
164 struct sway_transaction *transaction) {
165 for (int i = 0; i < floating->children->length; ++i) { 150 for (int i = 0; i < floating->children->length; ++i) {
166 struct sway_container *floater = floating->children->items[i]; 151 struct sway_container *floater = floating->children->items[i];
167 if (floater->type == C_VIEW) { 152 if (floater->type == C_VIEW) {
168 view_autoconfigure(floater->sway_view); 153 view_autoconfigure(floater->sway_view);
169 } else { 154 } else {
170 arrange_children_of(floater, transaction); 155 arrange_children_of(floater);
171 } 156 }
172 transaction_add_container(transaction, floater); 157 container_set_dirty(floater);
173 } 158 }
174 transaction_add_container(transaction, floating); 159 container_set_dirty(floating);
175} 160}
176 161
177static void arrange_children_of(struct sway_container *parent, 162static void arrange_children_of(struct sway_container *parent) {
178 struct sway_transaction *transaction) {
179 if (config->reloading) { 163 if (config->reloading) {
180 return; 164 return;
181 } 165 }
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
222static void arrange_workspace(struct sway_container *workspace, 206static void arrange_workspace(struct sway_container *workspace) {
223 struct sway_transaction *transaction) {
224 if (config->reloading) { 207 if (config->reloading) {
225 return; 208 return;
226 } 209 }
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
244static void arrange_output(struct sway_container *output, 227static void arrange_output(struct sway_container *output) {
245 struct sway_transaction *transaction) {
246 if (config->reloading) { 228 if (config->reloading) {
247 return; 229 return;
248 } 230 }
@@ -253,16 +235,16 @@ static void arrange_output(struct sway_container *output,
253 output->y = output_box->y; 235 output->y = output_box->y;
254 output->width = output_box->width; 236 output->width = output_box->width;
255 output->height = output_box->height; 237 output->height = output_box->height;
256 transaction_add_container(transaction, output); 238 container_set_dirty(output);
257 wlr_log(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
265static void arrange_root(struct sway_transaction *transaction) { 247static void arrange_root() {
266 if (config->reloading) { 248 if (config->reloading) {
267 return; 249 return;
268 } 250 }
@@ -274,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
284void arrange_windows(struct sway_container *container, 266void arrange_windows(struct sway_container *container) {
285 struct sway_transaction *transaction) {
286 switch (container->type) { 267 switch (container->type) {
287 case C_ROOT: 268 case C_ROOT:
288 arrange_root(transaction); 269 arrange_root();
289 break; 270 break;
290 case C_OUTPUT: 271 case C_OUTPUT:
291 arrange_output(container, transaction); 272 arrange_output(container);
292 break; 273 break;
293 case C_WORKSPACE: 274 case C_WORKSPACE:
294 arrange_workspace(container, transaction); 275 arrange_workspace(container);
295 break; 276 break;
296 case C_CONTAINER: 277 case C_CONTAINER:
297 arrange_children_of(container, transaction); 278 arrange_children_of(container);
298 transaction_add_container(transaction, container); 279 container_set_dirty(container);
299 break; 280 break;
300 case C_VIEW: 281 case C_VIEW:
301 view_autoconfigure(container->sway_view); 282 view_autoconfigure(container->sway_view);
302 transaction_add_container(transaction, container); 283 container_set_dirty(container);
303 break; 284 break;
304 case C_TYPES: 285 case C_TYPES:
305 break; 286 break;
306 } 287 }
307 add_deleted_containers(transaction);
308}
309
310void arrange_and_commit(struct sway_container *container) {
311 struct sway_transaction *transaction = transaction_create();
312 arrange_windows(container, transaction);
313 transaction_commit(transaction);
314} 288}
315 289
316void remove_gaps(struct sway_container *c) { 290void remove_gaps(struct sway_container *c) {
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
332void add_gaps(struct sway_container *c) { 306void 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
240static struct sway_container *container_output_destroy( 244static 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,
678void container_for_each_descendant_dfs(struct sway_container *container, 690void 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
693void container_for_each_descendant_bfs(struct sway_container *con, 712void 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 */
1024void 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 */
1050static 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
1078void 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
1104void 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
1112static 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
1117bool container_has_urgent_child(struct sway_container *container) {
1118 return container_find(container, find_urgent_iterator, NULL);
1119}
1120
1121void 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
23static void output_layout_handle_change(struct wl_listener *listener, 23static void output_layout_handle_change(struct wl_listener *listener,
24 void *data) { 24 void *data) {
25 arrange_and_commit(&root_container); 25 arrange_windows(&root_container);
26 transaction_commit_dirty();
26} 27}
27 28
28void layout_init(void) { 29void layout_init(void) {
@@ -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
128void container_add_child(struct sway_container *parent, 130void 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
139struct sway_container *container_remove_child(struct sway_container *child) { 145struct 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
221static bool sway_dir_to_wlr(enum movement_direction dir, 249static 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
545enum sway_container_layout container_get_default_layout( 576enum 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,
888void container_recursive_resize(struct sway_container *container, 919void 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
145void 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
144uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, 158uint32_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
152void view_init_floating(struct sway_view *view) { 166void 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
173void view_autoconfigure(struct sway_view *view) { 215void 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
285void view_set_activated(struct sway_view *view, bool activated) { 326void 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
291void view_set_tiled(struct sway_view *view, bool tiled) { 332void 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
540static 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
491void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { 560void 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
997bool view_is_visible(struct sway_view *view) { 1093bool 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
1133void 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
1158bool 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
110char *workspace_next_name(const char *output_name) { 111char *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 */
273struct sway_container *workspace_output_prev_next_impl( 270struct 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 */
305struct sway_container *workspace_prev_next_impl( 305struct 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
524void 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
521struct pid_workspace { 534struct 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
584void workspace_record_pid(pid_t pid) { 597void 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