diff options
-rw-r--r-- | common/stringop.c | 14 | ||||
-rw-r--r-- | include/stringop.h | 5 | ||||
-rw-r--r-- | include/sway/tree/container.h | 17 | ||||
-rw-r--r-- | include/swaylock/swaylock.h | 15 | ||||
-rw-r--r-- | sway/commands/layout.c | 3 | ||||
-rw-r--r-- | sway/desktop/output.c | 602 | ||||
-rw-r--r-- | sway/input/cursor.c | 4 | ||||
-rw-r--r-- | sway/ipc-server.c | 22 | ||||
-rw-r--r-- | sway/tree/arrange.c | 33 | ||||
-rw-r--r-- | sway/tree/container.c | 115 | ||||
-rw-r--r-- | sway/tree/layout.c | 44 | ||||
-rw-r--r-- | sway/tree/view.c | 68 | ||||
-rw-r--r-- | swaylock/main.c | 99 | ||||
-rw-r--r-- | swaylock/render.c | 3 |
14 files changed, 559 insertions, 485 deletions
diff --git a/common/stringop.c b/common/stringop.c index 4a37543d..d9ae9925 100644 --- a/common/stringop.c +++ b/common/stringop.c | |||
@@ -55,6 +55,20 @@ void strip_quotes(char *str) { | |||
55 | *end = '\0'; | 55 | *end = '\0'; |
56 | } | 56 | } |
57 | 57 | ||
58 | char *lenient_strcat(char *dest, const char *src) { | ||
59 | if (dest && src) { | ||
60 | return strcat(dest, src); | ||
61 | } | ||
62 | return dest; | ||
63 | } | ||
64 | |||
65 | char *lenient_strncat(char *dest, const char *src, size_t len) { | ||
66 | if (dest && src) { | ||
67 | return strncat(dest, src, len); | ||
68 | } | ||
69 | return dest; | ||
70 | } | ||
71 | |||
58 | // strcmp that also handles null pointers. | 72 | // strcmp that also handles null pointers. |
59 | int lenient_strcmp(char *a, char *b) { | 73 | int lenient_strcmp(char *a, char *b) { |
60 | if (a == b) { | 74 | if (a == b) { |
diff --git a/include/stringop.h b/include/stringop.h index 7c29a745..e7f58011 100644 --- a/include/stringop.h +++ b/include/stringop.h | |||
@@ -1,5 +1,6 @@ | |||
1 | #ifndef _SWAY_STRINGOP_H | 1 | #ifndef _SWAY_STRINGOP_H |
2 | #define _SWAY_STRINGOP_H | 2 | #define _SWAY_STRINGOP_H |
3 | #include <stdlib.h> | ||
3 | #include "list.h" | 4 | #include "list.h" |
4 | 5 | ||
5 | #if !HAVE_DECL_SETENV | 6 | #if !HAVE_DECL_SETENV |
@@ -14,6 +15,10 @@ char *strip_whitespace(char *str); | |||
14 | char *strip_comments(char *str); | 15 | char *strip_comments(char *str); |
15 | void strip_quotes(char *str); | 16 | void strip_quotes(char *str); |
16 | 17 | ||
18 | // strcat that does nothing if dest or src is NULL | ||
19 | char *lenient_strcat(char *dest, const char *src); | ||
20 | char *lenient_strncat(char *dest, const char *src, size_t len); | ||
21 | |||
17 | // strcmp that also handles null pointers. | 22 | // strcmp that also handles null pointers. |
18 | int lenient_strcmp(char *a, char *b); | 23 | int lenient_strcmp(char *a, char *b); |
19 | 24 | ||
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 598a4f3d..a5f591ce 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -11,6 +11,12 @@ extern struct sway_container root_container; | |||
11 | struct sway_view; | 11 | struct sway_view; |
12 | struct sway_seat; | 12 | struct sway_seat; |
13 | 13 | ||
14 | #define TITLEBAR_BORDER_THICKNESS 1 | ||
15 | |||
16 | // Padding includes titlebar border | ||
17 | #define TITLEBAR_H_PADDING 3 | ||
18 | #define TITLEBAR_V_PADDING 4 | ||
19 | |||
14 | /** | 20 | /** |
15 | * Different kinds of containers. | 21 | * Different kinds of containers. |
16 | * | 22 | * |
@@ -210,6 +216,15 @@ void container_update_title_textures(struct sway_container *container); | |||
210 | */ | 216 | */ |
211 | void container_calculate_title_height(struct sway_container *container); | 217 | void container_calculate_title_height(struct sway_container *container); |
212 | 218 | ||
213 | void container_notify_child_title_changed(struct sway_container *container); | 219 | /** |
220 | * Notify a container that a tree modification has changed in its children, | ||
221 | * so the container can update its tree representation. | ||
222 | */ | ||
223 | void container_notify_subtree_changed(struct sway_container *container); | ||
224 | |||
225 | /** | ||
226 | * Return the height of a regular title bar. | ||
227 | */ | ||
228 | size_t container_titlebar_height(void); | ||
214 | 229 | ||
215 | #endif | 230 | #endif |
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index e161ada9..dae823b8 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h | |||
@@ -10,13 +10,13 @@ | |||
10 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 10 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
11 | 11 | ||
12 | enum auth_state { | 12 | enum auth_state { |
13 | AUTH_STATE_IDLE, | 13 | AUTH_STATE_IDLE, |
14 | AUTH_STATE_CLEAR, | 14 | AUTH_STATE_CLEAR, |
15 | AUTH_STATE_INPUT, | 15 | AUTH_STATE_INPUT, |
16 | AUTH_STATE_INPUT_NOP, | 16 | AUTH_STATE_INPUT_NOP, |
17 | AUTH_STATE_BACKSPACE, | 17 | AUTH_STATE_BACKSPACE, |
18 | AUTH_STATE_VALIDATING, | 18 | AUTH_STATE_VALIDATING, |
19 | AUTH_STATE_INVALID, | 19 | AUTH_STATE_INVALID, |
20 | }; | 20 | }; |
21 | 21 | ||
22 | struct swaylock_args { | 22 | struct swaylock_args { |
@@ -50,6 +50,7 @@ struct swaylock_surface { | |||
50 | cairo_surface_t *image; | 50 | cairo_surface_t *image; |
51 | struct swaylock_state *state; | 51 | struct swaylock_state *state; |
52 | struct wl_output *output; | 52 | struct wl_output *output; |
53 | uint32_t output_global_name; | ||
53 | struct zxdg_output_v1 *xdg_output; | 54 | struct zxdg_output_v1 *xdg_output; |
54 | struct wl_surface *surface; | 55 | struct wl_surface *surface; |
55 | struct zwlr_layer_surface_v1 *layer_surface; | 56 | struct zwlr_layer_surface_v1 *layer_surface; |
diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 8aa321ae..6b44b001 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c | |||
@@ -41,6 +41,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
41 | parent->layout = L_VERT; | 41 | parent->layout = L_VERT; |
42 | } else if (strcasecmp(argv[0], "tabbed") == 0) { | 42 | } else if (strcasecmp(argv[0], "tabbed") == 0) { |
43 | parent->layout = L_TABBED; | 43 | parent->layout = L_TABBED; |
44 | } else if (strcasecmp(argv[0], "stacking") == 0) { | ||
45 | parent->layout = L_STACKED; | ||
44 | } 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) { |
45 | if (parent->layout == L_HORIZ) { | 47 | if (parent->layout == L_HORIZ) { |
46 | parent->layout = L_VERT; | 48 | parent->layout = L_VERT; |
@@ -50,6 +52,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { | |||
50 | } | 52 | } |
51 | } | 53 | } |
52 | 54 | ||
55 | container_notify_subtree_changed(parent); | ||
53 | arrange_children_of(parent); | 56 | arrange_children_of(parent); |
54 | 57 | ||
55 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 58 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 551e96fc..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 | ||
272 | static 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 | |||
283 | static void render_rect(struct wlr_output *wlr_output, | 272 | static 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]) { |
@@ -317,108 +306,161 @@ static void premultiply_alpha(float color[4], float opacity) { | |||
317 | color[2] *= color[3]; | 306 | color[2] *= color[3]; |
318 | } | 307 | } |
319 | 308 | ||
309 | static 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 | |||
320 | /** | 320 | /** |
321 | * Render decorations for a view with "border normal". | 321 | * Render a view's surface and left/bottom/right borders. |
322 | * | ||
323 | * Care must be taken not to render over the same pixel multiple times, | ||
324 | * otherwise the colors will be incorrect when using opacity. | ||
325 | */ | 322 | */ |
326 | static void render_container_simple_border_normal(struct sway_output *output, | 323 | static void render_view(struct sway_output *output, pixman_region32_t *damage, |
327 | pixman_region32_t *output_damage, | 324 | struct sway_container *con, struct border_colors *colors) { |
328 | struct sway_container *con, struct border_colors *colors, | ||
329 | struct wlr_texture *title_texture, struct wlr_texture *marks_texture) { | ||
330 | struct wlr_box box; | ||
331 | float color[4]; | ||
332 | struct sway_view *view = con->sway_view; | 325 | struct sway_view *view = con->sway_view; |
333 | float output_scale = output->wlr_output->scale; | 326 | render_view_surfaces(view, output, damage); |
334 | 327 | ||
335 | if (view->border_left) { | 328 | struct wlr_box box; |
336 | // Child border - left edge | 329 | float output_scale = output->wlr_output->scale; |
337 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 330 | float color[4]; |
338 | premultiply_alpha(color, con->alpha); | ||
339 | box.x = con->x; | ||
340 | box.y = con->y + 1; | ||
341 | box.width = view->border_thickness; | ||
342 | box.height = con->height - 1 | ||
343 | - view->border_thickness * view->border_bottom; | ||
344 | scale_box(&box, output_scale); | ||
345 | render_rect(output->wlr_output, output_damage, &box, color); | ||
346 | } | ||
347 | 331 | ||
348 | if (view->border_right) { | 332 | if (view->border != B_NONE) { |
349 | // Child border - right edge | 333 | if (view->border_left) { |
350 | if (con->parent->children->length == 1 | ||
351 | && con->parent->layout == L_HORIZ) { | ||
352 | memcpy(&color, colors->indicator, sizeof(float) * 4); | ||
353 | } else { | ||
354 | 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); | ||
355 | } | 342 | } |
356 | premultiply_alpha(color, con->alpha); | ||
357 | box.x = con->x + con->width - view->border_thickness; | ||
358 | box.y = con->y + 1; | ||
359 | box.width = view->border_thickness; | ||
360 | box.height = con->height - 1 | ||
361 | - view->border_thickness * view->border_bottom; | ||
362 | scale_box(&box, output_scale); | ||
363 | render_rect(output->wlr_output, output_damage, &box, color); | ||
364 | } | ||
365 | 343 | ||
366 | if (view->border_bottom) { | 344 | if (view->border_right) { |
367 | // Child border - bottom edge | 345 | if (con->parent->children->length == 1 |
368 | if (con->parent->children->length == 1 | 346 | && con->parent->layout == L_HORIZ) { |
369 | && con->parent->layout == L_VERT) { | 347 | memcpy(&color, colors->indicator, sizeof(float) * 4); |
370 | memcpy(&color, colors->indicator, sizeof(float) * 4); | 348 | } else { |
371 | } else { | 349 | memcpy(&color, colors->child_border, sizeof(float) * 4); |
372 | 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); | ||
373 | } | 374 | } |
374 | premultiply_alpha(color, con->alpha); | ||
375 | box.x = con->x; | ||
376 | box.y = con->y + con->height - view->border_thickness; | ||
377 | box.width = con->width; | ||
378 | box.height = view->border_thickness; | ||
379 | scale_box(&box, output_scale); | ||
380 | render_rect(output->wlr_output, output_damage, &box, color); | ||
381 | } | 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 | */ | ||
388 | static 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; | ||
382 | 400 | ||
383 | // Single pixel bar above title | 401 | // Single pixel bar above title |
384 | memcpy(&color, colors->border, sizeof(float) * 4); | 402 | memcpy(&color, colors->border, sizeof(float) * 4); |
385 | premultiply_alpha(color, con->alpha); | 403 | premultiply_alpha(color, con->alpha); |
386 | box.x = con->x; | 404 | box.x = x; |
387 | box.y = con->y; | 405 | box.y = y; |
388 | box.width = con->width; | 406 | box.width = width; |
389 | box.height = 1; | 407 | box.height = TITLEBAR_BORDER_THICKNESS; |
390 | scale_box(&box, output_scale); | 408 | scale_box(&box, output_scale); |
391 | render_rect(output->wlr_output, output_damage, &box, color); | 409 | render_rect(output->wlr_output, output_damage, &box, color); |
392 | 410 | ||
393 | // Setting these makes marks and title easier | ||
394 | size_t inner_x = con->x + view->border_thickness * view->border_left; | ||
395 | size_t inner_width = con->width - view->border_thickness * view->border_left | ||
396 | - view->border_thickness * view->border_right; | ||
397 | |||
398 | // Single pixel bar below title | 411 | // Single pixel bar below title |
399 | memcpy(&color, colors->border, sizeof(float) * 4); | 412 | size_t left_offset = 0, right_offset = 0; |
400 | premultiply_alpha(color, con->alpha); | 413 | bool connects_sides = false; |
401 | box.x = inner_x; | 414 | if (layout == L_HORIZ || layout == L_VERT || |
402 | box.y = view->y - 1; | 415 | (layout == L_STACKED && is_last_child)) { |
403 | box.width = inner_width; | 416 | if (view) { |
404 | 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; | ||
405 | scale_box(&box, output_scale); | 426 | scale_box(&box, output_scale); |
406 | render_rect(output->wlr_output, output_damage, &box, color); | 427 | render_rect(output->wlr_output, output_damage, &box, color); |
407 | 428 | ||
429 | if (layout == L_TABBED) { | ||
430 | // Single pixel left edge | ||
431 | box.x = x; | ||
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; | ||
445 | |||
408 | // Marks | 446 | // Marks |
409 | size_t marks_width = 0; | 447 | size_t marks_width = 0; |
410 | if (config->show_marks && marks_texture) { | 448 | if (config->show_marks && marks_texture) { |
411 | struct wlr_box texture_box; | 449 | struct wlr_box texture_box; |
412 | wlr_texture_get_size(marks_texture, | 450 | wlr_texture_get_size(marks_texture, |
413 | &texture_box.width, &texture_box.height); | 451 | &texture_box.width, &texture_box.height); |
414 | texture_box.x = (inner_x + inner_width) * output_scale - texture_box.width; | 452 | texture_box.x = |
415 | 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; | ||
416 | 455 | ||
417 | float matrix[9]; | 456 | float matrix[9]; |
418 | wlr_matrix_project_box(matrix, &texture_box, | 457 | wlr_matrix_project_box(matrix, &texture_box, |
419 | WL_OUTPUT_TRANSFORM_NORMAL, | 458 | WL_OUTPUT_TRANSFORM_NORMAL, |
420 | 0.0, output->wlr_output->transform_matrix); | 459 | 0.0, output->wlr_output->transform_matrix); |
421 | 460 | ||
461 | if (inner_width * output_scale < texture_box.width) { | ||
462 | texture_box.width = inner_width * output_scale; | ||
463 | } | ||
422 | render_texture(output->wlr_output, output_damage, marks_texture, | 464 | render_texture(output->wlr_output, output_damage, marks_texture, |
423 | &texture_box, matrix, con->alpha); | 465 | &texture_box, matrix, con->alpha); |
424 | marks_width = texture_box.width; | 466 | marks_width = texture_box.width; |
@@ -430,8 +472,8 @@ static void render_container_simple_border_normal(struct sway_output *output, | |||
430 | struct wlr_box texture_box; | 472 | struct wlr_box texture_box; |
431 | wlr_texture_get_size(title_texture, | 473 | wlr_texture_get_size(title_texture, |
432 | &texture_box.width, &texture_box.height); | 474 | &texture_box.width, &texture_box.height); |
433 | texture_box.x = inner_x * output_scale; | 475 | texture_box.x = (x + TITLEBAR_H_PADDING) * output_scale; |
434 | texture_box.y = (con->y + view->border_thickness) * output_scale; | 476 | texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale; |
435 | 477 | ||
436 | float matrix[9]; | 478 | float matrix[9]; |
437 | wlr_matrix_project_box(matrix, &texture_box, | 479 | wlr_matrix_project_box(matrix, &texture_box, |
@@ -446,104 +488,89 @@ static void render_container_simple_border_normal(struct sway_output *output, | |||
446 | title_width = texture_box.width; | 488 | title_width = texture_box.width; |
447 | } | 489 | } |
448 | 490 | ||
449 | // Title background - above the text | 491 | // Padding above title |
450 | memcpy(&color, colors->background, sizeof(float) * 4); | 492 | memcpy(&color, colors->background, sizeof(float) * 4); |
451 | premultiply_alpha(color, con->alpha); | 493 | premultiply_alpha(color, con->alpha); |
452 | box.x = inner_x; | 494 | box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; |
453 | box.y = con->y + 1; | 495 | box.y = y + TITLEBAR_BORDER_THICKNESS; |
454 | box.width = inner_width; | 496 | box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; |
455 | box.height = view->border_thickness - 1; | 497 | box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; |
456 | scale_box(&box, output_scale); | 498 | scale_box(&box, output_scale); |
457 | render_rect(output->wlr_output, output_damage, &box, color); | 499 | render_rect(output->wlr_output, output_damage, &box, color); |
458 | 500 | ||
459 | // Title background - below the text | 501 | // Padding below title |
460 | box.y = (con->y + view->border_thickness + config->font_height) | 502 | box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; |
461 | * output_scale; | ||
462 | render_rect(output->wlr_output, output_damage, &box, color); | 503 | render_rect(output->wlr_output, output_damage, &box, color); |
463 | 504 | ||
464 | // Title background - filler between title and marks | 505 | // Filler between title and marks |
465 | box.width = inner_width * output_scale - title_width - marks_width; | 506 | box.width = inner_width * output_scale - title_width - marks_width; |
466 | if (box.width > 0) { | 507 | if (box.width > 0) { |
467 | box.x = inner_x * output_scale + title_width; | 508 | box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width; |
468 | box.y = (con->y + view->border_thickness) * output_scale; | 509 | box.y = (y + TITLEBAR_V_PADDING) * output_scale; |
469 | box.height = config->font_height * output_scale; | 510 | box.height = config->font_height * output_scale; |
470 | render_rect(output->wlr_output, output_damage, &box, color); | 511 | render_rect(output->wlr_output, output_damage, &box, color); |
471 | } | 512 | } |
472 | } | ||
473 | 513 | ||
474 | /** | 514 | // Padding left of title |
475 | * Render decorations for a view with "border pixel". | 515 | left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; |
476 | * | 516 | box.x = x + left_offset; |
477 | * Care must be taken not to render over the same pixel multiple times, | 517 | box.y = y + TITLEBAR_V_PADDING; |
478 | * otherwise the colors will be incorrect when using opacity. | 518 | box.width = TITLEBAR_H_PADDING - left_offset; |
479 | */ | 519 | box.height = config->font_height; |
480 | static void render_container_simple_border_pixel(struct sway_output *output, | 520 | scale_box(&box, output_scale); |
481 | pixman_region32_t *output_damage, struct sway_container *con, | 521 | render_rect(output->wlr_output, output_damage, &box, color); |
482 | struct border_colors *colors) { | ||
483 | struct wlr_box box; | ||
484 | float color[4]; | ||
485 | struct sway_view *view = con->sway_view; | ||
486 | float output_scale = output->wlr_output->scale; | ||
487 | 522 | ||
488 | if (view->border_left) { | 523 | // Padding right of marks |
489 | // Child border - left edge | 524 | right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; |
490 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 525 | box.x = x + width - TITLEBAR_H_PADDING; |
491 | premultiply_alpha(color, con->alpha); | 526 | box.y = y + TITLEBAR_V_PADDING; |
492 | box.x = con->x; | 527 | box.width = TITLEBAR_H_PADDING - right_offset; |
493 | box.y = con->y + view->border_thickness * view->border_top; | 528 | box.height = config->font_height; |
494 | box.width = view->border_thickness; | 529 | scale_box(&box, output_scale); |
495 | box.height = con->height - view->border_thickness | 530 | render_rect(output->wlr_output, output_damage, &box, color); |
496 | * (view->border_top + view->border_bottom); | ||
497 | scale_box(&box, output_scale); | ||
498 | render_rect(output->wlr_output, output_damage, &box, color); | ||
499 | } | ||
500 | 531 | ||
501 | if (view->border_right) { | 532 | if (connects_sides) { |
502 | // Child border - right edge | 533 | // Left pixel in line with bottom bar |
503 | if (con->parent->children->length == 1 | 534 | box.x = x; |
504 | && con->parent->layout == L_HORIZ) { | 535 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; |
505 | memcpy(&color, colors->indicator, sizeof(float) * 4); | 536 | box.width = view->border_thickness * view->border_left; |
506 | } else { | 537 | box.height = TITLEBAR_BORDER_THICKNESS; |
507 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
508 | } | ||
509 | premultiply_alpha(color, con->alpha); | ||
510 | box.x = con->x + con->width - view->border_thickness; | ||
511 | box.y = con->y + view->border_thickness * view->border_top; | ||
512 | box.width = view->border_thickness; | ||
513 | box.height = con->height - view->border_thickness | ||
514 | * (view->border_top + view->border_bottom); | ||
515 | scale_box(&box, output_scale); | 538 | scale_box(&box, output_scale); |
516 | render_rect(output->wlr_output, output_damage, &box, color); | 539 | render_rect(output->wlr_output, output_damage, &box, color); |
517 | } | ||
518 | 540 | ||
519 | if (view->border_top) { | 541 | // Right pixel in line with bottom bar |
520 | // Child border - top edge | 542 | box.x = x + width - view->border_thickness * view->border_right; |
521 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 543 | box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; |
522 | premultiply_alpha(color, con->alpha); | 544 | box.width = view->border_thickness * view->border_right; |
523 | box.x = con->x; | 545 | box.height = TITLEBAR_BORDER_THICKNESS; |
524 | box.y = con->y; | ||
525 | box.width = con->width; | ||
526 | box.height = view->border_thickness; | ||
527 | scale_box(&box, output_scale); | 546 | scale_box(&box, output_scale); |
528 | render_rect(output->wlr_output, output_damage, &box, color); | 547 | render_rect(output->wlr_output, output_damage, &box, color); |
529 | } | 548 | } |
549 | } | ||
530 | 550 | ||
531 | if (view->border_bottom) { | 551 | /** |
532 | // Child border - bottom edge | 552 | * Render the top border line for a view using "border pixel". |
533 | if (con->parent->children->length == 1 | 553 | */ |
534 | && con->parent->layout == L_VERT) { | 554 | static void render_top_border(struct sway_output *output, |
535 | memcpy(&color, colors->indicator, sizeof(float) * 4); | 555 | pixman_region32_t *output_damage, struct sway_container *con, |
536 | } else { | 556 | struct border_colors *colors) { |
537 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 557 | struct sway_view *view = con->sway_view; |
538 | } | 558 | if (!view->border_top) { |
539 | premultiply_alpha(color, con->alpha); | 559 | return; |
540 | box.x = con->x; | ||
541 | box.y = con->y + con->height - view->border_thickness; | ||
542 | box.width = con->width; | ||
543 | box.height = view->border_thickness; | ||
544 | scale_box(&box, output_scale); | ||
545 | render_rect(output->wlr_output, output_damage, &box, color); | ||
546 | } | 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); | ||
547 | } | 574 | } |
548 | 575 | ||
549 | static void render_container(struct sway_output *output, | 576 | static void render_container(struct sway_output *output, |
@@ -565,33 +592,30 @@ static void render_container_simple(struct sway_output *output, | |||
565 | struct sway_container *child = con->children->items[i]; | 592 | struct sway_container *child = con->children->items[i]; |
566 | 593 | ||
567 | if (child->type == C_VIEW) { | 594 | if (child->type == C_VIEW) { |
568 | if (child->sway_view->border != B_NONE) { | 595 | struct border_colors *colors; |
569 | struct border_colors *colors; | 596 | struct wlr_texture *title_texture; |
570 | struct wlr_texture *title_texture; | 597 | struct wlr_texture *marks_texture; |
571 | struct wlr_texture *marks_texture; | 598 | if (focus == child || parent_focused) { |
572 | if (focus == child || parent_focused) { | 599 | colors = &config->border_colors.focused; |
573 | colors = &config->border_colors.focused; | 600 | title_texture = child->title_focused; |
574 | title_texture = child->title_focused; | 601 | marks_texture = child->sway_view->marks_focused; |
575 | marks_texture = child->sway_view->marks_focused; | 602 | } else if (seat_get_focus_inactive(seat, con) == child) { |
576 | } else if (seat_get_focus_inactive(seat, con) == child) { | 603 | colors = &config->border_colors.focused_inactive; |
577 | colors = &config->border_colors.focused_inactive; | 604 | title_texture = child->title_focused_inactive; |
578 | title_texture = child->title_focused_inactive; | 605 | marks_texture = child->sway_view->marks_focused_inactive; |
579 | marks_texture = child->sway_view->marks_focused_inactive; | 606 | } else { |
580 | } else { | 607 | colors = &config->border_colors.unfocused; |
581 | colors = &config->border_colors.unfocused; | 608 | title_texture = child->title_unfocused; |
582 | title_texture = child->title_unfocused; | 609 | marks_texture = child->sway_view->marks_unfocused; |
583 | marks_texture = child->sway_view->marks_unfocused; | 610 | } |
584 | } | 611 | |
585 | 612 | if (child->sway_view->border == B_NORMAL) { | |
586 | if (child->sway_view->border == B_NORMAL) { | 613 | render_titlebar(output, damage, child, child->x, child->y, |
587 | render_container_simple_border_normal(output, damage, | 614 | child->width, colors, title_texture, marks_texture); |
588 | child, colors, title_texture, marks_texture); | 615 | } else { |
589 | } else { | 616 | render_top_border(output, damage, child, colors); |
590 | render_container_simple_border_pixel(output, damage, child, | ||
591 | colors); | ||
592 | } | ||
593 | } | 617 | } |
594 | render_view(child->sway_view, output, damage); | 618 | render_view(output, damage, child, colors); |
595 | } else { | 619 | } else { |
596 | render_container(output, damage, child, | 620 | render_container(output, damage, child, |
597 | parent_focused || focus == child); | 621 | parent_focused || focus == child); |
@@ -599,154 +623,71 @@ static void render_container_simple(struct sway_output *output, | |||
599 | } | 623 | } |
600 | } | 624 | } |
601 | 625 | ||
602 | static void render_tab(struct sway_output *output, pixman_region32_t *damage, | 626 | /** |
603 | struct sway_container *parent, int child_index, | 627 | * Render a container's children using the L_TABBED layout. |
604 | struct border_colors *colors, struct wlr_texture *title_texture) { | 628 | */ |
605 | struct sway_container *con = parent->children->items[child_index]; | 629 | static void render_container_tabbed(struct sway_output *output, |
606 | float output_scale = output->wlr_output->scale; | 630 | pixman_region32_t *damage, struct sway_container *con, |
607 | float color[4]; | 631 | bool parent_focused) { |
608 | struct wlr_box box; | 632 | if (!con->children->length) { |
609 | 633 | return; | |
610 | int tab_width = parent->width / parent->children->length; | ||
611 | int x = parent->x + tab_width * child_index; | ||
612 | // Make last tab use the remaining width of the parent | ||
613 | if (child_index == parent->children->length - 1) { | ||
614 | tab_width = parent->width - tab_width * child_index; | ||
615 | } | 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; | ||
616 | 639 | ||
617 | // Single pixel bar above title | 640 | // Render tabs |
618 | memcpy(&color, colors->border, sizeof(float) * 4); | 641 | for (int i = 0; i < con->children->length; ++i) { |
619 | premultiply_alpha(color, con->alpha); | 642 | struct sway_container *child = con->children->items[i]; |
620 | box.x = x; | 643 | struct border_colors *colors; |
621 | box.y = parent->y; | 644 | struct wlr_texture *title_texture; |
622 | box.width = tab_width; | 645 | struct wlr_texture *marks_texture; |
623 | box.height = 1; | 646 | struct sway_view *view = |
624 | scale_box(&box, output_scale); | 647 | child->type == C_VIEW ? child->sway_view : NULL; |
625 | render_rect(output->wlr_output, damage, &box, color); | ||
626 | |||
627 | // Single pixel bar below title | ||
628 | box.y = (parent->y + config->border_thickness * 2 + config->font_height - 1) | ||
629 | * output_scale; | ||
630 | render_rect(output->wlr_output, damage, &box, color); | ||
631 | |||
632 | // Single pixel bar on left | ||
633 | box.x = x; | ||
634 | box.y = parent->y + 1; | ||
635 | box.width = 1; | ||
636 | box.height = config->border_thickness * 2 + config->font_height - 2; | ||
637 | scale_box(&box, output_scale); | ||
638 | render_rect(output->wlr_output, damage, &box, color); | ||
639 | |||
640 | // Single pixel bar on right | ||
641 | box.x = (x + tab_width - 1) * output_scale; | ||
642 | render_rect(output->wlr_output, damage, &box, color); | ||
643 | |||
644 | // Title text | ||
645 | size_t title_width = 0; | ||
646 | if (title_texture) { | ||
647 | struct wlr_box texture_box; | ||
648 | wlr_texture_get_size(title_texture, | ||
649 | &texture_box.width, &texture_box.height); | ||
650 | texture_box.x = (x + 1 + config->border_thickness) * output_scale; | ||
651 | texture_box.y = (parent->y + config->border_thickness) * output_scale; | ||
652 | |||
653 | float matrix[9]; | ||
654 | wlr_matrix_project_box(matrix, &texture_box, | ||
655 | WL_OUTPUT_TRANSFORM_NORMAL, | ||
656 | 0.0, output->wlr_output->transform_matrix); | ||
657 | 648 | ||
658 | int available = (tab_width - config->border_thickness * 2 - 2) | 649 | if (focus == child || parent_focused) { |
659 | * output_scale; | 650 | colors = &config->border_colors.focused; |
660 | if (texture_box.width > available) { | 651 | title_texture = child->title_focused; |
661 | texture_box.width = available; | 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; | ||
662 | } | 661 | } |
663 | render_texture(output->wlr_output, damage, title_texture, | ||
664 | &texture_box, matrix, 1.0); | ||
665 | title_width = texture_box.width; | ||
666 | } | ||
667 | |||
668 | // Title background - above the text | ||
669 | memcpy(&color, colors->background, sizeof(float) * 4); | ||
670 | premultiply_alpha(color, con->alpha); | ||
671 | box.x = x + 1; | ||
672 | box.y = parent->y + 1; | ||
673 | box.width = tab_width - 2; | ||
674 | box.height = config->border_thickness - 1; | ||
675 | scale_box(&box, output_scale); | ||
676 | render_rect(output->wlr_output, damage, &box, color); | ||
677 | 662 | ||
678 | // Title background - below the text | 663 | int tab_width = con->width / con->children->length; |
679 | box.y = (parent->y + config->border_thickness + config->font_height) | 664 | int x = con->x + tab_width * i; |
680 | * output_scale; | 665 | // Make last tab use the remaining width of the parent |
681 | render_rect(output->wlr_output, damage, &box, color); | 666 | if (i == con->children->length - 1) { |
682 | 667 | tab_width = con->width - tab_width * i; | |
683 | // Title background - left of text | ||
684 | box.x = x + 1; | ||
685 | box.y = parent->y + config->border_thickness; | ||
686 | box.width = config->border_thickness; | ||
687 | box.height = config->font_height; | ||
688 | scale_box(&box, output_scale); | ||
689 | render_rect(output->wlr_output, damage, &box, color); | ||
690 | |||
691 | // Title background - right of text | ||
692 | box.x = (x + 1 + config->border_thickness) * output_scale + title_width; | ||
693 | box.y = (parent->y + config->border_thickness) * output_scale; | ||
694 | box.width = (tab_width - config->border_thickness - 2) * output_scale | ||
695 | - title_width; | ||
696 | box.height = config->font_height * output_scale; | ||
697 | render_rect(output->wlr_output, damage, &box, color); | ||
698 | } | ||
699 | |||
700 | static void render_tab_content(struct sway_output *output, | ||
701 | pixman_region32_t *damage, struct sway_container *con, | ||
702 | struct border_colors *colors) { | ||
703 | struct sway_view *view = con->sway_view; | ||
704 | render_view(view, output, damage); | ||
705 | |||
706 | struct wlr_box box; | ||
707 | float output_scale = output->wlr_output->scale; | ||
708 | float color[4]; | ||
709 | |||
710 | if (view->border != B_NONE) { | ||
711 | if (view->border_left) { | ||
712 | memcpy(&color, colors->child_border, sizeof(float) * 4); | ||
713 | premultiply_alpha(color, con->alpha); | ||
714 | box.x = con->x; | ||
715 | box.y = con->y + config->border_thickness * 2 + config->font_height; | ||
716 | box.width = view->border_thickness; | ||
717 | box.height = view->height; | ||
718 | scale_box(&box, output_scale); | ||
719 | render_rect(output->wlr_output, damage, &box, color); | ||
720 | } | 668 | } |
721 | 669 | ||
722 | if (view->border_right) { | 670 | render_titlebar(output, damage, child, x, child->y, tab_width, colors, |
723 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 671 | title_texture, marks_texture); |
724 | premultiply_alpha(color, con->alpha); | ||
725 | box.x = view->x + view->width; | ||
726 | box.y = con->y + config->border_thickness * 2 + config->font_height; | ||
727 | box.width = view->border_thickness; | ||
728 | box.height = view->height; | ||
729 | scale_box(&box, output_scale); | ||
730 | render_rect(output->wlr_output, damage, &box, color); | ||
731 | } | ||
732 | 672 | ||
733 | if (view->border_bottom) { | 673 | if (child == current) { |
734 | memcpy(&color, colors->child_border, sizeof(float) * 4); | 674 | current_colors = colors; |
735 | premultiply_alpha(color, con->alpha); | ||
736 | box.x = con->x; | ||
737 | box.y = view->y + view->height; | ||
738 | box.width = con->width; | ||
739 | box.height = view->border_thickness; | ||
740 | scale_box(&box, output_scale); | ||
741 | render_rect(output->wlr_output, damage, &box, color); | ||
742 | } | 675 | } |
743 | } | 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 | } | ||
744 | } | 685 | } |
745 | 686 | ||
746 | /** | 687 | /** |
747 | * Render a container's children using the L_TABBED layout. | 688 | * Render a container's children using the L_STACKED layout. |
748 | */ | 689 | */ |
749 | static void render_container_tabbed(struct sway_output *output, | 690 | static void render_container_stacked(struct sway_output *output, |
750 | pixman_region32_t *damage, struct sway_container *con, | 691 | pixman_region32_t *damage, struct sway_container *con, |
751 | bool parent_focused) { | 692 | bool parent_focused) { |
752 | if (!con->children->length) { | 693 | if (!con->children->length) { |
@@ -757,24 +698,32 @@ static void render_container_tabbed(struct sway_output *output, | |||
757 | struct sway_container *current = seat_get_active_child(seat, con); | 698 | struct sway_container *current = seat_get_active_child(seat, con); |
758 | struct border_colors *current_colors = NULL; | 699 | struct border_colors *current_colors = NULL; |
759 | 700 | ||
760 | // Render tabs | 701 | // Render titles |
761 | for (int i = 0; i < con->children->length; ++i) { | 702 | for (int i = 0; i < con->children->length; ++i) { |
762 | struct sway_container *child = con->children->items[i]; | 703 | struct sway_container *child = con->children->items[i]; |
763 | struct border_colors *colors; | 704 | struct border_colors *colors; |
764 | struct wlr_texture *title_texture; | 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; | ||
765 | 709 | ||
766 | if (focus == child || parent_focused) { | 710 | if (focus == child || parent_focused) { |
767 | colors = &config->border_colors.focused; | 711 | colors = &config->border_colors.focused; |
768 | title_texture = child->title_focused; | 712 | title_texture = child->title_focused; |
713 | marks_texture = view ? view->marks_focused : NULL; | ||
769 | } else if (child == current) { | 714 | } else if (child == current) { |
770 | colors = &config->border_colors.focused_inactive; | 715 | colors = &config->border_colors.focused_inactive; |
771 | title_texture = child->title_focused_inactive; | 716 | title_texture = child->title_focused_inactive; |
717 | marks_texture = view ? view->marks_focused_inactive : NULL; | ||
772 | } else { | 718 | } else { |
773 | colors = &config->border_colors.unfocused; | 719 | colors = &config->border_colors.unfocused; |
774 | title_texture = child->title_unfocused; | 720 | title_texture = child->title_unfocused; |
721 | marks_texture = view ? view->marks_unfocused : NULL; | ||
775 | } | 722 | } |
776 | 723 | ||
777 | render_tab(output, damage, con, i, colors, title_texture); | 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); | ||
778 | 727 | ||
779 | if (child == current) { | 728 | if (child == current) { |
780 | current_colors = colors; | 729 | current_colors = colors; |
@@ -783,21 +732,13 @@ static void render_container_tabbed(struct sway_output *output, | |||
783 | 732 | ||
784 | // Render surface and left/right/bottom borders | 733 | // Render surface and left/right/bottom borders |
785 | if (current->type == C_VIEW) { | 734 | if (current->type == C_VIEW) { |
786 | render_tab_content(output, damage, current, current_colors); | 735 | render_view(output, damage, current, current_colors); |
787 | } else { | 736 | } else { |
788 | render_container(output, damage, current, | 737 | render_container(output, damage, current, |
789 | parent_focused || current == focus); | 738 | parent_focused || current == focus); |
790 | } | 739 | } |
791 | } | 740 | } |
792 | 741 | ||
793 | /** | ||
794 | * Render a container's children using the L_STACKED layout. | ||
795 | */ | ||
796 | static void render_container_stacked(struct sway_output *output, | ||
797 | pixman_region32_t *damage, struct sway_container *con) { | ||
798 | // TODO | ||
799 | } | ||
800 | |||
801 | static void render_container(struct sway_output *output, | 742 | static void render_container(struct sway_output *output, |
802 | pixman_region32_t *damage, struct sway_container *con, | 743 | pixman_region32_t *damage, struct sway_container *con, |
803 | bool parent_focused) { | 744 | bool parent_focused) { |
@@ -808,7 +749,7 @@ static void render_container(struct sway_output *output, | |||
808 | render_container_simple(output, damage, con, parent_focused); | 749 | render_container_simple(output, damage, con, parent_focused); |
809 | break; | 750 | break; |
810 | case L_STACKED: | 751 | case L_STACKED: |
811 | render_container_stacked(output, damage, con); | 752 | render_container_stacked(output, damage, con, parent_focused); |
812 | break; | 753 | break; |
813 | case L_TABBED: | 754 | case L_TABBED: |
814 | render_container_tabbed(output, damage, con, parent_focused); | 755 | render_container_tabbed(output, damage, con, parent_focused); |
@@ -868,7 +809,8 @@ static void render_output(struct sway_output *output, struct timespec *when, | |||
868 | } | 809 | } |
869 | 810 | ||
870 | // TODO: handle views smaller than the output | 811 | // TODO: handle views smaller than the output |
871 | render_view(workspace->sway_workspace->fullscreen, output, damage); | 812 | render_view_surfaces( |
813 | workspace->sway_workspace->fullscreen, output, damage); | ||
872 | 814 | ||
873 | if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { | 815 | if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { |
874 | render_unmanaged(output, damage, | 816 | render_unmanaged(output, damage, |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index e0b987d2..9a0b4f01 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -147,10 +147,10 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
147 | struct sway_container *c = container_at_coords(cursor->seat, | 147 | struct sway_container *c = container_at_coords(cursor->seat, |
148 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); | 148 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); |
149 | if (c && config->focus_follows_mouse && allow_refocusing) { | 149 | if (c && config->focus_follows_mouse && allow_refocusing) { |
150 | if (c->type == C_WORKSPACE) { | 150 | struct sway_container *focus = seat_get_focus(cursor->seat); |
151 | if (focus && c->type == C_WORKSPACE) { | ||
151 | // Only follow the mouse if it would move to a new output | 152 | // Only follow the mouse if it would move to a new output |
152 | // Otherwise we'll focus the workspace, which is probably wrong | 153 | // Otherwise we'll focus the workspace, which is probably wrong |
153 | struct sway_container *focus = seat_get_focus(cursor->seat); | ||
154 | if (focus->type != C_OUTPUT) { | 154 | if (focus->type != C_OUTPUT) { |
155 | focus = container_parent(focus, C_OUTPUT); | 155 | focus = container_parent(focus, C_OUTPUT); |
156 | } | 156 | } |
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 | ||
433 | static 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 | |||
432 | void ipc_client_handle_command(struct ipc_client *client) { | 443 | void 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 8aebc0cc..bdef56ea 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -86,12 +86,14 @@ 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_height = parent->height; | ||
90 | size_t parent_offset = 0; | 89 | size_t parent_offset = 0; |
91 | if (parent->parent->layout == L_TABBED) { | 90 | if (parent->parent->layout == L_TABBED) { |
92 | parent_offset = config->border_thickness * 2 + config->font_height; | 91 | parent_offset = container_titlebar_height(); |
93 | parent_height -= parent_offset; | 92 | } else if (parent->parent->layout == L_STACKED) { |
93 | parent_offset = | ||
94 | container_titlebar_height() * parent->parent->children->length; | ||
94 | } | 95 | } |
96 | size_t parent_height = parent->height - parent_offset; | ||
95 | 97 | ||
96 | // Calculate total width of children | 98 | // Calculate total width of children |
97 | double total_width = 0; | 99 | double total_width = 0; |
@@ -132,12 +134,14 @@ static void apply_vert_layout(struct sway_container *parent) { | |||
132 | if (!num_children) { | 134 | if (!num_children) { |
133 | return; | 135 | return; |
134 | } | 136 | } |
135 | size_t parent_height = parent->height; | ||
136 | size_t parent_offset = 0; | 137 | size_t parent_offset = 0; |
137 | if (parent->parent->layout == L_TABBED) { | 138 | if (parent->parent->layout == L_TABBED) { |
138 | parent_offset = config->border_thickness * 2 + config->font_height; | 139 | parent_offset = container_titlebar_height(); |
139 | parent_height -= parent_offset; | 140 | } else if (parent->parent->layout == L_STACKED) { |
141 | parent_offset = | ||
142 | container_titlebar_height() * parent->parent->children->length; | ||
140 | } | 143 | } |
144 | size_t parent_height = parent->height - parent_offset; | ||
141 | 145 | ||
142 | // Calculate total height of children | 146 | // Calculate total height of children |
143 | double total_height = 0; | 147 | double total_height = 0; |
@@ -173,16 +177,24 @@ static void apply_vert_layout(struct sway_container *parent) { | |||
173 | child->height = parent->y + parent_offset + parent_height - child->y; | 177 | child->height = parent->y + parent_offset + parent_height - child->y; |
174 | } | 178 | } |
175 | 179 | ||
176 | static void apply_tabbed_layout(struct sway_container *parent) { | 180 | static void apply_tabbed_or_stacked_layout(struct sway_container *parent) { |
177 | if (!parent->children->length) { | 181 | if (!parent->children->length) { |
178 | return; | 182 | return; |
179 | } | 183 | } |
184 | size_t parent_offset = 0; | ||
185 | if (parent->parent->layout == L_TABBED) { | ||
186 | parent_offset = container_titlebar_height(); | ||
187 | } else if (parent->parent->layout == L_STACKED) { | ||
188 | parent_offset = | ||
189 | container_titlebar_height() * parent->parent->children->length; | ||
190 | } | ||
191 | size_t parent_height = parent->height - parent_offset; | ||
180 | for (int i = 0; i < parent->children->length; ++i) { | 192 | for (int i = 0; i < parent->children->length; ++i) { |
181 | struct sway_container *child = parent->children->items[i]; | 193 | struct sway_container *child = parent->children->items[i]; |
182 | child->x = parent->x; | 194 | child->x = parent->x; |
183 | child->y = parent->y; | 195 | child->y = parent->y + parent_offset; |
184 | child->width = parent->width; | 196 | child->width = parent->width; |
185 | child->height = parent->height; | 197 | child->height = parent_height; |
186 | } | 198 | } |
187 | } | 199 | } |
188 | 200 | ||
@@ -217,7 +229,8 @@ void arrange_children_of(struct sway_container *parent) { | |||
217 | apply_vert_layout(parent); | 229 | apply_vert_layout(parent); |
218 | break; | 230 | break; |
219 | case L_TABBED: | 231 | case L_TABBED: |
220 | apply_tabbed_layout(parent); | 232 | case L_STACKED: |
233 | apply_tabbed_or_stacked_layout(parent); | ||
221 | break; | 234 | break; |
222 | default: | 235 | default: |
223 | wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); | 236 | wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 5d88325f..f29a9adc 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "sway/tree/view.h" | 21 | #include "sway/tree/view.h" |
22 | #include "sway/tree/workspace.h" | 22 | #include "sway/tree/workspace.h" |
23 | #include "log.h" | 23 | #include "log.h" |
24 | #include "stringop.h" | ||
24 | 25 | ||
25 | static list_t *bfs_queue; | 26 | static list_t *bfs_queue; |
26 | 27 | ||
@@ -510,7 +511,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, | |||
510 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 511 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
511 | 512 | ||
512 | // Tab titles | 513 | // Tab titles |
513 | int title_height = config->border_thickness * 2 + config->font_height; | 514 | int title_height = container_titlebar_height(); |
514 | if (oy < parent->y + title_height) { | 515 | if (oy < parent->y + title_height) { |
515 | int tab_width = parent->width / parent->children->length; | 516 | int tab_width = parent->width / parent->children->length; |
516 | int child_index = (ox - parent->x) / tab_width; | 517 | int child_index = (ox - parent->x) / tab_width; |
@@ -533,8 +534,23 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, | |||
533 | static struct sway_container *container_at_stacked( | 534 | static struct sway_container *container_at_stacked( |
534 | struct sway_container *parent, double ox, double oy, | 535 | struct sway_container *parent, double ox, double oy, |
535 | struct wlr_surface **surface, double *sx, double *sy) { | 536 | struct wlr_surface **surface, double *sx, double *sy) { |
536 | // TODO | 537 | if (oy < parent->y || oy > parent->y + parent->height) { |
537 | return NULL; | 538 | return NULL; |
539 | } | ||
540 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
541 | |||
542 | // Title bars | ||
543 | int title_height = container_titlebar_height(); | ||
544 | int child_index = (oy - parent->y) / title_height; | ||
545 | if (child_index < parent->children->length) { | ||
546 | struct sway_container *child = parent->children->items[child_index]; | ||
547 | return seat_get_focus_inactive(seat, child); | ||
548 | } | ||
549 | |||
550 | // Surfaces | ||
551 | struct sway_container *current = seat_get_active_child(seat, parent); | ||
552 | |||
553 | return container_at(current, ox, oy, surface, sx, sy); | ||
538 | } | 554 | } |
539 | 555 | ||
540 | /** | 556 | /** |
@@ -759,42 +775,36 @@ void container_calculate_title_height(struct sway_container *container) { | |||
759 | } | 775 | } |
760 | 776 | ||
761 | /** | 777 | /** |
762 | * Calculate and return the length of the concatenated child titles. | 778 | * Calculate and return the length of the tree representation. |
763 | * An example concatenated title is: V[Terminal, Firefox] | 779 | * An example tree representation is: V[Terminal, Firefox] |
764 | * If buffer is not NULL, also populate the buffer with the concatenated title. | 780 | * If buffer is not NULL, also populate the buffer with the representation. |
765 | */ | 781 | */ |
766 | static size_t concatenate_child_titles(struct sway_container *parent, | 782 | static size_t get_tree_representation(struct sway_container *parent, char *buffer) { |
767 | char *buffer) { | 783 | size_t len = 2; |
768 | size_t len = 2; // V[ | 784 | switch (parent->layout) { |
769 | if (buffer) { | 785 | case L_VERT: |
770 | switch (parent->layout) { | 786 | lenient_strcat(buffer, "V["); |
771 | case L_VERT: | 787 | break; |
772 | strcpy(buffer, "V["); | 788 | case L_HORIZ: |
773 | break; | 789 | lenient_strcat(buffer, "H["); |
774 | case L_HORIZ: | 790 | break; |
775 | strcpy(buffer, "H["); | 791 | case L_TABBED: |
776 | break; | 792 | lenient_strcat(buffer, "T["); |
777 | case L_TABBED: | 793 | break; |
778 | strcpy(buffer, "T["); | 794 | case L_STACKED: |
779 | break; | 795 | lenient_strcat(buffer, "S["); |
780 | case L_STACKED: | 796 | break; |
781 | strcpy(buffer, "S["); | 797 | case L_FLOATING: |
782 | break; | 798 | lenient_strcat(buffer, "F["); |
783 | case L_FLOATING: | 799 | break; |
784 | strcpy(buffer, "F["); | 800 | case L_NONE: |
785 | break; | 801 | lenient_strcat(buffer, "D["); |
786 | case L_NONE: | 802 | break; |
787 | strcpy(buffer, "D["); | ||
788 | break; | ||
789 | } | ||
790 | } | 803 | } |
791 | |||
792 | for (int i = 0; i < parent->children->length; ++i) { | 804 | for (int i = 0; i < parent->children->length; ++i) { |
793 | if (i != 0) { | 805 | if (i != 0) { |
794 | len += 1; | 806 | ++len; |
795 | if (buffer) { | 807 | lenient_strcat(buffer, " "); |
796 | strcat(buffer, " "); | ||
797 | } | ||
798 | } | 808 | } |
799 | struct sway_container *child = parent->children->items[i]; | 809 | struct sway_container *child = parent->children->items[i]; |
800 | const char *identifier = NULL; | 810 | const char *identifier = NULL; |
@@ -804,46 +814,41 @@ static size_t concatenate_child_titles(struct sway_container *parent, | |||
804 | identifier = view_get_app_id(child->sway_view); | 814 | identifier = view_get_app_id(child->sway_view); |
805 | } | 815 | } |
806 | } else { | 816 | } else { |
807 | identifier = child->name; | 817 | identifier = child->formatted_title; |
808 | } | 818 | } |
809 | if (identifier) { | 819 | if (identifier) { |
810 | len += strlen(identifier); | 820 | len += strlen(identifier); |
811 | if (buffer) { | 821 | lenient_strcat(buffer, identifier); |
812 | strcat(buffer, identifier); | ||
813 | } | ||
814 | } else { | 822 | } else { |
815 | len += 6; | 823 | len += 6; |
816 | if (buffer) { | 824 | lenient_strcat(buffer, "(null)"); |
817 | strcat(buffer, "(null)"); | ||
818 | } | ||
819 | } | 825 | } |
820 | } | 826 | } |
821 | 827 | ++len; | |
822 | len += 1; | 828 | lenient_strcat(buffer, "]"); |
823 | if (buffer) { | ||
824 | strcat(buffer, "]"); | ||
825 | } | ||
826 | return len; | 829 | return len; |
827 | } | 830 | } |
828 | 831 | ||
829 | void container_notify_child_title_changed(struct sway_container *container) { | 832 | void container_notify_subtree_changed(struct sway_container *container) { |
830 | if (!container || container->type != C_CONTAINER) { | 833 | if (!container || container->type != C_CONTAINER) { |
831 | return; | 834 | return; |
832 | } | 835 | } |
833 | if (container->formatted_title) { | 836 | free(container->formatted_title); |
834 | free(container->formatted_title); | 837 | container->formatted_title = NULL; |
835 | } | ||
836 | 838 | ||
837 | size_t len = concatenate_child_titles(container, NULL); | 839 | size_t len = get_tree_representation(container, NULL); |
838 | char *buffer = calloc(len + 1, sizeof(char)); | 840 | char *buffer = calloc(len + 1, sizeof(char)); |
839 | if (!sway_assert(buffer, "Unable to allocate title string")) { | 841 | if (!sway_assert(buffer, "Unable to allocate title string")) { |
840 | return; | 842 | return; |
841 | } | 843 | } |
842 | concatenate_child_titles(container, buffer); | 844 | get_tree_representation(container, buffer); |
843 | 845 | ||
844 | container->name = buffer; | ||
845 | container->formatted_title = buffer; | 846 | container->formatted_title = buffer; |
846 | container_calculate_title_height(container); | 847 | container_calculate_title_height(container); |
847 | container_update_title_textures(container); | 848 | container_update_title_textures(container); |
848 | container_notify_child_title_changed(container->parent); | 849 | container_notify_subtree_changed(container->parent); |
850 | } | ||
851 | |||
852 | size_t container_titlebar_height() { | ||
853 | return config->font_height + TITLEBAR_V_PADDING * 2; | ||
849 | } | 854 | } |
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index f8acdf6c..21cec529 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -149,7 +149,7 @@ 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); | 152 | container_notify_subtree_changed(parent); |
153 | 153 | ||
154 | return parent; | 154 | return parent; |
155 | } | 155 | } |
@@ -184,12 +184,20 @@ void container_move_to(struct sway_container *container, | |||
184 | container_sort_workspaces(new_parent); | 184 | container_sort_workspaces(new_parent); |
185 | seat_set_focus(seat, new_parent); | 185 | seat_set_focus(seat, new_parent); |
186 | } | 186 | } |
187 | container_notify_child_title_changed(old_parent); | 187 | container_notify_subtree_changed(old_parent); |
188 | container_notify_child_title_changed(new_parent); | 188 | container_notify_subtree_changed(new_parent); |
189 | if (old_parent) { | 189 | if (old_parent) { |
190 | arrange_children_of(old_parent); | 190 | if (old_parent->type == C_OUTPUT) { |
191 | arrange_output(old_parent); | ||
192 | } else { | ||
193 | arrange_children_of(old_parent); | ||
194 | } | ||
195 | } | ||
196 | if (new_parent->type == C_OUTPUT) { | ||
197 | arrange_output(new_parent); | ||
198 | } else { | ||
199 | arrange_children_of(new_parent); | ||
191 | } | 200 | } |
192 | arrange_children_of(new_parent); | ||
193 | // If view was moved to a fullscreen workspace, refocus the fullscreen view | 201 | // If view was moved to a fullscreen workspace, refocus the fullscreen view |
194 | struct sway_container *new_workspace = container; | 202 | struct sway_container *new_workspace = container; |
195 | if (new_workspace->type != C_WORKSPACE) { | 203 | if (new_workspace->type != C_WORKSPACE) { |
@@ -319,9 +327,11 @@ void container_move(struct sway_container *container, | |||
319 | current = container_parent(container, C_OUTPUT); | 327 | current = container_parent(container, C_OUTPUT); |
320 | } | 328 | } |
321 | 329 | ||
322 | if (parent != container_flatten(parent)) { | 330 | struct sway_container *new_parent = container_flatten(parent); |
331 | if (new_parent != parent) { | ||
323 | // Special case: we were the last one in this container, so flatten it | 332 | // Special case: we were the last one in this container, so flatten it |
324 | // and leave | 333 | // and leave |
334 | arrange_children_of(new_parent); | ||
325 | update_debug_tree(); | 335 | update_debug_tree(); |
326 | return; | 336 | return; |
327 | } | 337 | } |
@@ -489,8 +499,8 @@ void container_move(struct sway_container *container, | |||
489 | } | 499 | } |
490 | } | 500 | } |
491 | 501 | ||
492 | container_notify_child_title_changed(old_parent); | 502 | container_notify_subtree_changed(old_parent); |
493 | container_notify_child_title_changed(container->parent); | 503 | container_notify_subtree_changed(container->parent); |
494 | 504 | ||
495 | if (old_parent) { | 505 | if (old_parent) { |
496 | seat_set_focus(config->handler_context.seat, old_parent); | 506 | seat_set_focus(config->handler_context.seat, old_parent); |
@@ -578,11 +588,19 @@ static struct sway_container *get_swayc_in_output_direction( | |||
578 | if (ws->children->length > 0) { | 588 | if (ws->children->length > 0) { |
579 | switch (dir) { | 589 | switch (dir) { |
580 | case MOVE_LEFT: | 590 | case MOVE_LEFT: |
581 | // get most right child of new output | 591 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { |
582 | return ws->children->items[ws->children->length-1]; | 592 | // get most right child of new output |
593 | return ws->children->items[ws->children->length-1]; | ||
594 | } else { | ||
595 | return seat_get_focus_inactive(seat, ws); | ||
596 | } | ||
583 | case MOVE_RIGHT: | 597 | case MOVE_RIGHT: |
584 | // get most left child of new output | 598 | if (ws->layout == L_HORIZ || ws->layout == L_TABBED) { |
585 | return ws->children->items[0]; | 599 | // get most left child of new output |
600 | return ws->children->items[0]; | ||
601 | } else { | ||
602 | return seat_get_focus_inactive(seat, ws); | ||
603 | } | ||
586 | case MOVE_UP: | 604 | case MOVE_UP: |
587 | case MOVE_DOWN: { | 605 | case MOVE_DOWN: { |
588 | struct sway_container *focused = | 606 | struct sway_container *focused = |
@@ -839,7 +857,7 @@ struct sway_container *container_split(struct sway_container *child, | |||
839 | container_add_child(cont, child); | 857 | container_add_child(cont, child); |
840 | } | 858 | } |
841 | 859 | ||
842 | container_notify_child_title_changed(cont); | 860 | container_notify_subtree_changed(cont); |
843 | 861 | ||
844 | return cont; | 862 | return cont; |
845 | } | 863 | } |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 64597c02..812d7740 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "sway/tree/workspace.h" | 17 | #include "sway/tree/workspace.h" |
18 | #include "sway/config.h" | 18 | #include "sway/config.h" |
19 | #include "pango.h" | 19 | #include "pango.h" |
20 | #include "stringop.h" | ||
20 | 21 | ||
21 | void view_init(struct sway_view *view, enum sway_view_type type, | 22 | void view_init(struct sway_view *view, enum sway_view_type type, |
22 | const struct sway_view_impl *impl) { | 23 | const struct sway_view_impl *impl) { |
@@ -141,9 +142,18 @@ void view_autoconfigure(struct sway_view *view) { | |||
141 | 142 | ||
142 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 143 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
143 | 144 | ||
144 | int other_views = 1; | 145 | int other_views = 0; |
145 | if (config->hide_edge_borders == E_SMART) { | 146 | if (config->hide_edge_borders == E_SMART) { |
146 | other_views = container_count_descendants_of_type(ws, C_VIEW) - 1; | 147 | struct sway_container *con = view->swayc; |
148 | while (con != output) { | ||
149 | if (con->layout != L_TABBED && con->layout != L_STACKED) { | ||
150 | other_views += con->children ? con->children->length - 1 : 0; | ||
151 | if (other_views > 0) { | ||
152 | break; | ||
153 | } | ||
154 | } | ||
155 | con = con->parent; | ||
156 | } | ||
147 | } | 157 | } |
148 | 158 | ||
149 | view->border_top = view->border_bottom = true; | 159 | view->border_top = view->border_bottom = true; |
@@ -173,11 +183,11 @@ void view_autoconfigure(struct sway_view *view) { | |||
173 | // area. We have to offset the surface y by the height of the title bar, and | 183 | // area. We have to offset the surface y by the height of the title bar, and |
174 | // disable any top border because we'll always have the title bar. | 184 | // disable any top border because we'll always have the title bar. |
175 | if (view->swayc->parent->layout == L_TABBED) { | 185 | if (view->swayc->parent->layout == L_TABBED) { |
176 | y_offset = config->border_thickness * 2 + config->font_height; | 186 | y_offset = container_titlebar_height(); |
177 | view->border_top = 0; | 187 | view->border_top = 0; |
178 | } else if (view->swayc->parent->layout == L_STACKED) { | 188 | } else if (view->swayc->parent->layout == L_STACKED) { |
179 | y_offset = config->border_thickness * 2 + config->font_height; | 189 | y_offset = container_titlebar_height() |
180 | y_offset *= view->swayc->parent->children->length; | 190 | * view->swayc->parent->children->length; |
181 | view->border_top = 0; | 191 | view->border_top = 0; |
182 | } | 192 | } |
183 | 193 | ||
@@ -199,7 +209,7 @@ void view_autoconfigure(struct sway_view *view) { | |||
199 | - view->border_thickness * view->border_bottom; | 209 | - view->border_thickness * view->border_bottom; |
200 | break; | 210 | break; |
201 | case B_NORMAL: | 211 | case B_NORMAL: |
202 | // Height is: border + title height + border + view height + border | 212 | // Height is: 1px border + 3px pad + title height + 3px pad + 1px border |
203 | x = view->swayc->x + view->border_thickness * view->border_left; | 213 | x = view->swayc->x + view->border_thickness * view->border_left; |
204 | width = view->swayc->width | 214 | width = view->swayc->width |
205 | - view->border_thickness * view->border_left | 215 | - view->border_thickness * view->border_left |
@@ -209,10 +219,9 @@ void view_autoconfigure(struct sway_view *view) { | |||
209 | height = view->swayc->height - y_offset | 219 | height = view->swayc->height - y_offset |
210 | - view->border_thickness * view->border_bottom; | 220 | - view->border_thickness * view->border_bottom; |
211 | } else { | 221 | } else { |
212 | y = view->swayc->y + config->font_height + view->border_thickness * 2 | 222 | y = view->swayc->y + container_titlebar_height(); |
213 | + y_offset; | 223 | height = view->swayc->height - container_titlebar_height() |
214 | height = view->swayc->height - config->font_height | 224 | - view->border_thickness * view->border_bottom; |
215 | - view->border_thickness * (2 + view->border_bottom); | ||
216 | } | 225 | } |
217 | break; | 226 | break; |
218 | } | 227 | } |
@@ -430,10 +439,11 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
430 | // Check if there's any `assign` criteria for the view | 439 | // Check if there's any `assign` criteria for the view |
431 | list_t *criterias = criteria_for_view(view, | 440 | list_t *criterias = criteria_for_view(view, |
432 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); | 441 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); |
442 | struct sway_container *workspace = NULL; | ||
433 | if (criterias->length) { | 443 | if (criterias->length) { |
434 | struct criteria *criteria = criterias->items[0]; | 444 | struct criteria *criteria = criterias->items[0]; |
435 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | 445 | if (criteria->type == CT_ASSIGN_WORKSPACE) { |
436 | struct sway_container *workspace = workspace_by_name(criteria->target); | 446 | workspace = workspace_by_name(criteria->target); |
437 | if (!workspace) { | 447 | if (!workspace) { |
438 | workspace = workspace_create(NULL, criteria->target); | 448 | workspace = workspace_create(NULL, criteria->target); |
439 | } | 449 | } |
@@ -460,9 +470,12 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
460 | 470 | ||
461 | arrange_children_of(cont->parent); | 471 | arrange_children_of(cont->parent); |
462 | input_manager_set_focus(input_manager, cont); | 472 | input_manager_set_focus(input_manager, cont); |
473 | if (workspace) { | ||
474 | workspace_switch(workspace); | ||
475 | } | ||
463 | 476 | ||
464 | view_update_title(view, false); | 477 | view_update_title(view, false); |
465 | container_notify_child_title_changed(view->swayc->parent); | 478 | container_notify_subtree_changed(view->swayc->parent); |
466 | view_execute_criteria(view); | 479 | view_execute_criteria(view); |
467 | 480 | ||
468 | container_damage_whole(cont); | 481 | container_damage_whole(cont); |
@@ -653,49 +666,35 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { | |||
653 | char *format = view->title_format; | 666 | char *format = view->title_format; |
654 | char *next = strchr(format, '%'); | 667 | char *next = strchr(format, '%'); |
655 | while (next) { | 668 | while (next) { |
656 | if (buffer) { | 669 | // Copy everything up to the % |
657 | // Copy everything up to the % | 670 | lenient_strncat(buffer, format, next - format); |
658 | strncat(buffer, format, next - format); | ||
659 | } | ||
660 | len += next - format; | 671 | len += next - format; |
661 | format = next; | 672 | format = next; |
662 | 673 | ||
663 | if (strncmp(next, "%title", 6) == 0) { | 674 | if (strncmp(next, "%title", 6) == 0) { |
664 | if (buffer && title) { | 675 | lenient_strcat(buffer, title); |
665 | strcat(buffer, title); | ||
666 | } | ||
667 | len += title_len; | 676 | len += title_len; |
668 | format += 6; | 677 | format += 6; |
669 | } else if (strncmp(next, "%class", 6) == 0) { | 678 | } else if (strncmp(next, "%class", 6) == 0) { |
670 | if (buffer && class) { | 679 | lenient_strcat(buffer, class); |
671 | strcat(buffer, class); | ||
672 | } | ||
673 | len += class_len; | 680 | len += class_len; |
674 | format += 6; | 681 | format += 6; |
675 | } else if (strncmp(next, "%instance", 9) == 0) { | 682 | } else if (strncmp(next, "%instance", 9) == 0) { |
676 | if (buffer && instance) { | 683 | lenient_strcat(buffer, instance); |
677 | strcat(buffer, instance); | ||
678 | } | ||
679 | len += instance_len; | 684 | len += instance_len; |
680 | format += 9; | 685 | format += 9; |
681 | } else if (strncmp(next, "%shell", 6) == 0) { | 686 | } else if (strncmp(next, "%shell", 6) == 0) { |
682 | if (buffer) { | 687 | lenient_strcat(buffer, shell); |
683 | strcat(buffer, shell); | ||
684 | } | ||
685 | len += shell_len; | 688 | len += shell_len; |
686 | format += 6; | 689 | format += 6; |
687 | } else { | 690 | } else { |
688 | if (buffer) { | 691 | lenient_strcat(buffer, "%"); |
689 | strcat(buffer, "%"); | ||
690 | } | ||
691 | ++format; | 692 | ++format; |
692 | ++len; | 693 | ++len; |
693 | } | 694 | } |
694 | next = strchr(format, '%'); | 695 | next = strchr(format, '%'); |
695 | } | 696 | } |
696 | if (buffer) { | 697 | lenient_strcat(buffer, format); |
697 | strcat(buffer, format); | ||
698 | } | ||
699 | len += strlen(format); | 698 | len += strlen(format); |
700 | 699 | ||
701 | return len; | 700 | return len; |
@@ -751,7 +750,6 @@ void view_update_title(struct sway_view *view, bool force) { | |||
751 | } | 750 | } |
752 | container_calculate_title_height(view->swayc); | 751 | container_calculate_title_height(view->swayc); |
753 | container_update_title_textures(view->swayc); | 752 | container_update_title_textures(view->swayc); |
754 | container_notify_child_title_changed(view->swayc->parent); | ||
755 | config_update_font_height(false); | 753 | config_update_font_height(false); |
756 | } | 754 | } |
757 | 755 | ||
diff --git a/swaylock/main.c b/swaylock/main.c index 11b5e8c8..f89f2849 100644 --- a/swaylock/main.c +++ b/swaylock/main.c | |||
@@ -64,6 +64,52 @@ static void daemonize() { | |||
64 | } | 64 | } |
65 | } | 65 | } |
66 | 66 | ||
67 | static void destroy_surface(struct swaylock_surface *surface) { | ||
68 | wl_list_remove(&surface->link); | ||
69 | if (surface->layer_surface != NULL) { | ||
70 | zwlr_layer_surface_v1_destroy(surface->layer_surface); | ||
71 | } | ||
72 | if (surface->surface != NULL) { | ||
73 | wl_surface_destroy(surface->surface); | ||
74 | } | ||
75 | destroy_buffer(&surface->buffers[0]); | ||
76 | destroy_buffer(&surface->buffers[1]); | ||
77 | wl_output_destroy(surface->output); | ||
78 | free(surface); | ||
79 | } | ||
80 | |||
81 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener; | ||
82 | |||
83 | static cairo_surface_t *select_image(struct swaylock_state *state, | ||
84 | struct swaylock_surface *surface); | ||
85 | |||
86 | static void create_layer_surface(struct swaylock_surface *surface) { | ||
87 | struct swaylock_state *state = surface->state; | ||
88 | |||
89 | surface->image = select_image(state, surface); | ||
90 | |||
91 | surface->surface = wl_compositor_create_surface(state->compositor); | ||
92 | assert(surface->surface); | ||
93 | |||
94 | surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||
95 | state->layer_shell, surface->surface, surface->output, | ||
96 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); | ||
97 | assert(surface->layer_surface); | ||
98 | |||
99 | zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0); | ||
100 | zwlr_layer_surface_v1_set_anchor(surface->layer_surface, | ||
101 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
102 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
103 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | | ||
104 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); | ||
105 | zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); | ||
106 | zwlr_layer_surface_v1_set_keyboard_interactivity( | ||
107 | surface->layer_surface, true); | ||
108 | zwlr_layer_surface_v1_add_listener(surface->layer_surface, | ||
109 | &layer_surface_listener, surface); | ||
110 | wl_surface_commit(surface->surface); | ||
111 | } | ||
112 | |||
67 | static void layer_surface_configure(void *data, | 113 | static void layer_surface_configure(void *data, |
68 | struct zwlr_layer_surface_v1 *layer_surface, | 114 | struct zwlr_layer_surface_v1 *layer_surface, |
69 | uint32_t serial, uint32_t width, uint32_t height) { | 115 | uint32_t serial, uint32_t width, uint32_t height) { |
@@ -77,9 +123,7 @@ static void layer_surface_configure(void *data, | |||
77 | static void layer_surface_closed(void *data, | 123 | static void layer_surface_closed(void *data, |
78 | struct zwlr_layer_surface_v1 *layer_surface) { | 124 | struct zwlr_layer_surface_v1 *layer_surface) { |
79 | struct swaylock_surface *surface = data; | 125 | struct swaylock_surface *surface = data; |
80 | zwlr_layer_surface_v1_destroy(surface->layer_surface); | 126 | destroy_surface(surface); |
81 | wl_surface_destroy(surface->surface); | ||
82 | surface->state->run_display = false; | ||
83 | } | 127 | } |
84 | 128 | ||
85 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { | 129 | static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { |
@@ -181,14 +225,27 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
181 | surface->state = state; | 225 | surface->state = state; |
182 | surface->output = wl_registry_bind(registry, name, | 226 | surface->output = wl_registry_bind(registry, name, |
183 | &wl_output_interface, 3); | 227 | &wl_output_interface, 3); |
228 | surface->output_global_name = name; | ||
184 | wl_output_add_listener(surface->output, &_wl_output_listener, surface); | 229 | wl_output_add_listener(surface->output, &_wl_output_listener, surface); |
185 | wl_list_insert(&state->surfaces, &surface->link); | 230 | wl_list_insert(&state->surfaces, &surface->link); |
231 | |||
232 | if (state->run_display) { | ||
233 | create_layer_surface(surface); | ||
234 | wl_display_roundtrip(state->display); | ||
235 | } | ||
186 | } | 236 | } |
187 | } | 237 | } |
188 | 238 | ||
189 | static void handle_global_remove(void *data, struct wl_registry *registry, | 239 | static void handle_global_remove(void *data, struct wl_registry *registry, |
190 | uint32_t name) { | 240 | uint32_t name) { |
191 | // who cares | 241 | struct swaylock_state *state = data; |
242 | struct swaylock_surface *surface; | ||
243 | wl_list_for_each(surface, &state->surfaces, link) { | ||
244 | if (surface->output_global_name == name) { | ||
245 | destroy_surface(surface); | ||
246 | break; | ||
247 | } | ||
248 | } | ||
192 | } | 249 | } |
193 | 250 | ||
194 | static const struct wl_registry_listener registry_listener = { | 251 | static const struct wl_registry_listener registry_listener = { |
@@ -276,7 +333,7 @@ int main(int argc, char **argv) { | |||
276 | {0, 0, 0, 0} | 333 | {0, 0, 0, 0} |
277 | }; | 334 | }; |
278 | 335 | ||
279 | const char *usage = | 336 | const char usage[] = |
280 | "Usage: swaylock [options...]\n" | 337 | "Usage: swaylock [options...]\n" |
281 | "\n" | 338 | "\n" |
282 | " -h, --help Show help message and quit.\n" | 339 | " -h, --help Show help message and quit.\n" |
@@ -288,13 +345,13 @@ int main(int argc, char **argv) { | |||
288 | " -u, --no-unlock-indicator Disable the unlock indicator.\n" | 345 | " -u, --no-unlock-indicator Disable the unlock indicator.\n" |
289 | " -f, --daemonize Detach from the controlling terminal.\n"; | 346 | " -f, --daemonize Detach from the controlling terminal.\n"; |
290 | 347 | ||
291 | struct swaylock_args args = { | 348 | state.args = (struct swaylock_args){ |
292 | .mode = BACKGROUND_MODE_SOLID_COLOR, | 349 | .mode = BACKGROUND_MODE_SOLID_COLOR, |
293 | .color = 0xFFFFFFFF, | 350 | .color = 0xFFFFFFFF, |
294 | .show_indicator = true, | 351 | .show_indicator = true, |
295 | }; | 352 | }; |
296 | state.args = args; | ||
297 | wl_list_init(&state.images); | 353 | wl_list_init(&state.images); |
354 | |||
298 | wlr_log_init(L_DEBUG, NULL); | 355 | wlr_log_init(L_DEBUG, NULL); |
299 | 356 | ||
300 | int c; | 357 | int c; |
@@ -369,6 +426,8 @@ int main(int argc, char **argv) { | |||
369 | return 0; | 426 | return 0; |
370 | } | 427 | } |
371 | 428 | ||
429 | zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); | ||
430 | |||
372 | if (state.zxdg_output_manager) { | 431 | if (state.zxdg_output_manager) { |
373 | struct swaylock_surface *surface; | 432 | struct swaylock_surface *surface; |
374 | wl_list_for_each(surface, &state.surfaces, link) { | 433 | wl_list_for_each(surface, &state.surfaces, link) { |
@@ -385,33 +444,9 @@ int main(int argc, char **argv) { | |||
385 | 444 | ||
386 | struct swaylock_surface *surface; | 445 | struct swaylock_surface *surface; |
387 | wl_list_for_each(surface, &state.surfaces, link) { | 446 | wl_list_for_each(surface, &state.surfaces, link) { |
388 | surface->image = select_image(&state, surface); | 447 | create_layer_surface(surface); |
389 | |||
390 | surface->surface = wl_compositor_create_surface(state.compositor); | ||
391 | assert(surface->surface); | ||
392 | |||
393 | surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||
394 | state.layer_shell, surface->surface, surface->output, | ||
395 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); | ||
396 | assert(surface->layer_surface); | ||
397 | |||
398 | zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0); | ||
399 | zwlr_layer_surface_v1_set_anchor(surface->layer_surface, | ||
400 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | | ||
401 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | | ||
402 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | | ||
403 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); | ||
404 | zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); | ||
405 | zwlr_layer_surface_v1_set_keyboard_interactivity( | ||
406 | surface->layer_surface, true); | ||
407 | zwlr_layer_surface_v1_add_listener(surface->layer_surface, | ||
408 | &layer_surface_listener, surface); | ||
409 | wl_surface_commit(surface->surface); | ||
410 | wl_display_roundtrip(state.display); | ||
411 | } | 448 | } |
412 | 449 | ||
413 | zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); | ||
414 | |||
415 | state.run_display = true; | 450 | state.run_display = true; |
416 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { | 451 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { |
417 | // This space intentionally left blank | 452 | // This space intentionally left blank |
diff --git a/swaylock/render.c b/swaylock/render.c index cc40f4e9..05236dea 100644 --- a/swaylock/render.c +++ b/swaylock/render.c | |||
@@ -17,6 +17,9 @@ void render_frame(struct swaylock_surface *surface) { | |||
17 | 17 | ||
18 | int buffer_width = surface->width * surface->scale; | 18 | int buffer_width = surface->width * surface->scale; |
19 | int buffer_height = surface->height * surface->scale; | 19 | int buffer_height = surface->height * surface->scale; |
20 | if (buffer_width == 0 || buffer_height == 0) { | ||
21 | return; // not yet configured | ||
22 | } | ||
20 | 23 | ||
21 | surface->current_buffer = get_next_buffer(state->shm, | 24 | surface->current_buffer = get_next_buffer(state->shm, |
22 | surface->buffers, buffer_width, buffer_height); | 25 | surface->buffers, buffer_width, buffer_height); |