1 /*
2 * Copyright © 2018 Jonas Ådahl
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include "config.h"
27
28 #include <linux/input.h>
29 #include <fcntl.h>
30 #include <poll.h>
31 #include <string.h>
32 #include <sys/mman.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <wayland-cursor.h>
38
39 #include "libdecor-plugin.h"
40 #include "utils.h"
41 #include "cursor-settings.h"
42 #include "os-compatibility.h"
43
44 #include <cairo/cairo.h>
45 #include <pango/pangocairo.h>
46
47 #include "libdecor-cairo-blur.h"
48
49 static const size_t SHADOW_MARGIN = 24; /* graspable part of the border */
50 static const size_t TITLE_HEIGHT = 24;
51 static const size_t BUTTON_WIDTH = 32;
52 static const size_t SYM_DIM = 14;
53
54 static const uint32_t COL_TITLE = 0xFF080706;
55 static const uint32_t COL_TITLE_INACT = 0xFF303030;
56 static const uint32_t COL_BUTTON_MIN = 0xFFFFBB00;
57 static const uint32_t COL_BUTTON_MAX = 0xFF238823;
58 static const uint32_t COL_BUTTON_CLOSE = 0xFFFB6542;
59 static const uint32_t COL_BUTTON_INACT = 0xFF404040;
60 static const uint32_t COL_SYM = 0xFFF4F4EF;
61 static const uint32_t COL_SYM_ACT = 0xFF20322A;
62 static const uint32_t COL_SYM_INACT = 0xFF909090;
63
64 static const uint32_t DOUBLE_CLICK_TIME_MS = 400;
65
66 static const char *cursor_names[] = {
67 "top_side",
68 "bottom_side",
69 "left_side",
70 "top_left_corner",
71 "bottom_left_corner",
72 "right_side",
73 "top_right_corner",
74 "bottom_right_corner"
75 };
76
77
78 /* color conversion function from 32bit integer to double components */
79
80 double
red(const uint32_t * const col)81 red(const uint32_t *const col) {
82 return ((const uint8_t*)(col))[2] / (double)(255);
83 }
84
85 double
green(const uint32_t * const col)86 green(const uint32_t *const col) {
87 return ((const uint8_t*)(col))[1] / (double)(255);
88 }
89
90 double
blue(const uint32_t * const col)91 blue(const uint32_t *const col) {
92 return ((const uint8_t*)(col))[0] / (double)(255);
93 }
94
95 double
alpha(const uint32_t * const col)96 alpha(const uint32_t *const col) {
97 return ((const uint8_t*)(col))[3] / (double)(255);
98 }
99
100 void
cairo_set_rgba32(cairo_t * cr,const uint32_t * const c)101 cairo_set_rgba32(cairo_t *cr, const uint32_t *const c) {
102 cairo_set_source_rgba(cr, red(c), green(c), blue(c), alpha(c));
103 }
104
105 static bool
streql(const char * str1,const char * str2)106 streql(const char *str1, const char *str2)
107 {
108 return (str1 && str2) && (strcmp(str1, str2) == 0);
109 }
110
111 enum decoration_type {
112 DECORATION_TYPE_NONE,
113 DECORATION_TYPE_ALL,
114 DECORATION_TYPE_TITLE_ONLY
115 };
116
117 enum component {
118 NONE = 0,
119 SHADOW,
120 TITLE,
121 BUTTON_MIN,
122 BUTTON_MAX,
123 BUTTON_CLOSE,
124 };
125
126 enum composite_mode {
127 COMPOSITE_SERVER,
128 COMPOSITE_CLIENT,
129 };
130
131 struct seat {
132 struct libdecor_plugin_cairo *plugin_cairo;
133
134 char *name;
135
136 struct wl_seat *wl_seat;
137 struct wl_pointer *wl_pointer;
138
139 struct wl_surface *cursor_surface;
140 struct wl_cursor *current_cursor;
141 int cursor_scale;
142 struct wl_list cursor_outputs;
143
144 struct wl_cursor_theme *cursor_theme;
145 /* cursors for resize edges and corners */
146 struct wl_cursor *cursors[ARRAY_LENGTH(cursor_names)];
147 struct wl_cursor *cursor_left_ptr;
148
149 struct wl_surface *pointer_focus;
150
151 int pointer_x, pointer_y;
152
153 uint32_t pointer_button_time_stamp;
154
155 uint32_t serial;
156
157 bool grabbed;
158
159 struct wl_list link;
160 };
161
162 struct output {
163 struct libdecor_plugin_cairo *plugin_cairo;
164
165 struct wl_output *wl_output;
166 uint32_t id;
167 int scale;
168
169 struct wl_list link;
170 };
171
172 struct buffer {
173 struct wl_buffer *wl_buffer;
174 bool in_use;
175 bool is_detached;
176
177 void *data;
178 size_t data_size;
179 int width;
180 int height;
181 int scale;
182 int buffer_width;
183 int buffer_height;
184 };
185
186 struct border_component {
187 enum component type;
188
189 bool is_hidden;
190 bool opaque;
191
192 enum composite_mode composite_mode;
193 struct {
194 struct wl_surface *wl_surface;
195 struct wl_subsurface *wl_subsurface;
196 struct buffer *buffer;
197 struct wl_list output_list;
198 int scale;
199 } server;
200 struct {
201 cairo_surface_t *image;
202 struct border_component *parent_component;
203 } client;
204
205 struct wl_list child_components; /* border_component::link */
206 struct wl_list link; /* border_component::child_components */
207 };
208
209 struct surface_output {
210 struct output *output;
211 struct wl_list link;
212 };
213
214 struct cursor_output {
215 struct output *output;
216 struct wl_list link;
217 };
218
219 struct libdecor_frame_cairo {
220 struct libdecor_frame frame;
221
222 struct libdecor_plugin_cairo *plugin_cairo;
223
224 int content_width;
225 int content_height;
226
227 enum decoration_type decoration_type;
228
229 enum libdecor_window_state window_state;
230
231 char *title;
232
233 enum libdecor_capabilities capabilities;
234
235 struct border_component *focus;
236 struct border_component *active;
237 struct border_component *grab;
238
239 bool shadow_showing;
240 struct border_component shadow;
241
242 struct {
243 bool is_showing;
244 struct border_component title;
245 struct border_component min;
246 struct border_component max;
247 struct border_component close;
248 } title_bar;
249
250 /* store pre-processed shadow tile */
251 cairo_surface_t *shadow_blur;
252
253 struct wl_list link;
254 };
255
256 struct libdecor_plugin_cairo {
257 struct libdecor_plugin plugin;
258
259 struct wl_callback *globals_callback;
260 struct wl_callback *globals_callback_shm;
261
262 struct libdecor *context;
263
264 struct wl_registry *wl_registry;
265 struct wl_subcompositor *wl_subcompositor;
266 struct wl_compositor *wl_compositor;
267
268 struct wl_shm *wl_shm;
269 struct wl_callback *shm_callback;
270 bool has_argb;
271
272 struct wl_list visible_frame_list;
273 struct wl_list seat_list;
274 struct wl_list output_list;
275
276 char *cursor_theme_name;
277 int cursor_size;
278
279 PangoFontDescription *font;
280 };
281
282 static const char *libdecor_cairo_proxy_tag = "libdecor-cairo";
283
284 static void
285 sync_active_component(struct libdecor_frame_cairo *frame_cairo,
286 struct seat *seat);
287
288 static void
289 synthesize_pointer_enter(struct seat *seat);
290
291 static void
292 synthesize_pointer_leave(struct seat *seat);
293
294 static bool
own_proxy(struct wl_proxy * proxy)295 own_proxy(struct wl_proxy *proxy)
296 {
297 return (wl_proxy_get_tag(proxy) == &libdecor_cairo_proxy_tag);
298 }
299
300 static bool
own_surface(struct wl_surface * surface)301 own_surface(struct wl_surface *surface)
302 {
303 return own_proxy((struct wl_proxy *) surface);
304 }
305
306 static bool
own_output(struct wl_output * output)307 own_output(struct wl_output *output)
308 {
309 return own_proxy((struct wl_proxy *) output);
310 }
311
312 static bool
moveable(struct libdecor_frame_cairo * frame_cairo)313 moveable(struct libdecor_frame_cairo *frame_cairo) {
314 return libdecor_frame_has_capability(&frame_cairo->frame,
315 LIBDECOR_ACTION_MOVE);
316 }
317
318 static bool
resizable(struct libdecor_frame_cairo * frame_cairo)319 resizable(struct libdecor_frame_cairo *frame_cairo) {
320 return libdecor_frame_has_capability(&frame_cairo->frame,
321 LIBDECOR_ACTION_RESIZE);
322 }
323
324 static bool
minimizable(struct libdecor_frame_cairo * frame_cairo)325 minimizable(struct libdecor_frame_cairo *frame_cairo) {
326 return libdecor_frame_has_capability(&frame_cairo->frame,
327 LIBDECOR_ACTION_MINIMIZE);
328 }
329
330 static bool
closeable(struct libdecor_frame_cairo * frame_cairo)331 closeable(struct libdecor_frame_cairo *frame_cairo) {
332 return libdecor_frame_has_capability(&frame_cairo->frame,
333 LIBDECOR_ACTION_CLOSE);
334 }
335
336 static void
337 buffer_free(struct buffer *buffer);
338
339 static void
340 draw_border_component(struct libdecor_frame_cairo *frame_cairo,
341 struct border_component *border_component);
342
343 static void
344 send_cursor(struct seat *seat);
345
346 static bool
347 update_local_cursor(struct seat *seat);
348
349 static void
libdecor_plugin_cairo_destroy(struct libdecor_plugin * plugin)350 libdecor_plugin_cairo_destroy(struct libdecor_plugin *plugin)
351 {
352 struct libdecor_plugin_cairo *plugin_cairo =
353 (struct libdecor_plugin_cairo *) plugin;
354 struct seat *seat, *seat_tmp;
355 struct output *output, *output_tmp;
356 struct libdecor_frame_cairo *frame, *frame_tmp;
357
358 if (plugin_cairo->globals_callback)
359 wl_callback_destroy(plugin_cairo->globals_callback);
360 if (plugin_cairo->globals_callback_shm)
361 wl_callback_destroy(plugin_cairo->globals_callback_shm);
362 if (plugin_cairo->shm_callback)
363 wl_callback_destroy(plugin_cairo->shm_callback);
364 wl_registry_destroy(plugin_cairo->wl_registry);
365
366 wl_list_for_each_safe(seat, seat_tmp, &plugin_cairo->seat_list, link) {
367 struct cursor_output *cursor_output, *tmp;
368
369 if (seat->wl_pointer)
370 wl_pointer_destroy(seat->wl_pointer);
371 if (seat->cursor_surface)
372 wl_surface_destroy(seat->cursor_surface);
373 wl_seat_destroy(seat->wl_seat);
374 if (seat->cursor_theme)
375 wl_cursor_theme_destroy(seat->cursor_theme);
376
377 wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) {
378 wl_list_remove(&cursor_output->link);
379 free(cursor_output);
380 }
381 free(seat->name);
382
383 free(seat);
384 }
385
386 wl_list_for_each_safe(output, output_tmp,
387 &plugin_cairo->output_list, link) {
388 wl_output_destroy(output->wl_output);
389 free(output);
390 }
391
392 wl_list_for_each_safe(frame, frame_tmp,
393 &plugin_cairo->visible_frame_list, link) {
394 wl_list_remove(&frame->link);
395 }
396
397 free(plugin_cairo->cursor_theme_name);
398
399 wl_shm_destroy(plugin_cairo->wl_shm);
400
401 pango_font_description_free(plugin_cairo->font);
402
403 wl_compositor_destroy(plugin_cairo->wl_compositor);
404 wl_subcompositor_destroy(plugin_cairo->wl_subcompositor);
405
406 libdecor_plugin_release(&plugin_cairo->plugin);
407 free(plugin_cairo);
408 }
409
410 static void
init_server_component(struct border_component * border_component,enum component type)411 init_server_component(struct border_component *border_component,
412 enum component type)
413 {
414 border_component->composite_mode = COMPOSITE_SERVER;
415 wl_list_init(&border_component->child_components);
416 border_component->type = type;
417 }
418
419 static void
init_client_component(struct border_component * border_component,struct border_component * parent,enum component type)420 init_client_component(struct border_component *border_component,
421 struct border_component *parent,
422 enum component type)
423 {
424 border_component->composite_mode = COMPOSITE_CLIENT;
425 wl_list_init(&border_component->child_components);
426 wl_list_insert(parent->child_components.prev, &border_component->link);
427 border_component->client.parent_component = parent;
428 border_component->type = type;
429 }
430
431 static void
init_components(struct libdecor_frame_cairo * frame_cairo)432 init_components(struct libdecor_frame_cairo *frame_cairo)
433 {
434 init_server_component(&frame_cairo->title_bar.title,
435 TITLE);
436 init_client_component(&frame_cairo->title_bar.min,
437 &frame_cairo->title_bar.title,
438 BUTTON_MIN);
439 init_client_component(&frame_cairo->title_bar.max,
440 &frame_cairo->title_bar.title,
441 BUTTON_MAX);
442 init_client_component(&frame_cairo->title_bar.close,
443 &frame_cairo->title_bar.title,
444 BUTTON_CLOSE);
445 init_server_component(&frame_cairo->shadow,
446 SHADOW);
447 }
448
449 static struct libdecor_frame_cairo *
libdecor_frame_cairo_new(struct libdecor_plugin_cairo * plugin_cairo)450 libdecor_frame_cairo_new(struct libdecor_plugin_cairo *plugin_cairo)
451 {
452 struct libdecor_frame_cairo *frame_cairo = zalloc(sizeof *frame_cairo);
453 cairo_t *cr;
454
455 static const int size = 128;
456 static const int boundary = 32;
457
458 frame_cairo->plugin_cairo = plugin_cairo;
459 frame_cairo->shadow_blur = cairo_image_surface_create(
460 CAIRO_FORMAT_ARGB32, size, size);
461 wl_list_insert(&plugin_cairo->visible_frame_list, &frame_cairo->link);
462
463 init_components(frame_cairo);
464
465 cr = cairo_create(frame_cairo->shadow_blur);
466 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
467 cairo_set_source_rgba(cr, 0, 0, 0, 1);
468 cairo_rectangle(cr, boundary, boundary, size-2*boundary, size-2*boundary);
469 cairo_fill(cr);
470 cairo_destroy(cr);
471 blur_surface(frame_cairo->shadow_blur, 64);
472
473 return frame_cairo;
474 }
475
476 static int
libdecor_plugin_cairo_get_fd(struct libdecor_plugin * plugin)477 libdecor_plugin_cairo_get_fd(struct libdecor_plugin *plugin)
478 {
479 struct libdecor_plugin_cairo *plugin_cairo =
480 (struct libdecor_plugin_cairo *) plugin;
481 struct wl_display *wl_display =
482 libdecor_get_wl_display(plugin_cairo->context);
483
484 return wl_display_get_fd(wl_display);
485 }
486
487 static int
libdecor_plugin_cairo_dispatch(struct libdecor_plugin * plugin,int timeout)488 libdecor_plugin_cairo_dispatch(struct libdecor_plugin *plugin,
489 int timeout)
490 {
491 struct libdecor_plugin_cairo *plugin_cairo =
492 (struct libdecor_plugin_cairo *) plugin;
493 struct wl_display *wl_display =
494 libdecor_get_wl_display(plugin_cairo->context);
495 struct pollfd fds[1];
496 int ret;
497 int dispatch_count = 0;
498
499 while (wl_display_prepare_read(wl_display) != 0)
500 dispatch_count += wl_display_dispatch_pending(wl_display);
501
502 if (wl_display_flush(wl_display) < 0 &&
503 errno != EAGAIN) {
504 wl_display_cancel_read(wl_display);
505 return -errno;
506 }
507
508 fds[0] = (struct pollfd) { wl_display_get_fd(wl_display), POLLIN };
509
510 ret = poll(fds, ARRAY_SIZE (fds), timeout);
511 if (ret > 0) {
512 if (fds[0].revents & POLLIN) {
513 wl_display_read_events(wl_display);
514 dispatch_count += wl_display_dispatch_pending(wl_display);
515 return dispatch_count;
516 } else {
517 wl_display_cancel_read(wl_display);
518 return dispatch_count;
519 }
520 } else if (ret == 0) {
521 wl_display_cancel_read(wl_display);
522 return dispatch_count;
523 } else {
524 wl_display_cancel_read(wl_display);
525 return -errno;
526 }
527 }
528
529 static struct libdecor_frame *
libdecor_plugin_cairo_frame_new(struct libdecor_plugin * plugin)530 libdecor_plugin_cairo_frame_new(struct libdecor_plugin *plugin)
531 {
532 struct libdecor_plugin_cairo *plugin_cairo =
533 (struct libdecor_plugin_cairo *) plugin;
534 struct libdecor_frame_cairo *frame_cairo;
535
536 frame_cairo = libdecor_frame_cairo_new(plugin_cairo);
537
538 return &frame_cairo->frame;
539 }
540
541 static void
toggle_maximized(struct libdecor_frame * const frame)542 toggle_maximized(struct libdecor_frame *const frame)
543 {
544 if (!resizable((struct libdecor_frame_cairo *)frame))
545 return;
546
547 if (!(libdecor_frame_get_window_state(frame) &
548 LIBDECOR_WINDOW_STATE_MAXIMIZED))
549 libdecor_frame_set_maximized(frame);
550 else
551 libdecor_frame_unset_maximized(frame);
552 }
553
554 static void
buffer_release(void * user_data,struct wl_buffer * wl_buffer)555 buffer_release(void *user_data,
556 struct wl_buffer *wl_buffer)
557 {
558 struct buffer *buffer = user_data;
559
560 if (buffer->is_detached)
561 buffer_free(buffer);
562 else
563 buffer->in_use = false;
564 }
565
566 static const struct wl_buffer_listener buffer_listener = {
567 buffer_release
568 };
569
570 static struct buffer *
create_shm_buffer(struct libdecor_plugin_cairo * plugin_cairo,int width,int height,bool opaque,int scale)571 create_shm_buffer(struct libdecor_plugin_cairo *plugin_cairo,
572 int width,
573 int height,
574 bool opaque,
575 int scale)
576 {
577 struct wl_shm_pool *pool;
578 int fd, size, buffer_width, buffer_height, stride;
579 void *data;
580 struct buffer *buffer;
581 enum wl_shm_format buf_fmt;
582
583 buffer_width = width * scale;
584 buffer_height = height * scale;
585 stride = buffer_width * 4;
586 size = stride * buffer_height;
587
588 fd = os_create_anonymous_file(size);
589 if (fd < 0) {
590 fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
591 size, strerror(errno));
592 return NULL;
593 }
594
595 data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
596 if (data == MAP_FAILED) {
597 fprintf(stderr, "mmap failed: %s\n", strerror(errno));
598 close(fd);
599 return NULL;
600 }
601
602 buf_fmt = opaque ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888;
603
604 pool = wl_shm_create_pool(plugin_cairo->wl_shm, fd, size);
605 buffer = zalloc(sizeof *buffer);
606 buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0,
607 buffer_width, buffer_height,
608 stride,
609 buf_fmt);
610 wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer);
611 wl_shm_pool_destroy(pool);
612 close(fd);
613
614 buffer->data = data;
615 buffer->data_size = size;
616 buffer->width = width;
617 buffer->height = height;
618 buffer->scale = scale;
619 buffer->buffer_width = buffer_width;
620 buffer->buffer_height = buffer_height;
621
622 return buffer;
623 }
624
625 static void
buffer_free(struct buffer * buffer)626 buffer_free(struct buffer *buffer)
627 {
628 if (buffer->wl_buffer) {
629 wl_buffer_destroy(buffer->wl_buffer);
630 munmap(buffer->data, buffer->data_size);
631 buffer->wl_buffer = NULL;
632 buffer->in_use = false;
633 }
634 free(buffer);
635 }
636
637 static void
free_border_component(struct border_component * border_component)638 free_border_component(struct border_component *border_component)
639 {
640 struct surface_output *surface_output, *surface_output_tmp;
641
642 if (border_component->server.wl_surface) {
643 wl_subsurface_destroy(border_component->server.wl_subsurface);
644 border_component->server.wl_subsurface = NULL;
645 wl_surface_destroy(border_component->server.wl_surface);
646 border_component->server.wl_surface = NULL;
647 }
648 if (border_component->server.buffer) {
649 buffer_free(border_component->server.buffer);
650 border_component->server.buffer = NULL;
651 }
652 if (border_component->client.image) {
653 cairo_surface_destroy(border_component->client.image);
654 border_component->client.image = NULL;
655 }
656 if (border_component->server.output_list.next != NULL) {
657 wl_list_for_each_safe(surface_output, surface_output_tmp,
658 &border_component->server.output_list, link) {
659 wl_list_remove(&surface_output->link);
660 free(surface_output);
661 }
662 }
663 }
664
665 static void
libdecor_plugin_cairo_frame_free(struct libdecor_plugin * plugin,struct libdecor_frame * frame)666 libdecor_plugin_cairo_frame_free(struct libdecor_plugin *plugin,
667 struct libdecor_frame *frame)
668 {
669 struct libdecor_plugin_cairo *plugin_cairo =
670 (struct libdecor_plugin_cairo *) plugin;
671 struct libdecor_frame_cairo *frame_cairo =
672 (struct libdecor_frame_cairo *) frame;
673 struct seat *seat;
674
675 wl_list_for_each(seat, &plugin_cairo->seat_list, link) {
676 if (seat->pointer_focus != NULL &&
677 wl_surface_get_user_data(seat->pointer_focus) == frame_cairo)
678 seat->pointer_focus = NULL;
679 }
680
681 free_border_component(&frame_cairo->title_bar.title);
682 free_border_component(&frame_cairo->title_bar.min);
683 free_border_component(&frame_cairo->title_bar.max);
684 free_border_component(&frame_cairo->title_bar.close);
685 frame_cairo->title_bar.is_showing = false;
686 free_border_component(&frame_cairo->shadow);
687 frame_cairo->shadow_showing = false;
688 if (frame_cairo->shadow_blur != NULL) {
689 cairo_surface_destroy(frame_cairo->shadow_blur);
690 frame_cairo->shadow_blur = NULL;
691 }
692
693 free(frame_cairo->title);
694 frame_cairo->title = NULL;
695
696 frame_cairo->decoration_type = DECORATION_TYPE_NONE;
697
698 if (frame_cairo->link.next != NULL)
699 wl_list_remove(&frame_cairo->link);
700 }
701
702 static bool
is_border_surfaces_showing(struct libdecor_frame_cairo * frame_cairo)703 is_border_surfaces_showing(struct libdecor_frame_cairo *frame_cairo)
704 {
705 return frame_cairo->shadow_showing;
706 }
707
708 static bool
is_title_bar_surfaces_showing(struct libdecor_frame_cairo * frame_cairo)709 is_title_bar_surfaces_showing(struct libdecor_frame_cairo *frame_cairo)
710 {
711 return frame_cairo->title_bar.is_showing;
712 }
713
714 static struct border_component *
get_server_component(struct border_component * border_component)715 get_server_component(struct border_component *border_component)
716 {
717 switch (border_component->composite_mode) {
718 case COMPOSITE_SERVER:
719 return border_component;
720 case COMPOSITE_CLIENT:
721 return get_server_component(border_component->client.parent_component);
722 }
723 return NULL;
724 }
725
726 static void
redraw_border_component(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component)727 redraw_border_component(struct libdecor_frame_cairo *frame_cairo,
728 struct border_component *border_component)
729 {
730 struct border_component *server_component;
731
732 server_component = get_server_component(border_component);
733 draw_border_component(frame_cairo, server_component);
734 }
735
736 static void
hide_border_component(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component)737 hide_border_component(struct libdecor_frame_cairo *frame_cairo,
738 struct border_component *border_component)
739 {
740 border_component->is_hidden = true;
741
742 switch (border_component->composite_mode) {
743 case COMPOSITE_SERVER:
744 if (!border_component->server.wl_surface)
745 return;
746
747 wl_surface_attach(border_component->server.wl_surface,
748 NULL, 0, 0);
749 wl_surface_commit(border_component->server.wl_surface);
750 break;
751 case COMPOSITE_CLIENT:
752 redraw_border_component(frame_cairo, border_component);
753 break;
754 }
755 }
756
757 static void
hide_border_surfaces(struct libdecor_frame_cairo * frame_cairo)758 hide_border_surfaces(struct libdecor_frame_cairo *frame_cairo)
759 {
760 hide_border_component(frame_cairo, &frame_cairo->shadow);
761 frame_cairo->shadow_showing = false;
762 }
763
764 static void
hide_title_bar_surfaces(struct libdecor_frame_cairo * frame_cairo)765 hide_title_bar_surfaces(struct libdecor_frame_cairo *frame_cairo)
766 {
767 hide_border_component(frame_cairo, &frame_cairo->title_bar.title);
768 hide_border_component(frame_cairo, &frame_cairo->title_bar.min);
769 hide_border_component(frame_cairo, &frame_cairo->title_bar.max);
770 hide_border_component(frame_cairo, &frame_cairo->title_bar.close);
771 frame_cairo->title_bar.is_showing = false;
772 }
773
774 static struct border_component *
get_component_for_surface(struct libdecor_frame_cairo * frame_cairo,struct wl_surface * surface)775 get_component_for_surface(struct libdecor_frame_cairo *frame_cairo,
776 struct wl_surface *surface)
777 {
778 if (frame_cairo->shadow.server.wl_surface == surface)
779 return &frame_cairo->shadow;
780 if (frame_cairo->title_bar.title.server.wl_surface == surface)
781 return &frame_cairo->title_bar.title;
782 return NULL;
783 }
784
785 static void
786 calculate_component_size(struct libdecor_frame_cairo *frame_cairo,
787 enum component component,
788 int *component_x,
789 int *component_y,
790 int *component_width,
791 int *component_height);
792
793 static void
update_component_focus(struct libdecor_frame_cairo * frame_cairo,struct wl_surface * surface,struct seat * seat)794 update_component_focus(struct libdecor_frame_cairo *frame_cairo,
795 struct wl_surface *surface,
796 struct seat *seat)
797 {
798 static struct border_component *border_component;
799 static struct border_component *child_component;
800 static struct border_component *focus_component;
801
802 border_component = get_component_for_surface(frame_cairo, surface);
803
804 focus_component = border_component;
805 wl_list_for_each(child_component, &border_component->child_components, link) {
806 int component_x = 0, component_y = 0;
807 int component_width = 0, component_height = 0;
808
809 calculate_component_size(frame_cairo, child_component->type,
810 &component_x, &component_y,
811 &component_width, &component_height);
812 if (seat->pointer_x >= component_x &&
813 seat->pointer_x < component_x + component_width &&
814 seat->pointer_y >= component_y &&
815 seat->pointer_y < component_y + component_height) {
816 focus_component = child_component;
817 break;
818 }
819 }
820
821 if (frame_cairo->grab)
822 frame_cairo->active = frame_cairo->grab;
823 else
824 frame_cairo->active = focus_component;
825 frame_cairo->focus = focus_component;
826
827 }
828
829 static void
830 ensure_component(struct libdecor_frame_cairo *frame_cairo,
831 struct border_component *cmpnt);
832
833 static bool
redraw_scale(struct libdecor_frame_cairo * frame_cairo,struct border_component * cmpnt)834 redraw_scale(struct libdecor_frame_cairo *frame_cairo,
835 struct border_component *cmpnt)
836 {
837 struct surface_output *surface_output;
838 int scale = 1;
839
840 if (cmpnt->is_hidden)
841 return false;
842
843 ensure_component(frame_cairo, cmpnt);
844
845 wl_list_for_each(surface_output, &cmpnt->server.output_list, link) {
846 scale = MAX(scale, surface_output->output->scale);
847 }
848 if (scale != cmpnt->server.scale) {
849 cmpnt->server.scale = scale;
850 if ((cmpnt->type != SHADOW) || is_border_surfaces_showing(frame_cairo)) {
851 draw_border_component(frame_cairo, cmpnt);
852 return true;
853 }
854 }
855 return false;
856 }
857
858 static bool
add_surface_output(struct libdecor_plugin_cairo * plugin_cairo,struct wl_output * wl_output,struct wl_list * list)859 add_surface_output(struct libdecor_plugin_cairo *plugin_cairo,
860 struct wl_output *wl_output,
861 struct wl_list *list)
862 {
863 struct output *output;
864 struct surface_output *surface_output;
865
866 if (!own_output(wl_output))
867 return false;
868
869 output = wl_output_get_user_data(wl_output);
870
871 if (output == NULL)
872 return false;
873
874 surface_output = zalloc(sizeof *surface_output);
875 surface_output->output = output;
876 wl_list_insert(list, &surface_output->link);
877 return true;
878 }
879
880 static void
surface_enter(void * data,struct wl_surface * wl_surface,struct wl_output * wl_output)881 surface_enter(void *data,
882 struct wl_surface *wl_surface,
883 struct wl_output *wl_output)
884 {
885 struct libdecor_frame_cairo *frame_cairo = data;
886 struct border_component *cmpnt;
887
888 if (!(own_surface(wl_surface) && own_output(wl_output)))
889 return;
890
891 cmpnt = get_component_for_surface(frame_cairo, wl_surface);
892 if (cmpnt == NULL)
893 return;
894
895 if (!add_surface_output(frame_cairo->plugin_cairo, wl_output,
896 &cmpnt->server.output_list))
897 return;
898
899 if (redraw_scale(frame_cairo, cmpnt))
900 libdecor_frame_toplevel_commit(&frame_cairo->frame);
901 }
902
903 static bool
remove_surface_output(struct wl_list * list,struct wl_output * wl_output)904 remove_surface_output(struct wl_list *list, struct wl_output *wl_output)
905 {
906 struct surface_output *surface_output;
907 wl_list_for_each(surface_output, list, link) {
908 if (surface_output->output->wl_output == wl_output) {
909 wl_list_remove(&surface_output->link);
910 free(surface_output);
911 return true;
912 }
913 }
914 return false;
915 }
916
917 static void
surface_leave(void * data,struct wl_surface * wl_surface,struct wl_output * wl_output)918 surface_leave(void *data,
919 struct wl_surface *wl_surface,
920 struct wl_output *wl_output)
921 {
922 struct libdecor_frame_cairo *frame_cairo = data;
923 struct border_component *cmpnt;
924
925 if (!(own_surface(wl_surface) && own_output(wl_output)))
926 return;
927
928 cmpnt = get_component_for_surface(frame_cairo, wl_surface);
929 if (cmpnt == NULL)
930 return;
931
932 if (!remove_surface_output(&cmpnt->server.output_list, wl_output))
933 return;
934
935 if (redraw_scale(frame_cairo, cmpnt))
936 libdecor_frame_toplevel_commit(&frame_cairo->frame);
937 }
938
939 static struct wl_surface_listener surface_listener = {
940 surface_enter,
941 surface_leave,
942 };
943
944 static void
create_surface_subsurface_pair(struct libdecor_frame_cairo * frame_cairo,struct wl_surface ** out_wl_surface,struct wl_subsurface ** out_wl_subsurface)945 create_surface_subsurface_pair(struct libdecor_frame_cairo *frame_cairo,
946 struct wl_surface **out_wl_surface,
947 struct wl_subsurface **out_wl_subsurface)
948 {
949 struct libdecor_plugin_cairo *plugin_cairo = frame_cairo->plugin_cairo;
950 struct libdecor_frame *frame = &frame_cairo->frame;
951 struct wl_compositor *wl_compositor = plugin_cairo->wl_compositor;
952 struct wl_subcompositor *wl_subcompositor = plugin_cairo->wl_subcompositor;
953 struct wl_surface *wl_surface;
954 struct wl_surface *parent;
955 struct wl_subsurface *wl_subsurface;
956
957 wl_surface = wl_compositor_create_surface(wl_compositor);
958 wl_proxy_set_tag((struct wl_proxy *) wl_surface,
959 &libdecor_cairo_proxy_tag);
960
961 parent = libdecor_frame_get_wl_surface(frame);
962 wl_subsurface = wl_subcompositor_get_subsurface(wl_subcompositor,
963 wl_surface,
964 parent);
965
966 *out_wl_surface = wl_surface;
967 *out_wl_subsurface = wl_subsurface;
968 }
969
970 static void
ensure_component(struct libdecor_frame_cairo * frame_cairo,struct border_component * cmpnt)971 ensure_component(struct libdecor_frame_cairo *frame_cairo,
972 struct border_component *cmpnt)
973 {
974 switch (cmpnt->composite_mode) {
975 case COMPOSITE_SERVER:
976 if (!cmpnt->server.wl_surface) {
977 wl_list_init(&cmpnt->server.output_list);
978 cmpnt->server.scale = 1;
979 create_surface_subsurface_pair(frame_cairo,
980 &cmpnt->server.wl_surface,
981 &cmpnt->server.wl_subsurface);
982 wl_surface_add_listener(cmpnt->server.wl_surface,
983 &surface_listener,
984 frame_cairo);
985 }
986 break;
987 case COMPOSITE_CLIENT:
988 wl_list_init(&cmpnt->server.output_list);
989 break;
990 }
991
992 cmpnt->is_hidden = false;
993 }
994
995 static void
ensure_border_surfaces(struct libdecor_frame_cairo * frame_cairo)996 ensure_border_surfaces(struct libdecor_frame_cairo *frame_cairo)
997 {
998 frame_cairo->shadow.opaque = false;
999 ensure_component(frame_cairo, &frame_cairo->shadow);
1000
1001 libdecor_frame_set_min_content_size(&frame_cairo->frame,
1002 MAX(56, 4 * BUTTON_WIDTH),
1003 MAX(56, TITLE_HEIGHT + 1));
1004 }
1005
1006 static void
ensure_title_bar_surfaces(struct libdecor_frame_cairo * frame_cairo)1007 ensure_title_bar_surfaces(struct libdecor_frame_cairo *frame_cairo)
1008 {
1009 frame_cairo->title_bar.title.opaque = true;
1010 ensure_component(frame_cairo, &frame_cairo->title_bar.title);
1011
1012 frame_cairo->title_bar.min.opaque = true;
1013 ensure_component(frame_cairo, &frame_cairo->title_bar.min);
1014
1015 frame_cairo->title_bar.max.opaque = true;
1016 ensure_component(frame_cairo, &frame_cairo->title_bar.max);
1017
1018 frame_cairo->title_bar.close.opaque = true;
1019 ensure_component(frame_cairo, &frame_cairo->title_bar.close);
1020 }
1021
1022 static void
calculate_component_size(struct libdecor_frame_cairo * frame_cairo,enum component component,int * component_x,int * component_y,int * component_width,int * component_height)1023 calculate_component_size(struct libdecor_frame_cairo *frame_cairo,
1024 enum component component,
1025 int *component_x,
1026 int *component_y,
1027 int *component_width,
1028 int *component_height)
1029 {
1030 struct libdecor_frame *frame = &frame_cairo->frame;
1031 int content_width, content_height;
1032
1033 content_width = libdecor_frame_get_content_width(frame);
1034 content_height = libdecor_frame_get_content_height(frame);
1035
1036 switch (component) {
1037 case NONE:
1038 *component_width = 0;
1039 *component_height = 0;
1040 return;
1041 case SHADOW:
1042 *component_x = -(int)SHADOW_MARGIN;
1043 *component_y = -(int)(SHADOW_MARGIN+TITLE_HEIGHT);
1044 *component_width = content_width + 2 * SHADOW_MARGIN;
1045 *component_height = content_height
1046 + 2 * SHADOW_MARGIN
1047 + TITLE_HEIGHT;
1048 return;
1049 case TITLE:
1050 *component_x = 0;
1051 *component_y = -(int)TITLE_HEIGHT;
1052 *component_width = content_width;
1053 *component_height = TITLE_HEIGHT;
1054 return;
1055 case BUTTON_MIN:
1056 *component_x = content_width - 3 * BUTTON_WIDTH;
1057 *component_y = 0;
1058 *component_width = BUTTON_WIDTH;
1059 *component_height = TITLE_HEIGHT;
1060 return;
1061 case BUTTON_MAX:
1062 *component_x = content_width - 2 * BUTTON_WIDTH;
1063 *component_y = 0;
1064 *component_width = BUTTON_WIDTH;
1065 *component_height = TITLE_HEIGHT;
1066 return;
1067 case BUTTON_CLOSE:
1068 *component_x = content_width - BUTTON_WIDTH;
1069 *component_y = 0;
1070 *component_width = BUTTON_WIDTH;
1071 *component_height = TITLE_HEIGHT;
1072 return;
1073 }
1074
1075 abort();
1076 }
1077
1078 static int
border_component_get_scale(struct border_component * border_component)1079 border_component_get_scale(struct border_component *border_component)
1080 {
1081 switch (border_component->composite_mode) {
1082 case COMPOSITE_SERVER:
1083 return border_component->server.scale;
1084 case COMPOSITE_CLIENT:
1085 return border_component_get_scale(
1086 border_component->client.parent_component);
1087 }
1088 return 0;
1089 }
1090
1091 static void
draw_title_text(struct libdecor_frame_cairo * frame_cairo,cairo_t * cr,const int * title_width,bool active)1092 draw_title_text(struct libdecor_frame_cairo *frame_cairo,
1093 cairo_t *cr,
1094 const int *title_width,
1095 bool active)
1096 {
1097 const uint32_t col_title = active ? COL_TITLE : COL_TITLE_INACT;
1098 const uint32_t col_title_text = active ? COL_SYM : COL_SYM_INACT;
1099
1100 PangoLayout *layout;
1101
1102 /* title fade out at buttons */
1103 const int fade_width = 5 * BUTTON_WIDTH;
1104 int fade_start;
1105 cairo_pattern_t *fade;
1106
1107 /* text position and dimensions */
1108 int text_extents_width, text_extents_height;
1109 double text_x, text_y;
1110 double text_width, text_height;
1111
1112 layout = pango_cairo_create_layout(cr);
1113
1114 pango_layout_set_text(layout,
1115 libdecor_frame_get_title((struct libdecor_frame*) frame_cairo),
1116 -1);
1117 pango_layout_set_font_description(layout, frame_cairo->plugin_cairo->font);
1118 pango_layout_get_size(layout, &text_extents_width, &text_extents_height);
1119
1120 /* set text position and dimensions */
1121 text_width = text_extents_width / PANGO_SCALE;
1122 text_height = text_extents_height / PANGO_SCALE;
1123 text_x = *title_width / 2.0 - text_width / 2.0;
1124 text_x += MIN(0.0, ((*title_width - fade_width) - (text_x + text_width)));
1125 text_x = MAX(text_x, BUTTON_WIDTH);
1126 text_y = TITLE_HEIGHT / 2.0 - text_height / 2.0;
1127
1128 /* draw title text */
1129 cairo_move_to(cr, text_x, text_y);
1130 cairo_set_rgba32(cr, &col_title_text);
1131 pango_cairo_show_layout(cr, layout);
1132
1133 /* draw fade-out from title text to buttons */
1134 fade_start = *title_width - fade_width;
1135 fade = cairo_pattern_create_linear(fade_start, 0,
1136 fade_start + 2 * BUTTON_WIDTH, 0);
1137 cairo_pattern_add_color_stop_rgba(fade, 0,
1138 red(&col_title),
1139 green(&col_title),
1140 blue(&col_title),
1141 0);
1142 cairo_pattern_add_color_stop_rgb(fade, 1,
1143 red(&col_title),
1144 green(&col_title),
1145 blue(&col_title));
1146 cairo_rectangle(cr, fade_start, 0, fade_width, TITLE_HEIGHT);
1147 cairo_set_source(cr, fade);
1148 cairo_fill(cr);
1149
1150 cairo_pattern_destroy(fade);
1151 g_object_unref(layout);
1152 }
1153
1154 static void
draw_component_content(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component,int component_width,int component_height,enum component component)1155 draw_component_content(struct libdecor_frame_cairo *frame_cairo,
1156 struct border_component *border_component,
1157 int component_width,
1158 int component_height,
1159 enum component component)
1160 {
1161 struct buffer *buffer;
1162 cairo_surface_t *surface = NULL;
1163 int width = 0, height = 0;
1164 int scale;
1165 cairo_t *cr;
1166
1167 /* button symbol origin */
1168 const double x = BUTTON_WIDTH / 2 - SYM_DIM / 2 + 0.5;
1169 const double y = TITLE_HEIGHT / 2 - SYM_DIM / 2 + 0.5;
1170
1171 enum libdecor_window_state state;
1172
1173 bool active;
1174
1175 uint32_t col_title;
1176
1177 bool cap_min, cap_max, cap_close;
1178
1179 /* capabilities of decorations */
1180 cap_min = minimizable(frame_cairo);
1181 cap_max = resizable(frame_cairo);
1182 cap_close = closeable(frame_cairo);
1183
1184 scale = border_component_get_scale(border_component);
1185
1186 state = libdecor_frame_get_window_state((struct libdecor_frame *) frame_cairo);
1187
1188 active = state & LIBDECOR_WINDOW_STATE_ACTIVE;
1189
1190 col_title = active ? COL_TITLE : COL_TITLE_INACT;
1191
1192 /* clear buffer */
1193 switch (border_component->composite_mode) {
1194 case COMPOSITE_SERVER:
1195 buffer = border_component->server.buffer;
1196
1197 surface = cairo_image_surface_create_for_data(
1198 buffer->data, CAIRO_FORMAT_ARGB32,
1199 buffer->buffer_width, buffer->buffer_height,
1200 cairo_format_stride_for_width(
1201 CAIRO_FORMAT_ARGB32,
1202 buffer->buffer_width)
1203 );
1204 cairo_surface_set_device_scale(surface, scale, scale);
1205 width = buffer->width;
1206 height = buffer->height;
1207 break;
1208 case COMPOSITE_CLIENT:
1209 surface = cairo_surface_reference(border_component->client.image);
1210 width = cairo_image_surface_get_width(surface);
1211 height = cairo_image_surface_get_height(surface);
1212 break;
1213 }
1214
1215 cr = cairo_create(surface);
1216 cairo_save(cr);
1217 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1218 cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1219 cairo_paint(cr);
1220 cairo_restore(cr);
1221
1222 /* background */
1223 switch (component) {
1224 case NONE:
1225 break;
1226 case SHADOW:
1227 render_shadow(cr,
1228 frame_cairo->shadow_blur,
1229 -(int)SHADOW_MARGIN/2,
1230 -(int)SHADOW_MARGIN/2,
1231 width + SHADOW_MARGIN,
1232 height + SHADOW_MARGIN,
1233 64,
1234 64);
1235 break;
1236 case TITLE:
1237 cairo_set_rgba32(cr, &col_title);
1238 cairo_paint(cr);
1239 break;
1240 case BUTTON_MIN:
1241 if (cap_min && frame_cairo->active == &frame_cairo->title_bar.min)
1242 cairo_set_rgba32(cr, active ? &COL_BUTTON_MIN : &COL_BUTTON_INACT);
1243 else
1244 cairo_set_rgba32(cr, &col_title);
1245 cairo_paint(cr);
1246 break;
1247 case BUTTON_MAX:
1248 if (cap_max && frame_cairo->active == &frame_cairo->title_bar.max)
1249 cairo_set_rgba32(cr, active ? &COL_BUTTON_MAX : &COL_BUTTON_INACT);
1250 else
1251 cairo_set_rgba32(cr, &col_title);
1252 cairo_paint(cr);
1253 break;
1254 case BUTTON_CLOSE:
1255 if (cap_close && frame_cairo->active == &frame_cairo->title_bar.close)
1256 cairo_set_rgba32(cr, active ? &COL_BUTTON_CLOSE : &COL_BUTTON_INACT);
1257 else
1258 cairo_set_rgba32(cr, &col_title);
1259 cairo_paint(cr);
1260 break;
1261 }
1262
1263 /* button symbols */
1264 /* https://www.cairographics.org/FAQ/#sharp_lines */
1265 cairo_set_line_width(cr, 1);
1266
1267 switch (component) {
1268 case TITLE:
1269 draw_title_text(frame_cairo,cr, &component_width, active);
1270 break;
1271 case BUTTON_MIN:
1272 if (!active) {
1273 /* inactive: use single desaturated color */
1274 cairo_set_rgba32(cr, &COL_SYM_INACT);
1275 } else {
1276 if (!cap_min ||
1277 frame_cairo->active == &frame_cairo->title_bar.min) {
1278 /* active (a.k.a. prelight) */
1279 cairo_set_rgba32(cr, &COL_SYM_ACT);
1280 } else {
1281 /* normal */
1282 cairo_set_rgba32(cr, &COL_SYM);
1283 }
1284 }
1285 cairo_move_to(cr, x, y + SYM_DIM - 1);
1286 cairo_rel_line_to(cr, SYM_DIM - 1, 0);
1287 cairo_stroke(cr);
1288 break;
1289 case BUTTON_MAX:
1290 if (!active) {
1291 /* inactive: use single desaturated color */
1292 cairo_set_rgba32(cr, &COL_SYM_INACT);
1293 } else {
1294 if (!cap_max ||
1295 frame_cairo->active == &frame_cairo->title_bar.max) {
1296 /* active (a.k.a. prelight) */
1297 cairo_set_rgba32(cr, &COL_SYM_ACT);
1298 } else {
1299 /* normal */
1300 cairo_set_rgba32(cr, &COL_SYM);
1301 }
1302 }
1303
1304 if (state & LIBDECOR_WINDOW_STATE_MAXIMIZED) {
1305 const size_t small = 12;
1306 cairo_rectangle(cr,
1307 x,
1308 y + SYM_DIM - small,
1309 small - 1,
1310 small - 1);
1311 cairo_move_to(cr,
1312 x + SYM_DIM - small,
1313 y + SYM_DIM - small);
1314 cairo_line_to(cr, x + SYM_DIM - small, y);
1315 cairo_rel_line_to(cr, small - 1, 0);
1316 cairo_rel_line_to(cr, 0, small - 1);
1317 cairo_line_to(cr, x + small - 1, y + small - 1);
1318 }
1319 else {
1320 cairo_rectangle(cr, x, y, SYM_DIM - 1, SYM_DIM - 1);
1321 }
1322 cairo_stroke(cr);
1323 break;
1324 case BUTTON_CLOSE:
1325 if (!active) {
1326 /* inactive: use single desaturated color */
1327 cairo_set_rgba32(cr, &COL_SYM_INACT);
1328 } else {
1329 if (!cap_close ||
1330 frame_cairo->active == &frame_cairo->title_bar.close) {
1331 /* active (a.k.a. prelight) */
1332 cairo_set_rgba32(cr, &COL_SYM_ACT);
1333 } else {
1334 /* normal */
1335 cairo_set_rgba32(cr, &COL_SYM);
1336 }
1337 }
1338 cairo_move_to(cr, x, y);
1339 cairo_rel_line_to(cr, SYM_DIM - 1, SYM_DIM - 1);
1340 cairo_move_to(cr, x + SYM_DIM - 1, y);
1341 cairo_line_to(cr, x, y + SYM_DIM - 1);
1342 cairo_stroke(cr);
1343 break;
1344 default:
1345 break;
1346 }
1347
1348 /* mask the toplevel surface */
1349 if (component == SHADOW) {
1350 int component_x, component_y, component_width, component_height;
1351 calculate_component_size(frame_cairo, component,
1352 &component_x, &component_y,
1353 &component_width, &component_height);
1354 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
1355 cairo_rectangle(cr, -component_x, -component_y,
1356 libdecor_frame_get_content_width(
1357 &frame_cairo->frame),
1358 libdecor_frame_get_content_height(
1359 &frame_cairo->frame));
1360 cairo_fill(cr);
1361 }
1362
1363 cairo_destroy(cr);
1364 cairo_surface_destroy(surface);
1365 }
1366
1367 static void
set_component_input_region(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component)1368 set_component_input_region(struct libdecor_frame_cairo *frame_cairo,
1369 struct border_component *border_component)
1370 {
1371 if (border_component->type == SHADOW && frame_cairo->shadow_showing) {
1372 struct wl_region *input_region;
1373 int component_x;
1374 int component_y;
1375 int component_width;
1376 int component_height;
1377
1378 calculate_component_size(frame_cairo, border_component->type,
1379 &component_x, &component_y,
1380 &component_width, &component_height);
1381
1382 /*
1383 * the input region is the outer surface size minus the inner
1384 * content size
1385 */
1386 input_region = wl_compositor_create_region(
1387 frame_cairo->plugin_cairo->wl_compositor);
1388 wl_region_add(input_region, 0, 0,
1389 component_width, component_height);
1390 wl_region_subtract(input_region, -component_x, -component_y,
1391 libdecor_frame_get_content_width(&frame_cairo->frame),
1392 libdecor_frame_get_content_height(&frame_cairo->frame));
1393 wl_surface_set_input_region(border_component->server.wl_surface,
1394 input_region);
1395 wl_region_destroy(input_region);
1396 }
1397 }
1398
1399 static void
ensure_component_realized_server(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component,int component_width,int component_height,int scale)1400 ensure_component_realized_server(struct libdecor_frame_cairo *frame_cairo,
1401 struct border_component *border_component,
1402 int component_width,
1403 int component_height,
1404 int scale)
1405 {
1406 struct buffer *old_buffer;
1407 struct buffer *buffer = NULL;
1408
1409 old_buffer = border_component->server.buffer;
1410 if (old_buffer) {
1411 if (!old_buffer->in_use &&
1412 old_buffer->buffer_width == component_width * scale &&
1413 old_buffer->buffer_height == component_height * scale) {
1414 buffer = old_buffer;
1415 } else {
1416 buffer_free(old_buffer);
1417 border_component->server.buffer = NULL;
1418 }
1419 }
1420
1421 if (!buffer)
1422 buffer = create_shm_buffer(frame_cairo->plugin_cairo,
1423 component_width,
1424 component_height,
1425 border_component->opaque,
1426 border_component->server.scale);
1427
1428 border_component->server.buffer = buffer;
1429 }
1430
1431 static void
ensure_component_realized_client(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component,int component_width,int component_height,int scale)1432 ensure_component_realized_client(struct libdecor_frame_cairo *frame_cairo,
1433 struct border_component *border_component,
1434 int component_width,
1435 int component_height,
1436 int scale)
1437 {
1438 cairo_surface_t *old_image;
1439
1440 old_image = border_component->client.image;
1441 if (old_image) {
1442 int cairo_buffer_width;
1443 int cairo_buffer_height;
1444 double x_scale;
1445 double y_scale;
1446
1447 cairo_surface_get_device_scale(old_image, &x_scale, &y_scale);
1448 cairo_buffer_width =
1449 (int) round(cairo_image_surface_get_width(old_image) *
1450 x_scale);
1451 cairo_buffer_height =
1452 (int) round(cairo_image_surface_get_height(old_image) *
1453 y_scale);
1454
1455 if (cairo_buffer_width != component_width * scale ||
1456 cairo_buffer_height != component_height * scale) {
1457 cairo_surface_destroy(old_image);
1458 border_component->client.image = NULL;
1459 }
1460 }
1461
1462 if (!border_component->client.image) {
1463 cairo_surface_t *new_image;
1464
1465 new_image =
1466 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1467 component_width * scale,
1468 component_height * scale);
1469 cairo_surface_set_device_scale(new_image, scale, scale);
1470 border_component->client.image = new_image;
1471 }
1472
1473 }
1474
1475 static void
ensure_component_realized(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component,int component_width,int component_height,int scale)1476 ensure_component_realized(struct libdecor_frame_cairo *frame_cairo,
1477 struct border_component *border_component,
1478 int component_width,
1479 int component_height,
1480 int scale)
1481 {
1482 switch (border_component->composite_mode) {
1483 case COMPOSITE_SERVER:
1484 ensure_component_realized_server(frame_cairo, border_component,
1485 component_width,
1486 component_height,
1487 scale);
1488 break;
1489 case COMPOSITE_CLIENT:
1490 ensure_component_realized_client(frame_cairo, border_component,
1491 component_width,
1492 component_height,
1493 scale);
1494 break;
1495 }
1496 }
1497
1498 static cairo_t *
create_cairo_for_parent(struct border_component * border_component)1499 create_cairo_for_parent(struct border_component *border_component)
1500 {
1501 struct border_component *parent =
1502 border_component->client.parent_component;
1503 struct buffer *buffer;
1504 struct border_component *server_component;
1505 cairo_surface_t *parent_surface;
1506 cairo_t *cr;
1507
1508 switch (parent->composite_mode) {
1509 case COMPOSITE_SERVER:
1510 buffer = parent->server.buffer;
1511 parent_surface = cairo_image_surface_create_for_data(
1512 buffer->data, CAIRO_FORMAT_ARGB32,
1513 buffer->buffer_width, buffer->buffer_height,
1514 cairo_format_stride_for_width(
1515 CAIRO_FORMAT_ARGB32,
1516 buffer->buffer_width)
1517 );
1518 cr = cairo_create(parent_surface);
1519 cairo_surface_destroy(parent_surface);
1520 cairo_scale(cr, buffer->scale, buffer->scale);
1521 return cr;
1522 case COMPOSITE_CLIENT:
1523 cr = cairo_create(parent->client.image);
1524 server_component = get_server_component(border_component);
1525 cairo_scale(cr,
1526 server_component->server.scale,
1527 server_component->server.scale);
1528 return cr;
1529 }
1530 return NULL;
1531 }
1532
1533 static void
draw_border_component(struct libdecor_frame_cairo * frame_cairo,struct border_component * border_component)1534 draw_border_component(struct libdecor_frame_cairo *frame_cairo,
1535 struct border_component *border_component)
1536 {
1537 enum component component = border_component->type;
1538 struct buffer *buffer;
1539 cairo_t *cr;
1540 int component_x;
1541 int component_y;
1542 int component_width;
1543 int component_height;
1544 int scale;
1545 struct border_component *child_component;
1546
1547 if (border_component->is_hidden)
1548 return;
1549
1550 calculate_component_size(frame_cairo, component,
1551 &component_x, &component_y,
1552 &component_width, &component_height);
1553
1554 set_component_input_region(frame_cairo, border_component);
1555
1556 scale = border_component_get_scale(border_component);
1557 ensure_component_realized(frame_cairo, border_component,
1558 component_width,
1559 component_height,
1560 scale);
1561
1562 draw_component_content(frame_cairo,
1563 border_component,
1564 component_width, component_height,
1565 component);
1566
1567 switch(border_component->composite_mode) {
1568 case COMPOSITE_SERVER:
1569 buffer = border_component->server.buffer;
1570 wl_surface_attach(border_component->server.wl_surface,
1571 buffer->wl_buffer,
1572 0, 0);
1573 wl_surface_set_buffer_scale(border_component->server.wl_surface,
1574 buffer->scale);
1575 buffer->in_use = true;
1576 wl_surface_commit(border_component->server.wl_surface);
1577 wl_surface_damage_buffer(border_component->server.wl_surface, 0, 0,
1578 component_width * scale,
1579 component_height * scale);
1580 wl_subsurface_set_position(border_component->server.wl_subsurface,
1581 component_x, component_y);
1582 break;
1583 case COMPOSITE_CLIENT:
1584 cr = create_cairo_for_parent(border_component);
1585 cairo_set_source_surface(cr,
1586 border_component->client.image,
1587 component_x, component_y);
1588 cairo_paint(cr);
1589 cairo_destroy(cr);
1590 break;
1591 }
1592
1593 wl_list_for_each(child_component, &border_component->child_components, link)
1594 draw_border_component(frame_cairo, child_component);
1595 }
1596
1597 static void
draw_border(struct libdecor_frame_cairo * frame_cairo)1598 draw_border(struct libdecor_frame_cairo *frame_cairo)
1599 {
1600 draw_border_component(frame_cairo, &frame_cairo->shadow);
1601 frame_cairo->shadow_showing = true;
1602 }
1603
1604 static void
draw_title_bar(struct libdecor_frame_cairo * frame_cairo)1605 draw_title_bar(struct libdecor_frame_cairo *frame_cairo)
1606 {
1607 draw_border_component(frame_cairo, &frame_cairo->title_bar.title);
1608 frame_cairo->title_bar.is_showing = true;
1609 }
1610
1611 static void
draw_decoration(struct libdecor_frame_cairo * frame_cairo)1612 draw_decoration(struct libdecor_frame_cairo *frame_cairo)
1613 {
1614 switch (frame_cairo->decoration_type) {
1615 case DECORATION_TYPE_NONE:
1616 if (frame_cairo->link.next != NULL)
1617 wl_list_remove(&frame_cairo->link);
1618 if (is_border_surfaces_showing(frame_cairo))
1619 hide_border_surfaces(frame_cairo);
1620 if (is_title_bar_surfaces_showing(frame_cairo))
1621 hide_title_bar_surfaces(frame_cairo);
1622 break;
1623 case DECORATION_TYPE_ALL:
1624 /* show borders */
1625 ensure_border_surfaces(frame_cairo);
1626 draw_border(frame_cairo);
1627 /* show title bar */
1628 ensure_title_bar_surfaces(frame_cairo);
1629 draw_title_bar(frame_cairo);
1630 /* link frame */
1631 if (frame_cairo->link.next == NULL)
1632 wl_list_insert(
1633 &frame_cairo->plugin_cairo->visible_frame_list,
1634 &frame_cairo->link);
1635 break;
1636 case DECORATION_TYPE_TITLE_ONLY:
1637 /* hide borders */
1638 if (is_border_surfaces_showing(frame_cairo))
1639 hide_border_surfaces(frame_cairo);
1640 /* show title bar */
1641 ensure_title_bar_surfaces(frame_cairo);
1642 draw_title_bar(frame_cairo);
1643 /* link frame */
1644 if (frame_cairo->link.next == NULL)
1645 wl_list_insert(
1646 &frame_cairo->plugin_cairo->visible_frame_list,
1647 &frame_cairo->link);
1648 break;
1649 }
1650 }
1651
1652 static void
set_window_geometry(struct libdecor_frame_cairo * frame_cairo)1653 set_window_geometry(struct libdecor_frame_cairo *frame_cairo)
1654 {
1655 struct libdecor_frame *frame = &frame_cairo->frame;
1656 int x = 0, y = 0, width = 0, height = 0;
1657
1658 switch (frame_cairo->decoration_type) {
1659 case DECORATION_TYPE_NONE:
1660 x = 0;
1661 y = 0;
1662 width = libdecor_frame_get_content_width(frame);
1663 height = libdecor_frame_get_content_height(frame);
1664 break;
1665 case DECORATION_TYPE_ALL:
1666 case DECORATION_TYPE_TITLE_ONLY:
1667 x = 0;
1668 y = -(int)TITLE_HEIGHT;
1669 width = libdecor_frame_get_content_width(frame);
1670 height = libdecor_frame_get_content_height(frame) + TITLE_HEIGHT;
1671 break;
1672 }
1673
1674 libdecor_frame_set_window_geometry(frame, x, y, width, height);
1675 }
1676
1677 static enum decoration_type
window_state_to_decoration_type(enum libdecor_window_state window_state)1678 window_state_to_decoration_type(enum libdecor_window_state window_state)
1679 {
1680 if (window_state & LIBDECOR_WINDOW_STATE_FULLSCREEN)
1681 return DECORATION_TYPE_NONE;
1682 else if (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED ||
1683 window_state & LIBDECOR_WINDOW_STATE_TILED_LEFT ||
1684 window_state & LIBDECOR_WINDOW_STATE_TILED_RIGHT ||
1685 window_state & LIBDECOR_WINDOW_STATE_TILED_TOP ||
1686 window_state & LIBDECOR_WINDOW_STATE_TILED_BOTTOM)
1687 /* title bar, no shadows */
1688 return DECORATION_TYPE_TITLE_ONLY;
1689 else
1690 /* title bar, shadows */
1691 return DECORATION_TYPE_ALL;
1692 }
1693
1694 static void
libdecor_plugin_cairo_frame_commit(struct libdecor_plugin * plugin,struct libdecor_frame * frame,struct libdecor_state * state,struct libdecor_configuration * configuration)1695 libdecor_plugin_cairo_frame_commit(struct libdecor_plugin *plugin,
1696 struct libdecor_frame *frame,
1697 struct libdecor_state *state,
1698 struct libdecor_configuration *configuration)
1699 {
1700 struct libdecor_frame_cairo *frame_cairo =
1701 (struct libdecor_frame_cairo *) frame;
1702 enum libdecor_window_state old_window_state;
1703 enum libdecor_window_state new_window_state;
1704 int old_content_width, old_content_height;
1705 int new_content_width, new_content_height;
1706 enum decoration_type old_decoration_type;
1707 enum decoration_type new_decoration_type;
1708
1709 old_window_state = frame_cairo->window_state;
1710 new_window_state = libdecor_frame_get_window_state(frame);
1711
1712 old_content_width = frame_cairo->content_width;
1713 old_content_height = frame_cairo->content_height;
1714 new_content_width = libdecor_frame_get_content_width(frame);
1715 new_content_height = libdecor_frame_get_content_height(frame);
1716
1717 old_decoration_type = frame_cairo->decoration_type;
1718 new_decoration_type = window_state_to_decoration_type(new_window_state);
1719
1720 if (old_decoration_type == new_decoration_type &&
1721 old_content_width == new_content_width &&
1722 old_content_height == new_content_height &&
1723 old_window_state == new_window_state)
1724 return;
1725
1726 frame_cairo->content_width = new_content_width;
1727 frame_cairo->content_height = new_content_height;
1728 frame_cairo->decoration_type = new_decoration_type;
1729 frame_cairo->window_state = new_window_state;
1730
1731 draw_decoration(frame_cairo);
1732 set_window_geometry(frame_cairo);
1733 }
1734
1735 static void
libdecor_plugin_cairo_frame_property_changed(struct libdecor_plugin * plugin,struct libdecor_frame * frame)1736 libdecor_plugin_cairo_frame_property_changed(struct libdecor_plugin *plugin,
1737 struct libdecor_frame *frame)
1738 {
1739 struct libdecor_frame_cairo *frame_cairo =
1740 (struct libdecor_frame_cairo *) frame;
1741 bool redraw_needed = false;
1742 const char *new_title;
1743
1744 new_title = libdecor_frame_get_title(frame);
1745 if (frame_cairo->title_bar.is_showing) {
1746 if (!streql(frame_cairo->title, new_title))
1747 redraw_needed = true;
1748 }
1749
1750 if (frame_cairo->title) {
1751 free(frame_cairo->title);
1752 frame_cairo->title = NULL;
1753 }
1754
1755 if (new_title) {
1756 frame_cairo->title = strdup(new_title);
1757 }
1758
1759 if (frame_cairo->capabilities != libdecor_frame_get_capabilities(frame)) {
1760 frame_cairo->capabilities = libdecor_frame_get_capabilities(frame);
1761 redraw_needed = true;
1762 }
1763
1764 if (redraw_needed) {
1765 draw_decoration(frame_cairo);
1766 libdecor_frame_toplevel_commit(frame);
1767 }
1768 }
1769
1770 static void
libdecor_plugin_cairo_frame_translate_coordinate(struct libdecor_plugin * plugin,struct libdecor_frame * frame,int content_x,int content_y,int * frame_x,int * frame_y)1771 libdecor_plugin_cairo_frame_translate_coordinate(struct libdecor_plugin *plugin,
1772 struct libdecor_frame *frame,
1773 int content_x,
1774 int content_y,
1775 int *frame_x,
1776 int *frame_y)
1777 {
1778 struct libdecor_frame_cairo *frame_cairo =
1779 (struct libdecor_frame_cairo *) frame;
1780
1781 *frame_x = content_x;
1782 *frame_y = content_y;
1783
1784 if (frame_cairo->title_bar.is_showing)
1785 *frame_y += TITLE_HEIGHT;
1786 }
1787
1788 static bool
streq(const char * str1,const char * str2)1789 streq(const char *str1,
1790 const char *str2)
1791 {
1792 if (!str1 && !str2)
1793 return true;
1794
1795 if (str1 && str2)
1796 return strcmp(str1, str2) == 0;
1797
1798 return false;
1799 }
1800
1801 static void
libdecor_plugin_cairo_frame_popup_grab(struct libdecor_plugin * plugin,struct libdecor_frame * frame,const char * seat_name)1802 libdecor_plugin_cairo_frame_popup_grab(struct libdecor_plugin *plugin,
1803 struct libdecor_frame *frame,
1804 const char *seat_name)
1805 {
1806 struct libdecor_frame_cairo *frame_cairo =
1807 (struct libdecor_frame_cairo *) frame;
1808 struct libdecor_plugin_cairo *plugin_cairo = frame_cairo->plugin_cairo;
1809 struct seat *seat;
1810
1811 wl_list_for_each(seat, &plugin_cairo->seat_list, link) {
1812 if (streq(seat->name, seat_name)) {
1813 if (seat->grabbed) {
1814 fprintf(stderr, "libdecor-WARNING: Application "
1815 "tried to grab seat twice\n");
1816 }
1817 synthesize_pointer_leave(seat);
1818 seat->grabbed = true;
1819 return;
1820 }
1821 }
1822
1823 fprintf(stderr,
1824 "libdecor-WARNING: Application tried to grab unknown seat\n");
1825 }
1826
1827 static void
libdecor_plugin_cairo_frame_popup_ungrab(struct libdecor_plugin * plugin,struct libdecor_frame * frame,const char * seat_name)1828 libdecor_plugin_cairo_frame_popup_ungrab(struct libdecor_plugin *plugin,
1829 struct libdecor_frame *frame,
1830 const char *seat_name)
1831 {
1832 struct libdecor_frame_cairo *frame_cairo =
1833 (struct libdecor_frame_cairo *) frame;
1834 struct libdecor_plugin_cairo *plugin_cairo = frame_cairo->plugin_cairo;
1835 struct seat *seat;
1836
1837 wl_list_for_each(seat, &plugin_cairo->seat_list, link) {
1838 if (streq(seat->name, seat_name)) {
1839 if (!seat->grabbed) {
1840 fprintf(stderr, "libdecor-WARNING: Application "
1841 "tried to ungrab seat twice\n");
1842 }
1843 seat->grabbed = false;
1844 synthesize_pointer_enter(seat);
1845 sync_active_component(frame_cairo, seat);
1846 return;
1847 }
1848 }
1849
1850 fprintf(stderr,
1851 "libdecor-WARNING: Application tried to ungrab unknown seat\n");
1852 }
1853
1854 static bool
libdecor_plugin_cairo_configuration_get_content_size(struct libdecor_plugin * plugin,struct libdecor_configuration * configuration,struct libdecor_frame * frame,int * content_width,int * content_height)1855 libdecor_plugin_cairo_configuration_get_content_size(
1856 struct libdecor_plugin *plugin,
1857 struct libdecor_configuration *configuration,
1858 struct libdecor_frame *frame,
1859 int *content_width,
1860 int *content_height)
1861 {
1862 int win_width, win_height;
1863 if (!libdecor_configuration_get_window_size(configuration,
1864 &win_width,
1865 &win_height))
1866 return false;
1867
1868 enum libdecor_window_state state;
1869 if (!libdecor_configuration_get_window_state(configuration, &state)) {
1870 return false;
1871 }
1872
1873 switch (window_state_to_decoration_type(state)) {
1874 case DECORATION_TYPE_NONE:
1875 *content_width = win_width;
1876 *content_height = win_height;
1877 break;
1878 case DECORATION_TYPE_ALL:
1879 case DECORATION_TYPE_TITLE_ONLY:
1880 *content_width = win_width;
1881 *content_height = win_height - TITLE_HEIGHT;
1882 break;
1883 }
1884
1885 return true;
1886 }
1887
1888 static bool
libdecor_plugin_cairo_frame_get_window_size_for(struct libdecor_plugin * plugin,struct libdecor_frame * frame,struct libdecor_state * state,int * window_width,int * window_height)1889 libdecor_plugin_cairo_frame_get_window_size_for(
1890 struct libdecor_plugin *plugin,
1891 struct libdecor_frame *frame,
1892 struct libdecor_state *state,
1893 int *window_width,
1894 int *window_height)
1895 {
1896 enum libdecor_window_state window_state =
1897 libdecor_state_get_window_state(state);
1898
1899 switch (window_state_to_decoration_type(window_state)) {
1900 case DECORATION_TYPE_NONE:
1901 *window_width = libdecor_state_get_content_width(state);
1902 *window_height = libdecor_state_get_content_height(state);
1903 break;
1904 case DECORATION_TYPE_ALL:
1905 case DECORATION_TYPE_TITLE_ONLY:
1906 *window_width = libdecor_state_get_content_width(state);
1907 *window_height =
1908 libdecor_state_get_content_height(state) + TITLE_HEIGHT;
1909 break;
1910 }
1911
1912 return true;
1913 }
1914
1915 static struct libdecor_plugin_interface cairo_plugin_iface = {
1916 .destroy = libdecor_plugin_cairo_destroy,
1917 .get_fd = libdecor_plugin_cairo_get_fd,
1918 .dispatch = libdecor_plugin_cairo_dispatch,
1919
1920 .frame_new = libdecor_plugin_cairo_frame_new,
1921 .frame_free = libdecor_plugin_cairo_frame_free,
1922 .frame_commit = libdecor_plugin_cairo_frame_commit,
1923 .frame_property_changed = libdecor_plugin_cairo_frame_property_changed,
1924 .frame_translate_coordinate =
1925 libdecor_plugin_cairo_frame_translate_coordinate,
1926 .frame_popup_grab = libdecor_plugin_cairo_frame_popup_grab,
1927 .frame_popup_ungrab = libdecor_plugin_cairo_frame_popup_ungrab,
1928
1929 .configuration_get_content_size =
1930 libdecor_plugin_cairo_configuration_get_content_size,
1931 .frame_get_window_size_for =
1932 libdecor_plugin_cairo_frame_get_window_size_for,
1933 };
1934
1935 static void
init_wl_compositor(struct libdecor_plugin_cairo * plugin_cairo,uint32_t id,uint32_t version)1936 init_wl_compositor(struct libdecor_plugin_cairo *plugin_cairo,
1937 uint32_t id,
1938 uint32_t version)
1939 {
1940 plugin_cairo->wl_compositor =
1941 wl_registry_bind(plugin_cairo->wl_registry,
1942 id, &wl_compositor_interface,
1943 MIN(version, 4));
1944 }
1945
1946 static void
init_wl_subcompositor(struct libdecor_plugin_cairo * plugin_cairo,uint32_t id,uint32_t version)1947 init_wl_subcompositor(struct libdecor_plugin_cairo *plugin_cairo,
1948 uint32_t id,
1949 uint32_t version)
1950 {
1951 plugin_cairo->wl_subcompositor =
1952 wl_registry_bind(plugin_cairo->wl_registry,
1953 id, &wl_subcompositor_interface, 1);
1954 }
1955
1956 static void
shm_format(void * user_data,struct wl_shm * wl_shm,uint32_t format)1957 shm_format(void *user_data,
1958 struct wl_shm *wl_shm,
1959 uint32_t format)
1960 {
1961 struct libdecor_plugin_cairo *plugin_cairo = user_data;
1962
1963 if (format == WL_SHM_FORMAT_ARGB8888)
1964 plugin_cairo->has_argb = true;
1965 }
1966
1967 struct wl_shm_listener shm_listener = {
1968 shm_format
1969 };
1970
1971 static void
shm_callback(void * user_data,struct wl_callback * callback,uint32_t time)1972 shm_callback(void *user_data,
1973 struct wl_callback *callback,
1974 uint32_t time)
1975 {
1976 struct libdecor_plugin_cairo *plugin_cairo = user_data;
1977 struct libdecor *context = plugin_cairo->context;
1978
1979 wl_callback_destroy(callback);
1980 plugin_cairo->globals_callback_shm = NULL;
1981
1982 if (!plugin_cairo->has_argb) {
1983 libdecor_notify_plugin_error(
1984 context,
1985 LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE,
1986 "Compositor is missing required shm format");
1987 return;
1988 }
1989
1990 libdecor_notify_plugin_ready(context);
1991 }
1992
1993 static const struct wl_callback_listener shm_callback_listener = {
1994 shm_callback
1995 };
1996
1997 static void
init_wl_shm(struct libdecor_plugin_cairo * plugin_cairo,uint32_t id,uint32_t version)1998 init_wl_shm(struct libdecor_plugin_cairo *plugin_cairo,
1999 uint32_t id,
2000 uint32_t version)
2001 {
2002 struct libdecor *context = plugin_cairo->context;
2003 struct wl_display *wl_display = libdecor_get_wl_display(context);
2004
2005 plugin_cairo->wl_shm =
2006 wl_registry_bind(plugin_cairo->wl_registry,
2007 id, &wl_shm_interface, 1);
2008 wl_shm_add_listener(plugin_cairo->wl_shm, &shm_listener, plugin_cairo);
2009
2010 plugin_cairo->globals_callback_shm = wl_display_sync(wl_display);
2011 wl_callback_add_listener(plugin_cairo->globals_callback_shm,
2012 &shm_callback_listener,
2013 plugin_cairo);
2014 }
2015
2016 static void
cursor_surface_enter(void * data,struct wl_surface * wl_surface,struct wl_output * wl_output)2017 cursor_surface_enter(void *data,
2018 struct wl_surface *wl_surface,
2019 struct wl_output *wl_output)
2020 {
2021 struct seat *seat = data;
2022
2023 if(own_output(wl_output)) {
2024 struct cursor_output *cursor_output;
2025 cursor_output = zalloc(sizeof *cursor_output);
2026 cursor_output->output = wl_output_get_user_data(wl_output);
2027 wl_list_insert(&seat->cursor_outputs, &cursor_output->link);
2028 if (update_local_cursor(seat))
2029 send_cursor(seat);
2030 }
2031 }
2032
2033 static void
cursor_surface_leave(void * data,struct wl_surface * wl_surface,struct wl_output * wl_output)2034 cursor_surface_leave(void *data,
2035 struct wl_surface *wl_surface,
2036 struct wl_output *wl_output)
2037 {
2038 struct seat *seat = data;
2039
2040 if(own_output(wl_output)) {
2041 struct cursor_output *cursor_output, *tmp;
2042 wl_list_for_each_safe(cursor_output, tmp, &seat->cursor_outputs, link) {
2043 if (cursor_output->output->wl_output == wl_output) {
2044 wl_list_remove(&cursor_output->link);
2045 free(cursor_output);
2046 }
2047 }
2048
2049 if (update_local_cursor(seat))
2050 send_cursor(seat);
2051 }
2052 }
2053
2054 static struct wl_surface_listener cursor_surface_listener = {
2055 cursor_surface_enter,
2056 cursor_surface_leave,
2057 };
2058
2059 static void
ensure_cursor_surface(struct seat * seat)2060 ensure_cursor_surface(struct seat *seat)
2061 {
2062 struct wl_compositor *wl_compositor = seat->plugin_cairo->wl_compositor;
2063
2064 if (seat->cursor_surface)
2065 return;
2066
2067 seat->cursor_surface = wl_compositor_create_surface(wl_compositor);
2068 wl_surface_add_listener(seat->cursor_surface,
2069 &cursor_surface_listener, seat);
2070 }
2071
2072 static bool
ensure_cursor_theme(struct seat * seat)2073 ensure_cursor_theme(struct seat *seat)
2074 {
2075 struct libdecor_plugin_cairo *plugin_cairo = seat->plugin_cairo;
2076 int scale = 1;
2077 unsigned int i;
2078 struct wl_cursor_theme *theme;
2079 struct cursor_output *cursor_output;
2080
2081 wl_list_for_each(cursor_output, &seat->cursor_outputs, link) {
2082 scale = MAX(scale, cursor_output->output->scale);
2083 }
2084
2085 if (seat->cursor_theme && seat->cursor_scale == scale)
2086 return false;
2087
2088 seat->cursor_scale = scale;
2089 theme = wl_cursor_theme_load(plugin_cairo->cursor_theme_name,
2090 plugin_cairo->cursor_size * scale,
2091 plugin_cairo->wl_shm);
2092 if (theme == NULL)
2093 return false;
2094
2095 if (seat->cursor_theme)
2096 wl_cursor_theme_destroy(seat->cursor_theme);
2097
2098 seat->cursor_theme = theme;
2099
2100 for (i = 0; i < ARRAY_LENGTH(cursor_names); i++) {
2101 seat->cursors[i] = wl_cursor_theme_get_cursor(
2102 seat->cursor_theme,
2103 cursor_names[i]);
2104 }
2105
2106 seat->cursor_left_ptr = wl_cursor_theme_get_cursor(seat->cursor_theme,
2107 "left_ptr");
2108 seat->current_cursor = seat->cursor_left_ptr;
2109
2110 return true;
2111 }
2112
2113 enum libdecor_resize_edge
component_edge(const struct border_component * cmpnt,const int pointer_x,const int pointer_y,const int margin)2114 component_edge(const struct border_component *cmpnt,
2115 const int pointer_x,
2116 const int pointer_y,
2117 const int margin)
2118 {
2119 const bool top = pointer_y < margin;
2120 const bool bottom = pointer_y > (cmpnt->server.buffer->height - margin);
2121 const bool left = pointer_x < margin;
2122 const bool right = pointer_x > (cmpnt->server.buffer->width - margin);
2123
2124 if (top)
2125 if (left)
2126 return LIBDECOR_RESIZE_EDGE_TOP_LEFT;
2127 else if (right)
2128 return LIBDECOR_RESIZE_EDGE_TOP_RIGHT;
2129 else
2130 return LIBDECOR_RESIZE_EDGE_TOP;
2131 else if (bottom)
2132 if (left)
2133 return LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT;
2134 else if (right)
2135 return LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT;
2136 else
2137 return LIBDECOR_RESIZE_EDGE_BOTTOM;
2138 else if (left)
2139 return LIBDECOR_RESIZE_EDGE_LEFT;
2140 else if (right)
2141 return LIBDECOR_RESIZE_EDGE_RIGHT;
2142 else
2143 return LIBDECOR_RESIZE_EDGE_NONE;
2144 }
2145
2146 static bool
update_local_cursor(struct seat * seat)2147 update_local_cursor(struct seat *seat)
2148 {
2149 if (!seat->pointer_focus) {
2150 seat->current_cursor = seat->cursor_left_ptr;
2151 return false;
2152 }
2153
2154 if (!own_surface(seat->pointer_focus))
2155 return false;
2156
2157 struct libdecor_frame_cairo *frame_cairo =
2158 wl_surface_get_user_data(seat->pointer_focus);
2159 struct wl_cursor *wl_cursor = NULL;
2160
2161 if (!frame_cairo || !frame_cairo->active) {
2162 seat->current_cursor = seat->cursor_left_ptr;
2163 return false;
2164 }
2165
2166 bool theme_updated = ensure_cursor_theme(seat);
2167
2168 if (frame_cairo->active->type == SHADOW &&
2169 is_border_surfaces_showing(frame_cairo) &&
2170 resizable(frame_cairo)) {
2171 enum libdecor_resize_edge edge;
2172 edge = component_edge(frame_cairo->active,
2173 seat->pointer_x,
2174 seat->pointer_y, SHADOW_MARGIN);
2175
2176 if (edge != LIBDECOR_RESIZE_EDGE_NONE)
2177 wl_cursor = seat->cursors[edge - 1];
2178 } else {
2179 wl_cursor = seat->cursor_left_ptr;
2180 }
2181
2182 if (seat->current_cursor != wl_cursor) {
2183 seat->current_cursor = wl_cursor;
2184 return true;
2185 }
2186
2187 return theme_updated;
2188 }
2189
2190 static void
send_cursor(struct seat * seat)2191 send_cursor(struct seat *seat)
2192 {
2193 struct wl_cursor_image *image;
2194 struct wl_buffer *buffer;
2195
2196 if (seat->pointer_focus == NULL || seat->current_cursor == NULL)
2197 return;
2198
2199 image = seat->current_cursor->images[0];
2200 buffer = wl_cursor_image_get_buffer(image);
2201 wl_surface_attach(seat->cursor_surface, buffer, 0, 0);
2202 wl_surface_set_buffer_scale(seat->cursor_surface, seat->cursor_scale);
2203 wl_surface_damage_buffer(seat->cursor_surface, 0, 0,
2204 image->width * seat->cursor_scale,
2205 image->height * seat->cursor_scale);
2206 wl_surface_commit(seat->cursor_surface);
2207 wl_pointer_set_cursor(seat->wl_pointer, seat->serial,
2208 seat->cursor_surface,
2209 image->hotspot_x / seat->cursor_scale,
2210 image->hotspot_y / seat->cursor_scale);
2211 }
2212
2213 static void
sync_active_component(struct libdecor_frame_cairo * frame_cairo,struct seat * seat)2214 sync_active_component(struct libdecor_frame_cairo *frame_cairo,
2215 struct seat *seat)
2216 {
2217 struct border_component *old_active;
2218
2219 if (!seat->pointer_focus)
2220 return;
2221
2222 old_active = frame_cairo->active;
2223 update_component_focus(frame_cairo, seat->pointer_focus, seat);
2224 if (old_active != frame_cairo->active) {
2225 draw_decoration(frame_cairo);
2226 libdecor_frame_toplevel_commit(&frame_cairo->frame);
2227 }
2228
2229 if (update_local_cursor(seat))
2230 send_cursor(seat);
2231 }
2232
2233 static void
synthesize_pointer_enter(struct seat * seat)2234 synthesize_pointer_enter(struct seat *seat)
2235 {
2236 struct wl_surface *surface;
2237 struct libdecor_frame_cairo *frame_cairo;
2238
2239 surface = seat->pointer_focus;
2240 if (!surface)
2241 return;
2242
2243 frame_cairo = wl_surface_get_user_data(surface);
2244 if (!frame_cairo)
2245 return;
2246
2247 update_component_focus(frame_cairo, seat->pointer_focus, seat);
2248 frame_cairo->grab = NULL;
2249
2250 /* update decorations */
2251 if (frame_cairo->active) {
2252 draw_decoration(frame_cairo);
2253 libdecor_frame_toplevel_commit(&frame_cairo->frame);
2254 }
2255
2256 update_local_cursor(seat);
2257 send_cursor(seat);
2258 }
2259
2260 static void
synthesize_pointer_leave(struct seat * seat)2261 synthesize_pointer_leave(struct seat *seat)
2262 {
2263 struct wl_surface *surface;
2264 struct libdecor_frame_cairo *frame_cairo;
2265
2266 surface = seat->pointer_focus;
2267 if (!surface)
2268 return;
2269
2270 frame_cairo = wl_surface_get_user_data(surface);
2271 if (!frame_cairo)
2272 return;
2273
2274 if (!frame_cairo->active)
2275 return;
2276
2277 frame_cairo->active = NULL;
2278 draw_decoration(frame_cairo);
2279 libdecor_frame_toplevel_commit(&frame_cairo->frame);
2280 update_local_cursor(seat);
2281 }
2282
2283 static void
pointer_enter(void * data,struct wl_pointer * wl_pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t surface_x,wl_fixed_t surface_y)2284 pointer_enter(void *data,
2285 struct wl_pointer *wl_pointer,
2286 uint32_t serial,
2287 struct wl_surface *surface,
2288 wl_fixed_t surface_x,
2289 wl_fixed_t surface_y)
2290 {
2291 struct seat *seat = data;
2292
2293 if (!surface)
2294 return;
2295
2296 if (!own_surface(surface))
2297 return;
2298
2299 ensure_cursor_surface(seat);
2300
2301 seat->pointer_x = wl_fixed_to_int(surface_x);
2302 seat->pointer_y = wl_fixed_to_int(surface_y);
2303 seat->serial = serial;
2304 seat->pointer_focus = surface;
2305
2306 if (seat->grabbed)
2307 return;
2308
2309 synthesize_pointer_enter(seat);
2310 }
2311
2312 static void
pointer_leave(void * data,struct wl_pointer * wl_pointer,uint32_t serial,struct wl_surface * surface)2313 pointer_leave(void *data,
2314 struct wl_pointer *wl_pointer,
2315 uint32_t serial,
2316 struct wl_surface *surface)
2317 {
2318 struct seat *seat = data;
2319
2320 if (!surface)
2321 return;
2322
2323 if (!own_surface(surface))
2324 return;
2325
2326 synthesize_pointer_leave(seat);
2327 seat->pointer_focus = NULL;
2328 }
2329
2330 static void
pointer_motion(void * data,struct wl_pointer * wl_pointer,uint32_t time,wl_fixed_t surface_x,wl_fixed_t surface_y)2331 pointer_motion(void *data,
2332 struct wl_pointer *wl_pointer,
2333 uint32_t time,
2334 wl_fixed_t surface_x,
2335 wl_fixed_t surface_y)
2336 {
2337 struct seat *seat = data;
2338 struct libdecor_frame_cairo *frame_cairo;
2339
2340 seat->pointer_x = wl_fixed_to_int(surface_x);
2341 seat->pointer_y = wl_fixed_to_int(surface_y);
2342
2343 if (seat->grabbed)
2344 return;
2345
2346 if (!seat->pointer_focus)
2347 return;
2348
2349 frame_cairo = wl_surface_get_user_data(seat->pointer_focus);
2350
2351 sync_active_component(frame_cairo, seat);
2352 }
2353
2354 static void
pointer_button(void * data,struct wl_pointer * wl_pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)2355 pointer_button(void *data,
2356 struct wl_pointer *wl_pointer,
2357 uint32_t serial,
2358 uint32_t time,
2359 uint32_t button,
2360 uint32_t state)
2361 {
2362 struct seat *seat = data;
2363 struct libdecor_frame_cairo *frame_cairo;
2364
2365 if (!seat->pointer_focus || !own_surface(seat->pointer_focus))
2366 return;
2367
2368 frame_cairo = wl_surface_get_user_data(seat->pointer_focus);
2369 if (!frame_cairo)
2370 return;
2371
2372 if (seat->grabbed) {
2373 libdecor_frame_dismiss_popup(&frame_cairo->frame, seat->name);
2374 return;
2375 }
2376
2377 if (!frame_cairo->active)
2378 return;
2379
2380 if (button == BTN_LEFT) {
2381 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
2382 enum libdecor_resize_edge edge =
2383 LIBDECOR_RESIZE_EDGE_NONE;
2384
2385 frame_cairo->grab = NULL;
2386
2387 switch (frame_cairo->active->type) {
2388 case SHADOW:
2389 edge = component_edge(frame_cairo->active,
2390 seat->pointer_x,
2391 seat->pointer_y,
2392 SHADOW_MARGIN);
2393 break;
2394 case TITLE:
2395 if (time-seat->pointer_button_time_stamp <
2396 DOUBLE_CLICK_TIME_MS) {
2397 toggle_maximized(&frame_cairo->frame);
2398 }
2399 else if (moveable(frame_cairo)) {
2400 seat->pointer_button_time_stamp = time;
2401 libdecor_frame_move(&frame_cairo->frame,
2402 seat->wl_seat,
2403 serial);
2404 }
2405 break;
2406 case BUTTON_MIN:
2407 case BUTTON_MAX:
2408 case BUTTON_CLOSE:
2409 frame_cairo->grab = frame_cairo->active;
2410 break;
2411 default:
2412 break;
2413 }
2414
2415 if (edge != LIBDECOR_RESIZE_EDGE_NONE &&
2416 resizable(frame_cairo)) {
2417 libdecor_frame_resize(
2418 &frame_cairo->frame,
2419 seat->wl_seat,
2420 serial,
2421 edge);
2422 }
2423 }
2424 else if (state == WL_POINTER_BUTTON_STATE_RELEASED &&
2425 frame_cairo->grab) {
2426 if (frame_cairo->grab == frame_cairo->focus) {
2427 switch (frame_cairo->active->type) {
2428 case BUTTON_MIN:
2429 if (minimizable(frame_cairo))
2430 libdecor_frame_set_minimized(
2431 &frame_cairo->frame);
2432 break;
2433 case BUTTON_MAX:
2434 toggle_maximized(&frame_cairo->frame);
2435 break;
2436 case BUTTON_CLOSE:
2437 if (closeable(frame_cairo))
2438 libdecor_frame_close(&frame_cairo->frame);
2439 break;
2440 default:
2441 break;
2442 }
2443 }
2444 frame_cairo->grab = NULL;
2445 sync_active_component(frame_cairo, seat);
2446 }
2447 }
2448 else if (button == BTN_RIGHT &&
2449 state == WL_POINTER_BUTTON_STATE_PRESSED &&
2450 seat->pointer_focus == frame_cairo->title_bar.title.server.wl_surface) {
2451 libdecor_frame_show_window_menu(&frame_cairo->frame,
2452 seat->wl_seat,
2453 serial,
2454 seat->pointer_x,
2455 seat->pointer_y - TITLE_HEIGHT);
2456 }
2457 }
2458
2459 static void
pointer_axis(void * data,struct wl_pointer * wl_pointer,uint32_t time,uint32_t axis,wl_fixed_t value)2460 pointer_axis(void *data,
2461 struct wl_pointer *wl_pointer,
2462 uint32_t time,
2463 uint32_t axis,
2464 wl_fixed_t value)
2465 {
2466 }
2467
2468 static struct wl_pointer_listener pointer_listener = {
2469 pointer_enter,
2470 pointer_leave,
2471 pointer_motion,
2472 pointer_button,
2473 pointer_axis
2474 };
2475
2476 static void
seat_capabilities(void * data,struct wl_seat * wl_seat,uint32_t capabilities)2477 seat_capabilities(void *data,
2478 struct wl_seat *wl_seat,
2479 uint32_t capabilities)
2480 {
2481 struct seat *seat = data;
2482
2483 if ((capabilities & WL_SEAT_CAPABILITY_POINTER) &&
2484 !seat->wl_pointer) {
2485 seat->wl_pointer = wl_seat_get_pointer(wl_seat);
2486 wl_pointer_add_listener(seat->wl_pointer,
2487 &pointer_listener, seat);
2488 } else if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) &&
2489 seat->wl_pointer) {
2490 wl_pointer_release(seat->wl_pointer);
2491 seat->wl_pointer = NULL;
2492 }
2493 }
2494
2495 static void
seat_name(void * data,struct wl_seat * wl_seat,const char * name)2496 seat_name(void *data,
2497 struct wl_seat *wl_seat,
2498 const char *name)
2499 {
2500 struct seat *seat = data;
2501
2502 seat->name = strdup(name);
2503 }
2504
2505 static struct wl_seat_listener seat_listener = {
2506 seat_capabilities,
2507 seat_name
2508 };
2509
2510 static void
init_wl_seat(struct libdecor_plugin_cairo * plugin_cairo,uint32_t id,uint32_t version)2511 init_wl_seat(struct libdecor_plugin_cairo *plugin_cairo,
2512 uint32_t id,
2513 uint32_t version)
2514 {
2515 struct seat *seat;
2516
2517 if (version < 3) {
2518 libdecor_notify_plugin_error(
2519 plugin_cairo->context,
2520 LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE,
2521 "%s version 3 required but only version %i is available\n",
2522 wl_seat_interface.name, version);
2523 }
2524
2525 seat = zalloc(sizeof *seat);
2526 seat->cursor_scale = 1;
2527 seat->plugin_cairo = plugin_cairo;
2528 wl_list_init(&seat->cursor_outputs);
2529 wl_list_insert(&plugin_cairo->seat_list, &seat->link);
2530 seat->wl_seat =
2531 wl_registry_bind(plugin_cairo->wl_registry,
2532 id, &wl_seat_interface, 3);
2533 wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
2534 }
2535
2536 static void
output_geometry(void * data,struct wl_output * wl_output,int32_t x,int32_t y,int32_t physical_width,int32_t physical_height,int32_t subpixel,const char * make,const char * model,int32_t transform)2537 output_geometry(void *data,
2538 struct wl_output *wl_output,
2539 int32_t x,
2540 int32_t y,
2541 int32_t physical_width,
2542 int32_t physical_height,
2543 int32_t subpixel,
2544 const char *make,
2545 const char *model,
2546 int32_t transform)
2547 {
2548 }
2549
2550 static void
output_mode(void * data,struct wl_output * wl_output,uint32_t flags,int32_t width,int32_t height,int32_t refresh)2551 output_mode(void *data,
2552 struct wl_output *wl_output,
2553 uint32_t flags,
2554 int32_t width,
2555 int32_t height,
2556 int32_t refresh)
2557 {
2558 }
2559
2560 static void
output_done(void * data,struct wl_output * wl_output)2561 output_done(void *data,
2562 struct wl_output *wl_output)
2563 {
2564 struct output *output = data;
2565 struct libdecor_frame_cairo *frame_cairo;
2566 struct seat *seat;
2567
2568 wl_list_for_each(frame_cairo,
2569 &output->plugin_cairo->visible_frame_list, link) {
2570 bool updated = false;
2571 updated |= redraw_scale(frame_cairo, &frame_cairo->shadow);
2572 updated |= redraw_scale(frame_cairo, &frame_cairo->title_bar.title);
2573 if (updated)
2574 libdecor_frame_toplevel_commit(&frame_cairo->frame);
2575 }
2576 wl_list_for_each(seat, &output->plugin_cairo->seat_list, link) {
2577 if (update_local_cursor(seat))
2578 send_cursor(seat);
2579 }
2580 }
2581
2582 static void
output_scale(void * data,struct wl_output * wl_output,int32_t factor)2583 output_scale(void *data,
2584 struct wl_output *wl_output,
2585 int32_t factor)
2586 {
2587 struct output *output = data;
2588
2589 output->scale = factor;
2590 }
2591
2592 static struct wl_output_listener output_listener = {
2593 output_geometry,
2594 output_mode,
2595 output_done,
2596 output_scale
2597 };
2598
2599 static void
init_wl_output(struct libdecor_plugin_cairo * plugin_cairo,uint32_t id,uint32_t version)2600 init_wl_output(struct libdecor_plugin_cairo *plugin_cairo,
2601 uint32_t id,
2602 uint32_t version)
2603 {
2604 struct output *output;
2605
2606 if (version < 2) {
2607 libdecor_notify_plugin_error(
2608 plugin_cairo->context,
2609 LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE,
2610 "%s version 2 required but only version %i is available\n",
2611 wl_output_interface.name, version);
2612 }
2613
2614 output = zalloc(sizeof *output);
2615 output->plugin_cairo = plugin_cairo;
2616 wl_list_insert(&plugin_cairo->output_list, &output->link);
2617 output->id = id;
2618 output->wl_output =
2619 wl_registry_bind(plugin_cairo->wl_registry,
2620 id, &wl_output_interface, 2);
2621 wl_proxy_set_tag((struct wl_proxy *) output->wl_output,
2622 &libdecor_cairo_proxy_tag);
2623 wl_output_add_listener(output->wl_output, &output_listener, output);
2624 }
2625
2626 static void
registry_handle_global(void * user_data,struct wl_registry * wl_registry,uint32_t id,const char * interface,uint32_t version)2627 registry_handle_global(void *user_data,
2628 struct wl_registry *wl_registry,
2629 uint32_t id,
2630 const char *interface,
2631 uint32_t version)
2632 {
2633 struct libdecor_plugin_cairo *plugin_cairo = user_data;
2634
2635 if (strcmp(interface, "wl_compositor") == 0)
2636 init_wl_compositor(plugin_cairo, id, version);
2637 else if (strcmp(interface, "wl_subcompositor") == 0)
2638 init_wl_subcompositor(plugin_cairo, id, version);
2639 else if (strcmp(interface, "wl_shm") == 0)
2640 init_wl_shm(plugin_cairo, id, version);
2641 else if (strcmp(interface, "wl_seat") == 0)
2642 init_wl_seat(plugin_cairo, id, version);
2643 else if (strcmp(interface, "wl_output") == 0)
2644 init_wl_output(plugin_cairo, id, version);
2645 }
2646
2647 static void
remove_surface_outputs(struct border_component * cmpnt,struct output * output)2648 remove_surface_outputs(struct border_component *cmpnt, struct output *output)
2649 {
2650 struct surface_output *surface_output;
2651 wl_list_for_each(surface_output, &cmpnt->server.output_list, link) {
2652 if (surface_output->output == output) {
2653 wl_list_remove(&surface_output->link);
2654 free(surface_output);
2655 break;
2656 }
2657 }
2658 }
2659
2660 static void
output_removed(struct libdecor_plugin_cairo * plugin_cairo,struct output * output)2661 output_removed(struct libdecor_plugin_cairo *plugin_cairo,
2662 struct output *output)
2663 {
2664 struct libdecor_frame_cairo *frame_cairo;
2665 struct seat *seat;
2666
2667 wl_list_for_each(frame_cairo, &plugin_cairo->visible_frame_list, link) {
2668 remove_surface_outputs(&frame_cairo->shadow, output);
2669 remove_surface_outputs(&frame_cairo->title_bar.title, output);
2670 remove_surface_outputs(&frame_cairo->title_bar.min, output);
2671 remove_surface_outputs(&frame_cairo->title_bar.max, output);
2672 remove_surface_outputs(&frame_cairo->title_bar.close, output);
2673 }
2674 wl_list_for_each(seat, &plugin_cairo->seat_list, link) {
2675 struct cursor_output *cursor_output;
2676 wl_list_for_each(cursor_output, &seat->cursor_outputs, link) {
2677 if (cursor_output->output == output) {
2678 wl_list_remove(&cursor_output->link);
2679 free(cursor_output);
2680 }
2681 }
2682 }
2683
2684 wl_list_remove(&output->link);
2685 wl_output_destroy(output->wl_output);
2686 free(output);
2687 }
2688
2689 static void
registry_handle_global_remove(void * user_data,struct wl_registry * wl_registry,uint32_t name)2690 registry_handle_global_remove(void *user_data,
2691 struct wl_registry *wl_registry,
2692 uint32_t name)
2693 {
2694 struct libdecor_plugin_cairo *plugin_cairo = user_data;
2695 struct output *output;
2696
2697 wl_list_for_each(output, &plugin_cairo->output_list, link) {
2698 if (output->id == name) {
2699 output_removed(plugin_cairo, output);
2700 break;
2701 }
2702 }
2703 }
2704
2705 static const struct wl_registry_listener registry_listener = {
2706 registry_handle_global,
2707 registry_handle_global_remove
2708 };
2709
2710 static bool
has_required_globals(struct libdecor_plugin_cairo * plugin_cairo)2711 has_required_globals(struct libdecor_plugin_cairo *plugin_cairo)
2712 {
2713 if (!plugin_cairo->wl_compositor)
2714 return false;
2715 if (!plugin_cairo->wl_subcompositor)
2716 return false;
2717 if (!plugin_cairo->wl_shm)
2718 return false;
2719
2720 return true;
2721 }
2722
2723 static void
globals_callback(void * user_data,struct wl_callback * callback,uint32_t time)2724 globals_callback(void *user_data,
2725 struct wl_callback *callback,
2726 uint32_t time)
2727 {
2728 struct libdecor_plugin_cairo *plugin_cairo = user_data;
2729
2730 wl_callback_destroy(callback);
2731 plugin_cairo->globals_callback = NULL;
2732
2733 if (!has_required_globals(plugin_cairo)) {
2734 libdecor_notify_plugin_error(
2735 plugin_cairo->context,
2736 LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE,
2737 "Compositor is missing required globals");
2738 }
2739 }
2740
2741 static const struct wl_callback_listener globals_callback_listener = {
2742 globals_callback
2743 };
2744
2745 static struct libdecor_plugin *
libdecor_plugin_new(struct libdecor * context)2746 libdecor_plugin_new(struct libdecor *context)
2747 {
2748 struct libdecor_plugin_cairo *plugin_cairo;
2749 struct wl_display *wl_display;
2750
2751 plugin_cairo = zalloc(sizeof *plugin_cairo);
2752 libdecor_plugin_init(&plugin_cairo->plugin,
2753 context,
2754 &cairo_plugin_iface);
2755 plugin_cairo->context = context;
2756
2757 wl_list_init(&plugin_cairo->visible_frame_list);
2758 wl_list_init(&plugin_cairo->seat_list);
2759 wl_list_init(&plugin_cairo->output_list);
2760
2761 /* fetch cursor theme and size*/
2762 if (!libdecor_get_cursor_settings(&plugin_cairo->cursor_theme_name,
2763 &plugin_cairo->cursor_size)) {
2764 plugin_cairo->cursor_theme_name = NULL;
2765 plugin_cairo->cursor_size = 24;
2766 }
2767
2768 /* define a sens-serif bold font at symbol size */
2769 plugin_cairo->font = pango_font_description_new();
2770 pango_font_description_set_family(plugin_cairo->font, "sans");
2771 pango_font_description_set_weight(plugin_cairo->font, PANGO_WEIGHT_BOLD);
2772 pango_font_description_set_size(plugin_cairo->font, SYM_DIM * PANGO_SCALE);
2773
2774 wl_display = libdecor_get_wl_display(context);
2775 plugin_cairo->wl_registry = wl_display_get_registry(wl_display);
2776 wl_registry_add_listener(plugin_cairo->wl_registry,
2777 ®istry_listener,
2778 plugin_cairo);
2779
2780 plugin_cairo->globals_callback = wl_display_sync(wl_display);
2781 wl_callback_add_listener(plugin_cairo->globals_callback,
2782 &globals_callback_listener,
2783 plugin_cairo);
2784
2785 return &plugin_cairo->plugin;
2786 }
2787
2788 static struct libdecor_plugin_priority priorities[] = {
2789 { NULL, LIBDECOR_PLUGIN_PRIORITY_MEDIUM }
2790 };
2791
2792 LIBDECOR_EXPORT const struct libdecor_plugin_description
2793 libdecor_plugin_description = {
2794 .api_version = LIBDECOR_PLUGIN_API_VERSION,
2795 .capabilities = LIBDECOR_PLUGIN_CAPABILITY_BASE,
2796 .description = "libdecor plugin using Cairo",
2797 .priorities = priorities,
2798 .constructor = libdecor_plugin_new,
2799 };
2800