// SPDX-License-Identifier: GPL-2.0-only
-#include "config.h"
#include <assert.h>
#include <cairo.h>
-#include <drm_fourcc.h>
-#include <pango/pangocairo.h>
#include <wlr/util/log.h>
#include <wlr/util/box.h>
-#include "buffer.h"
#include "common/array.h"
#include "common/buf.h"
#include "common/font.h"
-#include "common/graphic-helpers.h"
+#include "common/macros.h"
+#include "common/scaled-font-buffer.h"
+#include "common/scaled-rect-buffer.h"
#include "common/scene-helpers.h"
#include "config/rcxml.h"
#include "labwc.h"
#include "window-rules.h"
#include "workspaces.h"
+struct osd_scene_item {
+ struct view *view;
+ struct wlr_scene_node *highlight_outline;
+};
+
static void update_osd(struct server *server);
static void
-destroy_osd_nodes(struct output *output)
+destroy_osd_scenes(struct server *server)
{
- struct wlr_scene_node *child, *next;
- struct wl_list *children = &output->osd_tree->children;
- wl_list_for_each_safe(child, next, children, link) {
- wlr_scene_node_destroy(child);
+ struct output *output;
+ wl_list_for_each(output, &server->outputs, link) {
+ wlr_scene_node_destroy(&output->osd_scene.tree->node);
+ output->osd_scene.tree = NULL;
+
+ wl_array_release(&output->osd_scene.items);
+ wl_array_init(&output->osd_scene.items);
}
}
}
if (osd_state->cycle_view) {
- /* Update the OSD to reflect the view has now gone. */
+ /* Recreate the OSD to reflect the view has now gone. */
+ destroy_osd_scenes(view->server);
update_osd(view->server);
}
server->osd_state.preview_anchor = NULL;
server->osd_state.cycle_view = NULL;
- struct output *output;
- wl_list_for_each(output, &server->outputs, link) {
- destroy_osd_nodes(output);
- wlr_scene_node_set_enabled(&output->osd_tree->node, false);
- }
+ destroy_osd_scenes(server);
+
if (server->osd_state.preview_outline) {
/* Destroy the whole multi_rect so we can easily react to new themes */
wlr_scene_node_destroy(&server->osd_state.preview_outline->tree->node);
}
static void
-render_osd(struct server *server, cairo_t *cairo, int w, int h,
- bool show_workspace, const char *workspace_name,
- struct wl_array *views)
+create_osd_scene(struct output *output, struct wl_array *views)
{
- struct view *cycle_view = server->osd_state.cycle_view;
+ struct server *server = output->server;
struct theme *theme = server->theme;
+ bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
+ const char *workspace_name = server->workspaces.current->name;
- cairo_surface_t *surf = cairo_get_target(cairo);
-
- /* Draw background */
- set_cairo_color(cairo, theme->osd_bg_color);
- cairo_rectangle(cairo, 0, 0, w, h);
- cairo_fill(cairo);
-
- /* Draw border */
- set_cairo_color(cairo, theme->osd_border_color);
- struct wlr_fbox fbox = {
- .width = w,
- .height = h,
- };
- draw_cairo_border(cairo, fbox, theme->osd_border_width);
+ int w = theme->osd_window_switcher_width;
+ if (theme->osd_window_switcher_width_is_percent) {
+ w = output->wlr_output->width
+ * theme->osd_window_switcher_width / 100;
+ }
+ int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height
+ + 2 * rc.theme->osd_border_width
+ + 2 * rc.theme->osd_window_switcher_padding;
+ if (show_workspace) {
+ /* workspace indicator */
+ h += theme->osd_window_switcher_item_height;
+ }
- /* Set up text rendering */
- set_cairo_color(cairo, theme->osd_label_text_color);
- PangoLayout *layout = pango_cairo_create_layout(cairo);
- pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false);
- pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+ output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree);
- PangoFontDescription *desc = font_to_pango_desc(&rc.font_osd);
- pango_layout_set_font_description(layout, desc);
+ float *text_color = theme->osd_label_text_color;
+ float *bg_color = theme->osd_bg_color;
- pango_cairo_update_layout(cairo, layout);
+ /* Draw background */
+ scaled_rect_buffer_create(output->osd_scene.tree, w, h,
+ theme->osd_border_width, bg_color, theme->osd_border_color);
int y = theme->osd_border_width + theme->osd_window_switcher_padding;
/* Draw workspace indicator */
if (show_workspace) {
+ struct font font = rc.font_osd;
+ font.weight = FONT_WEIGHT_BOLD;
+
/* Center workspace indicator on the x axis */
- int x = font_width(&rc.font_osd, workspace_name);
- x = (w - x) / 2;
- cairo_move_to(cairo, x, y + theme->osd_window_switcher_item_active_border_width);
- PangoWeight weight = pango_font_description_get_weight(desc);
- pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD);
- pango_layout_set_font_description(layout, desc);
- pango_layout_set_text(layout, workspace_name, -1);
- pango_cairo_show_layout(cairo, layout);
- pango_font_description_set_weight(desc, weight);
- pango_layout_set_font_description(layout, desc);
+ int x = (w - font_width(&font, workspace_name)) / 2;
+ struct scaled_font_buffer *font_buffer =
+ scaled_font_buffer_create(output->osd_scene.tree);
+ wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
+ x, y + theme->osd_window_switcher_item_active_border_width);
+ scaled_font_buffer_update(font_buffer, workspace_name, 0,
+ &font, text_color, bg_color);
y += theme->osd_window_switcher_item_height;
}
- pango_font_description_free(desc);
struct buf buf = BUF_INIT;
/* Draw text for each node */
struct view **view;
wl_array_for_each(view, views) {
+ struct osd_scene_item *item =
+ wl_array_add(&output->osd_scene.items, sizeof(*item));
+ item->view = *view;
/*
* OSD border
* +---------------------------------+
+ theme->osd_window_switcher_padding
+ theme->osd_window_switcher_item_active_border_width
+ theme->osd_window_switcher_item_padding_x;
+ struct wlr_scene_tree *item_root =
+ wlr_scene_tree_create(output->osd_scene.tree);
int nr_fields = wl_list_length(&rc.window_switcher.fields);
struct window_switcher_field *field;
wl_list_for_each(field, &rc.window_switcher.fields, link) {
- buf_clear(&buf);
- cairo_move_to(cairo, x, y
- + theme->osd_window_switcher_item_padding_y
- + theme->osd_window_switcher_item_active_border_width);
-
- osd_field_get_content(field, &buf, *view);
-
int field_width = (available_width - (nr_fields + 1)
* theme->osd_window_switcher_item_padding_x)
* field->width / 100.0;
- pango_layout_set_width(layout, field_width * PANGO_SCALE);
- pango_layout_set_text(layout, buf.data, -1);
- pango_cairo_show_layout(cairo, layout);
+
+ buf_clear(&buf);
+ osd_field_get_content(field, &buf, *view);
+
+ struct scaled_font_buffer *font_buffer =
+ scaled_font_buffer_create(item_root);
+ scaled_font_buffer_update(font_buffer, buf.data, field_width,
+ &rc.font_osd, text_color, bg_color);
+
+ wlr_scene_node_set_position(&font_buffer->scene_buffer->node,
+ x, y + theme->osd_window_switcher_item_padding_y
+ + theme->osd_window_switcher_item_active_border_width);
x += field_width + theme->osd_window_switcher_item_padding_x;
}
- if (*view == cycle_view) {
- /* Highlight current window */
- struct wlr_fbox fbox = {
- .x = theme->osd_border_width + theme->osd_window_switcher_padding,
- .y = y,
- .width = w
- - 2 * theme->osd_border_width
- - 2 * theme->osd_window_switcher_padding,
- .height = theme->osd_window_switcher_item_height,
- };
- draw_cairo_border(cairo, fbox,
- theme->osd_window_switcher_item_active_border_width);
- cairo_stroke(cairo);
- }
+ /* Highlight around selected window's item */
+ int highlight_w = w - 2 * theme->osd_border_width
+ - 2 * theme->osd_window_switcher_padding;
+ int highlight_h = theme->osd_window_switcher_item_height;
+ int highlight_x = theme->osd_border_width
+ + theme->osd_window_switcher_padding;
+ int border_width = theme->osd_window_switcher_item_active_border_width;
+ float transparent[4] = {0};
+
+ struct scaled_rect_buffer *highlight_buffer = scaled_rect_buffer_create(
+ output->osd_scene.tree, highlight_w, highlight_h,
+ border_width, transparent, text_color);
+ assert(highlight_buffer);
+ item->highlight_outline = &highlight_buffer->scene_buffer->node;
+ wlr_scene_node_set_position(item->highlight_outline, highlight_x, y);
+ wlr_scene_node_set_enabled(item->highlight_outline, false);
y += theme->osd_window_switcher_item_height;
}
buf_reset(&buf);
- g_object_unref(layout);
- cairo_surface_flush(surf);
+ /* Center OSD */
+ struct wlr_box usable = output_usable_area_in_layout_coords(output);
+ wlr_scene_node_set_position(&output->osd_scene.tree->node,
+ usable.x + usable.width / 2 - w / 2,
+ usable.y + usable.height / 2 - h / 2);
}
static void
-display_osd(struct output *output, struct wl_array *views)
+update_item_highlight(struct output *output)
{
- struct server *server = output->server;
- struct theme *theme = server->theme;
- bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1;
- const char *workspace_name = server->workspaces.current->name;
-
- float scale = output->wlr_output->scale;
- int w = theme->osd_window_switcher_width;
- if (theme->osd_window_switcher_width_is_percent) {
- w = output->wlr_output->width / output->wlr_output->scale
- * theme->osd_window_switcher_width / 100;
+ struct osd_scene_item *item;
+ wl_array_for_each(item, &output->osd_scene.items) {
+ wlr_scene_node_set_enabled(item->highlight_outline,
+ item->view == output->server->osd_state.cycle_view);
}
- int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height
- + 2 * rc.theme->osd_border_width
- + 2 * rc.theme->osd_window_switcher_padding;
- if (show_workspace) {
- /* workspace indicator */
- h += theme->osd_window_switcher_item_height;
- }
-
- struct lab_data_buffer *buffer = buffer_create_cairo(w, h, scale);
- if (!buffer) {
- wlr_log(WLR_ERROR, "Failed to allocate cairo buffer for the window switcher");
- return;
- }
-
- /* Render OSD image */
- cairo_t *cairo = cairo_create(buffer->surface);
- render_osd(server, cairo, w, h, show_workspace, workspace_name, views);
- cairo_destroy(cairo);
-
- struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create(
- output->osd_tree, &buffer->base);
- wlr_buffer_drop(&buffer->base);
- wlr_scene_buffer_set_dest_size(scene_buffer, w, h);
-
- /* Center OSD */
- struct wlr_box output_box;
- wlr_output_layout_get_box(output->server->output_layout,
- output->wlr_output, &output_box);
- int lx = output->usable_area.x + output->usable_area.width / 2
- - w / 2 + output_box.x;
- int ly = output->usable_area.y + output->usable_area.height / 2
- - h / 2 + output_box.y;
- wlr_scene_node_set_position(&scene_buffer->node, lx, ly);
- wlr_scene_node_set_enabled(&output->osd_tree->node, true);
}
static void
/* Display the actual OSD */
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
- destroy_osd_nodes(output);
- if (output_is_usable(output)) {
- display_osd(output, &views);
+ if (!output_is_usable(output)) {
+ continue;
+ }
+ if (!output->osd_scene.tree) {
+ create_osd_scene(output, &views);
+ assert(output->osd_scene.tree);
}
+ update_item_highlight(output);
}
}