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 				 &registry_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