From 9fd28aea8cc78fc19bbde3ca9c25e3e7a5465f96 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 19 May 2018 23:33:36 +1000 Subject: [PATCH 01/23] Rebuild textures if needed when moving a container When moving a container to an output which has a different scale than the previous, rebuild the title and marks textures at the new scale. Fixes #1999. --- include/sway/tree/container.h | 2 ++ sway/tree/container.c | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ec9e2385..e7e9d944 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -98,6 +98,8 @@ struct sway_container { // Passed the previous parent struct wl_signal reparent; } events; + + struct wl_listener reparent; }; struct sway_container *container_create(enum sway_container_type type); diff --git a/sway/tree/container.c b/sway/tree/container.c index e47338e7..feaf7647 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -73,6 +73,44 @@ static void container_close_notify(struct sway_container *container) { } } +static void container_update_textures_recursive(struct sway_container *con) { + container_update_title_textures(con); + + if (con->type == C_VIEW) { + view_update_marks_textures(con->sway_view); + } else { + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + container_update_textures_recursive(child); + } + } +} + +static void handle_reparent(struct wl_listener *listener, + void *data) { + struct sway_container *container = + wl_container_of(listener, container, reparent); + struct sway_container *old_parent = data; + + struct sway_container *old_output = old_parent; + if (old_output != NULL && old_output->type != C_OUTPUT) { + old_output = container_parent(old_output, C_OUTPUT); + } + + struct sway_container *new_output = container->parent; + if (new_output != NULL && new_output->type != C_OUTPUT) { + new_output = container_parent(new_output, C_OUTPUT); + } + + if (old_output && new_output) { + float old_scale = old_output->sway_output->wlr_output->scale; + float new_scale = new_output->sway_output->wlr_output->scale; + if (old_scale != new_scale) { + container_update_textures_recursive(container); + } + } +} + struct sway_container *container_create(enum sway_container_type type) { // next id starts at 1 because 0 is assigned to root_container in layout.c static size_t next_id = 1; @@ -92,6 +130,9 @@ struct sway_container *container_create(enum sway_container_type type) { wl_signal_init(&c->events.destroy); wl_signal_init(&c->events.reparent); + wl_signal_add(&c->events.reparent, &c->reparent); + c->reparent.notify = handle_reparent; + return c; } From fcc11f977102524d4f8fe78d60d9d99936b15b67 Mon Sep 17 00:00:00 2001 From: Konstantin Pospelov Date: Sat, 19 May 2018 18:15:25 +0300 Subject: [PATCH 02/23] Fix pixel bar below title This commit changes how the left and right indents are calculated for the title bottom pixel bar, so that it is displayed properly in case the left or right border is hidden. --- sway/desktop/output.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 94562052..ebb3ddaa 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -383,21 +383,21 @@ static void render_container_simple_border_normal(struct sway_output *output, scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); - // Single pixel bar below title - memcpy(&color, colors->border, sizeof(float) * 4); - color[3] *= con->alpha; - box.x = con->x + view->border_thickness; - box.y = view->y - 1; - box.width = con->width - view->border_thickness * 2; - box.height = 1; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - // Setting these makes marks and title easier size_t inner_x = con->x + view->border_thickness * view->border_left; size_t inner_width = con->width - view->border_thickness * view->border_left - view->border_thickness * view->border_right; + // Single pixel bar below title + memcpy(&color, colors->border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = inner_x; + box.y = view->y - 1; + box.width = inner_width; + box.height = 1; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + // Marks size_t marks_width = 0; if (config->show_marks && marks_texture) { From 9d99e5c2e72a464554b034c014437a495bb7b409 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 19 May 2018 08:47:47 +1000 Subject: [PATCH 03/23] Swaylock: Allow per-output images --- include/swaylock/swaylock.h | 12 ++++ meson.build | 2 +- protocols/meson.build | 6 +- swaylock/main.c | 138 ++++++++++++++++++++++++++++++------ swaylock/render.c | 2 +- 5 files changed, 136 insertions(+), 24 deletions(-) diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index 07b908d7..e161ada9 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -37,23 +37,35 @@ struct swaylock_state { struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; struct wl_shm *shm; struct wl_list surfaces; + struct wl_list images; struct swaylock_args args; struct swaylock_password password; struct swaylock_xkb xkb; enum auth_state auth_state; bool run_display; + struct zxdg_output_manager_v1 *zxdg_output_manager; }; struct swaylock_surface { cairo_surface_t *image; struct swaylock_state *state; struct wl_output *output; + struct zxdg_output_v1 *xdg_output; struct wl_surface *surface; struct zwlr_layer_surface_v1 *layer_surface; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; uint32_t width, height; int32_t scale; + char *output_name; + struct wl_list link; +}; + +// There is exactly one swaylock_image for each -i argument +struct swaylock_image { + char *path; + char *output_name; + cairo_surface_t *cairo_surface; struct wl_list link; }; diff --git a/meson.build b/meson.build index b943236f..d4ee1a11 100644 --- a/meson.build +++ b/meson.build @@ -29,7 +29,7 @@ wayland_server = dependency('wayland-server') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_egl = dependency('wayland-egl') -wayland_protos = dependency('wayland-protocols') +wayland_protos = dependency('wayland-protocols', version: '>=1.14') xkbcommon = dependency('xkbcommon') cairo = dependency('cairo') pango = dependency('pango') diff --git a/protocols/meson.build b/protocols/meson.build index 9966c02f..a031245c 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -29,16 +29,18 @@ wayland_scanner_server = generator( client_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], ['idle.xml'], - ['wlr-input-inhibitor-unstable-v1.xml'] + ['wlr-input-inhibitor-unstable-v1.xml'], ] server_protocols = [ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], ['wlr-layer-shell-unstable-v1.xml'], - ['wlr-input-inhibitor-unstable-v1.xml'] + ['wlr-input-inhibitor-unstable-v1.xml'], ] client_protos_src = [] diff --git a/swaylock/main.c b/swaylock/main.c index 200c1b5f..91c28bd2 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "swaylock/seat.h" #include "swaylock/swaylock.h" @@ -20,9 +21,11 @@ #include "pool-buffer.h" #include "cairo.h" #include "log.h" +#include "stringop.h" #include "util.h" #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "xdg-output-unstable-v1-client-protocol.h" void sway_terminate(int exit_code) { exit(exit_code); @@ -84,22 +87,23 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = layer_surface_closed, }; -static void output_geometry(void *data, struct wl_output *output, int32_t x, +static void handle_wl_output_geometry(void *data, struct wl_output *output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, const char *make, const char *model, int32_t transform) { // Who cares } -static void output_mode(void *data, struct wl_output *output, uint32_t flags, +static void handle_wl_output_mode(void *data, struct wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { // Who cares } -static void output_done(void *data, struct wl_output *output) { +static void handle_wl_output_done(void *data, struct wl_output *output) { // Who cares } -static void output_scale(void *data, struct wl_output *output, int32_t factor) { +static void handle_wl_output_scale(void *data, struct wl_output *output, + int32_t factor) { struct swaylock_surface *surface = data; surface->scale = factor; if (surface->state->run_display) { @@ -107,11 +111,46 @@ static void output_scale(void *data, struct wl_output *output, int32_t factor) { } } -struct wl_output_listener output_listener = { - .geometry = output_geometry, - .mode = output_mode, - .done = output_done, - .scale = output_scale, +struct wl_output_listener _wl_output_listener = { + .geometry = handle_wl_output_geometry, + .mode = handle_wl_output_mode, + .done = handle_wl_output_done, + .scale = handle_wl_output_scale, +}; + +static void handle_xdg_output_logical_size(void *data, struct zxdg_output_v1 *output, + int width, int height) { + // Who cares +} + +static void handle_xdg_output_logical_position(void *data, + struct zxdg_output_v1 *output, int x, int y) { + // Who cares +} + +static void handle_xdg_output_name(void *data, struct zxdg_output_v1 *output, + const char *name) { + wlr_log(L_DEBUG, "output name is %s", name); + struct swaylock_surface *surface = data; + surface->xdg_output = output; + surface->output_name = strdup(name); +} + +static void handle_xdg_output_description(void *data, struct zxdg_output_v1 *output, + const char *description) { + // Who cares +} + +static void handle_xdg_output_done(void *data, struct zxdg_output_v1 *output) { + // Who cares +} + +struct zxdg_output_v1_listener _xdg_output_listener = { + .logical_position = handle_xdg_output_logical_position, + .logical_size = handle_xdg_output_logical_size, + .done = handle_xdg_output_done, + .name = handle_xdg_output_name, + .description = handle_xdg_output_description, }; static void handle_global(void *data, struct wl_registry *registry, @@ -133,13 +172,16 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { state->input_inhibit_manager = wl_registry_bind( registry, name, &zwlr_input_inhibit_manager_v1_interface, 1); + } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { + state->zxdg_output_manager = wl_registry_bind( + registry, name, &zxdg_output_manager_v1_interface, 2); } else if (strcmp(interface, wl_output_interface.name) == 0) { struct swaylock_surface *surface = calloc(1, sizeof(struct swaylock_surface)); surface->state = state; surface->output = wl_registry_bind(registry, name, &wl_output_interface, 3); - wl_output_add_listener(surface->output, &output_listener, surface); + wl_output_add_listener(surface->output, &_wl_output_listener, surface); wl_list_insert(&state->surfaces, &surface->link); } } @@ -154,6 +196,53 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; +static cairo_surface_t *select_image(struct swaylock_state *state, + struct swaylock_surface *surface) { + struct swaylock_image *image; + cairo_surface_t *default_image = NULL; + wl_list_for_each(image, &state->images, link) { + if (lenient_strcmp(image->output_name, surface->output_name) == 0) { + return image->cairo_surface; + } else if (!image->output_name) { + default_image = image->cairo_surface; + } + } + return default_image; +} + +static void load_image(char *arg, struct swaylock_state *state) { + // [:] + struct swaylock_image *image = malloc(sizeof(struct swaylock_image)); + char *separator = strchr(arg, ':'); + if (separator) { + *separator = '\0'; + image->output_name = strdup(arg); + image->path = strdup(separator + 1); + } else { + image->output_name = NULL; + image->path = strdup(arg); + } + + // Bash doesn't replace the ~ with $HOME if the output name is supplied + wordexp_t p; + if (wordexp(image->path, &p, 0) == 0) { + free(image->path); + image->path = strdup(p.we_wordv[0]); + wordfree(&p); + } + + // Load the actual image + image->cairo_surface = load_background_image(image->path); + if (!image->cairo_surface) { + free(image); + return; + } + wl_list_insert(&state->images, &image->link); + state->args.mode = BACKGROUND_MODE_FILL; + wlr_log(L_DEBUG, "Loaded image %s for output %s", + image->path, image->output_name ? image->output_name : "*"); +} + static struct swaylock_state state; int main(int argc, char **argv) { @@ -180,16 +269,15 @@ int main(int argc, char **argv) { " -v, --version Show the version number and quit.\n" " -i, --image [:] Display the given image.\n" " -u, --no-unlock-indicator Disable the unlock indicator.\n" - " -f, --daemonize Detach from the controlling terminal.\n" - " --socket Use the specified socket.\n"; + " -f, --daemonize Detach from the controlling terminal.\n"; struct swaylock_args args = { .mode = BACKGROUND_MODE_SOLID_COLOR, .color = 0xFFFFFFFF, .show_indicator = true, }; - cairo_surface_t *background_image = NULL; state.args = args; + wl_list_init(&state.images); wlr_log_init(L_DEBUG, NULL); int c; @@ -206,12 +294,7 @@ int main(int argc, char **argv) { break; } case 'i': - // TODO: Multiple background images (bleh) - background_image = load_background_image(optarg); - if (!background_image) { - return 1; - } - state.args.mode = BACKGROUND_MODE_FILL; + load_image(optarg, &state); break; case 's': state.args.mode = parse_background_mode(optarg); @@ -261,6 +344,7 @@ int main(int argc, char **argv) { if (!state.input_inhibit_manager) { wlr_log(L_ERROR, "Compositor does not support the input inhibitor " "protocol, refusing to run insecurely"); + return 1; } if (wl_list_empty(&state.surfaces)) { @@ -268,9 +352,23 @@ int main(int argc, char **argv) { return 0; } + if (state.zxdg_output_manager) { + struct swaylock_surface *surface; + wl_list_for_each(surface, &state.surfaces, link) { + surface->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state.zxdg_output_manager, surface->output); + zxdg_output_v1_add_listener( + surface->xdg_output, &_xdg_output_listener, surface); + } + wl_display_roundtrip(state.display); + } else { + wlr_log(L_INFO, "Compositor does not support zxdg output manager, " + "images assigned to named outputs will not work"); + } + struct swaylock_surface *surface; wl_list_for_each(surface, &state.surfaces, link) { - surface->image = background_image; + surface->image = select_image(&state, surface); surface->surface = wl_compositor_create_surface(state.compositor); assert(surface->surface); diff --git a/swaylock/render.c b/swaylock/render.c index 7d9d25a5..cc40f4e9 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -23,7 +23,7 @@ void render_frame(struct swaylock_surface *surface) { cairo_t *cairo = surface->current_buffer->cairo; cairo_identity_matrix(cairo); - if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR || !surface->image) { cairo_set_source_u32(cairo, state->args.color); cairo_paint(cairo); } else { From bde9711f3cea13a0283f7c2b5b5aa1deb91edf41 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 22:39:08 +1000 Subject: [PATCH 04/23] Swaylock: Log error if multiple images are defined for the same output --- swaylock/main.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/swaylock/main.c b/swaylock/main.c index 91c28bd2..74a00a35 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -223,6 +223,23 @@ static void load_image(char *arg, struct swaylock_state *state) { image->path = strdup(arg); } + bool exists = false; + struct swaylock_image *iter_image; + wl_list_for_each(iter_image, &state->images, link) { + if (lenient_strcmp(iter_image->output_name, image->output_name) == 0) { + exists = true; + break; + } + } + if (exists) { + if (image->output_name) { + wlr_log(L_ERROR, "Multiple images defined for output %s", + image->output_name); + } else { + wlr_log(L_ERROR, "Multiple default images defined"); + } + } + // Bash doesn't replace the ~ with $HOME if the output name is supplied wordexp_t p; if (wordexp(image->path, &p, 0) == 0) { From 836123329504af8b4fd735063eb44690a23e626e Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 22:47:07 +1000 Subject: [PATCH 05/23] Swaylock: Use calloc instead of malloc --- swaylock/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaylock/main.c b/swaylock/main.c index 74a00a35..11b5e8c8 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -212,7 +212,7 @@ static cairo_surface_t *select_image(struct swaylock_state *state, static void load_image(char *arg, struct swaylock_state *state) { // [:] - struct swaylock_image *image = malloc(sizeof(struct swaylock_image)); + struct swaylock_image *image = calloc(1, sizeof(struct swaylock_image)); char *separator = strchr(arg, ':'); if (separator) { *separator = '\0'; From 750a497a8b5cf7c1ec8c46db7d7a531fcfb1a86f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 20 May 2018 09:33:48 -0400 Subject: [PATCH 06/23] Pre-multiply alpha for non-textures --- sway/desktop/output.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ebb3ddaa..51c1ffbe 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -310,6 +310,13 @@ damage_finish: pixman_region32_fini(&damage); } +static void premultiply_alpha(float color[4], float opacity) { + color[3] *= opacity; + color[0] *= color[3]; + color[1] *= color[3]; + color[2] *= color[3]; +} + /** * Render decorations for a view with "border normal". * @@ -328,7 +335,7 @@ static void render_container_simple_border_normal(struct sway_output *output, if (view->border_left) { // Child border - left edge memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y + 1; box.width = view->border_thickness; @@ -346,7 +353,7 @@ static void render_container_simple_border_normal(struct sway_output *output, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x + con->width - view->border_thickness; box.y = con->y + 1; box.width = view->border_thickness; @@ -364,7 +371,7 @@ static void render_container_simple_border_normal(struct sway_output *output, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y + con->height - view->border_thickness; box.width = con->width; @@ -375,7 +382,7 @@ static void render_container_simple_border_normal(struct sway_output *output, // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y; box.width = con->width; @@ -390,7 +397,7 @@ static void render_container_simple_border_normal(struct sway_output *output, // Single pixel bar below title memcpy(&color, colors->border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = inner_x; box.y = view->y - 1; box.width = inner_width; @@ -441,7 +448,7 @@ static void render_container_simple_border_normal(struct sway_output *output, // Title background - above the text memcpy(&color, colors->background, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = inner_x; box.y = con->y + 1; box.width = inner_width; @@ -481,7 +488,7 @@ static void render_container_simple_border_pixel(struct sway_output *output, if (view->border_left) { // Child border - left edge memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y + view->border_thickness * view->border_top; box.width = view->border_thickness; @@ -499,7 +506,7 @@ static void render_container_simple_border_pixel(struct sway_output *output, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x + con->width - view->border_thickness; box.y = con->y + view->border_thickness * view->border_top; box.width = view->border_thickness; @@ -512,7 +519,7 @@ static void render_container_simple_border_pixel(struct sway_output *output, if (view->border_top) { // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y; box.width = con->width; @@ -529,7 +536,7 @@ static void render_container_simple_border_pixel(struct sway_output *output, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y + con->height - view->border_thickness; box.width = con->width; From a3d3c819cf38d7a04f79f6d5b16e11ac0a786135 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 19 May 2018 19:57:10 -0400 Subject: [PATCH 07/23] Fix focus_follows_mouse over swaybar If you moved your mouse over swaybar (e.g. to scroll between workspaces), focus would move to the workspace. This is not the right thing to do. The solution is complicated by the fact that if you move your mouse into a new output with an empty workspace, that workspace _should_ receive focus. --- sway/input/cursor.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 9259c475..3a73954c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -146,7 +146,23 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) struct sway_container *c = container_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (c && config->focus_follows_mouse) { - seat_set_focus_warp(cursor->seat, c, false); + if (c->type == C_WORKSPACE) { + // Only follow the mouse if it would move to a new output + // Otherwise we'll focus the workspace, which is probably wrong + struct sway_container *focus = seat_get_focus(cursor->seat); + if (focus->type != C_OUTPUT) { + focus = container_parent(focus, C_OUTPUT); + } + struct sway_container *output = c; + if (output->type != C_OUTPUT) { + output = container_parent(c, C_OUTPUT); + } + if (output != focus) { + seat_set_focus_warp(cursor->seat, c, false); + } + } else { + seat_set_focus_warp(cursor->seat, c, false); + } } // reset cursor if switching between clients From 82cd55a67006a2817daa466fd2a56e3e5aa11e17 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 20 May 2018 23:12:33 -0400 Subject: [PATCH 08/23] Fix border commands from changing focus --- include/sway/input/cursor.h | 3 ++- sway/commands/border.c | 2 +- sway/commands/seat/cursor.c | 4 ++-- sway/input/cursor.c | 11 ++++++----- sway/input/seat.c | 4 ++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 20c1c903..42c894a4 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -29,7 +29,8 @@ struct sway_cursor { void sway_cursor_destroy(struct sway_cursor *cursor); struct sway_cursor *sway_cursor_create(struct sway_seat *seat); -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec); +void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, + bool allow_refocusing); void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, uint32_t button, enum wlr_button_state state); 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) { struct sway_seat *seat = input_manager_current_seat(input_manager); if (seat->cursor) { - cursor_send_pointer_motion(seat->cursor, 0); + cursor_send_pointer_motion(seat->cursor, 0, false); } return cmd_results_new(CMD_SUCCESS, NULL, NULL); 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) { int delta_x = strtol(argv[1], NULL, 10); int delta_y = strtol(argv[2], NULL, 10); wlr_cursor_move(cursor->cursor, NULL, delta_x, delta_y); - cursor_send_pointer_motion(cursor, 0); + cursor_send_pointer_motion(cursor, 0, true); } else if (strcasecmp(argv[0], "set") == 0) { if (argc < 3) { return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); @@ -45,7 +45,7 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { float x = strtof(argv[1], NULL) / root_container.width; float y = strtof(argv[2], NULL) / root_container.height; wlr_cursor_warp_absolute(cursor->cursor, NULL, x, y); - cursor_send_pointer_motion(cursor, 0); + cursor_send_pointer_motion(cursor, 0, true); } else { if (argc < 2) { return cmd_results_new(CMD_INVALID, "cursor", expected_syntax); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3a73954c..b0ce8002 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -135,7 +135,8 @@ static struct sway_container *container_at_coords( return output->swayc; } -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) { +void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, + bool allow_refocusing) { if (time_msec == 0) { time_msec = get_current_time_msec(); } @@ -145,7 +146,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec) double sx, sy; struct sway_container *c = container_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (c && config->focus_follows_mouse) { + if (c && config->focus_follows_mouse && allow_refocusing) { if (c->type == C_WORKSPACE) { // Only follow the mouse if it would move to a new output // Otherwise we'll focus the workspace, which is probably wrong @@ -193,7 +194,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct wlr_event_pointer_motion *event = data; wlr_cursor_move(cursor->cursor, event->device, event->delta_x, event->delta_y); - cursor_send_pointer_motion(cursor, event->time_msec); + cursor_send_pointer_motion(cursor, event->time_msec, true); } static void handle_cursor_motion_absolute( @@ -203,7 +204,7 @@ static void handle_cursor_motion_absolute( wlr_idle_notify_activity(cursor->seat->input->server->idle, cursor->seat->wlr_seat); struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y); - cursor_send_pointer_motion(cursor, event->time_msec); + cursor_send_pointer_motion(cursor, event->time_msec, true); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -373,7 +374,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { } wlr_cursor_warp_absolute(cursor->cursor, event->device, x, y); - cursor_send_pointer_motion(cursor, event->time_msec); + cursor_send_pointer_motion(cursor, event->time_msec, true); } static void handle_tool_tip(struct wl_listener *listener, void *data) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 9ac3e6a8..7d541f6e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -602,7 +602,7 @@ void seat_set_focus_warp(struct sway_seat *seat, wlr_output, seat->cursor->cursor->x, seat->cursor->cursor->y)) { wlr_cursor_warp(seat->cursor->cursor, NULL, x, y); - cursor_send_pointer_motion(seat->cursor, 0); + cursor_send_pointer_motion(seat->cursor, 0, true); } } } @@ -613,7 +613,7 @@ void seat_set_focus_warp(struct sway_seat *seat, } if (last_workspace && last_workspace != new_workspace) { - cursor_send_pointer_motion(seat->cursor, 0); + cursor_send_pointer_motion(seat->cursor, 0, true); } seat->has_focus = (container != NULL); From c4ea2b51f6dc2ae15cc2bebc9c3b0d4d18dc1766 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 21 May 2018 13:10:52 +1000 Subject: [PATCH 09/23] Fix hide_edge_borders constraints When checking if a border is on the edge, the check should be done against the workspace rather than the output. --- sway/tree/view.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 648c1655..192a73c5 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -139,9 +139,10 @@ void view_autoconfigure(struct sway_view *view) { return; } + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + int other_views = 1; if (config->hide_edge_borders == E_SMART) { - struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); other_views = container_count_descendants_of_type(ws, C_VIEW) - 1; } @@ -151,16 +152,16 @@ void view_autoconfigure(struct sway_view *view) { if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_VERTICAL || (config->hide_edge_borders == E_SMART && !other_views)) { - view->border_left = view->swayc->x != 0; + view->border_left = view->swayc->x != ws->x; int right_x = view->swayc->x + view->swayc->width; - view->border_right = right_x != output->width; + view->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH || config->hide_edge_borders == E_HORIZONTAL || (config->hide_edge_borders == E_SMART && !other_views)) { - view->border_top = view->swayc->y != 0; + view->border_top = view->swayc->y != ws->y; int bottom_y = view->swayc->y + view->swayc->height; - view->border_bottom = bottom_y != output->height; + view->border_bottom = bottom_y != ws->y + ws->height; } } From c08f9bf257c38c92a75988d89fba2d4de6bb2aea Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 19 May 2018 22:54:50 +1000 Subject: [PATCH 10/23] Implement tabbed layout --- include/sway/tree/container.h | 2 +- sway/commands/layout.c | 2 + sway/desktop/output.c | 192 +++++++++++++++++++++++- sway/input/cursor.c | 2 +- sway/tree/arrange.c | 44 +++++- sway/tree/container.c | 271 ++++++++++++++++++++++++---------- sway/tree/layout.c | 11 +- sway/tree/view.c | 1 + 8 files changed, 438 insertions(+), 87 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index e7e9d944..598a4f3d 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -162,7 +162,7 @@ struct sway_container *container_parent(struct sway_container *container, * is a view and the view contains a surface at those coordinates. */ struct sway_container *container_at(struct sway_container *container, - double lx, double ly, struct wlr_surface **surface, + double ox, double oy, struct wlr_surface **surface, double *sx, double *sy); /** diff --git a/sway/commands/layout.c b/sway/commands/layout.c index bb36bb18..8aa321ae 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -39,6 +39,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) { parent->layout = L_HORIZ; } else if (strcasecmp(argv[0], "splitv") == 0) { parent->layout = L_VERT; + } else if (strcasecmp(argv[0], "tabbed") == 0) { + parent->layout = L_TABBED; } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { if (parent->layout == L_HORIZ) { parent->layout = L_VERT; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 51c1ffbe..e39ef8db 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -599,12 +599,198 @@ static void render_container_simple(struct sway_output *output, } } +static void render_tab(struct sway_output *output, pixman_region32_t *damage, + struct sway_container *parent, int child_index, + struct border_colors *colors, struct wlr_texture *title_texture) { + float output_scale = output->wlr_output->scale; + float color[4]; + struct wlr_box box; + bool is_first = (child_index == 0); + bool is_last = (child_index == parent->children->length - 1); + + int tab_width = parent->width / parent->children->length; + int x = parent->x + tab_width * child_index; + // Make last tab use the remaining width of the parent + if (is_last) { + tab_width = parent->width - tab_width * child_index; + } + + // Single pixel bar above title + memcpy(&color, colors->border, sizeof(float) * 4); + box.x = x; + box.y = parent->y; + box.width = tab_width; + box.height = 1; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + + // Single pixel bar below title + memcpy(&color, colors->border, sizeof(float) * 4); + box.x = x + config->border_thickness * is_first; + box.y = parent->y + config->border_thickness * 2 + config->font_height - 1; + box.width = tab_width - config->border_thickness * is_first + - config->border_thickness * is_last; + box.height = 1; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + + // Title text + size_t title_width = 0; + if (title_texture) { + struct wlr_box texture_box; + wlr_texture_get_size(title_texture, + &texture_box.width, &texture_box.height); + texture_box.x = (x + config->border_thickness) * output_scale; + texture_box.y = (parent->y + config->border_thickness) * output_scale; + + float matrix[9]; + wlr_matrix_project_box(matrix, &texture_box, + WL_OUTPUT_TRANSFORM_NORMAL, + 0.0, output->wlr_output->transform_matrix); + + int available = (tab_width - config->border_thickness * 2) + * output_scale; + if (texture_box.width > available) { + texture_box.width = available; + } + render_texture(output->wlr_output, damage, title_texture, + &texture_box, matrix, 1.0); + title_width = texture_box.width; + } + + // Title background - above the text + memcpy(&color, colors->background, sizeof(float) * 4); + box.x = x + config->border_thickness; + box.y = parent->y + 1; + box.width = tab_width - config->border_thickness * 2; + box.height = config->border_thickness - 1; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + + // Title background - below the text + box.y = (parent->y + config->border_thickness + config->font_height) + * output_scale; + render_rect(output->wlr_output, damage, &box, color); + + // Title background - left border + box.x = x; + box.y = parent->y + 1; + box.width = config->border_thickness; + box.height = config->border_thickness * 2 + + config->font_height - 1 - !is_first; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + + // Title background - right border + box.x = x + tab_width - config->border_thickness; + box.y = parent->y + 1; + box.width = config->border_thickness; + box.height = config->border_thickness * 2 + + config->font_height - 1 - !is_last; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + + // Title background - right of text + box.x = (x + config->border_thickness) * output_scale + title_width; + box.y = (parent->y + config->border_thickness) * output_scale; + box.width = (tab_width - config->border_thickness * 2) * output_scale + - title_width; + box.height = config->font_height * output_scale; + render_rect(output->wlr_output, damage, &box, color); +} + +static void render_tab_content(struct sway_output *output, + pixman_region32_t *damage, struct sway_container *con, + struct border_colors *colors) { + struct sway_view *view = con->sway_view; + render_view(view, output, damage); + + struct wlr_box box; + float output_scale = output->wlr_output->scale; + float color[4]; + + if (view->border_left) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = con->x; + box.y = con->y + config->border_thickness * 2 + config->font_height; + box.width = view->border_thickness; + box.height = view->height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (view->border_right) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = view->x + view->width; + box.y = con->y + config->border_thickness * 2 + config->font_height; + box.width = view->border_thickness; + box.height = view->height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (view->border_bottom) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = con->x; + box.y = view->y + view->height; + box.width = con->width; + box.height = view->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } +} + /** * Render a container's children using the L_TABBED layout. */ static void render_container_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con) { - // TODO + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + if (!con->children->length) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *current = seat_get_focus_inactive(seat, con); + while (current->parent != con) { + current = current->parent; + } + struct border_colors *current_colors = NULL; + + // Render tabs + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + struct border_colors *colors; + struct wlr_texture *title_texture; + + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + } else if (child == current) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + } + + render_tab(output, damage, con, i, colors, title_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current->type == C_VIEW) { + render_tab_content(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } /** @@ -628,7 +814,7 @@ static void render_container(struct sway_output *output, render_container_stacked(output, damage, con); break; case L_TABBED: - render_container_tabbed(output, damage, con); + render_container_tabbed(output, damage, con, parent_focused); break; case L_FLOATING: // TODO diff --git a/sway/input/cursor.c b/sway/input/cursor.c index b0ce8002..e0b987d2 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -108,7 +108,7 @@ static struct sway_container *container_at_coords( } struct sway_container *c; - if ((c = container_at(ws, x, y, surface, sx, sy))) { + if ((c = container_at(ws, ox, oy, surface, sx, sy))) { return c; } diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 83bb20fb..8aebc0cc 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -86,6 +86,13 @@ static void apply_horiz_layout(struct sway_container *parent) { if (!num_children) { return; } + size_t parent_height = parent->height; + size_t parent_offset = 0; + if (parent->parent->layout == L_TABBED) { + parent_offset = config->border_thickness * 2 + config->font_height; + parent_height -= parent_offset; + } + // Calculate total width of children double total_width = 0; for (size_t i = 0; i < num_children; ++i) { @@ -111,9 +118,9 @@ static void apply_horiz_layout(struct sway_container *parent) { "Calculating arrangement for %p:%d (will scale %f by %f)", child, child->type, child->width, scale); child->x = child_x; - child->y = parent->y; + child->y = parent->y + parent_offset; child->width = floor(child->width * scale); - child->height = parent->height; + child->height = parent_height; child_x += child->width; } // Make last child use remaining width of parent @@ -125,24 +132,31 @@ static void apply_vert_layout(struct sway_container *parent) { if (!num_children) { return; } + size_t parent_height = parent->height; + size_t parent_offset = 0; + if (parent->parent->layout == L_TABBED) { + parent_offset = config->border_thickness * 2 + config->font_height; + parent_height -= parent_offset; + } + // Calculate total height of children double total_height = 0; for (size_t i = 0; i < num_children; ++i) { struct sway_container *child = parent->children->items[i]; if (child->height <= 0) { if (num_children > 1) { - child->height = parent->height / (num_children - 1); + child->height = parent_height / (num_children - 1); } else { - child->height = parent->height; + child->height = parent_height; } } total_height += child->height; } - double scale = parent->height / total_height; + double scale = parent_height / total_height; // Resize wlr_log(L_DEBUG, "Arranging %p vertically", parent); - double child_y = parent->y; + double child_y = parent->y + parent_offset; struct sway_container *child; for (size_t i = 0; i < num_children; ++i) { child = parent->children->items[i]; @@ -156,7 +170,20 @@ static void apply_vert_layout(struct sway_container *parent) { child_y += child->height; } // Make last child use remaining height of parent - child->height = parent->y + parent->height - child->y; + child->height = parent->y + parent_offset + parent_height - child->y; +} + +static void apply_tabbed_layout(struct sway_container *parent) { + if (!parent->children->length) { + return; + } + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + child->x = parent->x; + child->y = parent->y; + child->width = parent->width; + child->height = parent->height; + } } void arrange_children_of(struct sway_container *parent) { @@ -189,6 +216,9 @@ void arrange_children_of(struct sway_container *parent) { case L_VERT: apply_vert_layout(parent); break; + case L_TABBED: + apply_tabbed_layout(parent); + break; default: wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); apply_horiz_layout(parent); diff --git a/sway/tree/container.c b/sway/tree/container.c index feaf7647..76a21c19 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -452,79 +452,138 @@ struct sway_container *container_parent(struct sway_container *container, return container; } -struct sway_container *container_at(struct sway_container *parent, - double lx, double ly, +static struct sway_container *container_at_view(struct sway_container *swayc, + double ox, double oy, struct wlr_surface **surface, double *sx, double *sy) { - list_t *queue = get_bfs_queue(); - if (!queue) { + struct sway_view *sview = swayc->sway_view; + double view_sx = ox - sview->x; + double view_sy = oy - sview->y; + + double _sx, _sy; + struct wlr_surface *_surface = NULL; + switch (sview->type) { + case SWAY_VIEW_XWAYLAND: + _surface = wlr_surface_surface_at(sview->surface, + view_sx, view_sy, &_sx, &_sy); + break; + case SWAY_VIEW_XDG_SHELL_V6: + // the top left corner of the sway container is the + // coordinate of the top left corner of the window geometry + view_sx += sview->wlr_xdg_surface_v6->geometry.x; + view_sy += sview->wlr_xdg_surface_v6->geometry.y; + + _surface = wlr_xdg_surface_v6_surface_at( + sview->wlr_xdg_surface_v6, + view_sx, view_sy, &_sx, &_sy); + break; + case SWAY_VIEW_XDG_SHELL: + // the top left corner of the sway container is the + // coordinate of the top left corner of the window geometry + view_sx += sview->wlr_xdg_surface->geometry.x; + view_sy += sview->wlr_xdg_surface->geometry.y; + + _surface = wlr_xdg_surface_surface_at( + sview->wlr_xdg_surface, + view_sx, view_sy, &_sx, &_sy); + break; + } + if (_surface) { + *sx = _sx; + *sy = _sy; + *surface = _surface; + } + return swayc; +} + +/** + * container_at for a container with layout L_TABBED. + */ +static struct sway_container *container_at_tabbed(struct sway_container *parent, + double ox, double oy, + struct wlr_surface **surface, double *sx, double *sy) { + if (oy < parent->y || oy > parent->y + parent->height) { + return NULL; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + + // Tab titles + int title_height = config->border_thickness * 2 + config->font_height; + if (oy < parent->y + title_height) { + int tab_width = parent->width / parent->children->length; + int child_index = (ox - parent->x) / tab_width; + if (child_index >= parent->children->length) { + child_index = parent->children->length - 1; + } + struct sway_container *child = parent->children->items[child_index]; + return seat_get_focus_inactive(seat, child); + } + + // Surfaces + struct sway_container *current = seat_get_focus_inactive(seat, parent); + while (current->parent != parent) { + current = current->parent; + } + + return container_at(current, ox, oy, surface, sx, sy); +} + +/** + * container_at for a container with layout L_STACKED. + */ +static struct sway_container *container_at_stacked( + struct sway_container *parent, double ox, double oy, + struct wlr_surface **surface, double *sx, double *sy) { + // TODO + return NULL; +} + +/** + * container_at for a container with layout L_HORIZ or L_VERT. + */ +static struct sway_container *container_at_linear(struct sway_container *parent, + double ox, double oy, + struct wlr_surface **surface, double *sx, double *sy) { + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + struct wlr_box box = { + .x = child->x, + .y = child->y, + .width = child->width, + .height = child->height, + }; + if (wlr_box_contains_point(&box, ox, oy)) { + return container_at(child, ox, oy, surface, sx, sy); + } + } + return NULL; +} + +struct sway_container *container_at(struct sway_container *parent, + double ox, double oy, + struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(parent->type >= C_WORKSPACE, + "Expected workspace or deeper")) { + return NULL; + } + if (parent->type == C_VIEW) { + return container_at_view(parent, ox, oy, surface, sx, sy); + } + if (!parent->children->length) { return NULL; } - list_add(queue, parent); - - struct sway_container *swayc = NULL; - while (queue->length) { - swayc = queue->items[0]; - list_del(queue, 0); - if (swayc->type == C_VIEW) { - struct sway_view *sview = swayc->sway_view; - struct sway_container *soutput = container_parent(swayc, C_OUTPUT); - struct wlr_box *output_box = - wlr_output_layout_get_box( - root_container.sway_root->output_layout, - soutput->sway_output->wlr_output); - double ox = lx - output_box->x; - double oy = ly - output_box->y; - double view_sx = ox - sview->x; - double view_sy = oy - sview->y; - - double _sx, _sy; - struct wlr_surface *_surface; - switch (sview->type) { - case SWAY_VIEW_XWAYLAND: - _surface = wlr_surface_surface_at(sview->surface, - view_sx, view_sy, &_sx, &_sy); - break; - case SWAY_VIEW_XDG_SHELL_V6: - // the top left corner of the sway container is the - // coordinate of the top left corner of the window geometry - view_sx += sview->wlr_xdg_surface_v6->geometry.x; - view_sy += sview->wlr_xdg_surface_v6->geometry.y; - - _surface = wlr_xdg_surface_v6_surface_at( - sview->wlr_xdg_surface_v6, - view_sx, view_sy, &_sx, &_sy); - break; - case SWAY_VIEW_XDG_SHELL: - // the top left corner of the sway container is the - // coordinate of the top left corner of the window geometry - view_sx += sview->wlr_xdg_surface->geometry.x; - view_sy += sview->wlr_xdg_surface->geometry.y; - - _surface = wlr_xdg_surface_surface_at( - sview->wlr_xdg_surface, - view_sx, view_sy, &_sx, &_sy); - break; - } - if (_surface) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return swayc; - } - // Check the view's decorations - struct wlr_box swayc_box = { - .x = swayc->x, - .y = swayc->y, - .width = swayc->width, - .height = swayc->height, - }; - if (wlr_box_contains_point(&swayc_box, ox, oy)) { - return swayc; - } - } else { - list_cat(queue, swayc->children); - } + switch (parent->layout) { + case L_HORIZ: + case L_VERT: + return container_at_linear(parent, ox, oy, surface, sx, sy); + case L_TABBED: + return container_at_tabbed(parent, ox, oy, surface, sx, sy); + case L_STACKED: + return container_at_stacked(parent, ox, oy, surface, sx, sy); + case L_FLOATING: + return NULL; // TODO + case L_NONE: + return NULL; } return NULL; @@ -699,18 +758,82 @@ void container_calculate_title_height(struct sway_container *container) { container->title_height = height; } +/** + * Calculate and return the length of the concatenated child titles. + * An example concatenated title is: V[Terminal, Firefox] + * If buffer is not NULL, also populate the buffer with the concatenated title. + */ +static size_t concatenate_child_titles(struct sway_container *parent, + char *buffer) { + size_t len = 2; // V[ + if (buffer) { + switch (parent->layout) { + case L_VERT: + strcpy(buffer, "V["); + break; + case L_HORIZ: + strcpy(buffer, "H["); + break; + case L_TABBED: + strcpy(buffer, "T["); + break; + case L_STACKED: + strcpy(buffer, "S["); + break; + case L_FLOATING: + strcpy(buffer, "F["); + break; + case L_NONE: + strcpy(buffer, "?["); + break; + } + } + + for (int i = 0; i < parent->children->length; ++i) { + if (i != 0) { + len += 2; + if (buffer) { + strcat(buffer, ", "); + } + } + struct sway_container *child = parent->children->items[i]; + if (child->name) { + len += strlen(child->name); + if (buffer) { + strcat(buffer, child->name); + } + } else { + len += 6; + if (buffer) { + strcat(buffer, "(null)"); + } + } + } + + len += 1; + if (buffer) { + strcat(buffer, "]"); + } + return len; +} + void container_notify_child_title_changed(struct sway_container *container) { if (!container || container->type != C_CONTAINER) { return; } - if (container->layout != L_TABBED && container->layout != L_STACKED) { - return; - } if (container->formatted_title) { free(container->formatted_title); } - // TODO: iterate children and concatenate their titles - container->formatted_title = strdup(""); + + size_t len = concatenate_child_titles(container, NULL); + char *buffer = calloc(len + 1, sizeof(char)); + if (!sway_assert(buffer, "Unable to allocate title string")) { + return; + } + concatenate_child_titles(container, buffer); + + container->name = buffer; + container->formatted_title = buffer; container_calculate_title_height(container); container_update_title_textures(container); container_notify_child_title_changed(container->parent); 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) { } } child->parent = NULL; + container_notify_child_title_changed(parent); + return parent; } @@ -182,6 +184,8 @@ void container_move_to(struct sway_container *container, container_sort_workspaces(new_parent); seat_set_focus(seat, new_parent); } + container_notify_child_title_changed(old_parent); + container_notify_child_title_changed(new_parent); if (old_parent) { arrange_children_of(old_parent); } @@ -234,9 +238,9 @@ static bool is_parallel(enum sway_container_layout layout, enum movement_direction dir) { switch (layout) { case L_TABBED: - case L_STACKED: case L_HORIZ: return dir == MOVE_LEFT || dir == MOVE_RIGHT; + case L_STACKED: case L_VERT: return dir == MOVE_UP || dir == MOVE_DOWN; default: @@ -485,6 +489,9 @@ void container_move(struct sway_container *container, } } + container_notify_child_title_changed(old_parent); + container_notify_child_title_changed(container->parent); + if (old_parent) { seat_set_focus(config->handler_context.seat, old_parent); seat_set_focus(config->handler_context.seat, container); @@ -832,6 +839,8 @@ struct sway_container *container_split(struct sway_container *child, container_add_child(cont, child); } + container_notify_child_title_changed(cont); + return cont; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 192a73c5..636abb25 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -441,6 +441,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { input_manager_set_focus(input_manager, cont); view_update_title(view, false); + container_notify_child_title_changed(view->swayc->parent); view_execute_criteria(view); container_damage_whole(cont); From bd79584f659428df5d34f64f3b1cdb4c4388c3c6 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 08:34:43 +1000 Subject: [PATCH 11/23] Add assertion in container_at_view --- sway/tree/container.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 76a21c19..62dca487 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -455,6 +455,9 @@ struct sway_container *container_parent(struct sway_container *container, static struct sway_container *container_at_view(struct sway_container *swayc, double ox, double oy, struct wlr_surface **surface, double *sx, double *sy) { + if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { + return NULL; + } struct sway_view *sview = swayc->sway_view; double view_sx = ox - sview->x; double view_sy = oy - sview->y; From efc07fb3d45e07529e3817b4a1598f2c3256d600 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 09:11:55 +1000 Subject: [PATCH 12/23] Don't track damage for views on inactive tabs --- include/sway/input/seat.h | 6 ++++++ include/sway/tree/view.h | 6 ++++++ sway/desktop/output.c | 9 ++------- sway/input/seat.c | 12 ++++++++++++ sway/tree/container.c | 5 +---- sway/tree/view.c | 25 +++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 11 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index ff76841e..2e4da438 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -94,6 +94,12 @@ struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, struct sway_container *container); +/** + * Return the immediate child of container which was most recently focused. + */ +struct sway_container *seat_get_active_child(struct sway_seat *seat, + struct sway_container *container); + /** * Iterate over the focus-inactive children of the container calling the * function on each. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 951912d0..0fb8f1b3 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -274,4 +274,10 @@ bool view_has_mark(struct sway_view *view, char *mark); void view_update_marks_textures(struct sway_view *view); +/** + * Returns true if there's a possibility the view may be rendered on screen. + * Intended for damage tracking. + */ +bool view_is_visible(struct sway_view *view); + #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e39ef8db..6d5777f3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -754,10 +754,7 @@ static void render_container_tabbed(struct sway_output *output, } struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = seat_get_focus(seat); - struct sway_container *current = seat_get_focus_inactive(seat, con); - while (current->parent != con) { - current = current->parent; - } + struct sway_container *current = seat_get_active_child(seat, con); struct border_colors *current_colors = NULL; // Render tabs @@ -1082,9 +1079,7 @@ static void output_damage_view(struct sway_output *output, return; } - struct sway_container *workspace = container_parent(view->swayc, - C_WORKSPACE); - if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { + if (!view_is_visible(view)) { return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index 7d541f6e..7a3e928a 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -718,6 +718,18 @@ struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, return seat_get_focus_by_type(seat, container, C_TYPES); } +struct sway_container *seat_get_active_child(struct sway_seat *seat, + struct sway_container *container) { + struct sway_container *focus = seat_get_focus_inactive(seat, container); + if (!focus) { + return NULL; + } + while (focus->parent != container) { + focus = focus->parent; + } + return focus; +} + struct sway_container *sway_seat_get_focus(struct sway_seat *seat) { if (!seat->has_focus) { return NULL; diff --git a/sway/tree/container.c b/sway/tree/container.c index 62dca487..5c1f42c2 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -522,10 +522,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, } // Surfaces - struct sway_container *current = seat_get_focus_inactive(seat, parent); - while (current->parent != parent) { - current = current->parent; - } + struct sway_container *current = seat_get_active_child(seat, parent); return container_at(current, ox, oy, surface, sx, sy); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 636abb25..51316507 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -865,3 +865,28 @@ void view_update_marks_textures(struct sway_view *view) { &config->border_colors.urgent); container_damage_whole(view->swayc); } + +bool view_is_visible(struct sway_view *view) { + if (!view->swayc) { + return false; + } + // Check view isn't in a tabbed or stacked container on an inactive tab + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *container = view->swayc; + while (container->type != C_WORKSPACE) { + if (container->parent->layout == L_TABBED || + container->parent->layout == L_STACKED) { + if (seat_get_active_child(seat, container->parent) != container) { + return false; + } + } + container = container->parent; + } + // Check view isn't hidden by another fullscreen view + struct sway_container *workspace = container; + if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { + return false; + } + // Check the workspace is visible + return workspace_is_visible(workspace); +} From 4672cb7ccfcba0c628e4f04cc1abcad7d769bb3d Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 09:26:46 +1000 Subject: [PATCH 13/23] Use class or app_id in tree representation --- sway/tree/container.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 5c1f42c2..5d88325f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -784,23 +784,32 @@ static size_t concatenate_child_titles(struct sway_container *parent, strcpy(buffer, "F["); break; case L_NONE: - strcpy(buffer, "?["); + strcpy(buffer, "D["); break; } } for (int i = 0; i < parent->children->length; ++i) { if (i != 0) { - len += 2; + len += 1; if (buffer) { - strcat(buffer, ", "); + strcat(buffer, " "); } } struct sway_container *child = parent->children->items[i]; - if (child->name) { - len += strlen(child->name); + const char *identifier = NULL; + if (child->type == C_VIEW) { + identifier = view_get_class(child->sway_view); + if (!identifier) { + identifier = view_get_app_id(child->sway_view); + } + } else { + identifier = child->name; + } + if (identifier) { + len += strlen(identifier); if (buffer) { - strcat(buffer, child->name); + strcat(buffer, identifier); } } else { len += 6; From 5ab4930185d32c5ac1adbf56f7b74525a2bab98d Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 15:34:08 +1000 Subject: [PATCH 14/23] Fix tab border issues --- sway/desktop/output.c | 60 ++++++++++++++++++++++--------------------- sway/tree/view.c | 35 ++++++++++++++++++++----- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 6d5777f3..c052cafb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -709,37 +709,39 @@ static void render_tab_content(struct sway_output *output, float output_scale = output->wlr_output->scale; float color[4]; - if (view->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; - box.x = con->x; - box.y = con->y + config->border_thickness * 2 + config->font_height; - box.width = view->border_thickness; - box.height = view->height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } + if (view->border != B_NONE) { + if (view->border_left) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = con->x; + box.y = con->y + config->border_thickness * 2 + config->font_height; + box.width = view->border_thickness; + box.height = view->height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } - if (view->border_right) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; - box.x = view->x + view->width; - box.y = con->y + config->border_thickness * 2 + config->font_height; - box.width = view->border_thickness; - box.height = view->height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } + if (view->border_right) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = view->x + view->width; + box.y = con->y + config->border_thickness * 2 + config->font_height; + box.width = view->border_thickness; + box.height = view->height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } - if (view->border_bottom) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; - box.x = con->x; - box.y = view->y + view->height; - box.width = con->width; - box.height = view->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + if (view->border_bottom) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + color[3] *= con->alpha; + box.x = con->x; + box.y = view->y + view->height; + box.width = con->width; + box.height = view->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } } } diff --git a/sway/tree/view.c b/sway/tree/view.c index 51316507..64597c02 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -167,32 +167,53 @@ void view_autoconfigure(struct sway_view *view) { double x, y, width, height; x = y = width = height = 0; + double y_offset = 0; + + // In a tabbed or stacked container, the swayc's y is the top of the title + // area. We have to offset the surface y by the height of the title bar, and + // disable any top border because we'll always have the title bar. + if (view->swayc->parent->layout == L_TABBED) { + y_offset = config->border_thickness * 2 + config->font_height; + view->border_top = 0; + } else if (view->swayc->parent->layout == L_STACKED) { + y_offset = config->border_thickness * 2 + config->font_height; + y_offset *= view->swayc->parent->children->length; + view->border_top = 0; + } + switch (view->border) { case B_NONE: x = view->swayc->x; - y = view->swayc->y; + y = view->swayc->y + y_offset; width = view->swayc->width; - height = view->swayc->height; + height = view->swayc->height - y_offset; break; case B_PIXEL: x = view->swayc->x + view->border_thickness * view->border_left; - y = view->swayc->y + view->border_thickness * view->border_top; + y = view->swayc->y + view->border_thickness * view->border_top + y_offset; width = view->swayc->width - view->border_thickness * view->border_left - view->border_thickness * view->border_right; - height = view->swayc->height + height = view->swayc->height - y_offset - view->border_thickness * view->border_top - view->border_thickness * view->border_bottom; break; case B_NORMAL: // Height is: border + title height + border + view height + border x = view->swayc->x + view->border_thickness * view->border_left; - y = view->swayc->y + config->font_height + view->border_thickness * 2; width = view->swayc->width - view->border_thickness * view->border_left - view->border_thickness * view->border_right; - height = view->swayc->height - config->font_height - - view->border_thickness * (2 + view->border_bottom); + if (y_offset) { + y = view->swayc->y + y_offset; + height = view->swayc->height - y_offset + - view->border_thickness * view->border_bottom; + } else { + y = view->swayc->y + config->font_height + view->border_thickness * 2 + + y_offset; + height = view->swayc->height - config->font_height + - view->border_thickness * (2 + view->border_bottom); + } break; } From 90607dc32970a422f375234539cd6d6182ee89d1 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sun, 20 May 2018 23:16:51 +1000 Subject: [PATCH 15/23] Add left/right borders to tabs --- sway/desktop/output.c | 53 ++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c052cafb..829029a7 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -605,13 +605,11 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, float output_scale = output->wlr_output->scale; float color[4]; struct wlr_box box; - bool is_first = (child_index == 0); - bool is_last = (child_index == parent->children->length - 1); int tab_width = parent->width / parent->children->length; int x = parent->x + tab_width * child_index; // Make last tab use the remaining width of the parent - if (is_last) { + if (child_index == parent->children->length - 1) { tab_width = parent->width - tab_width * child_index; } @@ -625,22 +623,29 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, render_rect(output->wlr_output, damage, &box, color); // Single pixel bar below title - memcpy(&color, colors->border, sizeof(float) * 4); - box.x = x + config->border_thickness * is_first; - box.y = parent->y + config->border_thickness * 2 + config->font_height - 1; - box.width = tab_width - config->border_thickness * is_first - - config->border_thickness * is_last; - box.height = 1; + box.y = (parent->y + config->border_thickness * 2 + config->font_height - 1) + * output_scale; + render_rect(output->wlr_output, damage, &box, color); + + // Single pixel bar on left + box.x = x; + box.y = parent->y + 1; + box.width = 1; + box.height = config->border_thickness * 2 + config->font_height - 2; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); + // Single pixel bar on right + box.x = (x + tab_width - 1) * output_scale; + render_rect(output->wlr_output, damage, &box, color); + // Title text size_t title_width = 0; if (title_texture) { struct wlr_box texture_box; wlr_texture_get_size(title_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x + config->border_thickness) * output_scale; + texture_box.x = (x + 1 + config->border_thickness) * output_scale; texture_box.y = (parent->y + config->border_thickness) * output_scale; float matrix[9]; @@ -648,7 +653,7 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, WL_OUTPUT_TRANSFORM_NORMAL, 0.0, output->wlr_output->transform_matrix); - int available = (tab_width - config->border_thickness * 2) + int available = (tab_width - config->border_thickness * 2 - 2) * output_scale; if (texture_box.width > available) { texture_box.width = available; @@ -660,9 +665,9 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, // Title background - above the text memcpy(&color, colors->background, sizeof(float) * 4); - box.x = x + config->border_thickness; + box.x = x + 1; box.y = parent->y + 1; - box.width = tab_width - config->border_thickness * 2; + box.width = tab_width - 2; box.height = config->border_thickness - 1; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); @@ -672,28 +677,18 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, * output_scale; render_rect(output->wlr_output, damage, &box, color); - // Title background - left border - box.x = x; - box.y = parent->y + 1; + // Title background - left of text + box.x = x + 1; + box.y = parent->y + config->border_thickness; box.width = config->border_thickness; - box.height = config->border_thickness * 2 - + config->font_height - 1 - !is_first; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - - // Title background - right border - box.x = x + tab_width - config->border_thickness; - box.y = parent->y + 1; - box.width = config->border_thickness; - box.height = config->border_thickness * 2 - + config->font_height - 1 - !is_last; + box.height = config->font_height; scale_box(&box, output_scale); render_rect(output->wlr_output, damage, &box, color); // Title background - right of text - box.x = (x + config->border_thickness) * output_scale + title_width; + box.x = (x + 1 + config->border_thickness) * output_scale + title_width; box.y = (parent->y + config->border_thickness) * output_scale; - box.width = (tab_width - config->border_thickness * 2) * output_scale + box.width = (tab_width - config->border_thickness - 2) * output_scale - title_width; box.height = config->font_height * output_scale; render_rect(output->wlr_output, damage, &box, color); From 048b29527b8b0ec9c6cd9d0439b74bf73f4f6e2d Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 21 May 2018 08:29:52 +1000 Subject: [PATCH 16/23] Pre-multiply alpha for tab decorations --- sway/desktop/output.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 829029a7..551e96fc 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -602,6 +602,7 @@ static void render_container_simple(struct sway_output *output, static void render_tab(struct sway_output *output, pixman_region32_t *damage, struct sway_container *parent, int child_index, struct border_colors *colors, struct wlr_texture *title_texture) { + struct sway_container *con = parent->children->items[child_index]; float output_scale = output->wlr_output->scale; float color[4]; struct wlr_box box; @@ -615,6 +616,7 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); box.x = x; box.y = parent->y; box.width = tab_width; @@ -665,6 +667,7 @@ static void render_tab(struct sway_output *output, pixman_region32_t *damage, // Title background - above the text memcpy(&color, colors->background, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); box.x = x + 1; box.y = parent->y + 1; box.width = tab_width - 2; @@ -707,7 +710,7 @@ static void render_tab_content(struct sway_output *output, if (view->border != B_NONE) { if (view->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = con->y + config->border_thickness * 2 + config->font_height; box.width = view->border_thickness; @@ -718,7 +721,7 @@ static void render_tab_content(struct sway_output *output, if (view->border_right) { memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = view->x + view->width; box.y = con->y + config->border_thickness * 2 + config->font_height; box.width = view->border_thickness; @@ -729,7 +732,7 @@ static void render_tab_content(struct sway_output *output, if (view->border_bottom) { memcpy(&color, colors->child_border, sizeof(float) * 4); - color[3] *= con->alpha; + premultiply_alpha(color, con->alpha); box.x = con->x; box.y = view->y + view->height; box.width = con->width; From 0273c6438c13dfd98204ba07fd42dac0081b0c7f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 21 May 2018 13:18:18 -0400 Subject: [PATCH 17/23] Fix hide_edge_borders smart for tabs and stacks --- sway/tree/view.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 64597c02..c013e635 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -141,9 +141,18 @@ void view_autoconfigure(struct sway_view *view) { struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); - int other_views = 1; + int other_views = 0; if (config->hide_edge_borders == E_SMART) { - other_views = container_count_descendants_of_type(ws, C_VIEW) - 1; + struct sway_container *con = view->swayc; + while (con != output) { + if (con->layout != L_TABBED && con->layout != L_STACKED) { + other_views += con->children ? con->children->length - 1 : 0; + if (other_views > 0) { + break; + } + } + con = con->parent; + } } view->border_top = view->border_bottom = true; From 8bbf78fdd430a7e315356ebeda36adbf48b8953d Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 21 May 2018 17:21:01 -0400 Subject: [PATCH 18/23] Fix focus follows mouse with no focus --- sway/input/cursor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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, struct sway_container *c = container_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (c && config->focus_follows_mouse && allow_refocusing) { - if (c->type == C_WORKSPACE) { + struct sway_container *focus = seat_get_focus(cursor->seat); + if (focus && c->type == C_WORKSPACE) { // Only follow the mouse if it would move to a new output // Otherwise we'll focus the workspace, which is probably wrong - struct sway_container *focus = seat_get_focus(cursor->seat); if (focus->type != C_OUTPUT) { focus = container_parent(focus, C_OUTPUT); } From 664169fbf1c4e07f17a48b2b801dad9cea31ea4c Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 21 May 2018 22:58:46 +1000 Subject: [PATCH 19/23] Implement stacked layout --- sway/commands/layout.c | 2 + sway/desktop/output.c | 608 +++++++++++++++++++---------------------- sway/tree/arrange.c | 32 ++- sway/tree/view.c | 15 +- 4 files changed, 309 insertions(+), 348 deletions(-) diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 8aa321ae..58728f16 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -41,6 +41,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) { parent->layout = L_VERT; } else if (strcasecmp(argv[0], "tabbed") == 0) { parent->layout = L_TABBED; + } else if (strcasecmp(argv[0], "stacking") == 0) { + parent->layout = L_STACKED; } else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) { if (parent->layout == L_HORIZ) { parent->layout = L_VERT; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 551e96fc..3b501a63 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -269,17 +269,6 @@ static void render_unmanaged(struct sway_output *output, render_surface_iterator, &data); } -static void render_view(struct sway_view *view, struct sway_output *output, - pixman_region32_t *damage) { - struct render_data data = { - .output = output, - .damage = damage, - .alpha = view->swayc->alpha, - }; - output_view_for_each_surface( - view, &data.root_geo, render_surface_iterator, &data); -} - static void render_rect(struct wlr_output *wlr_output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]) { @@ -317,108 +306,159 @@ static void premultiply_alpha(float color[4], float opacity) { color[2] *= color[3]; } +static void render_view_surfaces(struct sway_view *view, + struct sway_output *output, pixman_region32_t *damage) { + struct render_data data = { + .output = output, + .damage = damage, + .alpha = view->swayc->alpha, + }; + output_view_for_each_surface( + view, &data.root_geo, render_surface_iterator, &data); +} + /** - * Render decorations for a view with "border normal". + * Render a view's surface and left/bottom/right borders. + */ +static void render_view(struct sway_output *output, pixman_region32_t *damage, + struct sway_container *con, struct border_colors *colors) { + struct sway_view *view = con->sway_view; + render_view_surfaces(view, output, damage); + + struct wlr_box box; + float output_scale = output->wlr_output->scale; + float color[4]; + + if (view->border != B_NONE) { + if (view->border_left) { + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = con->x; + box.y = view->y; + box.width = view->border_thickness; + box.height = view->height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (view->border_right) { + if (con->parent->children->length == 1 + && con->parent->layout == L_HORIZ) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = view->x + view->width; + box.y = view->y; + box.width = view->border_thickness; + box.height = view->height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + + if (view->border_bottom) { + if (con->parent->children->length == 1 + && con->parent->layout == L_VERT) { + memcpy(&color, colors->indicator, sizeof(float) * 4); + } else { + memcpy(&color, colors->child_border, sizeof(float) * 4); + } + premultiply_alpha(color, con->alpha); + box.x = con->x; + box.y = view->y + view->height; + box.width = con->width; + box.height = view->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, damage, &box, color); + } + } +} + +/** + * Render a titlebar. * * Care must be taken not to render over the same pixel multiple times, * otherwise the colors will be incorrect when using opacity. + * + * The height is: 1px border, 3px padding, font height, 3px padding, 1px border + * The left side for L_TABBED is: 1px border, 2px padding, title + * The left side for other layouts is: 3px padding, title */ -static void render_container_simple_border_normal(struct sway_output *output, - pixman_region32_t *output_damage, - struct sway_container *con, struct border_colors *colors, - struct wlr_texture *title_texture, struct wlr_texture *marks_texture) { +static void render_titlebar(struct sway_output *output, + pixman_region32_t *output_damage, struct sway_container *con, + int x, int y, int width, + struct border_colors *colors, struct wlr_texture *title_texture, + struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; - struct sway_view *view = con->sway_view; + struct sway_view *view = con->type == C_VIEW ? con->sway_view : NULL; float output_scale = output->wlr_output->scale; - - if (view->border_left) { - // Child border - left edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y + 1; - box.width = view->border_thickness; - box.height = con->height - 1 - - view->border_thickness * view->border_bottom; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } - - if (view->border_right) { - // Child border - right edge - if (con->parent->children->length == 1 - && con->parent->layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = con->x + con->width - view->border_thickness; - box.y = con->y + 1; - box.width = view->border_thickness; - box.height = con->height - 1 - - view->border_thickness * view->border_bottom; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } - - if (view->border_bottom) { - // Child border - bottom edge - if (con->parent->children->length == 1 - && con->parent->layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y + con->height - view->border_thickness; - box.width = con->width; - box.height = view->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } + enum sway_container_layout layout = con->parent->layout; + bool is_last_child = + con->parent->children->items[con->parent->children->length - 1] == con; // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y; - box.width = con->width; + box.x = x; + box.y = y; + box.width = width; box.height = 1; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); - // Setting these makes marks and title easier - size_t inner_x = con->x + view->border_thickness * view->border_left; - size_t inner_width = con->width - view->border_thickness * view->border_left - - view->border_thickness * view->border_right; - // Single pixel bar below title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = inner_x; - box.y = view->y - 1; - box.width = inner_width; + size_t left_offset = 0, right_offset = 0; + bool connects_sides = false; + if (layout == L_HORIZ || layout == L_VERT || + (layout == L_STACKED && is_last_child)) { + if (view) { + left_offset = view->border_left * view->border_thickness; + right_offset = view->border_right * view->border_thickness; + connects_sides = true; + } + } + box.x = x + left_offset; + box.y = y + config->font_height + 7; + box.width = width - left_offset - right_offset; box.height = 1; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); + if (layout == L_TABBED) { + // Single pixel left edge + box.x = x; + box.y = y + 1; + box.width = 1; + box.height = config->font_height + 6; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Single pixel right edge + box.x = (x + width - 1) * output_scale; + render_rect(output->wlr_output, output_damage, &box, color); + } + + size_t inner_width = width - 6; + // Marks size_t marks_width = 0; if (config->show_marks && marks_texture) { struct wlr_box texture_box; wlr_texture_get_size(marks_texture, &texture_box.width, &texture_box.height); - texture_box.x = (inner_x + inner_width) * output_scale - texture_box.width; - texture_box.y = (con->y + view->border_thickness) * output_scale; + texture_box.x = (x + width - 3) * output_scale - texture_box.width; + texture_box.y = (y + 4) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, WL_OUTPUT_TRANSFORM_NORMAL, 0.0, output->wlr_output->transform_matrix); + if (inner_width * output_scale < texture_box.width) { + texture_box.width = inner_width * output_scale; + } render_texture(output->wlr_output, output_damage, marks_texture, &texture_box, matrix, con->alpha); marks_width = texture_box.width; @@ -430,8 +470,8 @@ static void render_container_simple_border_normal(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(title_texture, &texture_box.width, &texture_box.height); - texture_box.x = inner_x * output_scale; - texture_box.y = (con->y + view->border_thickness) * output_scale; + texture_box.x = (x + 3) * output_scale; + texture_box.y = (y + 4) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -446,104 +486,89 @@ static void render_container_simple_border_normal(struct sway_output *output, title_width = texture_box.width; } - // Title background - above the text + // Padding above title memcpy(&color, colors->background, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = inner_x; - box.y = con->y + 1; - box.width = inner_width; - box.height = view->border_thickness - 1; + box.x = x + (layout == L_TABBED); + box.y = y + 1; + box.width = width - (layout == L_TABBED) * 2; + box.height = 3; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); - // Title background - below the text - box.y = (con->y + view->border_thickness + config->font_height) - * output_scale; + // Padding below title + box.y = (y + 4 + config->font_height) * output_scale; render_rect(output->wlr_output, output_damage, &box, color); - // Title background - filler between title and marks + // Filler between title and marks box.width = inner_width * output_scale - title_width - marks_width; if (box.width > 0) { - box.x = inner_x * output_scale + title_width; - box.y = (con->y + view->border_thickness) * output_scale; + box.x = (x + 3) * output_scale + title_width; + box.y = (y + 4) * output_scale; box.height = config->font_height * output_scale; render_rect(output->wlr_output, output_damage, &box, color); } + + // Padding left of title + left_offset = layout == L_TABBED ? 1 : 0; + box.x = x + left_offset; + box.y = y + 4; + box.width = 3 - left_offset; + box.height = config->font_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Padding right of marks + right_offset = layout == L_TABBED ? 1 : 0; + box.x = x + width - 3; + box.y = y + 4; + box.width = 3 - right_offset; + box.height = config->font_height; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + if (connects_sides) { + // Left pixel in line with bottom bar + box.x = x; + box.y = y + config->font_height + 7; + box.width = view->border_thickness * view->border_left; + box.height = 1; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + + // Right pixel in line with bottom bar + box.x = x + width - view->border_thickness * view->border_right; + box.y = y + config->font_height + 7; + box.width = view->border_thickness * view->border_right; + box.height = 1; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); + } } /** - * Render decorations for a view with "border pixel". - * - * Care must be taken not to render over the same pixel multiple times, - * otherwise the colors will be incorrect when using opacity. + * Render the top border line for a view using "border pixel". */ -static void render_container_simple_border_pixel(struct sway_output *output, +static void render_top_border(struct sway_output *output, pixman_region32_t *output_damage, struct sway_container *con, struct border_colors *colors) { + struct sway_view *view = con->sway_view; + if (!view->border_top) { + return; + } struct wlr_box box; float color[4]; - struct sway_view *view = con->sway_view; float output_scale = output->wlr_output->scale; - if (view->border_left) { - // Child border - left edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y + view->border_thickness * view->border_top; - box.width = view->border_thickness; - box.height = con->height - view->border_thickness - * (view->border_top + view->border_bottom); - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } - - if (view->border_right) { - // Child border - right edge - if (con->parent->children->length == 1 - && con->parent->layout == L_HORIZ) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = con->x + con->width - view->border_thickness; - box.y = con->y + view->border_thickness * view->border_top; - box.width = view->border_thickness; - box.height = con->height - view->border_thickness - * (view->border_top + view->border_bottom); - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } - - if (view->border_top) { - // Child border - top edge - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y; - box.width = con->width; - box.height = view->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } - - if (view->border_bottom) { - // Child border - bottom edge - if (con->parent->children->length == 1 - && con->parent->layout == L_VERT) { - memcpy(&color, colors->indicator, sizeof(float) * 4); - } else { - memcpy(&color, colors->child_border, sizeof(float) * 4); - } - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y + con->height - view->border_thickness; - box.width = con->width; - box.height = view->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); - } + // Child border - top edge + memcpy(&color, colors->child_border, sizeof(float) * 4); + premultiply_alpha(color, con->alpha); + box.x = con->x; + box.y = con->y; + box.width = con->width; + box.height = view->border_thickness; + scale_box(&box, output_scale); + render_rect(output->wlr_output, output_damage, &box, color); } static void render_container(struct sway_output *output, @@ -565,33 +590,30 @@ static void render_container_simple(struct sway_output *output, struct sway_container *child = con->children->items[i]; if (child->type == C_VIEW) { - if (child->sway_view->border != B_NONE) { - struct border_colors *colors; - struct wlr_texture *title_texture; - struct wlr_texture *marks_texture; - if (focus == child || parent_focused) { - colors = &config->border_colors.focused; - title_texture = child->title_focused; - marks_texture = child->sway_view->marks_focused; - } else if (seat_get_focus_inactive(seat, con) == child) { - colors = &config->border_colors.focused_inactive; - title_texture = child->title_focused_inactive; - marks_texture = child->sway_view->marks_focused_inactive; - } else { - colors = &config->border_colors.unfocused; - title_texture = child->title_unfocused; - marks_texture = child->sway_view->marks_unfocused; - } - - if (child->sway_view->border == B_NORMAL) { - render_container_simple_border_normal(output, damage, - child, colors, title_texture, marks_texture); - } else { - render_container_simple_border_pixel(output, damage, child, - colors); - } + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = child->sway_view->marks_focused; + } else if (seat_get_focus_inactive(seat, con) == child) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = child->sway_view->marks_focused_inactive; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = child->sway_view->marks_unfocused; } - render_view(child->sway_view, output, damage); + + if (child->sway_view->border == B_NORMAL) { + render_titlebar(output, damage, child, child->x, child->y, + child->width, colors, title_texture, marks_texture); + } else { + render_top_border(output, damage, child, colors); + } + render_view(output, damage, child, colors); } else { render_container(output, damage, child, parent_focused || focus == child); @@ -599,150 +621,6 @@ static void render_container_simple(struct sway_output *output, } } -static void render_tab(struct sway_output *output, pixman_region32_t *damage, - struct sway_container *parent, int child_index, - struct border_colors *colors, struct wlr_texture *title_texture) { - struct sway_container *con = parent->children->items[child_index]; - float output_scale = output->wlr_output->scale; - float color[4]; - struct wlr_box box; - - int tab_width = parent->width / parent->children->length; - int x = parent->x + tab_width * child_index; - // Make last tab use the remaining width of the parent - if (child_index == parent->children->length - 1) { - tab_width = parent->width - tab_width * child_index; - } - - // Single pixel bar above title - memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x; - box.y = parent->y; - box.width = tab_width; - box.height = 1; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - - // Single pixel bar below title - box.y = (parent->y + config->border_thickness * 2 + config->font_height - 1) - * output_scale; - render_rect(output->wlr_output, damage, &box, color); - - // Single pixel bar on left - box.x = x; - box.y = parent->y + 1; - box.width = 1; - box.height = config->border_thickness * 2 + config->font_height - 2; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - - // Single pixel bar on right - box.x = (x + tab_width - 1) * output_scale; - render_rect(output->wlr_output, damage, &box, color); - - // Title text - size_t title_width = 0; - if (title_texture) { - struct wlr_box texture_box; - wlr_texture_get_size(title_texture, - &texture_box.width, &texture_box.height); - texture_box.x = (x + 1 + config->border_thickness) * output_scale; - texture_box.y = (parent->y + config->border_thickness) * output_scale; - - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - int available = (tab_width - config->border_thickness * 2 - 2) - * output_scale; - if (texture_box.width > available) { - texture_box.width = available; - } - render_texture(output->wlr_output, damage, title_texture, - &texture_box, matrix, 1.0); - title_width = texture_box.width; - } - - // Title background - above the text - memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = x + 1; - box.y = parent->y + 1; - box.width = tab_width - 2; - box.height = config->border_thickness - 1; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - - // Title background - below the text - box.y = (parent->y + config->border_thickness + config->font_height) - * output_scale; - render_rect(output->wlr_output, damage, &box, color); - - // Title background - left of text - box.x = x + 1; - box.y = parent->y + config->border_thickness; - box.width = config->border_thickness; - box.height = config->font_height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - - // Title background - right of text - box.x = (x + 1 + config->border_thickness) * output_scale + title_width; - box.y = (parent->y + config->border_thickness) * output_scale; - box.width = (tab_width - config->border_thickness - 2) * output_scale - - title_width; - box.height = config->font_height * output_scale; - render_rect(output->wlr_output, damage, &box, color); -} - -static void render_tab_content(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, - struct border_colors *colors) { - struct sway_view *view = con->sway_view; - render_view(view, output, damage); - - struct wlr_box box; - float output_scale = output->wlr_output->scale; - float color[4]; - - if (view->border != B_NONE) { - if (view->border_left) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = con->y + config->border_thickness * 2 + config->font_height; - box.width = view->border_thickness; - box.height = view->height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - - if (view->border_right) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = view->x + view->width; - box.y = con->y + config->border_thickness * 2 + config->font_height; - box.width = view->border_thickness; - box.height = view->height; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - - if (view->border_bottom) { - memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, con->alpha); - box.x = con->x; - box.y = view->y + view->height; - box.width = con->width; - box.height = view->border_thickness; - scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); - } - } -} - /** * Render a container's children using the L_TABBED layout. */ @@ -762,19 +640,33 @@ static void render_container_tabbed(struct sway_output *output, struct sway_container *child = con->children->items[i]; struct border_colors *colors; struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + struct sway_view *view = + child->type == C_VIEW ? child->sway_view : NULL; if (focus == child || parent_focused) { colors = &config->border_colors.focused; title_texture = child->title_focused; + marks_texture = view ? view->marks_focused : NULL; } else if (child == current) { colors = &config->border_colors.focused_inactive; title_texture = child->title_focused_inactive; + marks_texture = view ? view->marks_focused : NULL; } else { colors = &config->border_colors.unfocused; title_texture = child->title_unfocused; + marks_texture = view ? view->marks_unfocused : NULL; } - render_tab(output, damage, con, i, colors, title_texture); + int tab_width = con->width / con->children->length; + int x = con->x + tab_width * i; + // Make last tab use the remaining width of the parent + if (i == con->children->length - 1) { + tab_width = con->width - tab_width * i; + } + + render_titlebar(output, damage, child, x, child->y, tab_width, colors, + title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -783,7 +675,7 @@ static void render_container_tabbed(struct sway_output *output, // Render surface and left/right/bottom borders if (current->type == C_VIEW) { - render_tab_content(output, damage, current, current_colors); + render_view(output, damage, current, current_colors); } else { render_container(output, damage, current, parent_focused || current == focus); @@ -794,8 +686,55 @@ static void render_container_tabbed(struct sway_output *output, * Render a container's children using the L_STACKED layout. */ static void render_container_stacked(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con) { - // TODO + pixman_region32_t *damage, struct sway_container *con, + bool parent_focused) { + if (!con->children->length) { + return; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focus(seat); + struct sway_container *current = seat_get_active_child(seat, con); + struct border_colors *current_colors = NULL; + + // Render titles + for (int i = 0; i < con->children->length; ++i) { + struct sway_container *child = con->children->items[i]; + struct border_colors *colors; + struct wlr_texture *title_texture; + struct wlr_texture *marks_texture; + struct sway_view *view = + child->type == C_VIEW ? child->sway_view : NULL; + + if (focus == child || parent_focused) { + colors = &config->border_colors.focused; + title_texture = child->title_focused; + marks_texture = view ? view->marks_focused : NULL; + } else if (child == current) { + colors = &config->border_colors.focused_inactive; + title_texture = child->title_focused_inactive; + marks_texture = view ? view->marks_focused_inactive : NULL; + } else { + colors = &config->border_colors.unfocused; + title_texture = child->title_unfocused; + marks_texture = view ? view->marks_unfocused : NULL; + } + + int y = con->y + (config->font_height + 8) * i; + render_titlebar(output, damage, child, child->x, y, child->width, + colors, title_texture, marks_texture); + + if (child == current) { + current_colors = colors; + } + } + + // Render surface and left/right/bottom borders + if (current->type == C_VIEW) { + render_view(output, damage, current, current_colors); + } else { + render_container(output, damage, current, + parent_focused || current == focus); + } } static void render_container(struct sway_output *output, @@ -808,7 +747,7 @@ static void render_container(struct sway_output *output, render_container_simple(output, damage, con, parent_focused); break; case L_STACKED: - render_container_stacked(output, damage, con); + render_container_stacked(output, damage, con, parent_focused); break; case L_TABBED: render_container_tabbed(output, damage, con, parent_focused); @@ -868,7 +807,8 @@ static void render_output(struct sway_output *output, struct timespec *when, } // TODO: handle views smaller than the output - render_view(workspace->sway_workspace->fullscreen, output, damage); + render_view_surfaces( + workspace->sway_workspace->fullscreen, output, damage); if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) { render_unmanaged(output, damage, diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 8aebc0cc..b8e07bca 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -86,12 +86,14 @@ static void apply_horiz_layout(struct sway_container *parent) { if (!num_children) { return; } - size_t parent_height = parent->height; size_t parent_offset = 0; if (parent->parent->layout == L_TABBED) { - parent_offset = config->border_thickness * 2 + config->font_height; - parent_height -= parent_offset; + parent_offset = config->font_height + 8; + } else if (parent->parent->layout == L_STACKED) { + parent_offset = (config->font_height + 8) + * parent->parent->children->length; } + size_t parent_height = parent->height - parent_offset; // Calculate total width of children double total_width = 0; @@ -132,12 +134,14 @@ static void apply_vert_layout(struct sway_container *parent) { if (!num_children) { return; } - size_t parent_height = parent->height; size_t parent_offset = 0; if (parent->parent->layout == L_TABBED) { - parent_offset = config->border_thickness * 2 + config->font_height; - parent_height -= parent_offset; + parent_offset = config->font_height + 8; + } else if (parent->parent->layout == L_STACKED) { + parent_offset = (config->font_height + 8) + * parent->parent->children->length; } + size_t parent_height = parent->height - parent_offset; // Calculate total height of children double total_height = 0; @@ -186,6 +190,19 @@ static void apply_tabbed_layout(struct sway_container *parent) { } } +static void apply_stacked_layout(struct sway_container *parent) { + if (!parent->children->length) { + return; + } + for (int i = 0; i < parent->children->length; ++i) { + struct sway_container *child = parent->children->items[i]; + child->x = parent->x; + child->y = parent->y; + child->width = parent->width; + child->height = parent->height; + } +} + void arrange_children_of(struct sway_container *parent) { if (config->reloading) { return; @@ -219,6 +236,9 @@ void arrange_children_of(struct sway_container *parent) { case L_TABBED: apply_tabbed_layout(parent); break; + case L_STACKED: + apply_stacked_layout(parent); + break; default: wlr_log(L_DEBUG, "TODO: arrange layout type %d", parent->layout); apply_horiz_layout(parent); diff --git a/sway/tree/view.c b/sway/tree/view.c index c013e635..1280dc8d 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -182,11 +182,11 @@ void view_autoconfigure(struct sway_view *view) { // area. We have to offset the surface y by the height of the title bar, and // disable any top border because we'll always have the title bar. if (view->swayc->parent->layout == L_TABBED) { - y_offset = config->border_thickness * 2 + config->font_height; + y_offset = config->font_height + 8; view->border_top = 0; } else if (view->swayc->parent->layout == L_STACKED) { - y_offset = config->border_thickness * 2 + config->font_height; - y_offset *= view->swayc->parent->children->length; + y_offset = (config->font_height + 8) + * view->swayc->parent->children->length; view->border_top = 0; } @@ -208,7 +208,7 @@ void view_autoconfigure(struct sway_view *view) { - view->border_thickness * view->border_bottom; break; case B_NORMAL: - // Height is: border + title height + border + view height + border + // Height is: 1px border + 3px pad + title height + 3px pad + 1px border x = view->swayc->x + view->border_thickness * view->border_left; width = view->swayc->width - view->border_thickness * view->border_left @@ -218,10 +218,9 @@ void view_autoconfigure(struct sway_view *view) { height = view->swayc->height - y_offset - view->border_thickness * view->border_bottom; } else { - y = view->swayc->y + config->font_height + view->border_thickness * 2 - + y_offset; - height = view->swayc->height - config->font_height - - view->border_thickness * (2 + view->border_bottom); + y = view->swayc->y + config->font_height + 8; + height = view->swayc->height - config->font_height - 8 + - view->border_thickness * view->border_bottom; } break; } From f6c3682c05bce05f00b13b8f469b52923ecd8ddb Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 22 May 2018 08:27:42 +1000 Subject: [PATCH 20/23] Use constants for titlebar dimensions --- include/sway/tree/container.h | 11 ++++++ sway/desktop/output.c | 64 ++++++++++++++++++----------------- sway/tree/arrange.c | 12 +++---- sway/tree/container.c | 6 +++- sway/tree/view.c | 8 ++--- 5 files changed, 59 insertions(+), 42 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 598a4f3d..64e8634a 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -11,6 +11,12 @@ extern struct sway_container root_container; struct sway_view; struct sway_seat; +#define TITLEBAR_BORDER_THICKNESS 1 + +// Padding includes titlebar border +#define TITLEBAR_H_PADDING 3 +#define TITLEBAR_V_PADDING 4 + /** * Different kinds of containers. * @@ -212,4 +218,9 @@ void container_calculate_title_height(struct sway_container *container); void container_notify_child_title_changed(struct sway_container *container); +/** + * Return the height of a regular title bar. + */ +size_t container_titlebar_height(); + #endif diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3b501a63..765647fd 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -404,7 +404,7 @@ static void render_titlebar(struct sway_output *output, box.x = x; box.y = y; box.width = width; - box.height = 1; + box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -420,27 +420,28 @@ static void render_titlebar(struct sway_output *output, } } box.x = x + left_offset; - box.y = y + config->font_height + 7; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; box.width = width - left_offset - right_offset; - box.height = 1; + box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); if (layout == L_TABBED) { // Single pixel left edge box.x = x; - box.y = y + 1; - box.width = 1; - box.height = config->font_height + 6; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = TITLEBAR_BORDER_THICKNESS; + box.height = + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS * 2; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Single pixel right edge - box.x = (x + width - 1) * output_scale; + box.x = (x + width - TITLEBAR_BORDER_THICKNESS) * output_scale; render_rect(output->wlr_output, output_damage, &box, color); } - size_t inner_width = width - 6; + size_t inner_width = width - TITLEBAR_H_PADDING * 2; // Marks size_t marks_width = 0; @@ -448,8 +449,9 @@ static void render_titlebar(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(marks_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x + width - 3) * output_scale - texture_box.width; - texture_box.y = (y + 4) * output_scale; + texture_box.x = + (x + width - TITLEBAR_H_PADDING) * output_scale - texture_box.width; + texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -470,8 +472,8 @@ static void render_titlebar(struct sway_output *output, struct wlr_box texture_box; wlr_texture_get_size(title_texture, &texture_box.width, &texture_box.height); - texture_box.x = (x + 3) * output_scale; - texture_box.y = (y + 4) * output_scale; + texture_box.x = (x + TITLEBAR_H_PADDING) * output_scale; + texture_box.y = (y + TITLEBAR_V_PADDING) * output_scale; float matrix[9]; wlr_matrix_project_box(matrix, &texture_box, @@ -489,40 +491,40 @@ static void render_titlebar(struct sway_output *output, // Padding above title memcpy(&color, colors->background, sizeof(float) * 4); premultiply_alpha(color, con->alpha); - box.x = x + (layout == L_TABBED); - box.y = y + 1; - box.width = width - (layout == L_TABBED) * 2; - box.height = 3; + box.x = x + (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.y = y + TITLEBAR_BORDER_THICKNESS; + box.width = width - (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS * 2; + box.height = TITLEBAR_V_PADDING - TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Padding below title - box.y = (y + 4 + config->font_height) * output_scale; + box.y = (y + TITLEBAR_V_PADDING + config->font_height) * output_scale; render_rect(output->wlr_output, output_damage, &box, color); // Filler between title and marks box.width = inner_width * output_scale - title_width - marks_width; if (box.width > 0) { - box.x = (x + 3) * output_scale + title_width; - box.y = (y + 4) * output_scale; + box.x = (x + TITLEBAR_H_PADDING) * output_scale + title_width; + box.y = (y + TITLEBAR_V_PADDING) * output_scale; box.height = config->font_height * output_scale; render_rect(output->wlr_output, output_damage, &box, color); } // Padding left of title - left_offset = layout == L_TABBED ? 1 : 0; + left_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; box.x = x + left_offset; - box.y = y + 4; - box.width = 3 - left_offset; + box.y = y + TITLEBAR_V_PADDING; + box.width = TITLEBAR_H_PADDING - left_offset; box.height = config->font_height; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Padding right of marks - right_offset = layout == L_TABBED ? 1 : 0; - box.x = x + width - 3; - box.y = y + 4; - box.width = 3 - right_offset; + right_offset = (layout == L_TABBED) * TITLEBAR_BORDER_THICKNESS; + box.x = x + width - TITLEBAR_H_PADDING; + box.y = y + TITLEBAR_V_PADDING; + box.width = TITLEBAR_H_PADDING - right_offset; box.height = config->font_height; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); @@ -530,17 +532,17 @@ static void render_titlebar(struct sway_output *output, if (connects_sides) { // Left pixel in line with bottom bar box.x = x; - box.y = y + config->font_height + 7; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; box.width = view->border_thickness * view->border_left; - box.height = 1; + box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); // Right pixel in line with bottom bar box.x = x + width - view->border_thickness * view->border_right; - box.y = y + config->font_height + 7; + box.y = y + container_titlebar_height() - TITLEBAR_BORDER_THICKNESS; box.width = view->border_thickness * view->border_right; - box.height = 1; + box.height = TITLEBAR_BORDER_THICKNESS; scale_box(&box, output_scale); render_rect(output->wlr_output, output_damage, &box, color); } @@ -719,7 +721,7 @@ static void render_container_stacked(struct sway_output *output, marks_texture = view ? view->marks_unfocused : NULL; } - int y = con->y + (config->font_height + 8) * i; + int y = con->y + container_titlebar_height() * i; render_titlebar(output, damage, child, child->x, y, child->width, colors, title_texture, marks_texture); diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index b8e07bca..37f4a066 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -88,10 +88,10 @@ static void apply_horiz_layout(struct sway_container *parent) { } size_t parent_offset = 0; if (parent->parent->layout == L_TABBED) { - parent_offset = config->font_height + 8; + parent_offset = container_titlebar_height(); } else if (parent->parent->layout == L_STACKED) { - parent_offset = (config->font_height + 8) - * parent->parent->children->length; + parent_offset = + container_titlebar_height() * parent->parent->children->length; } size_t parent_height = parent->height - parent_offset; @@ -136,10 +136,10 @@ static void apply_vert_layout(struct sway_container *parent) { } size_t parent_offset = 0; if (parent->parent->layout == L_TABBED) { - parent_offset = config->font_height + 8; + parent_offset = container_titlebar_height(); } else if (parent->parent->layout == L_STACKED) { - parent_offset = (config->font_height + 8) - * parent->parent->children->length; + parent_offset = + container_titlebar_height() * parent->parent->children->length; } size_t parent_height = parent->height - parent_offset; diff --git a/sway/tree/container.c b/sway/tree/container.c index 5d88325f..3f30a079 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -510,7 +510,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, struct sway_seat *seat = input_manager_current_seat(input_manager); // Tab titles - int title_height = config->border_thickness * 2 + config->font_height; + int title_height = container_titlebar_height(); if (oy < parent->y + title_height) { int tab_width = parent->width / parent->children->length; int child_index = (ox - parent->x) / tab_width; @@ -847,3 +847,7 @@ void container_notify_child_title_changed(struct sway_container *container) { container_update_title_textures(container); container_notify_child_title_changed(container->parent); } + +size_t container_titlebar_height() { + return config->font_height + TITLEBAR_V_PADDING * 2; +} diff --git a/sway/tree/view.c b/sway/tree/view.c index 1280dc8d..07157818 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -182,10 +182,10 @@ void view_autoconfigure(struct sway_view *view) { // area. We have to offset the surface y by the height of the title bar, and // disable any top border because we'll always have the title bar. if (view->swayc->parent->layout == L_TABBED) { - y_offset = config->font_height + 8; + y_offset = container_titlebar_height(); view->border_top = 0; } else if (view->swayc->parent->layout == L_STACKED) { - y_offset = (config->font_height + 8) + y_offset = container_titlebar_height() * view->swayc->parent->children->length; view->border_top = 0; } @@ -218,8 +218,8 @@ void view_autoconfigure(struct sway_view *view) { height = view->swayc->height - y_offset - view->border_thickness * view->border_bottom; } else { - y = view->swayc->y + config->font_height + 8; - height = view->swayc->height - config->font_height - 8 + y = view->swayc->y + container_titlebar_height(); + height = view->swayc->height - container_titlebar_height() - view->border_thickness * view->border_bottom; } break; From 5354988317f07e436f23c4022992b09623ef1322 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 22 May 2018 08:43:00 +1000 Subject: [PATCH 21/23] Implement clicking stacked title bars --- sway/tree/container.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 3f30a079..9cf18f61 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -533,8 +533,23 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, static struct sway_container *container_at_stacked( struct sway_container *parent, double ox, double oy, struct wlr_surface **surface, double *sx, double *sy) { - // TODO - return NULL; + if (oy < parent->y || oy > parent->y + parent->height) { + return NULL; + } + struct sway_seat *seat = input_manager_current_seat(input_manager); + + // Title bars + int title_height = container_titlebar_height(); + int child_index = (oy - parent->y) / title_height; + if (child_index < parent->children->length) { + struct sway_container *child = parent->children->items[child_index]; + return seat_get_focus_inactive(seat, child); + } + + // Surfaces + struct sway_container *current = seat_get_active_child(seat, parent); + + return container_at(current, ox, oy, surface, sx, sy); } /** From 4de137e02161ef8188775d50fe5dc8d9e9bb2216 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 22 May 2018 09:44:34 +1000 Subject: [PATCH 22/23] Replace empty argument with void --- include/sway/tree/container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 64e8634a..493c70e2 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -221,6 +221,6 @@ void container_notify_child_title_changed(struct sway_container *container); /** * Return the height of a regular title bar. */ -size_t container_titlebar_height(); +size_t container_titlebar_height(void); #endif From a6d43ff7465d88b4a0b6f81d9c3e61f290864946 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 22 May 2018 17:35:39 -0400 Subject: [PATCH 23/23] Implement IPC_GET_MARKS --- sway/ipc-server.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) 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 @@ #include "sway/server.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/tree/view.h" #include "list.h" #include "log.h" @@ -429,6 +430,16 @@ static void ipc_get_workspaces_callback(struct sway_container *workspace, json_object_new_boolean(visible)); } +static void ipc_get_marks_callback(struct sway_container *con, void *data) { + json_object *marks = (json_object *)data; + if (con->type == C_VIEW && con->sway_view->marks) { + for (int i = 0; i < con->sway_view->marks->length; ++i) { + char *mark = (char *)con->sway_view->marks->items[i]; + json_object_array_add(marks, json_object_new_string(mark)); + } + } +} + void ipc_client_handle_command(struct ipc_client *client) { if (!sway_assert(client != NULL, "client != NULL")) { return; @@ -569,6 +580,17 @@ void ipc_client_handle_command(struct ipc_client *client) { goto exit_cleanup; } + case IPC_GET_MARKS: + { + json_object *marks = json_object_new_array(); + container_descendants(&root_container, C_VIEW, ipc_get_marks_callback, + marks); + const char *json_string = json_object_to_json_string(marks); + ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + json_object_put(marks); + goto exit_cleanup; + } + case IPC_GET_VERSION: { json_object *version = ipc_json_get_version();