aboutsummaryrefslogtreecommitdiff
path: root/sway/tree
diff options
context:
space:
mode:
authorAlexander Orzechowski <alex@ozal.ski>2023-11-21 19:51:57 -0500
committerKirill Primak <vyivel@eclair.cafe>2024-01-18 18:36:54 +0300
commit6d7b1321db54155cf78305dbafdcfc7de9b78415 (patch)
treeacf9f02681b1d5a40416be5f4151641920176a93 /sway/tree
parent188811f80861caacd016b857b0d07f6d2d62d15a (diff)
scene_graph: Port container server side decorations
Diffstat (limited to 'sway/tree')
-rw-r--r--sway/tree/container.c545
-rw-r--r--sway/tree/view.c10
2 files changed, 342 insertions, 213 deletions
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 <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_subcompositor.h>
#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);
}
diff --git a/sway/tree/view.c b/sway/tree/view.c
index d349e5fa..7af2fd3f 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -27,6 +27,7 @@
#include "sway/scene_descriptor.h"
#include "sway/server.h"
#include "sway/surface.h"
+#include "sway/sway_text_node.h"
#include "sway/tree/arrange.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
@@ -1337,7 +1338,13 @@ void view_update_title(struct sway_view *view, bool force) {
view->container->title = title ? strdup(title) : NULL;
// Update title after the global font height is updated
- container_update_title_textures(view->container);
+ if (view->container->title_bar.title_text && len) {
+ sway_text_node_set_text(view->container->title_bar.title_text,
+ view->container->formatted_title);
+ container_arrange_title_bar(view->container);
+ } else {
+ container_update_title_bar(view->container);
+ }
ipc_event_window(view->container, "title");
@@ -1404,6 +1411,7 @@ void view_set_urgent(struct sway_view *view, bool enable) {
return;
}
clock_gettime(CLOCK_MONOTONIC, &view->urgent);
+ container_update_itself_and_parents(view->container);
} else {
view->urgent = (struct timespec){ 0 };
if (view->urgent_timer) {