summaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
Diffstat (limited to 'sway')
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/layout.c4
-rw-r--r--sway/commands/seat/cursor.c4
-rw-r--r--sway/desktop/output.c518
-rw-r--r--sway/input/cursor.c31
-rw-r--r--sway/input/seat.c16
-rw-r--r--sway/ipc-server.c22
-rw-r--r--sway/tree/arrange.c64
-rw-r--r--sway/tree/container.c336
-rw-r--r--sway/tree/layout.c11
-rw-r--r--sway/tree/view.c86
11 files changed, 793 insertions, 301 deletions
diff --git a/sway/commands/border.c b/sway/commands/border.c
index 1eb06a21..4ba361da 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -41,7 +41,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
41 41
42 struct sway_seat *seat = input_manager_current_seat(input_manager); 42 struct sway_seat *seat = input_manager_current_seat(input_manager);
43 if (seat->cursor) { 43 if (seat->cursor) {
44 cursor_send_pointer_motion(seat->cursor, 0); 44 cursor_send_pointer_motion(seat->cursor, 0, false);
45 } 45 }
46 46
47 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 47 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
diff --git a/sway/commands/layout.c b/sway/commands/layout.c
index bb36bb18..58728f16 100644
--- a/sway/commands/layout.c
+++ b/sway/commands/layout.c
@@ -39,6 +39,10 @@ struct cmd_results *cmd_layout(int argc, char **argv) {
39 parent->layout = L_HORIZ; 39 parent->layout = L_HORIZ;
40 } else if (strcasecmp(argv[0], "splitv") == 0) { 40 } else if (strcasecmp(argv[0], "splitv") == 0) {
41 parent->layout = L_VERT; 41 parent->layout = L_VERT;
42 } else if (strcasecmp(argv[0], "tabbed") == 0) {
43 parent->layout = L_TABBED;
44 } else if (strcasecmp(argv[0], "stacking") == 0) {
45 parent->layout = L_STACKED;
42 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { 46 } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
43 if (parent->layout == L_HORIZ) { 47 if (parent->layout == L_HORIZ) {
44 parent->layout = L_VERT; 48 parent->layout = L_VERT;
diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c
index 929384b0..4d0a22c7 100644
--- a/sway/commands/seat/cursor.c
+++ b/sway/commands/seat/cursor.c
@@ -36,7 +36,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
36 int delta_x = strtol(argv[1], NULL, 10); 36 int delta_x = strtol(argv[1], NULL, 10);
37 int delta_y = strtol(argv[2], NULL, 10); 37 int delta_y = strtol(argv[2], NULL, 10);
38 wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y); 38 wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y);
39 cursor_send_pointer_motion(cursor, 0); 39 cursor_send_pointer_motion(cursor, 0, true);
40 } else if (strcasecmp(argv[0], "set") == 0) { 40 } else if (strcasecmp(argv[0], "set") == 0) {
41 if (argc < 3) { 41 if (argc < 3) {
42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 42 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
@@ -45,7 +45,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) {
45 float x = strtof(argv[1], NULL) / root_container.width; 45 float x = strtof(argv[1], NULL) / root_container.width;
46 float y = strtof(argv[2], NULL) / root_container.height; 46 float y = strtof(argv[2], NULL) / root_container.height;
47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); 47 wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y);
48 cursor_send_pointer_motion(cursor, 0); 48 cursor_send_pointer_motion(cursor, 0, true);
49 } else { 49 } else {
50 if (argc < 2) { 50 if (argc < 2) {
51 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); 51 return cmd_results_new(CMD_INVALID, "cursor", expected_syntax);
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index 94562052..765647fd 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -269,17 +269,6 @@ static void render_unmanaged(struct sway_output *output,
269 render_surface_iterator, &data); 269 render_surface_iterator, &data);
270} 270}
271 271
272static void render_view(struct sway_view *view, struct sway_output *output,
273 pixman_region32_t *damage) {
274 struct render_data data = {
275 .output = output,
276 .damage = damage,
277 .alpha = view->swayc->alpha,
278 };
279 output_view_for_each_surface(
280 view, &data.root_geo, render_surface_iterator, &data);
281}
282
283static void render_rect(struct wlr_output *wlr_output, 272static void render_rect(struct wlr_output *wlr_output,
284 pixman_region32_t *output_damage, const struct wlr_box *_box, 273 pixman_region32_t *output_damage, const struct wlr_box *_box,
285 float color[static 4]) { 274 float color[static 4]) {
@@ -310,93 +299,149 @@ damage_finish:
310 pixman_region32_fini(&damage); 299 pixman_region32_fini(&damage);
311} 300}
312 301
302static void premultiply_alpha(float color[4], float opacity) {
303 color[3] *= opacity;
304 color[0] *= color[3];
305 color[1] *= color[3];
306 color[2] *= color[3];
307}
308
309static void render_view_surfaces(struct sway_view *view,
310 struct sway_output *output, pixman_region32_t *damage) {
311 struct render_data data = {
312 .output = output,
313 .damage = damage,
314 .alpha = view->swayc->alpha,
315 };
316 output_view_for_each_surface(
317 view, &data.root_geo, render_surface_iterator, &data);
318}
319
313/** 320/**
314 * Render decorations for a view with "border normal". 321 * Render a view's surface and left/bottom/right borders.
315 *
316 * Care must be taken not to render over the same pixel multiple times,
317 * otherwise the colors will be incorrect when using opacity.
318 */ 322 */
319static void render_container_simple_border_normal(struct sway_output *output, 323static void render_view(struct sway_output *output, pixman_region32_t *damage,
320 pixman_region32_t *output_damage, 324 struct sway_container *con, struct border_colors *colors) {
321 struct sway_container *con, struct border_colors *colors,
322 struct wlr_texture *title_texture, struct wlr_texture *marks_texture) {
323 struct wlr_box box;
324 float color[4];
325 struct sway_view *view = con->sway_view; 325 struct sway_view *view = con->sway_view;
326 float output_scale = output->wlr_output->scale; 326 render_view_surfaces(view, output, damage);
327 327
328 if (view->border_left) { 328 struct wlr_box box;
329 // Child border - left edge 329 float output_scale = output->wlr_output->scale;
330 memcpy(&color, colors->child_border, sizeof(float) * 4); 330 float color[4];
331 color[3] *= con->alpha;
332 box.x = con->x;
333 box.y = con->y + 1;
334 box.width = view->border_thickness;
335 box.height = con->height - 1
336 - view->border_thickness * view->border_bottom;
337 scale_box(&box, output_scale);
338 render_rect(output->wlr_output, output_damage, &box, color);
339 }
340 331
341 if (view->border_right) { 332 if (view->border != B_NONE) {
342 // Child border - right edge 333 if (view->border_left) {
343 if (con->parent->children->length == 1
344 && con->parent->layout == L_HORIZ) {
345 memcpy(&color, colors->indicator, sizeof(float) * 4);
346 } else {
347 memcpy(&color, colors->child_border, sizeof(float) * 4); 334 memcpy(&color, colors->child_border, sizeof(float) * 4);
335 premultiply_alpha(color, con->alpha);
336 box.x = con->x;
337 box.y = view->y;
338 box.width = view->border_thickness;
339 box.height = view->height;
340 scale_box(&box, output_scale);
341 render_rect(output->wlr_output, damage, &box, color);
348 } 342 }
349 color[3] *= con->alpha;
350 box.x = con->x + con->width - view->border_thickness;
351 box.y = con->y + 1;
352 box.width = view->border_thickness;
353 box.height = con->height - 1
354 - view->border_thickness * view->border_bottom;
355 scale_box(&box, output_scale);
356 render_rect(output->wlr_output, output_damage, &box, color);
357 }
358 343
359 if (view->border_bottom) { 344 if (view->border_right) {
360 // Child border - bottom edge 345 if (con->parent->children->length == 1
361 if (con->parent->children->length == 1 346 && con->parent->layout == L_HORIZ) {
362 && con->parent->layout == L_VERT) { 347 memcpy(&color, colors->indicator, sizeof(float) * 4);
363 memcpy(&color, colors->indicator, sizeof(float) * 4); 348 } else {
364 } else { 349 memcpy(&color, colors->child_border, sizeof(float) * 4);
365 memcpy(&color, colors->child_border, sizeof(float) * 4); 350 }
351 premultiply_alpha(color, con->alpha);
352 box.x = view->x + view->width;
353 box.y = view->y;
354 box.width = view->border_thickness;
355 box.height = view->height;
356 scale_box(&box, output_scale);
357 render_rect(output->wlr_output, damage, &box, color);
358 }
359
360 if (view->border_bottom) {
361 if (con->parent->children->length == 1
362 && con->parent->layout == L_VERT) {
363 memcpy(&color, colors->indicator, sizeof(float) * 4);
364 } else {
365 memcpy(&color, colors->child_border, sizeof(float) * 4);
366 }
367 premultiply_alpha(color, con->alpha);
368 box.x = con->x;
369 box.y = view->y + view->height;
370 box.width = con->width;
371 box.height = view->border_thickness;
372 scale_box(&box, output_scale);
373 render_rect(output->wlr_output, damage, &box, color);
366 } 374 }
367 color[3] *= con->alpha;
368 box.x = con->x;
369 box.y = con->y + con->height - view->border_thickness;
370 box.width = con->width;
371 box.height = view->border_thickness;
372 scale_box(&box, output_scale);
373 render_rect(output->wlr_output, output_damage, &box, color);
374 } 375 }
376}
377
378/**
379 * Render a titlebar.
380 *
381 * Care must be taken not to render over the same pixel multiple times,
382 * otherwise the colors will be incorrect when using opacity.
383 *
384 * The height is: 1px border, 3px padding, font height, 3px padding, 1px border
385 * The left side for L_TABBED is: 1px border, 2px padding, title
386 * The left side for other layouts is: 3px padding, title
387 */
388static void render_titlebar(struct sway_output *output,
389 pixman_region32_t *output_damage, struct sway_container *con,
390 int x, int y, int width,
391 struct border_colors *colors, struct wlr_texture *title_texture,
392 struct wlr_texture *marks_texture) {
393 struct wlr_box box;
394 float color[4];
395 struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL;
396 float output_scale = output->wlr_output->scale;
397 enum sway_container_layout layout = con->parent->layout;
398 bool is_last_child =
399 con->parent->children->items[con->parent->children->length - 1] == con;
375 400
376 // Single pixel bar above title 401 // Single pixel bar above title
377 memcpy(&color, colors->border, sizeof(float) * 4); 402 memcpy(&color, colors->border, sizeof(float) * 4);
378 color[3] *= con->alpha; 403 premultiply_alpha(color, con->alpha);
379 box.x = con->x; 404 box.x = x;
380 box.y = con->y; 405 box.y = y;
381 box.width = con->width; 406 box.width = width;
382 box.height = 1; 407 box.height = TITLEBAR_BORDER_THICKNESS;
383 scale_box(&box, output_scale); 408 scale_box(&box, output_scale);
384 render_rect(output->wlr_output, output_damage, &box, color); 409 render_rect(output->wlr_output, output_damage, &box, color);
385 410
386 // Single pixel bar below title 411 // Single pixel bar below title
387 memcpy(&color, colors->border, sizeof(float) * 4); 412 size_t left_offset = 0, right_offset = 0;
388 color[3] *= con->alpha; 413 bool connects_sides = false;
389 box.x = con->x + view->border_thickness; 414 if (layout == L_HORIZ || layout == L_VERT ||
390 box.y = view->y - 1; 415 (layout == L_STACKED && is_last_child)) {
391 box.width = con->width - view->border_thickness * 2; 416 if (view) {
392 box.height = 1; 417 left_offset = view->border_left * view->border_thickness;
418 right_offset = view->border_right * view->border_thickness;
419 connects_sides = true;
420 }
421 }
422 box.x = x + left_offset;
423 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
424 box.width = width - left_offset - right_offset;
425 box.height = TITLEBAR_BORDER_THICKNESS;
393 scale_box(&box, output_scale); 426 scale_box(&box, output_scale);
394 render_rect(output->wlr_output, output_damage, &box, color); 427 render_rect(output->wlr_output, output_damage, &box, color);
395 428
396 // Setting these makes marks and title easier 429 if (layout == L_TABBED) {
397 size_t inner_x = con->x + view->border_thickness * view->border_left; 430 // Single pixel left edge
398 size_t inner_width = con->width - view->border_thickness * view->border_left 431 box.x = x;
399 - view->border_thickness * view->border_right; 432 box.y = y + TITLEBAR_BORDER_THICKNESS;
433 box.width = TITLEBAR_BORDER_THICKNESS;
434 box.height =
435 container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2;
436 scale_box(&box, output_scale);
437 render_rect(output->wlr_output, output_damage, &box, color);
438
439 // Single pixel right edge
440 box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale;
441 render_rect(output->wlr_output, output_damage, &box, color);
442 }
443
444 size_t inner_width = width - TITLEBAR_H_PADDING * 2;
400 445
401 // Marks 446 // Marks
402 size_t marks_width = 0; 447 size_t marks_width = 0;
@@ -404,14 +449,18 @@ static void render_container_simple_border_normal(struct sway_output *output,
404 struct wlr_box texture_box; 449 struct wlr_box texture_box;
405 wlr_texture_get_size(marks_texture, 450 wlr_texture_get_size(marks_texture,
406 &texture_box.width, &texture_box.height); 451 &texture_box.width, &texture_box.height);
407 texture_box.x = (inner_x + inner_width) * output_scale - texture_box.width; 452 texture_box.x =
408 texture_box.y = (con->y + view->border_thickness) * output_scale; 453 (x + width - TITLEBAR_H_PADDING) * output_scale - texture_box.width;
454 texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale;
409 455
410 float matrix[9]; 456 float matrix[9];
411 wlr_matrix_project_box(matrix, &texture_box, 457 wlr_matrix_project_box(matrix, &texture_box,
412 WL_OUTPUT_TRANSFORM_NORMAL, 458 WL_OUTPUT_TRANSFORM_NORMAL,
413 0.0, output->wlr_output->transform_matrix); 459 0.0, output->wlr_output->transform_matrix);
414 460
461 if (inner_width * output_scale < texture_box.width) {
462 texture_box.width = inner_width * output_scale;
463 }
415 render_texture(output->wlr_output, output_damage, marks_texture, 464 render_texture(output->wlr_output, output_damage, marks_texture,
416 &texture_box, matrix, con->alpha); 465 &texture_box, matrix, con->alpha);
417 marks_width = texture_box.width; 466 marks_width = texture_box.width;
@@ -423,8 +472,8 @@ static void render_container_simple_border_normal(struct sway_output *output,
423 struct wlr_box texture_box; 472 struct wlr_box texture_box;
424 wlr_texture_get_size(title_texture, 473 wlr_texture_get_size(title_texture,
425 &texture_box.width, &texture_box.height); 474 &texture_box.width, &texture_box.height);
426 texture_box.x = inner_x * output_scale; 475 texture_box.x = (x + TITLEBAR_H_PADDING) * output_scale;
427 texture_box.y = (con->y + view->border_thickness) * output_scale; 476 texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale;
428 477
429 float matrix[9]; 478 float matrix[9];
430 wlr_matrix_project_box(matrix, &texture_box, 479 wlr_matrix_project_box(matrix, &texture_box,
@@ -439,104 +488,89 @@ static void render_container_simple_border_normal(struct sway_output *output,
439 title_width = texture_box.width; 488 title_width = texture_box.width;
440 } 489 }
441 490
442 // Title background - above the text 491 // Padding above title
443 memcpy(&color, colors->background, sizeof(float) * 4); 492 memcpy(&color, colors->background, sizeof(float) * 4);
444 color[3] *= con->alpha; 493 premultiply_alpha(color, con->alpha);
445 box.x = inner_x; 494 box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
446 box.y = con->y + 1; 495 box.y = y + TITLEBAR_BORDER_THICKNESS;
447 box.width = inner_width; 496 box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2;
448 box.height = view->border_thickness - 1; 497 box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS;
449 scale_box(&box, output_scale); 498 scale_box(&box, output_scale);
450 render_rect(output->wlr_output, output_damage, &box, color); 499 render_rect(output->wlr_output, output_damage, &box, color);
451 500
452 // Title background - below the text 501 // Padding below title
453 box.y = (con->y + view->border_thickness + config->font_height) 502 box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale;
454 * output_scale;
455 render_rect(output->wlr_output, output_damage, &box, color); 503 render_rect(output->wlr_output, output_damage, &box, color);
456 504
457 // Title background - filler between title and marks 505 // Filler between title and marks
458 box.width = inner_width * output_scale - title_width - marks_width; 506 box.width = inner_width * output_scale - title_width - marks_width;
459 if (box.width > 0) { 507 if (box.width > 0) {
460 box.x = inner_x * output_scale + title_width; 508 box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width;
461 box.y = (con->y + view->border_thickness) * output_scale; 509 box.y = (y + TITLEBAR_V_PADDING) * output_scale;
462 box.height = config->font_height * output_scale; 510 box.height = config->font_height * output_scale;
463 render_rect(output->wlr_output, output_damage, &box, color); 511 render_rect(output->wlr_output, output_damage, &box, color);
464 } 512 }
465}
466 513
467/** 514 // Padding left of title
468 * Render decorations for a view with "border pixel". 515 left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
469 * 516 box.x = x + left_offset;
470 * Care must be taken not to render over the same pixel multiple times, 517 box.y = y + TITLEBAR_V_PADDING;
471 * otherwise the colors will be incorrect when using opacity. 518 box.width = TITLEBAR_H_PADDING - left_offset;
472 */ 519 box.height = config->font_height;
473static void render_container_simple_border_pixel(struct sway_output *output, 520 scale_box(&box, output_scale);
474 pixman_region32_t *output_damage, struct sway_container *con, 521 render_rect(output->wlr_output, output_damage, &box, color);
475 struct border_colors *colors) {
476 struct wlr_box box;
477 float color[4];
478 struct sway_view *view = con->sway_view;
479 float output_scale = output->wlr_output->scale;
480 522
481 if (view->border_left) { 523 // Padding right of marks
482 // Child border - left edge 524 right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS;
483 memcpy(&color, colors->child_border, sizeof(float) * 4); 525 box.x = x + width - TITLEBAR_H_PADDING;
484 color[3] *= con->alpha; 526 box.y = y + TITLEBAR_V_PADDING;
485 box.x = con->x; 527 box.width = TITLEBAR_H_PADDING - right_offset;
486 box.y = con->y + view->border_thickness * view->border_top; 528 box.height = config->font_height;
487 box.width = view->border_thickness; 529 scale_box(&box, output_scale);
488 box.height = con->height - view->border_thickness 530 render_rect(output->wlr_output, output_damage, &box, color);
489 * (view->border_top + view->border_bottom);
490 scale_box(&box, output_scale);
491 render_rect(output->wlr_output, output_damage, &box, color);
492 }
493 531
494 if (view->border_right) { 532 if (connects_sides) {
495 // Child border - right edge 533 // Left pixel in line with bottom bar
496 if (con->parent->children->length == 1 534 box.x = x;
497 && con->parent->layout == L_HORIZ) { 535 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
498 memcpy(&color, colors->indicator, sizeof(float) * 4); 536 box.width = view->border_thickness * view->border_left;
499 } else { 537 box.height = TITLEBAR_BORDER_THICKNESS;
500 memcpy(&color, colors->child_border, sizeof(float) * 4);
501 }
502 color[3] *= con->alpha;
503 box.x = con->x + con->width - view->border_thickness;
504 box.y = con->y + view->border_thickness * view->border_top;
505 box.width = view->border_thickness;
506 box.height = con->height - view->border_thickness
507 * (view->border_top + view->border_bottom);
508 scale_box(&box, output_scale); 538 scale_box(&box, output_scale);
509 render_rect(output->wlr_output, output_damage, &box, color); 539 render_rect(output->wlr_output, output_damage, &box, color);
510 }
511 540
512 if (view->border_top) { 541 // Right pixel in line with bottom bar
513 // Child border - top edge 542 box.x = x + width - view->border_thickness * view->border_right;
514 memcpy(&color, colors->child_border, sizeof(float) * 4); 543 box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS;
515 color[3] *= con->alpha; 544 box.width = view->border_thickness * view->border_right;
516 box.x = con->x; 545 box.height = TITLEBAR_BORDER_THICKNESS;
517 box.y = con->y;
518 box.width = con->width;
519 box.height = view->border_thickness;
520 scale_box(&box, output_scale); 546 scale_box(&box, output_scale);
521 render_rect(output->wlr_output, output_damage, &box, color); 547 render_rect(output->wlr_output, output_damage, &box, color);
522 } 548 }
549}
523 550
524 if (view->border_bottom) { 551/**
525 // Child border - bottom edge 552 * Render the top border line for a view using "border pixel".
526 if (con->parent->children->length == 1 553 */
527 && con->parent->layout == L_VERT) { 554static void render_top_border(struct sway_output *output,
528 memcpy(&color, colors->indicator, sizeof(float) * 4); 555 pixman_region32_t *output_damage, struct sway_container *con,
529 } else { 556 struct border_colors *colors) {
530 memcpy(&color, colors->child_border, sizeof(float) * 4); 557 struct sway_view *view = con->sway_view;
531 } 558 if (!view->border_top) {
532 color[3] *= con->alpha; 559 return;
533 box.x = con->x;
534 box.y = con->y + con->height - view->border_thickness;
535 box.width = con->width;
536 box.height = view->border_thickness;
537 scale_box(&box, output_scale);
538 render_rect(output->wlr_output, output_damage, &box, color);
539 } 560 }
561 struct wlr_box box;
562 float color[4];
563 float output_scale = output->wlr_output->scale;
564
565 // Child border - top edge
566 memcpy(&color, colors->child_border, sizeof(float) * 4);
567 premultiply_alpha(color, con->alpha);
568 box.x = con->x;
569 box.y = con->y;
570 box.width = con->width;
571 box.height = view->border_thickness;
572 scale_box(&box, output_scale);
573 render_rect(output->wlr_output, output_damage, &box, color);
540} 574}
541 575
542static void render_container(struct sway_output *output, 576static void render_container(struct sway_output *output,
@@ -558,33 +592,30 @@ static void render_container_simple(struct sway_output *output,
558 struct sway_container *child = con->children->items[i]; 592 struct sway_container *child = con->children->items[i];
559 593
560 if (child->type == C_VIEW) { 594 if (child->type == C_VIEW) {
561 if (child->sway_view->border != B_NONE) { 595 struct border_colors *colors;
562 struct border_colors *colors; 596 struct wlr_texture *title_texture;
563 struct wlr_texture *title_texture; 597 struct wlr_texture *marks_texture;
564 struct wlr_texture *marks_texture; 598 if (focus == child || parent_focused) {
565 if (focus == child || parent_focused) { 599 colors = &config->border_colors.focused;
566 colors = &config->border_colors.focused; 600 title_texture = child->title_focused;
567 title_texture = child->title_focused; 601 marks_texture = child->sway_view->marks_focused;
568 marks_texture = child->sway_view->marks_focused; 602 } else if (seat_get_focus_inactive(seat, con) == child) {
569 } else if (seat_get_focus_inactive(seat, con) == child) { 603 colors = &config->border_colors.focused_inactive;
570 colors = &config->border_colors.focused_inactive; 604 title_texture = child->title_focused_inactive;
571 title_texture = child->title_focused_inactive; 605 marks_texture = child->sway_view->marks_focused_inactive;
572 marks_texture = child->sway_view->marks_focused_inactive; 606 } else {
573 } else { 607 colors = &config->border_colors.unfocused;
574 colors = &config->border_colors.unfocused; 608 title_texture = child->title_unfocused;
575 title_texture = child->title_unfocused; 609 marks_texture = child->sway_view->marks_unfocused;
576 marks_texture = child->sway_view->marks_unfocused; 610 }
577 } 611
578 612 if (child->sway_view->border == B_NORMAL) {
579 if (child->sway_view->border == B_NORMAL) { 613 render_titlebar(output, damage, child, child->x, child->y,
580 render_container_simple_border_normal(output, damage, 614 child->width, colors, title_texture, marks_texture);
581 child, colors, title_texture, marks_texture); 615 } else {
582 } else { 616 render_top_border(output, damage, child, colors);
583 render_container_simple_border_pixel(output, damage, child,
584 colors);
585 }
586 } 617 }
587 render_view(child->sway_view, output, damage); 618 render_view(output, damage, child, colors);
588 } else { 619 } else {
589 render_container(output, damage, child, 620 render_container(output, damage, child,
590 parent_focused || focus == child); 621 parent_focused || focus == child);
@@ -596,16 +627,116 @@ static void render_container_simple(struct sway_output *output,
596 * Render a container's children using the L_TABBED layout. 627 * Render a container's children using the L_TABBED layout.
597 */ 628 */
598static void render_container_tabbed(struct sway_output *output, 629static void render_container_tabbed(struct sway_output *output,
599 pixman_region32_t *damage, struct sway_container *con) { 630 pixman_region32_t *damage, struct sway_container *con,
600 // TODO 631 bool parent_focused) {
632 if (!con->children->length) {
633 return;
634 }
635 struct sway_seat *seat = input_manager_current_seat(input_manager);
636 struct sway_container *focus = seat_get_focus(seat);
637 struct sway_container *current = seat_get_active_child(seat, con);
638 struct border_colors *current_colors = NULL;
639
640 // Render tabs
641 for (int i = 0; i < con->children->length; ++i) {
642 struct sway_container *child = con->children->items[i];
643 struct border_colors *colors;
644 struct wlr_texture *title_texture;
645 struct wlr_texture *marks_texture;
646 struct sway_view *view =
647 child->type == C_VIEW ? child->sway_view : NULL;
648
649 if (focus == child || parent_focused) {
650 colors = &config->border_colors.focused;
651 title_texture = child->title_focused;
652 marks_texture = view ? view->marks_focused : NULL;
653 } else if (child == current) {
654 colors = &config->border_colors.focused_inactive;
655 title_texture = child->title_focused_inactive;
656 marks_texture = view ? view->marks_focused : NULL;
657 } else {
658 colors = &config->border_colors.unfocused;
659 title_texture = child->title_unfocused;
660 marks_texture = view ? view->marks_unfocused : NULL;
661 }
662
663 int tab_width = con->width / con->children->length;
664 int x = con->x + tab_width * i;
665 // Make last tab use the remaining width of the parent
666 if (i == con->children->length - 1) {
667 tab_width = con->width - tab_width * i;
668 }
669
670 render_titlebar(output, damage, child, x, child->y, tab_width, colors,
671 title_texture, marks_texture);
672
673 if (child == current) {
674 current_colors = colors;
675 }
676 }
677
678 // Render surface and left/right/bottom borders
679 if (current->type == C_VIEW) {
680 render_view(output, damage, current, current_colors);
681 } else {
682 render_container(output, damage, current,
683 parent_focused || current == focus);
684 }
601} 685}
602 686
603/** 687/**
604 * Render a container's children using the L_STACKED layout. 688 * Render a container's children using the L_STACKED layout.
605 */ 689 */
606static void render_container_stacked(struct sway_output *output, 690static void render_container_stacked(struct sway_output *output,
607 pixman_region32_t *damage, struct sway_container *con) { 691 pixman_region32_t *damage, struct sway_container *con,
608 // TODO 692 bool parent_focused) {
693 if (!con->children->length) {
694 return;
695 }
696 struct sway_seat *seat = input_manager_current_seat(input_manager);
697 struct sway_container *focus = seat_get_focus(seat);
698 struct sway_container *current = seat_get_active_child(seat, con);
699 struct border_colors *current_colors = NULL;
700
701 // Render titles
702 for (int i = 0; i < con->children->length; ++i) {
703 struct sway_container *child = con->children->items[i];
704 struct border_colors *colors;
705 struct wlr_texture *title_texture;
706 struct wlr_texture *marks_texture;
707 struct sway_view *view =
708 child->type == C_VIEW ? child->sway_view : NULL;
709
710 if (focus == child || parent_focused) {
711 colors = &config->border_colors.focused;
712 title_texture = child->title_focused;
713 marks_texture = view ? view->marks_focused : NULL;
714 } else if (child == current) {
715 colors = &config->border_colors.focused_inactive;
716 title_texture = child->title_focused_inactive;
717 marks_texture = view ? view->marks_focused_inactive : NULL;
718 } else {
719 colors = &config->border_colors.unfocused;
720 title_texture = child->title_unfocused;
721 marks_texture = view ? view->marks_unfocused : NULL;
722 }
723
724 int y = con->y + container_titlebar_height() * i;
725 render_titlebar(output, damage, child, child->x, y, child->width,
726 colors, title_texture, marks_texture);
727
728 if (child == current) {
729 current_colors = colors;
730 }
731 }
732
733 // Render surface and left/right/bottom borders
734 if (current->type == C_VIEW) {
735 render_view(output, damage, current, current_colors);
736 } else {
737 render_container(output, damage, current,
738 parent_focused || current == focus);
739 }
609} 740}
610 741
611static void render_container(struct sway_output *output, 742static void render_container(struct sway_output *output,
@@ -618,10 +749,10 @@ static void render_container(struct sway_output *output,
618 render_container_simple(output, damage, con, parent_focused); 749 render_container_simple(output, damage, con, parent_focused);
619 break; 750 break;
620 case L_STACKED: 751 case L_STACKED:
621 render_container_stacked(output, damage, con); 752 render_container_stacked(output, damage, con, parent_focused);
622 break; 753 break;
623 case L_TABBED: 754 case L_TABBED:
624 render_container_tabbed(output, damage, con); 755 render_container_tabbed(output, damage, con, parent_focused);
625 break; 756 break;
626 case L_FLOATING: 757 case L_FLOATING:
627 // TODO 758 // TODO
@@ -678,7 +809,8 @@ static void render_output(struct sway_output *output, struct timespec *when,
678 } 809 }
679 810
680 // TODO: handle views smaller than the output 811 // TODO: handle views smaller than the output
681 render_view(workspace->sway_workspace->fullscreen, output, damage); 812 render_view_surfaces(
813 workspace->sway_workspace->fullscreen, output, damage);
682 814
683 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { 815 if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) {
684 render_unmanaged(output, damage, 816 render_unmanaged(output, damage,
@@ -889,9 +1021,7 @@ static void output_damage_view(struct sway_output *output,
889 return; 1021 return;
890 } 1022 }
891 1023
892 struct sway_container *workspace = container_parent(view->swayc, 1024 if (!view_is_visible(view)) {
893 C_WORKSPACE);
894 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
895 return; 1025 return;
896 } 1026 }
897 1027
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 9259c475..9a0b4f01 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -108,7 +108,7 @@ static struct sway_container *container_at_coords(
108 } 108 }
109 109
110 struct sway_container *c; 110 struct sway_container *c;
111 if ((c = container_at(ws, x, y, surface, sx, sy))) { 111 if ((c = container_at(ws, ox, oy, surface, sx, sy))) {
112 return c; 112 return c;
113 } 113 }
114 114
@@ -135,7 +135,8 @@ static struct sway_container *container_at_coords(
135 return output->swayc; 135 return output->swayc;
136} 136}
137 137
138void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) { 138void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
139 bool allow_refocusing) {
139 if (time_msec == 0) { 140 if (time_msec == 0) {
140 time_msec = get_current_time_msec(); 141 time_msec = get_current_time_msec();
141 } 142 }
@@ -145,8 +146,24 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec)
145 double sx, sy; 146 double sx, sy;
146 struct sway_container *c = container_at_coords(cursor->seat, 147 struct sway_container *c = container_at_coords(cursor->seat,
147 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); 148 cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy);
148 if (c && config->focus_follows_mouse) { 149 if (c && config->focus_follows_mouse && allow_refocusing) {
149 seat_set_focus_warp(cursor->seat, c, false); 150 struct sway_container *focus = seat_get_focus(cursor->seat);
151 if (focus && c->type == C_WORKSPACE) {
152 // Only follow the mouse if it would move to a new output
153 // Otherwise we'll focus the workspace, which is probably wrong
154 if (focus->type != C_OUTPUT) {
155 focus = container_parent(focus, C_OUTPUT);
156 }
157 struct sway_container *output = c;
158 if (output->type != C_OUTPUT) {
159 output = container_parent(c, C_OUTPUT);
160 }
161 if (output != focus) {
162 seat_set_focus_warp(cursor->seat, c, false);
163 }
164 } else {
165 seat_set_focus_warp(cursor->seat, c, false);
166 }
150 } 167 }
151 168
152 // reset cursor if switching between clients 169 // reset cursor if switching between clients
@@ -177,7 +194,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
177 struct wlr_event_pointer_motion *event = data; 194 struct wlr_event_pointer_motion *event = data;
178 wlr_cursor_move(cursor->cursor, event->device, 195 wlr_cursor_move(cursor->cursor, event->device,
179 event->delta_x, event->delta_y); 196 event->delta_x, event->delta_y);
180 cursor_send_pointer_motion(cursor, event->time_msec); 197 cursor_send_pointer_motion(cursor, event->time_msec, true);
181} 198}
182 199
183static void handle_cursor_motion_absolute( 200static void handle_cursor_motion_absolute(
@@ -187,7 +204,7 @@ static void handle_cursor_motion_absolute(
187 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); 204 wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat);
188 struct wlr_event_pointer_motion_absolute *event = data; 205 struct wlr_event_pointer_motion_absolute *event = data;
189 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); 206 wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
190 cursor_send_pointer_motion(cursor, event->time_msec); 207 cursor_send_pointer_motion(cursor, event->time_msec, true);
191} 208}
192 209
193void dispatch_cursor_button(struct sway_cursor *cursor, 210void dispatch_cursor_button(struct sway_cursor *cursor,
@@ -357,7 +374,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
357 } 374 }
358 375
359 wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); 376 wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y);
360 cursor_send_pointer_motion(cursor, event->time_msec); 377 cursor_send_pointer_motion(cursor, event->time_msec, true);
361} 378}
362 379
363static void handle_tool_tip(struct wl_listener *listener, void *data) { 380static void handle_tool_tip(struct wl_listener *listener, void *data) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 9ac3e6a8..7a3e928a 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -602,7 +602,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
602 wlr_output, seat->cursor->cursor->x, 602 wlr_output, seat->cursor->cursor->x,
603 seat->cursor->cursor->y)) { 603 seat->cursor->cursor->y)) {
604 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); 604 wlr_cursor_warp(seat->cursor->cursor, NULL, x, y);
605 cursor_send_pointer_motion(seat->cursor, 0); 605 cursor_send_pointer_motion(seat->cursor, 0, true);
606 } 606 }
607 } 607 }
608 } 608 }
@@ -613,7 +613,7 @@ void seat_set_focus_warp(struct sway_seat *seat,
613 } 613 }
614 614
615 if (last_workspace && last_workspace != new_workspace) { 615 if (last_workspace && last_workspace != new_workspace) {
616 cursor_send_pointer_motion(seat->cursor, 0); 616 cursor_send_pointer_motion(seat->cursor, 0, true);
617 } 617 }
618 618
619 seat->has_focus = (container != NULL); 619 seat->has_focus = (container != NULL);
@@ -718,6 +718,18 @@ struct sway_container *seat_get_focus_inactive(struct sway_seat *seat,
718 return seat_get_focus_by_type(seat, container, C_TYPES); 718 return seat_get_focus_by_type(seat, container, C_TYPES);
719} 719}
720 720
721struct sway_container *seat_get_active_child(struct sway_seat *seat,
722 struct sway_container *container) {
723 struct sway_container *focus = seat_get_focus_inactive(seat, container);
724 if (!focus) {
725 return NULL;
726 }
727 while (focus->parent != container) {
728 focus = focus->parent;
729 }
730 return focus;
731}
732
721struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { 733struct sway_container *sway_seat_get_focus(struct sway_seat *seat) {
722 if (!seat->has_focus) { 734 if (!seat->has_focus) {
723 return NULL; 735 return NULL;
diff --git a/sway/ipc-server.c b/sway/ipc-server.c
index 8734e8f8..15ed6f80 100644
--- a/sway/ipc-server.c
+++ b/sway/ipc-server.c
@@ -22,6 +22,7 @@
22#include "sway/server.h" 22#include "sway/server.h"
23#include "sway/input/input-manager.h" 23#include "sway/input/input-manager.h"
24#include "sway/input/seat.h" 24#include "sway/input/seat.h"
25#include "sway/tree/view.h"
25#include "list.h" 26#include "list.h"
26#include "log.h" 27#include "log.h"
27 28
@@ -429,6 +430,16 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace,
429 json_object_new_boolean(visible)); 430 json_object_new_boolean(visible));
430} 431}
431 432
433static void ipc_get_marks_callback(struct sway_container *con, void *data) {
434 json_object *marks = (json_object *)data;
435 if (con->type == C_VIEW && con->sway_view->marks) {
436 for (int i = 0; i < con->sway_view->marks->length; ++i) {
437 char *mark = (char *)con->sway_view->marks->items[i];
438 json_object_array_add(marks, json_object_new_string(mark));
439 }
440 }
441}
442
432void ipc_client_handle_command(struct ipc_client *client) { 443void ipc_client_handle_command(struct ipc_client *client) {
433 if (!sway_assert(client != NULL, "client != NULL")) { 444 if (!sway_assert(client != NULL, "client != NULL")) {
434 return; 445 return;
@@ -569,6 +580,17 @@ void ipc_client_handle_command(struct ipc_client *client) {
569 goto exit_cleanup; 580 goto exit_cleanup;
570 } 581 }
571 582
583 case IPC_GET_MARKS:
584 {
585 json_object *marks = json_object_new_array();
586 container_descendants(&root_container, C_VIEW, ipc_get_marks_callback,
587 marks);
588 const char *json_string = json_object_to_json_string(marks);
589 ipc_send_reply(client, json_string, (uint32_t)strlen(json_string));
590 json_object_put(marks);
591 goto exit_cleanup;
592 }
593
572 case IPC_GET_VERSION: 594 case IPC_GET_VERSION:
573 { 595 {
574 json_object *version = ipc_json_get_version(); 596 json_object *version = ipc_json_get_version();
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c
index 83bb20fb..37f4a066 100644
--- a/sway/tree/arrange.c
+++ b/sway/tree/arrange.c
@@ -86,6 +86,15 @@ static void apply_horiz_layout(struct sway_container *parent) {
86 if (!num_children) { 86 if (!num_children) {
87 return; 87 return;
88 } 88 }
89 size_t parent_offset = 0;
90 if (parent->parent->layout == L_TABBED) {
91 parent_offset = container_titlebar_height();
92 } else if (parent->parent->layout == L_STACKED) {
93 parent_offset =
94 container_titlebar_height() * parent->parent->children->length;
95 }
96 size_t parent_height = parent->height - parent_offset;
97
89 // Calculate total width of children 98 // Calculate total width of children
90 double total_width = 0; 99 double total_width = 0;
91 for (size_t i = 0; i < num_children; ++i) { 100 for (size_t i = 0; i < num_children; ++i) {
@@ -111,9 +120,9 @@ static void apply_horiz_layout(struct sway_container *parent) {
111 "Calculating arrangement for %p:%d (will scale %f by %f)", 120 "Calculating arrangement for %p:%d (will scale %f by %f)",
112 child, child->type, child->width, scale); 121 child, child->type, child->width, scale);
113 child->x = child_x; 122 child->x = child_x;
114 child->y = parent->y; 123 child->y = parent->y + parent_offset;
115 child->width = floor(child->width * scale); 124 child->width = floor(child->width * scale);
116 child->height = parent->height; 125 child->height = parent_height;
117 child_x += child->width; 126 child_x += child->width;
118 } 127 }
119 // Make last child use remaining width of parent 128 // Make last child use remaining width of parent
@@ -125,24 +134,33 @@ static void apply_vert_layout(struct sway_container *parent) {
125 if (!num_children) { 134 if (!num_children) {
126 return; 135 return;
127 } 136 }
137 size_t parent_offset = 0;
138 if (parent->parent->layout == L_TABBED) {
139 parent_offset = container_titlebar_height();
140 } else if (parent->parent->layout == L_STACKED) {
141 parent_offset =
142 container_titlebar_height() * parent->parent->children->length;
143 }
144 size_t parent_height = parent->height - parent_offset;
145
128 // Calculate total height of children 146 // Calculate total height of children
129 double total_height = 0; 147 double total_height = 0;
130 for (size_t i = 0; i < num_children; ++i) { 148 for (size_t i = 0; i < num_children; ++i) {
131 struct sway_container *child = parent->children->items[i]; 149 struct sway_container *child = parent->children->items[i];
132 if (child->height <= 0) { 150 if (child->height <= 0) {
133 if (num_children > 1) { 151 if (num_children > 1) {
134 child->height = parent->height / (num_children - 1); 152 child->height = parent_height / (num_children - 1);
135 } else { 153 } else {
136 child->height = parent->height; 154 child->height = parent_height;
137 } 155 }
138 } 156 }
139 total_height += child->height; 157 total_height += child->height;
140 } 158 }
141 double scale = parent->height / total_height; 159 double scale = parent_height / total_height;
142 160
143 // Resize 161 // Resize
144 wlr_log(L_DEBUG, "Arranging %p vertically", parent); 162 wlr_log(L_DEBUG, "Arranging %p vertically", parent);
145 double child_y = parent->y; 163 double child_y = parent->y + parent_offset;
146 struct sway_container *child; 164 struct sway_container *child;
147 for (size_t i = 0; i < num_children; ++i) { 165 for (size_t i = 0; i < num_children; ++i) {
148 child = parent->children->items[i]; 166 child = parent->children->items[i];
@@ -156,7 +174,33 @@ static void apply_vert_layout(struct sway_container *parent) {
156 child_y += child->height; 174 child_y += child->height;
157 } 175 }
158 // Make last child use remaining height of parent 176 // Make last child use remaining height of parent
159 child->height = parent->y + parent->height - child->y; 177 child->height = parent->y + parent_offset + parent_height - child->y;
178}
179
180static void apply_tabbed_layout(struct sway_container *parent) {
181 if (!parent->children->length) {
182 return;
183 }
184 for (int i = 0; i < parent->children->length; ++i) {
185 struct sway_container *child = parent->children->items[i];
186 child->x = parent->x;
187 child->y = parent->y;
188 child->width = parent->width;
189 child->height = parent->height;
190 }
191}
192
193static void apply_stacked_layout(struct sway_container *parent) {
194 if (!parent->children->length) {
195 return;
196 }
197 for (int i = 0; i < parent->children->length; ++i) {
198 struct sway_container *child = parent->children->items[i];
199 child->x = parent->x;
200 child->y = parent->y;
201 child->width = parent->width;
202 child->height = parent->height;
203 }
160} 204}
161 205
162void arrange_children_of(struct sway_container *parent) { 206void arrange_children_of(struct sway_container *parent) {
@@ -189,6 +233,12 @@ void arrange_children_of(struct sway_container *parent) {
189 case L_VERT: 233 case L_VERT:
190 apply_vert_layout(parent); 234 apply_vert_layout(parent);
191 break; 235 break;
236 case L_TABBED:
237 apply_tabbed_layout(parent);
238 break;
239 case L_STACKED:
240 apply_stacked_layout(parent);
241 break;
192 default: 242 default:
193 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); 243 wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout);
194 apply_horiz_layout(parent); 244 apply_horiz_layout(parent);
diff --git a/sway/tree/container.c b/sway/tree/container.c
index e47338e7..9cf18f61 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -73,6 +73,44 @@ static void container_close_notify(struct sway_container *container) {
73 } 73 }
74} 74}
75 75
76static void container_update_textures_recursive(struct sway_container *con) {
77 container_update_title_textures(con);
78
79 if (con->type == C_VIEW) {
80 view_update_marks_textures(con->sway_view);
81 } else {
82 for (int i = 0; i < con->children->length; ++i) {
83 struct sway_container *child = con->children->items[i];
84 container_update_textures_recursive(child);
85 }
86 }
87}
88
89static void handle_reparent(struct wl_listener *listener,
90 void *data) {
91 struct sway_container *container =
92 wl_container_of(listener, container, reparent);
93 struct sway_container *old_parent = data;
94
95 struct sway_container *old_output = old_parent;
96 if (old_output != NULL && old_output->type != C_OUTPUT) {
97 old_output = container_parent(old_output, C_OUTPUT);
98 }
99
100 struct sway_container *new_output = container->parent;
101 if (new_output != NULL && new_output->type != C_OUTPUT) {
102 new_output = container_parent(new_output, C_OUTPUT);
103 }
104
105 if (old_output && new_output) {
106 float old_scale = old_output->sway_output->wlr_output->scale;
107 float new_scale = new_output->sway_output->wlr_output->scale;
108 if (old_scale != new_scale) {
109 container_update_textures_recursive(container);
110 }
111 }
112}
113
76struct sway_container *container_create(enum sway_container_type type) { 114struct sway_container *container_create(enum sway_container_type type) {
77 // next id starts at 1 because 0 is assigned to root_container in layout.c 115 // next id starts at 1 because 0 is assigned to root_container in layout.c
78 static size_t next_id = 1; 116 static size_t next_id = 1;
@@ -92,6 +130,9 @@ struct sway_container *container_create(enum sway_container_type type) {
92 wl_signal_init(&c->events.destroy); 130 wl_signal_init(&c->events.destroy);
93 wl_signal_init(&c->events.reparent); 131 wl_signal_init(&c->events.reparent);
94 132
133 wl_signal_add(&c->events.reparent, &c->reparent);
134 c->reparent.notify = handle_reparent;
135
95 return c; 136 return c;
96} 137}
97 138
@@ -411,80 +452,154 @@ struct sway_container *container_parent(struct sway_container *container,
411 return container; 452 return container;
412} 453}
413 454
414struct sway_container *container_at(struct sway_container *parent, 455static struct sway_container *container_at_view(struct sway_container *swayc,
415 double lx, double ly, 456 double ox, double oy,
416 struct wlr_surface **surface, double *sx, double *sy) { 457 struct wlr_surface **surface, double *sx, double *sy) {
417 list_t *queue = get_bfs_queue(); 458 if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) {
418 if (!queue) {
419 return NULL; 459 return NULL;
420 } 460 }
461 struct sway_view *sview = swayc->sway_view;
462 double view_sx = ox - sview->x;
463 double view_sy = oy - sview->y;
421 464
422 list_add(queue, parent); 465 double _sx, _sy;
466 struct wlr_surface *_surface = NULL;
467 switch (sview->type) {
468 case SWAY_VIEW_XWAYLAND:
469 _surface = wlr_surface_surface_at(sview->surface,
470 view_sx, view_sy, &_sx, &_sy);
471 break;
472 case SWAY_VIEW_XDG_SHELL_V6:
473 // the top left corner of the sway container is the
474 // coordinate of the top left corner of the window geometry
475 view_sx += sview->wlr_xdg_surface_v6->geometry.x;
476 view_sy += sview->wlr_xdg_surface_v6->geometry.y;
477
478 _surface = wlr_xdg_surface_v6_surface_at(
479 sview->wlr_xdg_surface_v6,
480 view_sx, view_sy, &_sx, &_sy);
481 break;
482 case SWAY_VIEW_XDG_SHELL:
483 // the top left corner of the sway container is the
484 // coordinate of the top left corner of the window geometry
485 view_sx += sview->wlr_xdg_surface->geometry.x;
486 view_sy += sview->wlr_xdg_surface->geometry.y;
487
488 _surface = wlr_xdg_surface_surface_at(
489 sview->wlr_xdg_surface,
490 view_sx, view_sy, &_sx, &_sy);
491 break;
492 }
493 if (_surface) {
494 *sx = _sx;
495 *sy = _sy;
496 *surface = _surface;
497 }
498 return swayc;
499}
423 500
424 struct sway_container *swayc = NULL; 501/**
425 while (queue->length) { 502 * container_at for a container with layout L_TABBED.
426 swayc = queue->items[0]; 503 */
427 list_del(queue, 0); 504static struct sway_container *container_at_tabbed(struct sway_container *parent,
428 if (swayc->type == C_VIEW) { 505 double ox, double oy,
429 struct sway_view *sview = swayc->sway_view; 506 struct wlr_surface **surface, double *sx, double *sy) {
430 struct sway_container *soutput = container_parent(swayc, C_OUTPUT); 507 if (oy < parent->y || oy > parent->y + parent->height) {
431 struct wlr_box *output_box = 508 return NULL;
432 wlr_output_layout_get_box( 509 }
433 root_container.sway_root->output_layout, 510 struct sway_seat *seat = input_manager_current_seat(input_manager);
434 soutput->sway_output->wlr_output); 511
435 double ox = lx - output_box->x; 512 // Tab titles
436 double oy = ly - output_box->y; 513 int title_height = container_titlebar_height();
437 double view_sx = ox - sview->x; 514 if (oy < parent->y + title_height) {
438 double view_sy = oy - sview->y; 515 int tab_width = parent->width / parent->children->length;
439 516 int child_index = (ox - parent->x) / tab_width;
440 double _sx, _sy; 517 if (child_index >= parent->children->length) {
441 struct wlr_surface *_surface; 518 child_index = parent->children->length - 1;
442 switch (sview->type) { 519 }
443 case SWAY_VIEW_XWAYLAND: 520 struct sway_container *child = parent->children->items[child_index];
444 _surface = wlr_surface_surface_at(sview->surface, 521 return seat_get_focus_inactive(seat, child);
445 view_sx, view_sy, &_sx, &_sy); 522 }
446 break; 523
447 case SWAY_VIEW_XDG_SHELL_V6: 524 // Surfaces
448 // the top left corner of the sway container is the 525 struct sway_container *current = seat_get_active_child(seat, parent);
449 // coordinate of the top left corner of the window geometry 526
450 view_sx += sview->wlr_xdg_surface_v6->geometry.x; 527 return container_at(current, ox, oy, surface, sx, sy);
451 view_sy += sview->wlr_xdg_surface_v6->geometry.y; 528}
452 529
453 _surface = wlr_xdg_surface_v6_surface_at( 530/**
454 sview->wlr_xdg_surface_v6, 531 * container_at for a container with layout L_STACKED.
455 view_sx, view_sy, &_sx, &_sy); 532 */
456 break; 533static struct sway_container *container_at_stacked(
457 case SWAY_VIEW_XDG_SHELL: 534 struct sway_container *parent, double ox, double oy,
458 // the top left corner of the sway container is the 535 struct wlr_surface **surface, double *sx, double *sy) {
459 // coordinate of the top left corner of the window geometry 536 if (oy < parent->y || oy > parent->y + parent->height) {
460 view_sx += sview->wlr_xdg_surface->geometry.x; 537 return NULL;
461 view_sy += sview->wlr_xdg_surface->geometry.y; 538 }
462 539 struct sway_seat *seat = input_manager_current_seat(input_manager);
463 _surface = wlr_xdg_surface_surface_at( 540
464 sview->wlr_xdg_surface, 541 // Title bars
465 view_sx, view_sy, &_sx, &_sy); 542 int title_height = container_titlebar_height();
466 break; 543 int child_index = (oy - parent->y) / title_height;
467 } 544 if (child_index < parent->children->length) {
468 if (_surface) { 545 struct sway_container *child = parent->children->items[child_index];
469 *sx = _sx; 546 return seat_get_focus_inactive(seat, child);
470 *sy = _sy; 547 }
471 *surface = _surface; 548
472 return swayc; 549 // Surfaces
473 } 550 struct sway_container *current = seat_get_active_child(seat, parent);
474 // Check the view's decorations 551
475 struct wlr_box swayc_box = { 552 return container_at(current, ox, oy, surface, sx, sy);
476 .x = swayc->x, 553}
477 .y = swayc->y, 554
478 .width = swayc->width, 555/**
479 .height = swayc->height, 556 * container_at for a container with layout L_HORIZ or L_VERT.
480 }; 557 */
481 if (wlr_box_contains_point(&swayc_box, ox, oy)) { 558static struct sway_container *container_at_linear(struct sway_container *parent,
482 return swayc; 559 double ox, double oy,
483 } 560 struct wlr_surface **surface, double *sx, double *sy) {
484 } else { 561 for (int i = 0; i < parent->children->length; ++i) {
485 list_cat(queue, swayc->children); 562 struct sway_container *child = parent->children->items[i];
563 struct wlr_box box = {
564 .x = child->x,
565 .y = child->y,
566 .width = child->width,
567 .height = child->height,
568 };
569 if (wlr_box_contains_point(&box, ox, oy)) {
570 return container_at(child, ox, oy, surface, sx, sy);
486 } 571 }
487 } 572 }
573 return NULL;
574}
575
576struct sway_container *container_at(struct sway_container *parent,
577 double ox, double oy,
578 struct wlr_surface **surface, double *sx, double *sy) {
579 if (!sway_assert(parent->type >= C_WORKSPACE,
580 "Expected workspace or deeper")) {
581 return NULL;
582 }
583 if (parent->type == C_VIEW) {
584 return container_at_view(parent, ox, oy, surface, sx, sy);
585 }
586 if (!parent->children->length) {
587 return NULL;
588 }
589
590 switch (parent->layout) {
591 case L_HORIZ:
592 case L_VERT:
593 return container_at_linear(parent, ox, oy, surface, sx, sy);
594 case L_TABBED:
595 return container_at_tabbed(parent, ox, oy, surface, sx, sy);
596 case L_STACKED:
597 return container_at_stacked(parent, ox, oy, surface, sx, sy);
598 case L_FLOATING:
599 return NULL; // TODO
600 case L_NONE:
601 return NULL;
602 }
488 603
489 return NULL; 604 return NULL;
490} 605}
@@ -658,19 +773,96 @@ void container_calculate_title_height(struct sway_container *container) {
658 container->title_height = height; 773 container->title_height = height;
659} 774}
660 775
776/**
777 * Calculate and return the length of the concatenated child titles.
778 * An example concatenated title is: V[Terminal, Firefox]
779 * If buffer is not NULL, also populate the buffer with the concatenated title.
780 */
781static size_t concatenate_child_titles(struct sway_container *parent,
782 char *buffer) {
783 size_t len = 2; // V[
784 if (buffer) {
785 switch (parent->layout) {
786 case L_VERT:
787 strcpy(buffer, "V[");
788 break;
789 case L_HORIZ:
790 strcpy(buffer, "H[");
791 break;
792 case L_TABBED:
793 strcpy(buffer, "T[");
794 break;
795 case L_STACKED:
796 strcpy(buffer, "S[");
797 break;
798 case L_FLOATING:
799 strcpy(buffer, "F[");
800 break;
801 case L_NONE:
802 strcpy(buffer, "D[");
803 break;
804 }
805 }
806
807 for (int i = 0; i < parent->children->length; ++i) {
808 if (i != 0) {
809 len += 1;
810 if (buffer) {
811 strcat(buffer, " ");
812 }
813 }
814 struct sway_container *child = parent->children->items[i];
815 const char *identifier = NULL;
816 if (child->type == C_VIEW) {
817 identifier = view_get_class(child->sway_view);
818 if (!identifier) {
819 identifier = view_get_app_id(child->sway_view);
820 }
821 } else {
822 identifier = child->name;
823 }
824 if (identifier) {
825 len += strlen(identifier);
826 if (buffer) {
827 strcat(buffer, identifier);
828 }
829 } else {
830 len += 6;
831 if (buffer) {
832 strcat(buffer, "(null)");
833 }
834 }
835 }
836
837 len += 1;
838 if (buffer) {
839 strcat(buffer, "]");
840 }
841 return len;
842}
843
661void container_notify_child_title_changed(struct sway_container *container) { 844void container_notify_child_title_changed(struct sway_container *container) {
662 if (!container || container->type != C_CONTAINER) { 845 if (!container || container->type != C_CONTAINER) {
663 return; 846 return;
664 } 847 }
665 if (container->layout != L_TABBED && container->layout != L_STACKED) {
666 return;
667 }
668 if (container->formatted_title) { 848 if (container->formatted_title) {
669 free(container->formatted_title); 849 free(container->formatted_title);
670 } 850 }
671 // TODO: iterate children and concatenate their titles 851
672 container->formatted_title = strdup(""); 852 size_t len = concatenate_child_titles(container, NULL);
853 char *buffer = calloc(len + 1, sizeof(char));
854 if (!sway_assert(buffer, "Unable to allocate title string")) {
855 return;
856 }
857 concatenate_child_titles(container, buffer);
858
859 container->name = buffer;
860 container->formatted_title = buffer;
673 container_calculate_title_height(container); 861 container_calculate_title_height(container);
674 container_update_title_textures(container); 862 container_update_title_textures(container);
675 container_notify_child_title_changed(container->parent); 863 container_notify_child_title_changed(container->parent);
676} 864}
865
866size_t container_titlebar_height() {
867 return config->font_height + TITLEBAR_V_PADDING * 2;
868}
diff --git a/sway/tree/layout.c b/sway/tree/layout.c
index ec1c6fe5..f8acdf6c 100644
--- a/sway/tree/layout.c
+++ b/sway/tree/layout.c
@@ -149,6 +149,8 @@ struct sway_container *container_remove_child(struct sway_container *child) {
149 } 149 }
150 } 150 }
151 child->parent = NULL; 151 child->parent = NULL;
152 container_notify_child_title_changed(parent);
153
152 return parent; 154 return parent;
153} 155}
154 156
@@ -182,6 +184,8 @@ void container_move_to(struct sway_container *container,
182 container_sort_workspaces(new_parent); 184 container_sort_workspaces(new_parent);
183 seat_set_focus(seat, new_parent); 185 seat_set_focus(seat, new_parent);
184 } 186 }
187 container_notify_child_title_changed(old_parent);
188 container_notify_child_title_changed(new_parent);
185 if (old_parent) { 189 if (old_parent) {
186 arrange_children_of(old_parent); 190 arrange_children_of(old_parent);
187 } 191 }
@@ -234,9 +238,9 @@ static bool is_parallel(enum sway_container_layout layout,
234 enum movement_direction dir) { 238 enum movement_direction dir) {
235 switch (layout) { 239 switch (layout) {
236 case L_TABBED: 240 case L_TABBED:
237 case L_STACKED:
238 case L_HORIZ: 241 case L_HORIZ:
239 return dir == MOVE_LEFT || dir == MOVE_RIGHT; 242 return dir == MOVE_LEFT || dir == MOVE_RIGHT;
243 case L_STACKED:
240 case L_VERT: 244 case L_VERT:
241 return dir == MOVE_UP || dir == MOVE_DOWN; 245 return dir == MOVE_UP || dir == MOVE_DOWN;
242 default: 246 default:
@@ -485,6 +489,9 @@ void container_move(struct sway_container *container,
485 } 489 }
486 } 490 }
487 491
492 container_notify_child_title_changed(old_parent);
493 container_notify_child_title_changed(container->parent);
494
488 if (old_parent) { 495 if (old_parent) {
489 seat_set_focus(config->handler_context.seat, old_parent); 496 seat_set_focus(config->handler_context.seat, old_parent);
490 seat_set_focus(config->handler_context.seat, container); 497 seat_set_focus(config->handler_context.seat, container);
@@ -832,6 +839,8 @@ struct sway_container *container_split(struct sway_container *child,
832 container_add_child(cont, child); 839 container_add_child(cont, child);
833 } 840 }
834 841
842 container_notify_child_title_changed(cont);
843
835 return cont; 844 return cont;
836} 845}
837 846
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 648c1655..07157818 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -139,10 +139,20 @@ void view_autoconfigure(struct sway_view *view) {
139 return; 139 return;
140 } 140 }
141 141
142 int other_views = 1; 142 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE);
143
144 int other_views = 0;
143 if (config->hide_edge_borders == E_SMART) { 145 if (config->hide_edge_borders == E_SMART) {
144 struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); 146 struct sway_container *con = view->swayc;
145 other_views = container_count_descendants_of_type(ws, C_VIEW) - 1; 147 while (con != output) {
148 if (con->layout != L_TABBED && con->layout != L_STACKED) {
149 other_views += con->children ? con->children->length - 1 : 0;
150 if (other_views > 0) {
151 break;
152 }
153 }
154 con = con->parent;
155 }
146 } 156 }
147 157
148 view->border_top = view->border_bottom = true; 158 view->border_top = view->border_bottom = true;
@@ -151,47 +161,67 @@ void view_autoconfigure(struct sway_view *view) {
151 if (config->hide_edge_borders == E_BOTH 161 if (config->hide_edge_borders == E_BOTH
152 || config->hide_edge_borders == E_VERTICAL 162 || config->hide_edge_borders == E_VERTICAL
153 || (config->hide_edge_borders == E_SMART && !other_views)) { 163 || (config->hide_edge_borders == E_SMART && !other_views)) {
154 view->border_left = view->swayc->x != 0; 164 view->border_left = view->swayc->x != ws->x;
155 int right_x = view->swayc->x + view->swayc->width; 165 int right_x = view->swayc->x + view->swayc->width;
156 view->border_right = right_x != output->width; 166 view->border_right = right_x != ws->x + ws->width;
157 } 167 }
158 if (config->hide_edge_borders == E_BOTH 168 if (config->hide_edge_borders == E_BOTH
159 || config->hide_edge_borders == E_HORIZONTAL 169 || config->hide_edge_borders == E_HORIZONTAL
160 || (config->hide_edge_borders == E_SMART && !other_views)) { 170 || (config->hide_edge_borders == E_SMART && !other_views)) {
161 view->border_top = view->swayc->y != 0; 171 view->border_top = view->swayc->y != ws->y;
162 int bottom_y = view->swayc->y + view->swayc->height; 172 int bottom_y = view->swayc->y + view->swayc->height;
163 view->border_bottom = bottom_y != output->height; 173 view->border_bottom = bottom_y != ws->y + ws->height;
164 } 174 }
165 } 175 }
166 176
167 double x, y, width, height; 177 double x, y, width, height;
168 x = y = width = height = 0; 178 x = y = width = height = 0;
179 double y_offset = 0;
180
181 // In a tabbed or stacked container, the swayc's y is the top of the title
182 // area. We have to offset the surface y by the height of the title bar, and
183 // disable any top border because we'll always have the title bar.
184 if (view->swayc->parent->layout == L_TABBED) {
185 y_offset = container_titlebar_height();
186 view->border_top = 0;
187 } else if (view->swayc->parent->layout == L_STACKED) {
188 y_offset = container_titlebar_height()
189 * view->swayc->parent->children->length;
190 view->border_top = 0;
191 }
192
169 switch (view->border) { 193 switch (view->border) {
170 case B_NONE: 194 case B_NONE:
171 x = view->swayc->x; 195 x = view->swayc->x;
172 y = view->swayc->y; 196 y = view->swayc->y + y_offset;
173 width = view->swayc->width; 197 width = view->swayc->width;
174 height = view->swayc->height; 198 height = view->swayc->height - y_offset;
175 break; 199 break;
176 case B_PIXEL: 200 case B_PIXEL:
177 x = view->swayc->x + view->border_thickness * view->border_left; 201 x = view->swayc->x + view->border_thickness * view->border_left;
178 y = view->swayc->y + view->border_thickness * view->border_top; 202 y = view->swayc->y + view->border_thickness * view->border_top + y_offset;
179 width = view->swayc->width 203 width = view->swayc->width
180 - view->border_thickness * view->border_left 204 - view->border_thickness * view->border_left
181 - view->border_thickness * view->border_right; 205 - view->border_thickness * view->border_right;
182 height = view->swayc->height 206 height = view->swayc->height - y_offset
183 - view->border_thickness * view->border_top 207 - view->border_thickness * view->border_top
184 - view->border_thickness * view->border_bottom; 208 - view->border_thickness * view->border_bottom;
185 break; 209 break;
186 case B_NORMAL: 210 case B_NORMAL:
187 // Height is: border + title height + border + view height + border 211 // Height is: 1px border + 3px pad + title height + 3px pad + 1px border
188 x = view->swayc->x + view->border_thickness * view->border_left; 212 x = view->swayc->x + view->border_thickness * view->border_left;
189 y = view->swayc->y + config->font_height + view->border_thickness * 2;
190 width = view->swayc->width 213 width = view->swayc->width
191 - view->border_thickness * view->border_left 214 - view->border_thickness * view->border_left
192 - view->border_thickness * view->border_right; 215 - view->border_thickness * view->border_right;
193 height = view->swayc->height - config->font_height 216 if (y_offset) {
194 - view->border_thickness * (2 + view->border_bottom); 217 y = view->swayc->y + y_offset;
218 height = view->swayc->height - y_offset
219 - view->border_thickness * view->border_bottom;
220 } else {
221 y = view->swayc->y + container_titlebar_height();
222 height = view->swayc->height - container_titlebar_height()
223 - view->border_thickness * view->border_bottom;
224 }
195 break; 225 break;
196 } 226 }
197 227
@@ -440,6 +470,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
440 input_manager_set_focus(input_manager, cont); 470 input_manager_set_focus(input_manager, cont);
441 471
442 view_update_title(view, false); 472 view_update_title(view, false);
473 container_notify_child_title_changed(view->swayc->parent);
443 view_execute_criteria(view); 474 view_execute_criteria(view);
444 475
445 container_damage_whole(cont); 476 container_damage_whole(cont);
@@ -863,3 +894,28 @@ void view_update_marks_textures(struct sway_view *view) {
863 &config->border_colors.urgent); 894 &config->border_colors.urgent);
864 container_damage_whole(view->swayc); 895 container_damage_whole(view->swayc);
865} 896}
897
898bool view_is_visible(struct sway_view *view) {
899 if (!view->swayc) {
900 return false;
901 }
902 // Check view isn't in a tabbed or stacked container on an inactive tab
903 struct sway_seat *seat = input_manager_current_seat(input_manager);
904 struct sway_container *container = view->swayc;
905 while (container->type != C_WORKSPACE) {
906 if (container->parent->layout == L_TABBED ||
907 container->parent->layout == L_STACKED) {
908 if (seat_get_active_child(seat, container->parent) != container) {
909 return false;
910 }
911 }
912 container = container->parent;
913 }
914 // Check view isn't hidden by another fullscreen view
915 struct sway_container *workspace = container;
916 if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) {
917 return false;
918 }
919 // Check the workspace is visible
920 return workspace_is_visible(workspace);
921}