1From 15243cd3aec009b59b4b6aa4698b56ed3506c304 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Thu, 19 Nov 2020 09:41:47 +0800
4Subject: [PATCH 15/79] backend-drm: Support mirror mode
5
6Set env "WESTON_DRM_MIRROR" to enable mirror mode, and set env
7"WESTON_DRM_KEEP_RATIO" to keep the aspect ratio.
8
9Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
10---
11 clients/desktop-shell.c               |   9 +-
12 desktop-shell/shell.c                 |   3 +
13 include/libweston/libweston.h         |   4 +
14 libweston/backend-drm/drm-gbm.c       |   4 +-
15 libweston/backend-drm/drm-internal.h  |  10 +
16 libweston/backend-drm/drm.c           | 328 +++++++++++++++++++++++++-
17 libweston/backend-drm/meson.build     |   3 +-
18 libweston/backend-drm/state-propose.c |  22 +-
19 libweston/compositor.c                |  24 +-
20 libweston/input.c                     |   7 +
21 meson.build                           |   5 +
22 11 files changed, 398 insertions(+), 21 deletions(-)
23
24diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c
25index 56c3976..8711399 100644
26--- a/clients/desktop-shell.c
27+++ b/clients/desktop-shell.c
28@@ -1062,9 +1062,14 @@ desktop_shell_configure(void *data,
29 			struct wl_surface *surface,
30 			int32_t width, int32_t height)
31 {
32-	struct window *window = wl_surface_get_user_data(surface);
33-	struct surface *s = window_get_user_data(window);
34+	struct window *window;
35+	struct surface *s;
36+
37+	if (!surface)
38+		return;
39
40+	window = wl_surface_get_user_data(surface);
41+	s = window_get_user_data(window);
42 	s->configure(data, desktop_shell, edges, window, width, height);
43 }
44
45diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
46index 515c57f..18bec12 100644
47--- a/desktop-shell/shell.c
48+++ b/desktop-shell/shell.c
49@@ -4280,6 +4280,9 @@ weston_view_set_initial_position(struct weston_view *view,
50 	}
51
52 	wl_list_for_each(output, &compositor->output_list, link) {
53+		if (output->unavailable)
54+			continue;
55+
56 		if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) {
57 			target_output = output;
58 			break;
59diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
60index bdecc19..8868622 100644
61--- a/include/libweston/libweston.h
62+++ b/include/libweston/libweston.h
63@@ -411,7 +411,11 @@ struct weston_output {
64 	 */
65 	void (*detach_head)(struct weston_output *output,
66 			    struct weston_head *head);
67+
68+	bool unavailable;
69 };
70+#define weston_output_valid(o) \
71+	((o) && !(o)->destroying && !(o)->unavailable)
72
73 enum weston_pointer_motion_mask {
74 	WESTON_POINTER_MOTION_ABS = 1 << 0,
75diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c
76index d0a4c6c..d7bd05f 100644
77--- a/libweston/backend-drm/drm-gbm.c
78+++ b/libweston/backend-drm/drm-gbm.c
79@@ -265,8 +265,8 @@ drm_output_fini_egl(struct drm_output *output)
80 	/* Destroying the GBM surface will destroy all our GBM buffers,
81 	 * regardless of refcount. Ensure we destroy them here. */
82 	if (!b->shutting_down &&
83-	    output->scanout_plane->state_cur->fb &&
84-	    output->scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE) {
85+	    output->scanout_plane->state_cur->fb && (output->is_mirror ||
86+	    output->scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE)) {
87 		drm_plane_reset_state(output->scanout_plane);
88 	}
89
90diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
91index 996f587..1625be2 100644
92--- a/libweston/backend-drm/drm-internal.h
93+++ b/libweston/backend-drm/drm-internal.h
94@@ -346,6 +346,8 @@ struct drm_backend {
95
96 	int virtual_width;
97 	int virtual_height;
98+
99+	bool mirror_mode;
100 };
101
102 struct drm_mode {
103@@ -603,6 +605,10 @@ struct drm_output {
104 	int current_image;
105 	pixman_region32_t previous_damage;
106
107+	/* Wrap fb for scale/rotate usage */
108+	struct drm_fb *wrap[2];
109+	int next_wrap;
110+
111 	struct vaapi_recorder *recorder;
112 	struct wl_listener recorder_frame_listener;
113
114@@ -616,6 +622,10 @@ struct drm_output {
115
116 	/* The dummy framebuffer for SET_CRTC. */
117 	struct drm_fb *fb_dummy;
118+
119+	bool is_mirror;
120+
121+	pixman_box32_t plane_bounds;
122 };
123
124 static inline struct drm_head *
125diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
126index 1c839c5..696df68 100644
127--- a/libweston/backend-drm/drm.c
128+++ b/libweston/backend-drm/drm.c
129@@ -67,6 +67,11 @@
130 #include "linux-dmabuf-unstable-v1-server-protocol.h"
131 #include "linux-explicit-synchronization.h"
132
133+#ifdef HAVE_RGA
134+#include <rga/rga.h>
135+#include <rga/RgaApi.h>
136+#endif
137+
138 static const char default_seat[] = "seat0";
139
140 static inline bool
141@@ -85,6 +90,118 @@ drm_head_is_external(struct drm_head *head)
142 	}
143 };
144
145+static int
146+drm_output_get_rotation(struct drm_output *output)
147+{
148+	switch (output->base.transform) {
149+	case WL_OUTPUT_TRANSFORM_90:
150+	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
151+		return 90;
152+	case WL_OUTPUT_TRANSFORM_180:
153+	case WL_OUTPUT_TRANSFORM_FLIPPED_180:
154+		return 180;
155+	case WL_OUTPUT_TRANSFORM_270:
156+	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
157+		return 270;
158+	default:
159+		return 0;
160+	}
161+}
162+
163+#ifdef HAVE_RGA
164+static inline RgaSURF_FORMAT
165+rga_get_format(const struct pixel_format_info *format)
166+{
167+	switch (format->bpp) {
168+	case 32:
169+		return RK_FORMAT_BGRX_8888;
170+	case 16:
171+		return RK_FORMAT_RGB_565;
172+	default:
173+		return RK_FORMAT_UNKNOWN;
174+	}
175+}
176+#endif
177+
178+static int
179+drm_copy_fb(struct drm_fb *src, struct drm_fb *dst, int rotation,
180+	    int src_width, int src_height)
181+{
182+#ifndef HAVE_RGA
183+	/* TODO: Use pixman to do the copy */
184+	weston_log("rga not supported\n");
185+	return -1;
186+#else
187+	RgaSURF_FORMAT src_format, dst_format;
188+	rga_info_t src_info = {0};
189+	rga_info_t dst_info = {0};
190+	int src_fd, dst_fd;
191+	int ret;
192+
193+	static bool rga_supported = true;
194+	static bool rga_inited = false;
195+
196+	if (!rga_supported)
197+		return -1;
198+
199+	if (!rga_inited) {
200+		ret = c_RkRgaInit();
201+		if (ret < 0) {
202+			weston_log("rga not supported\n");
203+			rga_supported = false;
204+			return ret;
205+		}
206+		rga_inited = true;
207+	}
208+
209+	src_format = rga_get_format(src->format);
210+	dst_format = rga_get_format(dst->format);
211+
212+	if (src_format == RK_FORMAT_UNKNOWN ||
213+	    dst_format == RK_FORMAT_UNKNOWN) {
214+		weston_log("unsupported fb format\n");
215+		return -1;
216+	}
217+
218+	ret = drmPrimeHandleToFD(src->fd, src->handles[0],
219+				 DRM_CLOEXEC, &src_fd);
220+	if (ret < 0)
221+		return ret;
222+
223+	ret = drmPrimeHandleToFD(dst->fd, dst->handles[0],
224+				 DRM_CLOEXEC, &dst_fd);
225+	if (ret < 0)
226+		goto close_src;
227+
228+	src_info.fd = src_fd;
229+	src_info.mmuFlag = 1;
230+
231+	rga_set_rect(&src_info.rect, 0, 0, src_width, src_height,
232+		     src->strides[0] * 8 / src->format->bpp, src->height,
233+		     src_format);
234+
235+	if (rotation == 90)
236+		src_info.rotation = HAL_TRANSFORM_ROT_90;
237+	else if (rotation == 180)
238+		src_info.rotation = HAL_TRANSFORM_ROT_180;
239+	else if (rotation == 270)
240+		src_info.rotation = HAL_TRANSFORM_ROT_270;
241+
242+	dst_info.fd = dst_fd;
243+	dst_info.mmuFlag = 1;
244+
245+	rga_set_rect(&dst_info.rect, 0, 0, dst->width, dst->height,
246+		     dst->strides[0] * 8 / dst->format->bpp, dst->height,
247+		     dst_format);
248+
249+	ret = c_RkRgaBlit(&src_info, &dst_info, NULL);
250+	close(dst_fd);
251+close_src:
252+	close(src_fd);
253+	return ret;
254+#endif
255+}
256+
257 static void
258 drm_backend_update_outputs(struct drm_backend *b)
259 {
260@@ -94,6 +211,26 @@ drm_backend_update_outputs(struct drm_backend *b)
261 		return;
262
263 	primary = b->primary_head->base.output;
264+
265+	if (b->mirror_mode) {
266+		wl_list_for_each(base, &b->compositor->output_list, link) {
267+			struct drm_output *output = to_drm_output(base);
268+			bool is_mirror = base != primary;
269+
270+			if (output->is_mirror == is_mirror)
271+				continue;
272+
273+			/* Make mirrors unavailable for normal views */
274+			output->base.unavailable = is_mirror;
275+
276+			output->is_mirror = is_mirror;
277+			output->state_invalid = true;
278+
279+			weston_log("Output %s changed to %s output\n",
280+				   base->name, is_mirror ? "mirror" : "main");
281+		}
282+	}
283+
284 	if (!primary)
285 		return;
286
287@@ -392,6 +529,69 @@ drm_output_render_pixman(struct drm_output_state *state,
288 	return drm_fb_ref(output->dumb[output->current_image]);
289 }
290
291+static struct drm_fb *
292+drm_output_get_fb(struct drm_pending_state *pending_state,
293+		  struct weston_output *output_base)
294+{
295+	struct drm_output *output = to_drm_output(output_base);
296+	struct drm_plane_state *scanout_state;
297+	struct drm_output_state *state;
298+	struct drm_fb *fb = output->scanout_plane->state_cur->fb;
299+
300+	state = drm_pending_state_get_output(pending_state, output);
301+	if (!state)
302+		return fb;
303+
304+	scanout_state =
305+		drm_output_state_get_existing_plane(state,
306+						    output->scanout_plane);
307+	if (!scanout_state || !scanout_state->fb)
308+		return fb;
309+
310+	return scanout_state->fb;
311+}
312+
313+static void
314+drm_output_try_destroy_wrap_fb(struct drm_output *output)
315+{
316+	if (output->wrap[0]) {
317+		drm_fb_unref(output->wrap[0]);
318+		output->wrap[0] = NULL;
319+	}
320+
321+	if (output->wrap[1]) {
322+		drm_fb_unref(output->wrap[1]);
323+		output->wrap[1] = NULL;
324+	}
325+
326+	output->next_wrap = 0;
327+}
328+
329+static struct drm_fb *
330+drm_output_get_wrap_fb(struct drm_backend *b, struct drm_output *output,
331+		       int width, int height)
332+{
333+	struct drm_fb *fb = output->wrap[output->next_wrap];
334+
335+	if (fb) {
336+		if (fb->width == width && fb->height == height)
337+			goto out;
338+
339+		drm_fb_unref(fb);
340+	}
341+
342+	fb = drm_fb_create_dumb(b, width, height, output->gbm_format);
343+	if (!fb) {
344+		weston_log("failed to create wrap fb\n");
345+		return NULL;
346+	}
347+
348+	output->wrap[output->next_wrap] = fb;
349+out:
350+	output->next_wrap ^= 1;
351+	return drm_fb_ref(fb);
352+}
353+
354 void
355 drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
356 {
357@@ -403,10 +603,13 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
358 		&scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS];
359 	struct drm_backend *b = to_drm_backend(c);
360 	struct drm_mode *mode;
361-	struct drm_fb *fb;
362+	struct drm_fb *fb = NULL;
363 	pixman_region32_t scanout_damage;
364 	pixman_box32_t *rects;
365 	int n_rects;
366+	int sw, sh, dx, dy, dw, dh;
367+	int rotation = 0;
368+	bool scaling;
369
370 	/* If we already have a client buffer promoted to scanout, then we don't
371 	 * want to render. */
372@@ -414,6 +617,35 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
373 	if (scanout_state->fb)
374 		return;
375
376+	if (!output->is_mirror) {
377+		struct drm_output *tmp;
378+
379+		/* Repaint all mirrors when updating main output */
380+		wl_list_for_each(tmp, &b->compositor->output_list, base.link)
381+			if (tmp->is_mirror)
382+				weston_output_schedule_repaint(&tmp->base);
383+	} else {
384+		if (!b->primary_head)
385+			goto out;
386+
387+		rotation = drm_output_get_rotation(output);
388+
389+		fb = drm_output_get_fb(state->pending_state,
390+				       b->primary_head->base.output);
391+		if (fb) {
392+			drm_fb_ref(fb);
393+
394+			pixman_region32_init(&scanout_damage);
395+			wl_signal_emit(&output->base.frame_signal,
396+				       &scanout_damage);
397+			pixman_region32_fini(&scanout_damage);
398+		} else {
399+			weston_compositor_damage_all(b->compositor);
400+		}
401+
402+		goto out;
403+	}
404+
405 	/*
406 	 * If we don't have any damage on the primary plane, and we already
407 	 * have a renderer buffer active, we can reuse it; else we pass
408@@ -433,24 +665,86 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
409 		fb = drm_output_render_gl(state, damage);
410 	}
411
412+out:
413 	if (!fb) {
414 		drm_plane_state_put_back(scanout_state);
415 		return;
416 	}
417
418+	sw = fb->width;
419+	sh = fb->height;
420+
421+	dx = output->plane_bounds.x1;
422+	dy = output->plane_bounds.y1;
423+	dw = output->plane_bounds.x2 - output->plane_bounds.x1;
424+	dh = output->plane_bounds.y2 - output->plane_bounds.y1;
425+
426+	if (!dw || !dh) {
427+		mode = to_drm_mode(output->base.current_mode);
428+		dw = mode->mode_info.hdisplay;
429+		dh = mode->mode_info.vdisplay;
430+	}
431+
432+	if (output->is_mirror && getenv("WESTON_DRM_KEEP_RATIO")) {
433+		float src_ratio = (float) sw / sh;
434+		float dst_ratio = (float) dw / dh;
435+		int offset;
436+
437+		if (rotation % 180)
438+			src_ratio = 1 / src_ratio;
439+
440+		if (src_ratio > dst_ratio) {
441+			offset = dh - dw / src_ratio;
442+			dy = offset / 2;
443+			dh -= offset;
444+		} else {
445+			offset = dw - dh * src_ratio;
446+			dx = offset / 2;
447+			dw -= offset;
448+		}
449+	}
450+
451+	scaling = sw != dw || sh != dh;
452+
453+	if (rotation || (scaling && !output->scanout_plane->can_scale)) {
454+		struct drm_fb *wrap_fb =
455+			drm_output_get_wrap_fb(b, output, dw, dh);
456+		if (!wrap_fb) {
457+			weston_log("failed to get wrap fb\n");
458+			goto err;
459+		}
460+
461+		if (drm_copy_fb(fb, wrap_fb, rotation, sw, sh) < 0) {
462+			weston_log("failed to copy fb\n");
463+			goto err;
464+		}
465+
466+		sw = dw;
467+		sh = dh;
468+
469+		drm_fb_unref(fb);
470+		fb = wrap_fb;
471+	} else {
472+		drm_output_try_destroy_wrap_fb(output);
473+	}
474+
475 	scanout_state->fb = fb;
476+	fb = NULL;
477+
478 	scanout_state->output = output;
479
480 	scanout_state->src_x = 0;
481 	scanout_state->src_y = 0;
482-	scanout_state->src_w = fb->width << 16;
483-	scanout_state->src_h = fb->height << 16;
484+	scanout_state->src_w = sw << 16;
485+	scanout_state->src_h = sh << 16;
486
487-	mode = to_drm_mode(output->base.current_mode);
488-	scanout_state->dest_x = 0;
489-	scanout_state->dest_y = 0;
490-	scanout_state->dest_w = mode->mode_info.hdisplay;
491-	scanout_state->dest_h = mode->mode_info.vdisplay;
492+	scanout_state->dest_x = dx;
493+	scanout_state->dest_y = dy;
494+	scanout_state->dest_w = dw;
495+	scanout_state->dest_h = dh;
496+
497+	if (output->is_mirror)
498+		return;
499
500 	pixman_region32_subtract(&c->primary_plane.damage,
501 				 &c->primary_plane.damage, damage);
502@@ -499,6 +793,12 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage)
503 				  &scanout_state->damage_blob_id);
504
505 	pixman_region32_fini(&scanout_damage);
506+	return;
507+err:
508+	if (fb)
509+		drm_fb_unref(fb);
510+
511+	drm_plane_state_put_back(scanout_state);
512 }
513
514 static int
515@@ -1296,8 +1596,8 @@ drm_output_fini_pixman(struct drm_output *output)
516 	/* Destroying the Pixman surface will destroy all our buffers,
517 	 * regardless of refcount. Ensure we destroy them here. */
518 	if (!b->shutting_down &&
519-	    output->scanout_plane->state_cur->fb &&
520-	    output->scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB) {
521+	    output->scanout_plane->state_cur->fb && (output->is_mirror ||
522+	    output->scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB)) {
523 		drm_plane_reset_state(output->scanout_plane);
524 	}
525
526@@ -1975,6 +2275,8 @@ drm_output_destroy(struct weston_output *base)
527 	assert(!output->state_last);
528 	drm_output_state_free(output->state_cur);
529
530+	drm_output_try_destroy_wrap_fb(output);
531+
532 	free(output);
533 }
534
535@@ -3320,6 +3622,12 @@ drm_backend_create(struct weston_compositor *compositor,
536 	else
537 		b->resize_freeze_ms = DRM_RESIZE_FREEZE_MS;
538
539+	buf = getenv("WESTON_DRM_MIRROR");
540+	if (buf && buf[0] == '1') {
541+		b->mirror_mode = true;
542+		weston_log("Entering mirror mode.\n");
543+	}
544+
545 	b->state_invalid = true;
546 	b->drm.fd = -1;
547
548diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build
549index 23db912..6dbd05d 100644
550--- a/libweston/backend-drm/meson.build
551+++ b/libweston/backend-drm/meson.build
552@@ -38,7 +38,8 @@ deps_drm = [
553 	dep_libdrm,
554 	dep_libinput_backend,
555 	dependency('libudev', version: '>= 136'),
556-	dep_backlight
557+	dep_backlight,
558+	dep_rga
559 ]
560
561 if get_option('renderer-gl')
562diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c
563index 7b350aa..48e6de2 100644
564--- a/libweston/backend-drm/state-propose.c
565+++ b/libweston/backend-drm/state-propose.c
566@@ -54,6 +54,21 @@ static const char *const drm_output_propose_state_mode_as_string[] = {
567 	[DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY]	= "plane-only state"
568 };
569
570+static bool
571+drm_is_mirroring(struct drm_backend *b)
572+{
573+	struct drm_output *tmp;
574+
575+	if (!b->mirror_mode)
576+		return false;
577+
578+	wl_list_for_each(tmp, &b->compositor->output_list, base.link)
579+		if (tmp->is_mirror)
580+			return true;
581+
582+	return false;
583+}
584+
585 static const char *
586 drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
587 {
588@@ -466,7 +481,7 @@ drm_output_try_view_on_plane(struct drm_plane *plane,
589
590 	switch (plane->type) {
591 	case WDRM_PLANE_TYPE_CURSOR:
592-		if (b->cursors_are_broken) {
593+		if (b->cursors_are_broken || drm_is_mirroring(b)) {
594 			availability = NO_PLANES_ACCEPTED;
595 			goto out;
596 		}
597@@ -1102,7 +1117,10 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
598 	drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
599 		  output_base->name, (unsigned long) output_base->id);
600
601-	if (!b->sprites_are_broken && !output->virtual) {
602+	/* Force single plane in mirror mode */
603+	if (drm_is_mirroring(b)) {
604+		drm_debug(b, "\t[state] no overlay plane in mirror mode\n");
605+	} else if (!b->sprites_are_broken && !output->virtual) {
606 		drm_debug(b, "\t[repaint] trying planes-only build state\n");
607 		state = drm_output_propose_state(output_base, pending_state, mode);
608 		if (!state) {
609diff --git a/libweston/compositor.c b/libweston/compositor.c
610index 22a8593..7680445 100644
611--- a/libweston/compositor.c
612+++ b/libweston/compositor.c
613@@ -1407,7 +1407,7 @@ weston_view_assign_output(struct weston_view *ev)
614 	mask = 0;
615 	pixman_region32_init(&region);
616 	wl_list_for_each(output, &ec->output_list, link) {
617-		if (output->destroying)
618+		if (!weston_output_valid(output))
619 			continue;
620
621 		pixman_region32_intersect(&region, &ev->transform.boundingbox,
622@@ -5249,6 +5249,9 @@ bind_output(struct wl_client *client,
623 static void
624 weston_head_add_global(struct weston_head *head)
625 {
626+	if (head->global || !weston_output_valid(head->output))
627+		return;
628+
629 	head->global = wl_global_create(head->compositor->wl_display,
630 					&wl_output_interface, 3,
631 					head, bind_output);
632@@ -5284,6 +5287,15 @@ weston_head_remove_global(struct weston_head *head)
633 	wl_list_init(&head->xdg_output_resource_list);
634 }
635
636+static void
637+weston_head_update_global(struct weston_head *head)
638+{
639+	if (weston_output_valid(head->output))
640+		weston_head_add_global(head);
641+	else
642+		weston_head_remove_global(head);
643+}
644+
645 /** Get the backing object of wl_output
646  *
647  * \param resource A wl_output protocol object.
648@@ -6079,11 +6091,15 @@ WL_EXPORT void
649 weston_compositor_reflow_outputs(struct weston_compositor *compositor)
650 {
651 	struct weston_output *output;
652+	struct weston_head *head;
653 	int x, y, next_x, next_y;
654
655 	next_x = next_y = 0;
656 	wl_list_for_each(output, &compositor->output_list, link) {
657-		if (output->destroying)
658+		wl_list_for_each(head, &output->head_list, output_link)
659+			weston_head_update_global(head);
660+
661+		if (!weston_output_valid(output))
662 			continue;
663
664 		x = next_x;
665@@ -6300,11 +6316,11 @@ weston_compositor_add_output(struct weston_compositor *compositor,
666 	wl_list_insert(compositor->output_list.prev, &output->link);
667 	output->enabled = true;
668
669+	wl_signal_emit(&compositor->output_created_signal, output);
670+
671 	wl_list_for_each(head, &output->head_list, output_link)
672 		weston_head_add_global(head);
673
674-	wl_signal_emit(&compositor->output_created_signal, output);
675-
676 	/*
677 	 * Use view_list, as paint nodes have not been created for this
678 	 * output yet. Any existing view might touch this new output.
679diff --git a/libweston/input.c b/libweston/input.c
680index 6fb4bed..ec7e416 100644
681--- a/libweston/input.c
682+++ b/libweston/input.c
683@@ -1688,6 +1688,10 @@ weston_pointer_clamp(struct weston_pointer *pointer, wl_fixed_t *fx, wl_fixed_t
684 	wl_list_for_each(output, &ec->output_list, link) {
685 		if (pointer->seat->output && pointer->seat->output != output)
686 			continue;
687+
688+		if (output->unavailable)
689+			continue;
690+
691 		if (pixman_region32_contains_point(&output->region,
692 						   x, y, NULL))
693 			valid = 1;
694@@ -1757,6 +1761,9 @@ weston_pointer_handle_output_destroy(struct wl_listener *listener, void *data)
695 	y = wl_fixed_to_int(pointer->y);
696
697 	wl_list_for_each(output, &ec->output_list, link) {
698+		if (output->unavailable)
699+			continue;
700+
701 		if (pixman_region32_contains_point(&output->region,
702 						   x, y, NULL))
703 			return;
704diff --git a/meson.build b/meson.build
705index f5aa460..feecd45 100644
706--- a/meson.build
707+++ b/meson.build
708@@ -145,6 +145,11 @@ if get_option('deprecated-wl-shell')
709 	config_h.set('HAVE_DEPRECATED_WL_SHELL', '1')
710 endif
711
712+dep_rga = dependency('librga', required: false)
713+if dep_rga.found()
714+	config_h.set('HAVE_RGA', '1')
715+endif
716+
717 dep_wayland_server = dependency('wayland-server', version: '>= 1.18.0')
718 dep_wayland_client = dependency('wayland-client', version: '>= 1.18.0')
719 dep_pixman = dependency('pixman-1', version: '>= 0.25.2')
720--
7212.20.1
722
723