From 6d7b1321db54155cf78305dbafdcfc7de9b78415 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 21 Nov 2023 19:51:57 -0500 Subject: scene_graph: Port container server side decorations --- sway/tree/container.c | 545 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 333 insertions(+), 212 deletions(-) (limited to 'sway/tree/container.c') diff --git a/sway/tree/container.c b/sway/tree/container.c index 8fca6a12..4aae4d32 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -8,15 +8,13 @@ #include #include #include "linux-dmabuf-unstable-v1-protocol.h" -#include "cairo_util.h" -#include "pango.h" #include "sway/config.h" -#include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/scene_descriptor.h" +#include "sway/sway_text_node.h" #include "sway/output.h" #include "sway/server.h" #include "sway/surface.h" @@ -120,9 +118,328 @@ struct sway_container *container_create(struct sway_view *view) { wl_signal_init(&c->events.destroy); wl_signal_emit_mutable(&root->events.new_node, &c->node); + container_update(c); + return c; } +static bool container_is_focused(struct sway_container *con, void *data) { + return con->current.focused; +} + +static bool container_has_focused_child(struct sway_container *con) { + return container_find_child(con, container_is_focused, NULL); +} + +static bool container_is_current_parent_focused(struct sway_container *con) { + if (con->current.parent) { + struct sway_container *parent = con->current.parent; + return parent->current.focused || container_is_current_parent_focused(parent); + } else if (con->current.workspace) { + struct sway_workspace *ws = con->current.workspace; + return ws->current.focused; + } + + return false; +} + +static struct border_colors *container_get_current_colors( + struct sway_container *con) { + struct border_colors *colors; + + bool urgent = con->view ? + view_is_urgent(con->view) : container_has_urgent_child(con); + struct sway_container *active_child; + + if (con->current.parent) { + active_child = con->current.parent->current.focused_inactive_child; + } else if (con->current.workspace) { + active_child = con->current.workspace->current.focused_inactive_child; + } else { + active_child = NULL; + } + + if (urgent) { + colors = &config->border_colors.urgent; + } else if (con->current.focused || container_is_current_parent_focused(con)) { + colors = &config->border_colors.focused; + } else if (config->has_focused_tab_title && container_has_focused_child(con)) { + colors = &config->border_colors.focused_tab_title; + } else if (con == active_child) { + colors = &config->border_colors.focused_inactive; + } else { + colors = &config->border_colors.unfocused; + } + + return colors; +} + +static bool container_is_current_floating(struct sway_container *container) { + if (!container->current.parent && container->current.workspace && + list_find(container->current.workspace->floating, container) != -1) { + return true; + } + if (container->scratchpad) { + return true; + } + return false; +} + +// scene rect wants premultiplied colors +static void scene_rect_set_color(struct wlr_scene_rect *rect, + const float color[4], float opacity) { + const float premultiplied[] = { + color[0] * color[3] * opacity, + color[1] * color[3] * opacity, + color[2] * color[3] * opacity, + color[3] * opacity, + }; + + wlr_scene_rect_set_color(rect, premultiplied); +} + +void container_update(struct sway_container *con) { + struct border_colors *colors = container_get_current_colors(con); + list_t *siblings = NULL; + enum sway_container_layout layout = L_NONE; + float alpha = con->alpha; + + if (con->current.parent) { + siblings = con->current.parent->current.children; + layout = con->current.parent->current.layout; + } else if (con->current.workspace) { + siblings = con->current.workspace->current.tiling; + layout = con->current.workspace->current.layout; + } + + float bottom[4], right[4]; + memcpy(bottom, colors->child_border, sizeof(bottom)); + memcpy(right, colors->child_border, sizeof(right)); + + if (!container_is_current_floating(con) && siblings && siblings->length == 1) { + if (layout == L_HORIZ) { + memcpy(right, colors->indicator, sizeof(right)); + } else if (layout == L_VERT) { + memcpy(bottom, colors->indicator, sizeof(bottom)); + } + } + + struct wlr_scene_node *node; + wl_list_for_each(node, &con->title_bar.border->children, link) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + scene_rect_set_color(rect, colors->border, alpha); + } + + wl_list_for_each(node, &con->title_bar.background->children, link) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + scene_rect_set_color(rect, colors->background, alpha); + } + + if (con->view) { + scene_rect_set_color(con->border.top, colors->child_border, alpha); + scene_rect_set_color(con->border.bottom, bottom, alpha); + scene_rect_set_color(con->border.left, colors->child_border, alpha); + scene_rect_set_color(con->border.right, right, alpha); + } + + if (con->title_bar.title_text) { + sway_text_node_set_color(con->title_bar.title_text, colors->text); + sway_text_node_set_background(con->title_bar.title_text, colors->background); + } + + if (con->title_bar.marks_text) { + sway_text_node_set_color(con->title_bar.marks_text, colors->text); + sway_text_node_set_background(con->title_bar.marks_text, colors->background); + } +} + +void container_update_itself_and_parents(struct sway_container *con) { + container_update(con); + + if (con->current.parent) { + container_update_itself_and_parents(con->current.parent); + } +} + +static void update_rect_list(struct wlr_scene_tree *tree, pixman_region32_t *region) { + int len; + const pixman_box32_t *rects = pixman_region32_rectangles(region, &len); + + wlr_scene_node_set_enabled(&tree->node, len > 0); + if (len == 0) { + return; + } + + int i = 0; + struct wlr_scene_node *node; + wl_list_for_each(node, &tree->children, link) { + struct wlr_scene_rect *rect = wlr_scene_rect_from_node(node); + wlr_scene_node_set_enabled(&rect->node, i < len); + + if (i < len) { + const pixman_box32_t *box = &rects[i++]; + wlr_scene_node_set_position(&rect->node, box->x1, box->y1); + wlr_scene_rect_set_size(rect, box->x2 - box->x1, box->y2 - box->y1); + } + } +} + +void container_arrange_title_bar(struct sway_container *con) { + enum alignment title_align = config->title_align; + int marks_buffer_width = 0; + int width = con->title_width; + int height = container_titlebar_height(); + + pixman_region32_t text_area; + pixman_region32_init(&text_area); + + if (con->title_bar.marks_text) { + struct sway_text_node *node = con->title_bar.marks_text; + marks_buffer_width = node->width; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = config->titlebar_h_padding; + } else { + h_padding = width - config->titlebar_h_padding - marks_buffer_width; + } + + h_padding = MAX(h_padding, 0); + + int alloc_width = MIN((int)node->width, + width - h_padding - config->titlebar_h_padding); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + + pixman_region32_union_rect(&text_area, &text_area, + node->node->x, node->node->y, alloc_width, node->height); + } + + if (con->title_bar.title_text) { + struct sway_text_node *node = con->title_bar.title_text; + + int h_padding; + if (title_align == ALIGN_RIGHT) { + h_padding = width - config->titlebar_h_padding - node->width; + } else if (title_align == ALIGN_CENTER) { + h_padding = ((int)width - marks_buffer_width - node->width) >> 1; + } else { + h_padding = config->titlebar_h_padding; + } + + h_padding = MAX(h_padding, 0); + + int alloc_width = MIN((int) node->width, + width - h_padding - config->titlebar_h_padding); + sway_text_node_set_max_width(node, alloc_width); + wlr_scene_node_set_position(node->node, + h_padding, (height - node->height) >> 1); + + pixman_region32_union_rect(&text_area, &text_area, + node->node->x, node->node->y, alloc_width, node->height); + } + + // silence pixman errors + if (width <= 0 || height <= 0) { + pixman_region32_fini(&text_area); + return; + } + + pixman_region32_t background, border; + + int thickness = config->titlebar_border_thickness; + pixman_region32_init_rect(&background, + thickness, thickness, + width - thickness * 2, height - thickness * 2); + pixman_region32_init_rect(&border, 0, 0, width, height); + pixman_region32_subtract(&border, &border, &background); + + pixman_region32_subtract(&background, &background, &text_area); + pixman_region32_fini(&text_area); + + update_rect_list(con->title_bar.background, &background); + pixman_region32_fini(&background); + + update_rect_list(con->title_bar.border, &border); + pixman_region32_fini(&border); + + container_update(con); +} + +void container_update_marks(struct sway_container *con) { + char *buffer = NULL; + + if (config->show_marks && con->marks->length) { + size_t len = 0; + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + len += strlen(mark) + 2; + } + } + buffer = calloc(len + 1, 1); + char *part = malloc(len + 1); + + if (!sway_assert(buffer && part, "Unable to allocate memory")) { + free(buffer); + return; + } + + for (int i = 0; i < con->marks->length; ++i) { + char *mark = con->marks->items[i]; + if (mark[0] != '_') { + snprintf(part, len + 1, "[%s]", mark); + strcat(buffer, part); + } + } + free(part); + } + + if (!buffer) { + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + } else if (!con->title_bar.marks_text) { + struct border_colors *colors = container_get_current_colors(con); + + con->title_bar.marks_text = sway_text_node_create(con->title_bar.tree, + buffer, colors->text, false); + } else { + sway_text_node_set_text(con->title_bar.marks_text, buffer); + } + + container_arrange_title_bar(con); + free(buffer); +} + +void container_update_title_bar(struct sway_container *con) { + if (!con->formatted_title) { + return; + } + + struct border_colors *colors = container_get_current_colors(con); + + if (con->title_bar.title_text) { + wlr_scene_node_destroy(con->title_bar.title_text->node); + con->title_bar.title_text = NULL; + } + + con->title_bar.title_text = sway_text_node_create(con->title_bar.tree, + con->formatted_title, colors->text, config->pango_markup); + + // we always have to remake these text buffers completely for text font + // changes etc... + if (con->title_bar.marks_text) { + wlr_scene_node_destroy(con->title_bar.marks_text->node); + con->title_bar.marks_text = NULL; + } + + container_update_marks(con); + container_arrange_title_bar(con); +} + void container_destroy(struct sway_container *con) { if (!sway_assert(con->node.destroying, "Tried to free container which wasn't marked as destroying")) { @@ -134,21 +451,11 @@ void container_destroy(struct sway_container *con) { } free(con->title); free(con->formatted_title); - wlr_texture_destroy(con->title_focused); - wlr_texture_destroy(con->title_focused_inactive); - wlr_texture_destroy(con->title_unfocused); - wlr_texture_destroy(con->title_urgent); - wlr_texture_destroy(con->title_focused_tab_title); list_free(con->pending.children); list_free(con->current.children); list_free(con->outputs); list_free_items_and_destroy(con->marks); - wlr_texture_destroy(con->marks_focused); - wlr_texture_destroy(con->marks_focused_inactive); - wlr_texture_destroy(con->marks_unfocused); - wlr_texture_destroy(con->marks_urgent); - wlr_texture_destroy(con->marks_focused_tab_title); if (con->view && con->view->container == con) { con->view->container = NULL; @@ -308,108 +615,6 @@ struct sway_output *container_get_effective_output(struct sway_container *con) { return con->outputs->items[con->outputs->length - 1]; } -static void render_titlebar_text_texture(struct sway_output *output, - struct sway_container *con, struct wlr_texture **texture, - struct border_colors *class, bool pango_markup, char *text) { - double scale = output->wlr_output->scale; - int width = 0; - int height = config->font_height * scale; - int baseline; - - // We must use a non-nil cairo_t for cairo_set_font_options to work. - // Therefore, we cannot use cairo_create(NULL). - cairo_surface_t *dummy_surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, 0, 0); - cairo_t *c = cairo_create(dummy_surface); - cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); - cairo_font_options_t *fo = cairo_font_options_create(); - if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); - } else { - cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_subpixel_order(fo, - to_cairo_subpixel_order(output->wlr_output->subpixel)); - } - cairo_set_font_options(c, fo); - get_text_size(c, config->font_description, &width, NULL, &baseline, scale, - config->pango_markup, "%s", text); - cairo_surface_destroy(dummy_surface); - cairo_destroy(c); - - if (width == 0 || height == 0) { - return; - } - - if (height > config->font_height * scale) { - height = config->font_height * scale; - } - - cairo_surface_t *surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, width, height); - cairo_status_t status = cairo_surface_status(surface); - if (status != CAIRO_STATUS_SUCCESS) { - sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s", - cairo_status_to_string(status)); - return; - } - - cairo_t *cairo = cairo_create(surface); - cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); - cairo_set_font_options(cairo, fo); - cairo_font_options_destroy(fo); - cairo_set_source_rgba(cairo, class->background[0], class->background[1], - class->background[2], class->background[3]); - cairo_paint(cairo); - PangoContext *pango = pango_cairo_create_context(cairo); - cairo_set_source_rgba(cairo, class->text[0], class->text[1], - class->text[2], class->text[3]); - cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - - render_text(cairo, config->font_description, scale, pango_markup, "%s", text); - - cairo_surface_flush(surface); - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_image_surface_get_stride(surface); - struct wlr_renderer *renderer = output->wlr_output->renderer; - *texture = wlr_texture_from_pixels( - renderer, DRM_FORMAT_ARGB8888, stride, width, height, data); - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} - -static void update_title_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->formatted_title) { - return; - } - - render_titlebar_text_texture(output, con, texture, class, - config->pango_markup, con->formatted_title); -} - -void container_update_title_textures(struct sway_container *container) { - update_title_texture(container, &container->title_focused, - &config->border_colors.focused); - update_title_texture(container, &container->title_focused_inactive, - &config->border_colors.focused_inactive); - update_title_texture(container, &container->title_unfocused, - &config->border_colors.unfocused); - update_title_texture(container, &container->title_urgent, - &config->border_colors.urgent); - update_title_texture(container, &container->title_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(container); -} - /** * Calculate and return the length of the tree representation. * An example tree representation is: V[Terminal, Firefox] @@ -475,7 +680,13 @@ void container_update_representation(struct sway_container *con) { } container_build_representation(con->pending.layout, con->pending.children, con->formatted_title); - container_update_title_textures(con); + + if (con->title_bar.title_text) { + sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); + container_arrange_title_bar(con); + } else { + container_update_title_bar(con); + } } if (con->pending.parent) { container_update_representation(con->pending.parent); @@ -761,17 +972,6 @@ bool container_is_floating(struct sway_container *container) { return false; } -bool container_is_current_floating(struct sway_container *container) { - if (!container->current.parent && container->current.workspace && - list_find(container->current.workspace->floating, container) != -1) { - return true; - } - if (container->scratchpad) { - return true; - } - return false; -} - void container_get_box(struct sway_container *container, struct wlr_box *box) { box->x = container->pending.x; box->y = container->pending.y; @@ -1092,7 +1292,6 @@ void container_discover_outputs(struct sway_container *con) { .width = con->current.width, .height = con->current.height, }; - struct sway_output *old_output = container_get_effective_output(con); for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; @@ -1129,14 +1328,6 @@ void container_discover_outputs(struct sway_container *con) { list_del(con->outputs, index); } } - struct sway_output *new_output = container_get_effective_output(con); - double old_scale = old_output && old_output->enabled ? - old_output->wlr_output->scale : -1; - double new_scale = new_output ? new_output->wlr_output->scale : -1; - if (old_scale != new_scale) { - container_update_title_textures(con); - container_update_marks_textures(con); - } } enum sway_container_layout container_parent_layout(struct sway_container *con) { @@ -1149,14 +1340,6 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { return L_NONE; } -enum sway_container_layout container_current_parent_layout( - struct sway_container *con) { - if (con->current.parent) { - return con->current.parent->current.layout; - } - return con->current.workspace->current.layout; -} - list_t *container_get_siblings(struct sway_container *container) { if (container->pending.parent) { return container->pending.parent->pending.children; @@ -1174,13 +1357,6 @@ int container_sibling_index(struct sway_container *child) { return list_find(container_get_siblings(child), child); } -list_t *container_get_current_siblings(struct sway_container *container) { - if (container->current.parent) { - return container->current.parent->current.children; - } - return container->current.workspace->current.tiling; -} - void container_handle_fullscreen_reparent(struct sway_container *con) { if (con->pending.fullscreen_mode != FULLSCREEN_WORKSPACE || !con->pending.workspace || con->pending.workspace->fullscreen == con) { @@ -1395,7 +1571,7 @@ bool container_find_and_unmark(char *mark) { if (strcmp(con_mark, mark) == 0) { free(con_mark); list_del(con->marks, i); - container_update_marks_textures(con); + container_update_marks(con); ipc_event_window(con, "mark"); return true; } @@ -1426,70 +1602,15 @@ void container_add_mark(struct sway_container *con, char *mark) { ipc_event_window(con, "mark"); } -static void update_marks_texture(struct sway_container *con, - struct wlr_texture **texture, struct border_colors *class) { - struct sway_output *output = container_get_effective_output(con); - if (!output) { - return; - } - if (*texture) { - wlr_texture_destroy(*texture); - *texture = NULL; - } - if (!con->marks->length) { - return; - } - - size_t len = 0; - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - len += strlen(mark) + 2; - } - } - char *buffer = calloc(len + 1, 1); - char *part = malloc(len + 1); - - if (!sway_assert(buffer && part, "Unable to allocate memory")) { - free(buffer); - return; - } - - for (int i = 0; i < con->marks->length; ++i) { - char *mark = con->marks->items[i]; - if (mark[0] != '_') { - snprintf(part, len + 1, "[%s]", mark); - strcat(buffer, part); - } - } - free(part); - - render_titlebar_text_texture(output, con, texture, class, false, buffer); - - free(buffer); -} - -void container_update_marks_textures(struct sway_container *con) { - if (!config->show_marks) { - return; - } - update_marks_texture(con, &con->marks_focused, - &config->border_colors.focused); - update_marks_texture(con, &con->marks_focused_inactive, - &config->border_colors.focused_inactive); - update_marks_texture(con, &con->marks_unfocused, - &config->border_colors.unfocused); - update_marks_texture(con, &con->marks_urgent, - &config->border_colors.urgent); - update_marks_texture(con, &con->marks_focused_tab_title, - &config->border_colors.focused_tab_title); - container_damage_whole(con); -} - void container_raise_floating(struct sway_container *con) { // Bring container to front by putting it at the end of the floating list. struct sway_container *floater = container_toplevel_ancestor(con); if (container_is_floating(floater) && floater->pending.workspace) { + // it's okay to just raise the scene directly instead of waiting + // for the transaction to go through. We won't be reconfiguring + // surfaces + wlr_scene_node_raise_to_top(&floater->scene_tree->node); + list_move_to_end(floater->pending.workspace->floating, floater); node_set_dirty(&floater->pending.workspace->node); } -- cgit v1.2.3-54-g00ecf