1#include "decor.h"
2#include "backend.h"
3
4#include "compositor.h"
5#include "drm.h"
6#include "internal.h"
7#include "swc.h"
8#include "util.h"
9#include "window.h"
10
11#include <pixman.h>
12#include <stdlib.h>
13#include <string.h>
14#include <wld/wld.h>
15
16#define DEFAULT_DECOR_FONT "sans-serif:size=10"
17
18static struct wld_font_context *font_context;
19
20enum decor_part_index {
21 DECOR_PART_TOP_LEFT,
22 DECOR_PART_TOP,
23 DECOR_PART_TOP_RIGHT,
24 DECOR_PART_LEFT,
25 DECOR_PART_RIGHT,
26 DECOR_PART_BOTTOM_LEFT,
27 DECOR_PART_BOTTOM,
28 DECOR_PART_BOTTOM_RIGHT,
29 DECOR_PART_COUNT,
30};
31
32static uint32_t
33decor_title_length(struct wld_font *font, const char *title, uint32_t max_width)
34{
35 uint32_t len = 0;
36 struct wld_extents extents;
37
38 if (!title || !max_width) {
39 return 0;
40 }
41
42 while (title[len]) {
43 uint32_t next = len + 1;
44
45 /* we skip utf-8 continuation bytes */
46 while ((title[next] & 0xc0) == 0x80) {
47 next++;
48 }
49
50 wld_font_text_extents_n(font, title, (int32_t)next, &extents);
51 if (extents.advance > max_width) {
52 break;
53 }
54 len = next;
55 }
56
57 return len;
58}
59
60static uint32_t
61utf8_next_len(const char *text, uint32_t offset)
62{
63 uint32_t next = offset + 1;
64
65 while ((text[next] & 0xc0) == 0x80) {
66 next++;
67 }
68
69 return next - offset;
70}
71
72static uint32_t
73decor_title_stacked_length(struct wld_font *font, const char *title,
74 uint32_t max_height, uint32_t *glyph_count)
75{
76 uint32_t len = 0, count = 0;
77
78 if (!title || !font->height) {
79 *glyph_count = 0;
80 return 0;
81 }
82
83 while (title[len] && (count + 1) * font->height <= max_height) {
84 len += utf8_next_len(title, len);
85 count++;
86 }
87
88 *glyph_count = count;
89 return len;
90}
91
92static bool
93streq(const char *a, const char *b)
94{
95 if (!a || !b) {
96 return a == b;
97 }
98 return strcmp(a, b) == 0;
99}
100
101static const struct swc_decor_part *
102decor_part_at(const struct swc_decor_parts *parts, enum decor_part_index index)
103{
104 if (!parts) {
105 return NULL;
106 }
107
108 switch (index) {
109 case DECOR_PART_TOP_LEFT:
110 return &parts->top_left;
111 case DECOR_PART_TOP:
112 return &parts->top;
113 case DECOR_PART_TOP_RIGHT:
114 return &parts->top_right;
115 case DECOR_PART_LEFT:
116 return &parts->left;
117 case DECOR_PART_RIGHT:
118 return &parts->right;
119 case DECOR_PART_BOTTOM_LEFT:
120 return &parts->bottom_left;
121 case DECOR_PART_BOTTOM:
122 return &parts->bottom;
123 case DECOR_PART_BOTTOM_RIGHT:
124 return &parts->bottom_right;
125 case DECOR_PART_COUNT:
126 break;
127 }
128
129 return NULL;
130}
131
132static void
133close_decor_parts(struct compositor_view *view)
134{
135 for (uint32_t i = 0; i < DECOR_PART_COUNT; ++i) {
136 if (view->decor.parts[i].tiled_buffer) {
137 wld_buffer_unreference(view->decor.parts[i].tiled_buffer);
138 view->decor.parts[i].tiled_buffer = NULL;
139 }
140 if (view->decor.parts[i].buffer) {
141 wld_buffer_unreference(view->decor.parts[i].buffer);
142 view->decor.parts[i].buffer = NULL;
143 }
144 free(view->decor.parts[i].data);
145 view->decor.parts[i].data = NULL;
146 view->decor.parts[i].width = 0;
147 view->decor.parts[i].height = 0;
148 view->decor.parts[i].stride = 0;
149 view->decor.parts[i].tiled_width = 0;
150 view->decor.parts[i].tiled_height = 0;
151 view->decor.parts[i].opaque = false;
152 }
153 view->decor.parts_key = NULL;
154}
155
156static bool
157decor_part_is_empty(const struct swc_decor_part *part)
158{
159 return !part || !part->data || !part->width || !part->height ||
160 !part->stride;
161}
162
163static bool
164decor_part_equal(const struct decor_part_buffer *owned,
165 const struct swc_decor_part *part)
166{
167 size_t size;
168
169 if (decor_part_is_empty(part)) {
170 return !owned->data && !owned->buffer && !owned->width && !owned->height &&
171 !owned->stride;
172 }
173
174 if (!owned->data || owned->width != part->width ||
175 owned->height != part->height || owned->stride != part->stride) {
176 return false;
177 }
178
179 size = (size_t)part->stride * part->height;
180 return memcmp(owned->data, part->data, size) == 0;
181}
182
183static bool
184decor_parts_equal(struct compositor_view *view, const struct swc_decor_parts *parts)
185{
186 for (uint32_t i = 0; i < DECOR_PART_COUNT; ++i) {
187 if (!decor_part_equal(&view->decor.parts[i], decor_part_at(parts, i))) {
188 return false;
189 }
190 }
191
192 return true;
193}
194
195static bool
196copy_decor_part(struct decor_part_buffer *dst, const struct swc_decor_part *src)
197{
198 size_t row_size, size;
199
200 if (decor_part_is_empty(src)) {
201 memset(dst, 0, sizeof(*dst));
202 return true;
203 }
204
205 row_size = (size_t)src->width * 4;
206 if (src->stride < row_size ||
207 (src->height && src->stride > SIZE_MAX / src->height)) {
208 return false;
209 }
210 size = (size_t)src->stride * src->height;
211 dst->data = malloc(size);
212 if (!dst->data) {
213 return false;
214 }
215 memcpy(dst->data, src->data, size);
216
217 dst->width = src->width;
218 dst->height = src->height;
219 dst->stride = src->stride;
220 dst->opaque = true;
221 for (uint32_t y = 0; y < src->height && dst->opaque; ++y) {
222 const uint8_t *row = (const uint8_t *)src->data + (size_t)y * src->stride;
223
224 for (uint32_t x = 0; x < src->width; ++x) {
225 uint32_t pixel;
226
227 memcpy(&pixel, row + (size_t)x * 4, sizeof(pixel));
228 if ((pixel >> 24) != 0xff) {
229 dst->opaque = false;
230 break;
231 }
232 }
233 }
234 /* DRM renderers only allow read support for their native buffers not pixman/shmbuffers */
235 dst->buffer = wld_create_buffer(swc.backend->context, src->width, src->height,
236 WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
237 if (!dst->buffer) {
238 free(dst->data);
239 memset(dst, 0, sizeof(*dst));
240 return false;
241 }
242
243 if (dst->buffer->pitch < row_size || !wld_map(dst->buffer)) {
244 wld_buffer_unreference(dst->buffer);
245 free(dst->data);
246 memset(dst, 0, sizeof(*dst));
247 return false;
248 }
249 for (uint32_t y = 0; y < src->height; ++y) {
250 memcpy((uint8_t *)dst->buffer->map + (size_t)y * dst->buffer->pitch,
251 (uint8_t *)dst->data + (size_t)y * src->stride, row_size);
252 }
253 wld_unmap(dst->buffer);
254
255 return true;
256}
257
258static bool
259copy_decor_parts(struct compositor_view *view, const struct swc_decor_parts *parts)
260{
261 struct decor_part_buffer copied[DECOR_PART_COUNT] = { 0 };
262 uint32_t i;
263
264 for (i = 0; i < DECOR_PART_COUNT; ++i) {
265 if (!copy_decor_part(&copied[i], decor_part_at(parts, i))) {
266 goto error;
267 }
268 }
269
270 close_decor_parts(view);
271 memcpy(view->decor.parts, copied, sizeof(copied));
272 view->decor.parts_key = parts;
273 return true;
274
275error:
276 for (i = 0; i < DECOR_PART_COUNT; ++i) {
277 if (copied[i].buffer) {
278 wld_buffer_unreference(copied[i].buffer);
279 }
280 free(copied[i].data);
281 }
282 return false;
283}
284
285static void
286close_decor_font(struct compositor_view *view)
287{
288 if (view->decor.font) {
289 wld_font_close(view->decor.font);
290 view->decor.font = NULL;
291 }
292 free(view->decor.font_name);
293 view->decor.font_name = NULL;
294}
295
296static void
297close_decor_string(struct compositor_view *view)
298{
299 free(view->decor.string);
300 view->decor.string = NULL;
301}
302
303static struct wld_buffer *
304get_tiled_decor_buffer(struct decor_part_buffer *part, uint32_t width,
305 uint32_t height)
306{
307 struct wld_buffer *buffer;
308 uint32_t buffer_width = width, buffer_height = height;
309 size_t row_size;
310
311 if (width == part->width && height == part->height) {
312 return part->buffer;
313 }
314 if (part->tiled_buffer && part->tiled_width >= width &&
315 part->tiled_height >= height) {
316 return part->tiled_buffer;
317 }
318
319 if (part->tiled_buffer) {
320 wld_buffer_unreference(part->tiled_buffer);
321 part->tiled_buffer = NULL;
322 part->tiled_width = 0;
323 part->tiled_height = 0;
324 }
325
326 if (width != part->width && width <= UINT32_MAX - 255) {
327 buffer_width = (width + 255) & ~255U;
328 }
329 if (height != part->height && height <= UINT32_MAX - 255) {
330 buffer_height = (height + 255) & ~255U;
331 }
332
333 if (buffer_width > SIZE_MAX / 4) {
334 return NULL;
335 }
336 row_size = (size_t)buffer_width * 4;
337 buffer = wld_create_buffer(swc.backend->context, buffer_width, buffer_height,
338 WLD_FORMAT_ARGB8888, WLD_FLAG_MAP);
339 if (!buffer || buffer->pitch < row_size || !wld_map(buffer)) {
340 if (buffer) {
341 wld_buffer_unreference(buffer);
342 }
343 return NULL;
344 }
345
346 for (uint32_t y = 0; y < buffer_height; ++y) {
347 uint8_t *dst = (uint8_t *)buffer->map + (size_t)y * buffer->pitch;
348 const uint8_t *src = (const uint8_t *)part->data +
349 (size_t)(y % part->height) * part->stride;
350
351 for (uint32_t x = 0; x < buffer_width; x += part->width) {
352 uint32_t copy_width = MIN(part->width, buffer_width - x);
353
354 memcpy(dst + (size_t)x * 4, src, (size_t)copy_width * 4);
355 }
356 }
357 wld_unmap(buffer);
358
359 part->tiled_buffer = buffer;
360 part->tiled_width = buffer_width;
361 part->tiled_height = buffer_height;
362 return buffer;
363}
364
365/* draw the decor part by tiling it across the target reigon.
366 * the part buffer is repeated to fill the entirety of some width x height area
367 * but then we cache the expanded version so it can be just one op
368 * instead of one op per every time we repeat it */
369static void
370draw_decor_part(struct wld_renderer *renderer,
371 const struct swc_rectangle *target_geom,
372 struct compositor_view *view, pixman_region32_t *damage,
373 struct decor_part_buffer *part, int32_t x, int32_t y,
374 uint32_t width, uint32_t height)
375{
376 pixman_region32_t region;
377 struct wld_buffer *buffer;
378
379 if (!part->buffer || !part->width || !part->height || !width || !height) {
380 return;
381 }
382 if (!(buffer = get_tiled_decor_buffer(part, width, height))) {
383 return;
384 }
385
386 pixman_region32_init_rect(®ion, x, y, width, height);
387 pixman_region32_intersect(®ion, ®ion, damage);
388 pixman_region32_subtract(®ion, ®ion, &view->clip);
389 pixman_region32_translate(®ion, -x, -y);
390 if (part->opaque) {
391 wld_copy_region(renderer, buffer, x - target_geom->x,
392 y - target_geom->y, ®ion);
393 } else {
394 wld_blend_region(renderer, buffer, x - target_geom->x,
395 y - target_geom->y, ®ion);
396 }
397
398 pixman_region32_fini(®ion);
399}
400
401static bool
402decor_parts_complete(struct compositor_view *view)
403{
404 for (uint32_t i = 0; i < DECOR_PART_COUNT; ++i) {
405 if (!view->decor.parts[i].buffer) {
406 return false;
407 }
408 }
409
410 return true;
411}
412
413bool
414decor_initialize(void)
415{
416 font_context = wld_font_create_context();
417 return font_context != NULL;
418}
419
420void
421decor_finalize(void)
422{
423 if (font_context) {
424 wld_font_destroy_context(font_context);
425 font_context = NULL;
426 }
427}
428
429void
430decor_view_initialize(struct compositor_view *view)
431{
432 memset(&view->decor.text, 0, sizeof(view->decor.text));
433 view->decor.parts_key = NULL;
434 memset(view->decor.parts, 0, sizeof(view->decor.parts));
435 view->decor.string = NULL;
436 view->decor.font_name = NULL;
437 view->decor.font = NULL;
438}
439
440void
441decor_view_finalize(struct compositor_view *view)
442{
443 close_decor_parts(view);
444 close_decor_string(view);
445 close_decor_font(view);
446}
447
448void
449decor_repaint(struct wld_renderer *renderer,
450 const struct swc_rectangle *target_geom,
451 struct compositor_view *view, pixman_region32_t *damage)
452{
453 const struct swc_rectangle *geom = &view->base.geometry;
454 const struct swc_decor_text *text = &view->decor.text;
455 struct wld_font *font = view->decor.font;
456 const char *title = view->decor.string;
457 pixman_region32_t decor_region, content_region;
458 uint32_t title_len, max_width, decor_size;
459 int32_t x, y, base_x, base_y, advance, available_width;
460 struct wld_extents extents;
461 pixman_region32_t title_region;
462 int32_t outer_x = geom->x - (int32_t)view->decor.left;
463 int32_t outer_y = geom->y - (int32_t)view->decor.top;
464 uint32_t outer_width = geom->width + view->decor.left + view->decor.right;
465 uint32_t outer_height = geom->height + view->decor.top + view->decor.bottom;
466 uint32_t tl_width = view->decor.parts[DECOR_PART_TOP_LEFT].width;
467 uint32_t tl_height = view->decor.parts[DECOR_PART_TOP_LEFT].height;
468 uint32_t tr_width = view->decor.parts[DECOR_PART_TOP_RIGHT].width;
469 uint32_t tr_height = view->decor.parts[DECOR_PART_TOP_RIGHT].height;
470 uint32_t bl_width = view->decor.parts[DECOR_PART_BOTTOM_LEFT].width;
471 uint32_t bl_height = view->decor.parts[DECOR_PART_BOTTOM_LEFT].height;
472 uint32_t br_width = view->decor.parts[DECOR_PART_BOTTOM_RIGHT].width;
473 uint32_t br_height = view->decor.parts[DECOR_PART_BOTTOM_RIGHT].height;
474
475 if (!view->decor.top && !view->decor.right && !view->decor.bottom &&
476 !view->decor.left) {
477 return;
478 }
479
480 pixman_region32_init_rect(&decor_region, outer_x, outer_y, outer_width,
481 outer_height);
482 pixman_region32_init_rect(&content_region, geom->x, geom->y, geom->width,
483 geom->height);
484 pixman_region32_subtract(&decor_region, &decor_region, &content_region);
485 pixman_region32_intersect(&decor_region, &decor_region, damage);
486 pixman_region32_subtract(&decor_region, &decor_region, &view->clip);
487 if (!decor_parts_complete(view) &&
488 pixman_region32_not_empty(&decor_region)) {
489 pixman_region32_translate(&decor_region, -target_geom->x, -target_geom->y);
490 wld_fill_region(renderer, view->decor.color, &decor_region);
491 pixman_region32_translate(&decor_region, target_geom->x, target_geom->y);
492 }
493 pixman_region32_fini(&decor_region);
494 pixman_region32_fini(&content_region);
495
496 draw_decor_part(renderer, target_geom, view, damage,
497 &view->decor.parts[DECOR_PART_TOP_LEFT], outer_x, outer_y,
498 tl_width, tl_height);
499 draw_decor_part(renderer, target_geom, view, damage,
500 &view->decor.parts[DECOR_PART_TOP_RIGHT],
501 outer_x + (int32_t)outer_width - (int32_t)tr_width, outer_y,
502 tr_width, tr_height);
503 draw_decor_part(renderer, target_geom, view, damage,
504 &view->decor.parts[DECOR_PART_BOTTOM_LEFT], outer_x,
505 outer_y + (int32_t)outer_height - (int32_t)bl_height,
506 bl_width, bl_height);
507 draw_decor_part(renderer, target_geom, view, damage,
508 &view->decor.parts[DECOR_PART_BOTTOM_RIGHT],
509 outer_x + (int32_t)outer_width - (int32_t)br_width,
510 outer_y + (int32_t)outer_height - (int32_t)br_height,
511 br_width, br_height);
512
513 if (outer_width > tl_width + tr_width) {
514 draw_decor_part(renderer, target_geom, view, damage,
515 &view->decor.parts[DECOR_PART_TOP],
516 outer_x + (int32_t)tl_width, outer_y,
517 outer_width - tl_width - tr_width, view->decor.top);
518 }
519 if (outer_width > bl_width + br_width) {
520 draw_decor_part(renderer, target_geom, view, damage,
521 &view->decor.parts[DECOR_PART_BOTTOM],
522 outer_x + (int32_t)bl_width,
523 outer_y + (int32_t)outer_height - (int32_t)view->decor.bottom,
524 outer_width - bl_width - br_width, view->decor.bottom);
525 }
526
527 if (outer_height > tl_height + bl_height) {
528 draw_decor_part(renderer, target_geom, view, damage,
529 &view->decor.parts[DECOR_PART_LEFT], outer_x,
530 outer_y + (int32_t)tl_height, view->decor.left,
531 outer_height - tl_height - bl_height);
532 }
533 if (outer_height > tr_height + br_height) {
534 draw_decor_part(renderer, target_geom, view, damage,
535 &view->decor.parts[DECOR_PART_RIGHT],
536 outer_x + (int32_t)outer_width - (int32_t)view->decor.right,
537 outer_y + (int32_t)tr_height, view->decor.right,
538 outer_height - tr_height - br_height);
539 }
540
541 if (!title || !font || !text->enabled) {
542 return;
543 }
544
545 /* calculate text position based on which edge
546 * the decor is on. base_x base_y is the top-left corner,
547 * max_width is available space
548 * decor_size is perpendicular dimension of the edge where text is being drawn */
549 switch (text->edge) {
550 case SWC_DECOR_EDGE_TOP:
551 if (!view->decor.top) {
552 return;
553 }
554 base_x = geom->x - (int32_t)view->decor.left;
555 base_y = geom->y - (int32_t)view->decor.top;
556 max_width = geom->width + view->decor.left + view->decor.right;
557 decor_size = view->decor.top;
558 break;
559 case SWC_DECOR_EDGE_BOTTOM:
560 if (!view->decor.bottom) {
561 return;
562 }
563 base_x = geom->x - (int32_t)view->decor.left;
564 base_y = geom->y + (int32_t)geom->height;
565 max_width = geom->width + view->decor.left + view->decor.right;
566 decor_size = view->decor.bottom;
567 break;
568 case SWC_DECOR_EDGE_LEFT:
569 if (!view->decor.left) {
570 return;
571 }
572 base_x = geom->x - (int32_t)view->decor.left;
573 base_y = geom->y - (int32_t)view->decor.top;
574 max_width = view->decor.left;
575 decor_size = geom->height + view->decor.top + view->decor.bottom;
576 break;
577 case SWC_DECOR_EDGE_RIGHT:
578 if (!view->decor.right) {
579 return;
580 }
581 base_x = geom->x + (int32_t)geom->width;
582 base_y = geom->y - (int32_t)view->decor.top;
583 max_width = view->decor.right;
584 decor_size = geom->height + view->decor.top + view->decor.bottom;
585 break;
586 default:
587 return;
588 }
589
590 x = base_x;
591 y = base_y;
592
593 pixman_region32_init_rect(&title_region, x, y, max_width, decor_size);
594 pixman_region32_intersect(&title_region, &title_region, damage);
595 pixman_region32_subtract(&title_region, &title_region, &view->clip);
596 if (!pixman_region32_not_empty(&title_region)) {
597 pixman_region32_fini(&title_region);
598 return;
599 }
600 pixman_region32_fini(&title_region);
601
602 if (max_width <= text->padding * 2 || decor_size < font->height) {
603 return;
604 }
605 max_width -= text->padding * 2;
606
607 if (text->edge == SWC_DECOR_EDGE_LEFT || text->edge == SWC_DECOR_EDGE_RIGHT) {
608 uint32_t glyph_count, glyph_offset = 0, available_height, total_height;
609
610 if (decor_size <= text->padding * 2) {
611 return;
612 }
613
614 available_height = decor_size - text->padding * 2;
615 title_len = decor_title_stacked_length(font, title, available_height,
616 &glyph_count);
617 if (!title_len) {
618 return;
619 }
620
621 total_height = glyph_count * font->height;
622 y += (int32_t)text->padding;
623 switch (text->align) {
624 case SWC_DECOR_ALIGN_START:
625 break;
626 case SWC_DECOR_ALIGN_CENTER:
627 y += (int32_t)((available_height - total_height) / 2);
628 break;
629 case SWC_DECOR_ALIGN_END:
630 y += (int32_t)(available_height - total_height);
631 break;
632 }
633
634 while (glyph_offset < title_len) {
635 uint32_t glyph_len = utf8_next_len(title, glyph_offset);
636 int32_t glyph_x;
637
638 wld_font_text_extents_n(font, title + glyph_offset,
639 (int32_t)glyph_len, &extents);
640 advance = extents.advance > 0 ? extents.advance : 0;
641 glyph_x = x + (int32_t)text->padding;
642 if ((uint32_t)advance < max_width) {
643 glyph_x += ((int32_t)max_width - advance) / 2;
644 }
645
646 wld_draw_text(renderer, font, text->color,
647 glyph_x + text->offset_x - target_geom->x,
648 y + (int32_t)font->ascent + text->offset_y -
649 target_geom->y,
650 title + glyph_offset, glyph_len, NULL);
651
652 glyph_offset += glyph_len;
653 y += (int32_t)font->height;
654 }
655
656 return;
657 }
658
659 title_len = decor_title_length(font, title, max_width);
660 if (!title_len) {
661 return;
662 }
663 wld_font_text_extents_n(font, title, (int32_t)title_len, &extents);
664 advance = extents.advance > 0 ? extents.advance : 0;
665 available_width = (int32_t)max_width;
666
667 switch (text->align) {
668 case SWC_DECOR_ALIGN_START:
669 x += text->padding;
670 break;
671 case SWC_DECOR_ALIGN_CENTER:
672 x += (int32_t)text->padding + (available_width - advance) / 2;
673 break;
674 case SWC_DECOR_ALIGN_END:
675 x += (int32_t)text->padding + (int32_t)max_width - advance;
676 break;
677 }
678
679 y += (int32_t)((decor_size - font->height) / 2) + (int32_t)font->ascent;
680
681 x += text->offset_x;
682 y += text->offset_y;
683
684 wld_draw_text(renderer, font, text->color, x - target_geom->x,
685 y - target_geom->y, title, title_len, NULL);
686}
687
688void
689decor_view_set(struct compositor_view *view, const struct swc_decor *decor)
690{
691 const char *font_name = NULL;
692 struct wld_font *font = NULL;
693 struct swc_decor_text text = { 0 };
694 char *owned_string = NULL;
695 char *owned_font_name = NULL;
696 const struct swc_decor_parts *parts = NULL;
697 uint32_t color = 0, top = 0, right = 0, bottom = 0, left = 0;
698
699 if (!decor) {
700 goto apply;
701 }
702
703 color = decor->color;
704 top = decor->top;
705 right = decor->right;
706 bottom = decor->bottom;
707 left = decor->left;
708 parts = decor->parts;
709 text = decor->title;
710 if (text.enabled) {
711 font_name = text.font ? text.font : DEFAULT_DECOR_FONT;
712 }
713
714 if (view->decor.color == color && view->decor.top == top &&
715 view->decor.right == right && view->decor.bottom == bottom &&
716 view->decor.left == left &&
717 view->decor.text.enabled == text.enabled &&
718 view->decor.text.edge == text.edge &&
719 view->decor.text.align == text.align &&
720 streq(view->decor.string, text.string) &&
721 view->decor.text.color == text.color &&
722 view->decor.text.padding == text.padding &&
723 view->decor.text.offset_x == text.offset_x &&
724 view->decor.text.offset_y == text.offset_y &&
725 streq(view->decor.font_name, font_name) &&
726 decor_parts_equal(view, parts)) {
727 return;
728 }
729
730 if (text.string) {
731 owned_string = strdup(text.string);
732 }
733
734 if (font_name) {
735 owned_font_name = strdup(font_name);
736 if (owned_font_name && font_context) {
737 font = wld_font_open_name(font_context, font_name);
738 }
739 }
740
741apply:
742 view->decor.color = color;
743 view->decor.top = top;
744 view->decor.right = right;
745 view->decor.bottom = bottom;
746 view->decor.left = left;
747 view->decor.text = text;
748 view->decor.text.string = NULL;
749 view->decor.text.font = NULL;
750 if (!copy_decor_parts(view, parts)) {
751 close_decor_parts(view);
752 }
753 close_decor_string(view);
754 view->decor.string = owned_string;
755 close_decor_font(view);
756 view->decor.font_name = owned_font_name;
757 view->decor.font = font;
758 view->decor.damaged = true;
759}
760
761void
762decor_view_damage(struct compositor_view *view)
763{
764 view->decor.damaged = true;
765}