From ce1936bc65d01502e3a5f8681bb039cb95e82e0c Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 25 Nov 2017 10:59:49 -0500 Subject: [PATCH] Arrange windows on desktop --- include/sway/container.h | 2 + include/sway/layout.h | 1 + include/sway/output.h | 1 + include/sway/view.h | 2 + sway/desktop/output.c | 15 ++- sway/desktop/xdg_shell_v6.c | 20 +++- sway/tree/container.c | 13 +++ sway/tree/layout.c | 205 ++++++++++++++++++++++++++++++++++++ 8 files changed, 253 insertions(+), 6 deletions(-) diff --git a/include/sway/container.h b/include/sway/container.h index 96e4db69..1a173f3e 100644 --- a/include/sway/container.h +++ b/include/sway/container.h @@ -132,4 +132,6 @@ swayc_t *new_output(struct sway_output *sway_output); swayc_t *new_workspace(swayc_t *output, const char *name); swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view); +swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type); + #endif diff --git a/include/sway/layout.h b/include/sway/layout.h index 6356ad00..505036a0 100644 --- a/include/sway/layout.h +++ b/include/sway/layout.h @@ -7,5 +7,6 @@ void init_layout(void); void add_child(struct sway_container *parent, struct sway_container *child); enum swayc_layouts default_layout(struct sway_container *output); void sort_workspaces(struct sway_container *output); +void arrange_windows(struct sway_container *container, double width, double height); #endif diff --git a/include/sway/output.h b/include/sway/output.h index e2f81bcb..895cb07d 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -13,6 +13,7 @@ struct sway_output { struct sway_server *server; struct timespec last_frame; struct wl_listener frame; + struct wl_listener resolution; }; #endif diff --git a/include/sway/view.h b/include/sway/view.h index 2675a6f5..9d503674 100644 --- a/include/sway/view.h +++ b/include/sway/view.h @@ -52,6 +52,8 @@ struct sway_view { struct { const char *(*get_prop)(struct sway_view *view, enum sway_view_prop prop); + void (*set_dimensions)(struct sway_view *view, + int width, int height); } iface; }; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9e0c18e4..033a4c44 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -8,6 +8,7 @@ #include #include "log.h" #include "sway/container.h" +#include "sway/layout.h" #include "sway/output.h" #include "sway/server.h" #include "sway/view.h" @@ -25,10 +26,9 @@ static void output_frame_view(swayc_t *view, void *data) { return; } // TODO - // - Force sway's resolution // - Deal with wlr_output_layout - int width = surface->current->width; - int height = surface->current->height; + int width = sway_view->swayc->width; + int height = sway_view->swayc->height; int render_width = width * wlr_output->scale; int render_height = height * wlr_output->scale; double ox = view->x, oy = view->y; @@ -101,6 +101,12 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { soutput->last_frame = now; } +static void output_resolution_notify(struct wl_listener *listener, void *data) { + struct sway_output *soutput = wl_container_of( + listener, soutput, resolution); + arrange_windows(soutput->swayc, -1, -1); +} + void output_add_notify(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of(listener, server, output_add); struct wlr_output *wlr_output = data; @@ -113,6 +119,9 @@ void output_add_notify(struct wl_listener *listener, void *data) { output->frame.notify = output_frame_notify; wl_signal_add(&wlr_output->events.frame, &output->frame); + + output->resolution.notify = output_resolution_notify; + wl_signal_add(&wlr_output->events.resolution, &output->resolution); } void output_remove_notify(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index e2a61ebf..8f1885c1 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -1,15 +1,21 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include #include "sway/container.h" +#include "sway/layout.h" #include "sway/server.h" #include "sway/view.h" #include "log.h" +static bool assert_xdg(struct sway_view *view) { + return sway_assert(view->type == SWAY_XDG_SHELL_V6_VIEW, + "Expected xdg shell v6 view!"); +} + static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { - if (!sway_assert(view->type == SWAY_XDG_SHELL_V6_VIEW, - "xdg get_prop for non-xdg view!")) { + if (!assert_xdg(view)) { return NULL; } switch (prop) { @@ -22,6 +28,12 @@ static const char *get_prop(struct sway_view *view, enum sway_view_prop prop) { } } +static void set_dimensions(struct sway_view *view, int width, int height) { + if (assert_xdg(view)) { + wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height); + } +} + void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { struct sway_server *server = wl_container_of( listener, server, xdg_shell_v6_surface); @@ -48,13 +60,13 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { } sway_view->type = SWAY_XDG_SHELL_V6_VIEW; sway_view->iface.get_prop = get_prop; + sway_view->iface.set_dimensions = set_dimensions; sway_view->wlr_xdg_surface_v6 = xdg_surface; sway_view->sway_xdg_surface_v6 = sway_surface; sway_view->surface = xdg_surface->surface; sway_surface->view = sway_view; // TODO: - // - Add to tree // - Wire up listeners // - Handle popups // - Look up pid and open on appropriate workspace @@ -67,4 +79,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { swayc_t *cont = new_view(parent, sway_view); sway_view->swayc = cont; + + arrange_windows(cont->parent, -1, -1); } diff --git a/sway/tree/container.c b/sway/tree/container.c index d3931612..a83c0f6b 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -108,3 +108,16 @@ swayc_t *new_view(swayc_t *sibling, struct sway_view *sway_view) { } return swayc; } + +swayc_t *swayc_parent_by_type(swayc_t *container, enum swayc_types type) { + if (!sway_assert(container, "container is NULL")) { + return NULL; + } + if (!sway_assert(type < C_TYPES && type >= C_ROOT, "invalid type")) { + return NULL; + } + do { + container = container->parent; + } while (container && container->type != type); + return container; +} diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 5a70c570..3d6b404d 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -1,10 +1,14 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include +#include #include #include "sway/container.h" +#include "sway/output.h" +#include "sway/view.h" #include "list.h" #include "log.h" @@ -70,3 +74,204 @@ static int sort_workspace_cmp_qsort(const void *_a, const void *_b) { void sort_workspaces(swayc_t *output) { list_stable_sort(output->children, sort_workspace_cmp_qsort); } + +static void apply_horiz_layout(swayc_t *container, const double x, + const double y, const double width, + const double height, const int start, + const int end); + +static void apply_vert_layout(swayc_t *container, const double x, + const double y, const double width, + const double height, const int start, + const int end); + +void arrange_windows(swayc_t *container, double width, double height) { + int i; + if (width == -1 || height == -1) { + width = container->width; + height = container->height; + } + // pixels are indivisible. if we don't round the pixels, then the view + // calculations will be off (e.g. 50.5 + 50.5 = 101, but in reality it's + // 50 + 50 = 100). doing it here cascades properly to all width/height/x/y. + width = floor(width); + height = floor(height); + + sway_log(L_DEBUG, "Arranging layout for %p %s %fx%f+%f,%f", container, + container->name, container->width, container->height, container->x, + container->y); + + double x = 0, y = 0; + switch (container->type) { + case C_ROOT: + // TODO: wlr_output_layout probably + for (i = 0; i < container->children->length; ++i) { + swayc_t *output = container->children->items[i]; + sway_log(L_DEBUG, "Arranging output '%s' at %f,%f", + output->name, output->x, output->y); + arrange_windows(output, -1, -1); + } + return; + case C_OUTPUT: + { + int _width, _height; + wlr_output_effective_resolution( + container->sway_output->wlr_output, &_width, &_height); + width = container->width = _width; + height = container->height = _height; + } + // arrange all workspaces: + for (i = 0; i < container->children->length; ++i) { + swayc_t *child = container->children->items[i]; + arrange_windows(child, -1, -1); + } + return; + case C_WORKSPACE: + { + swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); + width = output->width, height = output->height; + container->x = x; + container->y = y; + width = container->width; + height = container->height; + sway_log(L_DEBUG, "Arranging workspace '%s' at %f, %f", + container->name, container->x, container->y); + } + // children are properly handled below + break; + case C_VIEW: + { + container->width = width; + container->height = height; + container->sway_view->iface.set_dimensions(container->sway_view, + container->width, container->height); + sway_log(L_DEBUG, "Set view to %.f x %.f @ %.f, %.f", + container->width, container->height, + container->x, container->y); + } + return; + default: + container->width = width; + container->height = height; + x = container->x; + y = container->y; + break; + } + + switch (container->layout) { + case L_HORIZ: + apply_horiz_layout(container, x, y, width, height, 0, + container->children->length); + break; + case L_VERT: + apply_vert_layout(container, x, y, width, height, 0, + container->children->length); + break; + default: + sway_log(L_DEBUG, "TODO: arrange layout type %d", container->layout); + apply_horiz_layout(container, x, y, width, height, 0, + container->children->length); + break; + } +} + +static void apply_horiz_layout(swayc_t *container, + const double x, const double y, + const double width, const double height, + const int start, const int end) { + double scale = 0; + // Calculate total width + for (int i = start; i < end; ++i) { + double *old_width = &((swayc_t *)container->children->items[i])->width; + if (*old_width <= 0) { + if (end - start > 1) { + *old_width = width / (end - start - 1); + } else { + *old_width = width; + } + } + scale += *old_width; + } + scale = width / scale; + + // Resize windows + double child_x = x; + if (scale > 0.1) { + sway_log(L_DEBUG, "Arranging %p horizontally", container); + for (int i = start; i < end; ++i) { + swayc_t *child = container->children->items[i]; + sway_log(L_DEBUG, + "Calculating arrangement for %p:%d (will scale %f by %f)", + child, child->type, width, scale); + child->x = child_x; + child->y = y; + + if (i == end - 1) { + double remaining_width = x + width - child_x; + arrange_windows(child, remaining_width, height); + } else { + arrange_windows(child, child->width * scale, height); + } + child_x += child->width; + } + + // update focused view border last because it may + // depend on the title bar geometry of its siblings. + /* TODO WLR + if (focused && container->children->length > 1) { + update_container_border(focused); + } + */ + } +} + +void apply_vert_layout(swayc_t *container, + const double x, const double y, + const double width, const double height, const int start, + const int end) { + int i; + double scale = 0; + // Calculate total height + for (i = start; i < end; ++i) { + double *old_height = &((swayc_t *)container->children->items[i])->height; + if (*old_height <= 0) { + if (end - start > 1) { + *old_height = height / (end - start - 1); + } else { + *old_height = height; + } + } + scale += *old_height; + } + scale = height / scale; + + // Resize + double child_y = y; + if (scale > 0.1) { + sway_log(L_DEBUG, "Arranging %p vertically", container); + for (i = start; i < end; ++i) { + swayc_t *child = container->children->items[i]; + sway_log(L_DEBUG, + "Calculating arrangement for %p:%d (will scale %f by %f)", + child, child->type, height, scale); + child->x = x; + child->y = child_y; + + if (i == end - 1) { + double remaining_height = y + height - child_y; + arrange_windows(child, width, remaining_height); + } else { + arrange_windows(child, width, child->height * scale); + } + child_y += child->height; + } + + // update focused view border last because it may + // depend on the title bar geometry of its siblings. + /* TODO WLR + if (focused && container->children->length > 1) { + update_container_border(focused); + } + */ + } +}