aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Orzechowski <alex@ozal.ski>2023-11-23 10:08:28 -0500
committerLibravatar Kirill Primak <vyivel@eclair.cafe>2024-01-18 18:36:54 +0300
commit946fc8094559801bc4be629368ac31025d28165a (patch)
treee320c34a26f9a515b24c7526a9c863408efd799c
parentrenderer: Remove in favor of scene_graph (diff)
downloadsway-946fc8094559801bc4be629368ac31025d28165a.tar.gz
sway-946fc8094559801bc4be629368ac31025d28165a.tar.zst
sway-946fc8094559801bc4be629368ac31025d28165a.zip
Introduce sway_text_node
This is a helper on top of a wlr_scene_buffer that will handle text rendering for us.
-rw-r--r--include/sway/sway_text_node.h28
-rw-r--r--sway/meson.build1
-rw-r--r--sway/sway_text_node.c303
3 files changed, 332 insertions, 0 deletions
diff --git a/include/sway/sway_text_node.h b/include/sway/sway_text_node.h
new file mode 100644
index 00000000..0d4209bb
--- /dev/null
+++ b/include/sway/sway_text_node.h
@@ -0,0 +1,28 @@
1#ifndef _SWAY_BUFFER_H
2#define _SWAY_BUFFER_H
3#include <wlr/types/wlr_scene.h>
4
5struct sway_text_node {
6 int width;
7 int max_width;
8 int height;
9 int baseline;
10 bool pango_markup;
11 float color[4];
12 float background[4];
13
14 struct wlr_scene_node *node;
15};
16
17struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
18 char *text, float color[4], bool pango_markup);
19
20void sway_text_node_set_color(struct sway_text_node *node, float color[4]);
21
22void sway_text_node_set_text(struct sway_text_node *node, char *text);
23
24void sway_text_node_set_max_width(struct sway_text_node *node, int max_width);
25
26void sway_text_node_set_background(struct sway_text_node *node, float background[4]);
27
28#endif
diff --git a/sway/meson.build b/sway/meson.build
index 04b0dd93..110de58c 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -10,6 +10,7 @@ sway_sources = files(
10 'realtime.c', 10 'realtime.c',
11 'scene_descriptor.c', 11 'scene_descriptor.c',
12 'server.c', 12 'server.c',
13 'sway_text_node.c',
13 'swaynag.c', 14 'swaynag.c',
14 'xdg_activation_v1.c', 15 'xdg_activation_v1.c',
15 'xdg_decoration.c', 16 'xdg_decoration.c',
diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c
new file mode 100644
index 00000000..b9a77d94
--- /dev/null
+++ b/sway/sway_text_node.c
@@ -0,0 +1,303 @@
1#define _POSIX_C_SOURCE 200809L
2#include <drm_fourcc.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <wlr/types/wlr_buffer.h>
7#include <wlr/interfaces/wlr_buffer.h>
8#include "cairo_util.h"
9#include "log.h"
10#include "pango.h"
11#include "sway/config.h"
12#include "sway/sway_text_node.h"
13
14struct cairo_buffer {
15 struct wlr_buffer base;
16 cairo_surface_t *surface;
17 cairo_t *cairo;
18};
19
20static void cairo_buffer_handle_destroy(struct wlr_buffer *wlr_buffer) {
21 struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
22
23 cairo_surface_destroy(buffer->surface);
24 cairo_destroy(buffer->cairo);
25 free(buffer);
26}
27
28static bool cairo_buffer_handle_begin_data_ptr_access(struct wlr_buffer *wlr_buffer,
29 uint32_t flags, void **data, uint32_t *format, size_t *stride) {
30 struct cairo_buffer *buffer = wl_container_of(wlr_buffer, buffer, base);
31 *data = cairo_image_surface_get_data(buffer->surface);
32 *stride = cairo_image_surface_get_stride(buffer->surface);
33 *format = DRM_FORMAT_ARGB8888;
34 return true;
35}
36
37static void cairo_buffer_handle_end_data_ptr_access(struct wlr_buffer *wlr_buffer) {
38 // This space is intentionally left blank
39}
40
41static const struct wlr_buffer_impl cairo_buffer_impl = {
42 .destroy = cairo_buffer_handle_destroy,
43 .begin_data_ptr_access = cairo_buffer_handle_begin_data_ptr_access,
44 .end_data_ptr_access = cairo_buffer_handle_end_data_ptr_access,
45};
46
47struct text_buffer {
48 struct wlr_scene_buffer *buffer_node;
49 char *text;
50 struct sway_text_node props;
51
52 bool visible;
53 float scale;
54 enum wl_output_subpixel subpixel;
55
56 struct wl_listener outputs_update;
57 struct wl_listener destroy;
58};
59
60static int get_text_width(struct sway_text_node *props) {
61 if (props->max_width) {
62 return MIN(props->max_width, props->width);
63 }
64
65 return props->width;
66}
67
68static void update_source_box(struct text_buffer *buffer) {
69 struct sway_text_node *props = &buffer->props;
70 struct wlr_fbox source_box = {
71 .x = 0,
72 .y = 0,
73 .width = ceil(get_text_width(props) * buffer->scale),
74 .height = ceil(props->height * buffer->scale),
75 };
76
77 wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box);
78}
79
80static void render_backing_buffer(struct text_buffer *buffer) {
81 if (!buffer->visible) {
82 return;
83 }
84
85 float scale = buffer->scale;
86 int width = ceil(buffer->props.width * scale);
87 int height = ceil(buffer->props.height * scale);
88 float *color = (float *)&buffer->props.color;
89 float *background = (float *)&buffer->props.background;
90 PangoContext *pango = NULL;
91
92 cairo_font_options_t *fo = cairo_font_options_create();
93 cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL);
94 enum wl_output_subpixel subpixel = buffer->subpixel;
95 if (subpixel == WL_OUTPUT_SUBPIXEL_NONE || subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) {
96 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY);
97 } else {
98 cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL);
99 cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(subpixel));
100 }
101
102 cairo_surface_t *surface = cairo_image_surface_create(
103 CAIRO_FORMAT_ARGB32, width, height);
104 cairo_status_t status = cairo_surface_status(surface);
105 if (status != CAIRO_STATUS_SUCCESS) {
106 sway_log(SWAY_ERROR, "cairo_image_surface_create failed: %s",
107 cairo_status_to_string(status));
108 goto err;
109 }
110
111 struct cairo_buffer *cairo_buffer = calloc(1, sizeof(*cairo_buffer));
112 if (!cairo_buffer) {
113 sway_log(SWAY_ERROR, "cairo_buffer allocation failed");
114 goto err;
115 }
116
117 cairo_t *cairo = cairo_create(surface);
118 if (!cairo) {
119 sway_log(SWAY_ERROR, "cairo_create failed");
120 free(cairo_buffer);
121 goto err;
122 }
123
124 cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
125 cairo_set_font_options(cairo, fo);
126 pango = pango_cairo_create_context(cairo);
127
128 cairo_set_source_rgba(cairo, background[0], background[1], background[2], background[3]);
129 cairo_rectangle(cairo, 0, 0, width, height);
130 cairo_fill(cairo);
131
132 cairo_set_source_rgba(cairo, color[0], color[1], color[2], color[3]);
133 cairo_move_to(cairo, 0, (config->font_baseline - buffer->props.baseline) * scale);
134
135 render_text(cairo, config->font_description, scale, buffer->props.pango_markup,
136 "%s", buffer->text);
137
138 cairo_surface_flush(surface);
139
140 wlr_buffer_init(&cairo_buffer->base, &cairo_buffer_impl, width, height);
141 cairo_buffer->surface = surface;
142 cairo_buffer->cairo = cairo;
143
144 wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base);
145 wlr_buffer_drop(&cairo_buffer->base);
146 update_source_box(buffer);
147
148 pixman_region32_t opaque;
149 pixman_region32_init(&opaque);
150 if (background[3] == 1) {
151 pixman_region32_union_rect(&opaque, &opaque, 0, 0,
152 buffer->props.width, buffer->props.height);
153 }
154 wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque);
155 pixman_region32_fini(&opaque);
156
157err:
158 if (pango) g_object_unref(pango);
159 cairo_font_options_destroy(fo);
160}
161
162static void handle_outputs_update(struct wl_listener *listener, void *data) {
163 struct text_buffer *buffer = wl_container_of(listener, buffer, outputs_update);
164 struct wlr_scene_outputs_update_event *event = data;
165
166 float scale = 0;
167 enum wl_output_subpixel subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
168
169 for (size_t i = 0; i < event->size; i++) {
170 struct wlr_scene_output *output = event->active[i];
171 if (subpixel == WL_OUTPUT_SUBPIXEL_UNKNOWN) {
172 subpixel = output->output->subpixel;
173 } else if (subpixel != output->output->subpixel) {
174 subpixel = WL_OUTPUT_SUBPIXEL_NONE;
175 }
176
177 if (scale != 0 && scale != output->output->scale) {
178 // drop down to gray scale if we encounter outputs with different
179 // scales or else we will have chromatic aberations
180 subpixel = WL_OUTPUT_SUBPIXEL_NONE;
181 }
182
183 if (scale < output->output->scale) {
184 scale = output->output->scale;
185 }
186 }
187
188 buffer->visible = event->size > 0;
189
190 if (scale != buffer->scale || subpixel != buffer->subpixel) {
191 buffer->scale = scale;
192 buffer->subpixel = subpixel;
193 render_backing_buffer(buffer);
194 }
195}
196
197static void handle_destroy(struct wl_listener *listener, void *data) {
198 struct text_buffer *buffer = wl_container_of(listener, buffer, destroy);
199
200 wl_list_remove(&buffer->outputs_update.link);
201 wl_list_remove(&buffer->destroy.link);
202
203 free(buffer->text);
204 free(buffer);
205}
206
207static void text_calc_size(struct text_buffer *buffer) {
208 struct sway_text_node *props = &buffer->props;
209
210 cairo_t *c = cairo_create(NULL);
211 if (!c) {
212 sway_log(SWAY_ERROR, "cairo_t allocation failed");
213 return;
214 }
215
216 cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
217 get_text_size(c, config->font_description, &props->width, NULL,
218 &props->baseline, 1, props->pango_markup, "%s", buffer->text);
219 cairo_destroy(c);
220
221 wlr_scene_buffer_set_dest_size(buffer->buffer_node,
222 get_text_width(props), props->height);
223}
224
225struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent,
226 char *text, float color[4], bool pango_markup) {
227 struct text_buffer *buffer = calloc(1, sizeof(*buffer));
228 if (!buffer) {
229 return NULL;
230 }
231
232 struct wlr_scene_buffer *node = wlr_scene_buffer_create(parent, NULL);
233 if (!node) {
234 free(buffer);
235 return NULL;
236 }
237
238 buffer->buffer_node = node;
239 buffer->props.node = &node->node;
240 buffer->text = strdup(text);
241 if (!buffer->text) {
242 free(buffer);
243 wlr_scene_node_destroy(&node->node);
244 return NULL;
245 }
246
247 buffer->props.height = config->font_height;
248 buffer->props.pango_markup = pango_markup;
249 memcpy(&buffer->props.color, color, sizeof(*color) * 4);
250
251 buffer->destroy.notify = handle_destroy;
252 wl_signal_add(&node->node.events.destroy, &buffer->destroy);
253 buffer->outputs_update.notify = handle_outputs_update;
254 wl_signal_add(&node->events.outputs_update, &buffer->outputs_update);
255
256 text_calc_size(buffer);
257
258 return &buffer->props;
259}
260
261void sway_text_node_set_color(struct sway_text_node *node, float color[4]) {
262 if (memcmp(&node->color, color, sizeof(*color) * 4) == 0) {
263 return;
264 }
265
266 memcpy(&node->color, color, sizeof(*color) * 4);
267 struct text_buffer *buffer = wl_container_of(node, buffer, props);
268
269 render_backing_buffer(buffer);
270}
271
272void sway_text_node_set_text(struct sway_text_node *node, char *text) {
273 struct text_buffer *buffer = wl_container_of(node, buffer, props);
274 if (strcmp(buffer->text, text) == 0) {
275 return;
276 }
277
278 char *new_text = strdup(text);
279 if (!new_text) {
280 return;
281 }
282
283 free(buffer->text);
284 buffer->text = new_text;
285
286 text_calc_size(buffer);
287 render_backing_buffer(buffer);
288}
289
290void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) {
291 struct text_buffer *buffer = wl_container_of(node, buffer, props);
292 buffer->props.max_width = max_width;
293 wlr_scene_buffer_set_dest_size(buffer->buffer_node,
294 get_text_width(&buffer->props), buffer->props.height);
295 update_source_box(buffer);
296 render_backing_buffer(buffer);
297}
298
299void sway_text_node_set_background(struct sway_text_node *node, float background[4]) {
300 struct text_buffer *buffer = wl_container_of(node, buffer, props);
301 memcpy(&node->background, background, sizeof(*background) * 4);
302 render_backing_buffer(buffer);
303}