1From ea9254bbf829ea582c004aeff0058c24f7bb1080 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Fri, 3 Jul 2020 12:37:37 +0800
4Subject: [PATCH 16/79] backend-drm: Support controlling output dynamically
5
6Use config file to set output's rotate/down-scale/size/pos/mode/off/
7freeze/display-rectangle and prefer/primary output.
8
9Default config file is "/tmp/.weston_drm.conf", can override with
10"WESTON_DRM_CONFIG" environment.
11
12Supported configs format is "output:<output name>:<config>", for
13example:
14echo "output:DSI-1:off" >> /tmp/.weston_drm.conf
15echo "output:eDP-1:freeze" >> /tmp/.weston_drm.conf
16echo "output:all:rotate90" >> /tmp/.weston_drm.conf
17echo "output:all:rect=<100,20,1636,2068>" >> /tmp/.weston_drm.conf
18echo "output:HDMI-A-1:mode=800x600" >> /tmp/.weston_drm.conf
19echo "output:HDMI-A-1:pos=100,200" >> /tmp/.weston_drm.conf
20echo "output:HDMI-A-1:size=1920x1080" >> /tmp/.weston_drm.conf
21echo "output:HDMI-A-1:prefer" >> /tmp/.weston_drm.conf
22echo "output:HDMI-A-1:primary" >> /tmp/.weston_drm.conf
23echo "output:HDMI-A-1:down-scale=0.5" >> /tmp/.weston_drm.conf
24
25Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
26---
27 compositor/main.c                    |  14 ++
28 desktop-shell/shell.c                |  58 +++++-
29 include/libweston/libweston.h        |   7 +
30 libweston/backend-drm/drm-internal.h |  16 ++
31 libweston/backend-drm/drm.c          | 264 ++++++++++++++++++++++++++-
32 libweston/backend-drm/modes.c        |  20 +-
33 libweston/compositor.c               |  50 +++--
34 libweston/pixman-renderer.c          |   3 +
35 libweston/renderer-gl/gl-renderer.c  |   4 +
36 9 files changed, 397 insertions(+), 39 deletions(-)
37
38diff --git a/compositor/main.c b/compositor/main.c
39index 47bf540..a4f679a 100644
40--- a/compositor/main.c
41+++ b/compositor/main.c
42@@ -1832,6 +1832,20 @@ drm_backend_output_configure(struct weston_output *output,
43 	}
44 	free(s);
45
46+	weston_config_section_get_string(section, "pos", &s, NULL);
47+	if (s) {
48+		if (sscanf(s, "%d,%d", &output->x, &output->y) == 2)
49+			output->fixed_position = true;
50+		free(s);
51+	}
52+
53+	weston_config_section_get_string(section, "size", &s, NULL);
54+	if (s) {
55+		if (sscanf(s, "%dx%d", &output->width, &output->height) == 2)
56+			output->fixed_size = true;
57+		free(s);
58+	}
59+
60 	if (api->set_mode(output, mode, modeline) < 0) {
61 		weston_log("Cannot configure an output using weston_drm_output_api.\n");
62 		free(modeline);
63diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
64index 18bec12..8b4ed45 100644
65--- a/desktop-shell/shell.c
66+++ b/desktop-shell/shell.c
67@@ -4254,7 +4254,7 @@ weston_view_set_initial_position(struct weston_view *view,
68 	int ix = 0, iy = 0;
69 	int32_t range_x, range_y;
70 	int32_t x, y;
71-	struct weston_output *output, *target_output = NULL;
72+	struct weston_output *output, *target_output = NULL, *prefer_output = NULL;
73 	struct weston_seat *seat;
74 	pixman_rectangle32_t area;
75
76@@ -4279,16 +4279,20 @@ weston_view_set_initial_position(struct weston_view *view,
77 		}
78 	}
79
80-	wl_list_for_each(output, &compositor->output_list, link) {
81+	wl_list_for_each_reverse(output, &compositor->output_list, link) {
82 		if (output->unavailable)
83 			continue;
84
85-		if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) {
86+		if (output == compositor->prefer_output)
87+			prefer_output = output;
88+
89+		if (pixman_region32_contains_point(&output->region, ix, iy, NULL))
90 			target_output = output;
91-			break;
92-		}
93 	}
94
95+	if (prefer_output)
96+		target_output = prefer_output;
97+
98 	if (!target_output) {
99 		weston_view_set_position(view, 10 + random() % 400,
100 					 10 + random() % 400);
101@@ -4890,6 +4894,41 @@ shell_resize_surface_to_output(struct desktop_shell *shell,
102 					output->height);
103 }
104
105+static void
106+handle_output_resize_layer(struct desktop_shell *shell,
107+			   struct weston_layer *layer, void *data)
108+{
109+	struct weston_output *output = data;
110+	struct weston_view *view;
111+
112+	wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
113+		struct weston_desktop_surface *desktop_surface;
114+		struct shell_surface *shsurf;
115+		bool dirty = false;
116+
117+		if (view->output != output)
118+			continue;
119+
120+		shsurf = get_shell_surface(view->surface);
121+		if (!shsurf)
122+			continue;
123+
124+		desktop_surface = shsurf->desktop_surface;
125+		if (weston_desktop_surface_get_fullscreen(desktop_surface)) {
126+			set_fullscreen(shsurf, true, output);
127+			dirty = true;
128+		}
129+		if (weston_desktop_surface_get_maximized(desktop_surface)) {
130+			set_maximized(shsurf, true);
131+			dirty = true;
132+		}
133+
134+		if (dirty) {
135+			weston_view_geometry_dirty(view);
136+			weston_surface_damage(view->surface);
137+		}
138+	}
139+}
140
141 static void
142 handle_output_resized(struct wl_listener *listener, void *data)
143@@ -4899,8 +4938,13 @@ handle_output_resized(struct wl_listener *listener, void *data)
144 	struct weston_output *output = (struct weston_output *)data;
145 	struct shell_output *sh_output = find_shell_output_from_weston_output(shell, output);
146
147+	if (shell->lock_surface)
148+		shell->lock_surface->committed(shell->lock_surface, 0, 0);
149+
150 	shell_resize_surface_to_output(shell, sh_output->background_surface, output);
151 	shell_resize_surface_to_output(shell, sh_output->panel_surface, output);
152+
153+	shell_for_each_layer(shell, handle_output_resize_layer, data);
154 }
155
156 static void
157@@ -4949,7 +4993,9 @@ handle_output_move_layer(struct desktop_shell *shell,
158
159 		x = view->geometry.x + output->move_x;
160 		y = view->geometry.y + output->move_y;
161-		weston_view_set_position(view, x, y);
162+
163+		if (pixman_region32_contains_point(&output->region, x, y, NULL))
164+			weston_view_set_position(view, x, y);
165 	}
166 }
167
168diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
169index 8868622..24e44a2 100644
170--- a/include/libweston/libweston.h
171+++ b/include/libweston/libweston.h
172@@ -413,6 +413,12 @@ struct weston_output {
173 			    struct weston_head *head);
174
175 	bool unavailable;
176+	bool freezing;
177+
178+	bool fixed_position;
179+	bool fixed_size;
180+
181+	double down_scale;
182 };
183 #define weston_output_valid(o) \
184 	((o) && !(o)->destroying && !(o)->unavailable)
185@@ -1198,6 +1204,7 @@ struct weston_compositor {
186 	struct content_protection *content_protection;
187
188 	enum weston_output_flow output_flow;
189+	struct weston_output *prefer_output;
190 };
191
192 struct weston_buffer {
193diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
194index 1625be2..9d24017 100644
195--- a/libweston/backend-drm/drm-internal.h
196+++ b/libweston/backend-drm/drm-internal.h
197@@ -115,6 +115,9 @@
198
199 #define DRM_RESIZE_FREEZE_MS    600
200
201+#define WESTON_DRM_CONFIG_FILE	"/tmp/.weston_drm.conf"
202+#define DRM_CONFIG_UPDATE_MS	100
203+
204 /**
205  * Represents the values of an enum-type KMS property
206  */
207@@ -348,6 +351,9 @@ struct drm_backend {
208 	int virtual_height;
209
210 	bool mirror_mode;
211+
212+	struct wl_event_source *config_timer;
213+	struct stat config_stat;
214 };
215
216 struct drm_mode {
217@@ -626,6 +632,9 @@ struct drm_output {
218 	bool is_mirror;
219
220 	pixman_box32_t plane_bounds;
221+
222+	uint32_t original_transform;
223+	int64_t last_resize_ms;
224 };
225
226 static inline struct drm_head *
227@@ -708,6 +717,13 @@ drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list);
228 void
229 drm_output_print_modes(struct drm_output *output);
230
231+struct drm_mode *
232+drm_output_choose_initial_mode(struct drm_backend *backend,
233+			       struct drm_output *output,
234+			       enum weston_drm_backend_output_mode mode,
235+			       const char *modeline,
236+			       const drmModeModeInfo *current_mode);
237+
238 int
239 drm_output_set_mode(struct weston_output *base,
240 		    enum weston_drm_backend_output_mode mode,
241diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
242index 696df68..1d6f7db 100644
243--- a/libweston/backend-drm/drm.c
244+++ b/libweston/backend-drm/drm.c
245@@ -40,6 +40,7 @@
246 #include <linux/vt.h>
247 #include <assert.h>
248 #include <sys/mman.h>
249+#include <sys/stat.h>
250 #include <time.h>
251
252 #include <xf86drm.h>
253@@ -74,6 +75,8 @@
254
255 static const char default_seat[] = "seat0";
256
257+static int config_timer_handler(void *data);
258+
259 static inline bool
260 drm_head_is_external(struct drm_head *head)
261 {
262@@ -205,7 +208,7 @@ close_src:
263 static void
264 drm_backend_update_outputs(struct drm_backend *b)
265 {
266-	struct weston_output *primary;
267+	struct weston_output *base, *primary;
268
269 	if (!b->primary_head)
270 		return;
271@@ -493,6 +496,12 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
272 		return;
273 	}
274
275+	if (!sec && !usec) {
276+		weston_output_finish_frame(&output->base, NULL,
277+					   WP_PRESENTATION_FEEDBACK_INVALID);
278+		return;
279+	}
280+
281 	ts.tv_sec = sec;
282 	ts.tv_nsec = usec * 1000;
283
284@@ -671,8 +680,8 @@ out:
285 		return;
286 	}
287
288-	sw = fb->width;
289-	sh = fb->height;
290+	sw = fb->width * output->base.down_scale;
291+	sh = fb->height * output->base.down_scale;
292
293 	dx = output->plane_bounds.x1;
294 	dy = output->plane_bounds.y1;
295@@ -823,7 +832,8 @@ drm_output_repaint(struct weston_output *output_base,
296
297 	weston_compositor_read_presentation_clock(b->compositor, &now);
298 	now_ms = timespec_to_msec(&now);
299-	if (now_ms < b->last_resize_ms + b->resize_freeze_ms) {
300+	if (now_ms < b->last_resize_ms + b->resize_freeze_ms ||
301+	    now_ms < output->last_resize_ms + b->resize_freeze_ms) {
302 		/* Resize fullscreen/maxmium views(not always success) */
303 		if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS)
304 			wl_signal_emit(&b->compositor->output_resized_signal,
305@@ -832,7 +842,7 @@ drm_output_repaint(struct weston_output *output_base,
306 		weston_output_damage(output_base);
307 		weston_output_finish_frame(output_base, NULL,
308 					   WP_PRESENTATION_FEEDBACK_INVALID);
309-		return 0;
310+		goto not_repainted;
311 	}
312
313 	/* If planes have been disabled in the core, we might not have
314@@ -860,7 +870,8 @@ drm_output_repaint(struct weston_output *output_base,
315
316 err:
317 	drm_output_state_free(state);
318-	return 0;
319+not_repainted:
320+	return 1;
321 }
322
323 /* Determine the type of vblank synchronization to use for the output.
324@@ -2213,6 +2224,8 @@ drm_output_enable(struct weston_output *base)
325 	output->base.switch_mode = drm_output_switch_mode;
326 	output->base.set_gamma = drm_output_set_gamma;
327
328+	output->original_transform = output->base.transform;
329+
330 	output->state_invalid = true;
331
332 	weston_log("Output %s (crtc %d) video modes:\n",
333@@ -3061,6 +3074,7 @@ drm_destroy(struct weston_compositor *ec)
334
335 	udev_input_destroy(&b->input);
336
337+	wl_event_source_remove(b->config_timer);
338 	wl_event_source_remove(b->hotplug_timer);
339 	wl_event_source_remove(b->udev_drm_source);
340 	wl_event_source_remove(b->drm_source);
341@@ -3483,6 +3497,10 @@ output_create_notify(struct wl_listener *listener, void *data)
342 					     output_create_listener);
343
344 	drm_backend_update_outputs(b);
345+
346+	/* Force reload config */
347+	memset(&b->config_stat, 0, sizeof(b->config_stat));
348+	config_timer_handler(b);
349 }
350
351 static const struct weston_drm_output_api api = {
352@@ -3491,6 +3509,236 @@ static const struct weston_drm_output_api api = {
353 	drm_output_set_seat,
354 };
355
356+static void
357+drm_output_rotate(struct drm_output *output, int rotate)
358+{
359+	struct drm_backend *b = to_drm_backend(output->base.compositor);
360+	uint32_t transform = output->original_transform;
361+	struct timespec now;
362+
363+	/* Hacky way to rotate transform */
364+	transform = (transform / 4) * 4 + (transform + rotate) % 4;
365+
366+	if (output->base.transform == transform)
367+		return;
368+
369+	/* Freeze output when rotating */
370+	weston_compositor_read_presentation_clock(b->compositor, &now);
371+	output->last_resize_ms = timespec_to_msec(&now);
372+
373+	weston_output_set_transform(&output->base, transform);
374+}
375+
376+static void
377+drm_output_modeset(struct drm_output *output, const char *modeline)
378+{
379+	struct drm_backend *b = to_drm_backend(output->base.compositor);
380+	struct drm_head *head =
381+		to_drm_head(weston_output_get_first_head(&output->base));
382+	struct drm_mode *mode;
383+	struct timespec now;
384+
385+	/* Unable to switch mode, let's retry later */
386+	if (output->page_flip_pending || output->atomic_complete_pending) {
387+		memset(&b->config_stat, 0, sizeof(b->config_stat));
388+		return;
389+	}
390+
391+	mode = drm_output_choose_initial_mode(b, output,
392+					      WESTON_DRM_BACKEND_OUTPUT_PREFERRED,
393+					      modeline,
394+					      &head->inherited_mode);
395+
396+	weston_output_mode_set_native(&output->base, &mode->base,
397+				      output->base.current_scale);
398+	weston_output_damage(&output->base);
399+
400+	mode = to_drm_mode(output->base.current_mode);
401+
402+	weston_log("Output %s changed to %dx%d@%d for mode(%s)\n",
403+		   output->base.name,
404+		   mode->mode_info.hdisplay, mode->mode_info.vdisplay,
405+		   mode->mode_info.vrefresh,
406+		   modeline);
407+
408+	weston_compositor_read_presentation_clock(b->compositor, &now);
409+	b->last_update_ms = timespec_to_msec(&now);
410+}
411+
412+static void
413+drm_output_set_size(struct drm_output *output, const int w, const int h)
414+{
415+	struct drm_backend *b = to_drm_backend(output->base.compositor);
416+	struct weston_mode *mode;
417+	struct timespec now;
418+
419+	if (output->base.fixed_size &&
420+	    output->base.current_mode->width == w &&
421+	    output->base.current_mode->height == h)
422+		return;
423+
424+	wl_list_for_each(mode, &output->base.mode_list, link) {
425+		mode->width = w;
426+		mode->height = h;
427+	}
428+
429+	output->base.fixed_size = true;
430+
431+	/* Freeze output when resizing */
432+	weston_compositor_read_presentation_clock(b->compositor, &now);
433+	output->last_resize_ms = timespec_to_msec(&now);
434+
435+	weston_output_set_transform(&output->base, output->base.transform);
436+
437+	if (b->use_pixman) {
438+		drm_output_fini_pixman(output);
439+		if (drm_output_init_pixman(output, b) < 0)
440+			weston_log("failed to init output pixman state with "
441+				   "new mode\n");
442+	} else {
443+		drm_output_fini_egl(output);
444+		if (drm_output_init_egl(output, b) < 0)
445+			weston_log("failed to init output egl state with "
446+				   "new mode");
447+	}
448+
449+	drm_output_print_modes(output);
450+}
451+
452+static void
453+config_handle_output(struct drm_backend *b, const char *name,
454+		     const char *config)
455+{
456+	struct drm_output *output;
457+	bool is_all = !strcmp(name, "all");
458+
459+	wl_list_for_each(output, &b->compositor->output_list, base.link) {
460+		if (!is_all && strcmp(name, output->base.name))
461+			continue;
462+
463+		if (!strcmp(config, "primary")) {
464+			setenv("WESTON_DRM_PRIMARY", name, 1);
465+			hotplug_timer_handler(b);
466+		} else if (!strcmp(config, "prefer")) {
467+			b->compositor->prefer_output = &output->base;
468+		} else if (!strncmp(config, "rotate", strlen("rotate"))) {
469+			int rotate = atoi(config + strlen("rotate")) / 90;
470+			drm_output_rotate(output, rotate);
471+		} else if (!strncmp(config, "mode=", strlen("mode="))) {
472+			drm_output_modeset(output, config + strlen("mode="));
473+		} else if (!strcmp(config, "freeze")) {
474+			output->base.freezing = true;
475+		} else if (!strcmp(config, "off")) {
476+			output->base.freezing = true;
477+			if (!output->virtual)
478+				drm_set_dpms(&output->base, WESTON_DPMS_OFF);
479+		} else if (!strcmp(config, "unfreeze") ||
480+			   !strcmp(config, "on")) {
481+			if (!output->base.freezing)
482+				continue;
483+
484+			output->base.freezing = false;
485+
486+			if (!output->virtual)
487+				drm_set_dpms(&output->base, WESTON_DPMS_ON);
488+
489+			drm_output_update_complete(output, 0, 0, 0);
490+			output->page_flip_pending = false;
491+			output->atomic_complete_pending = false;
492+
493+			weston_output_damage(&output->base);
494+		} else if (!strncmp(config, "down-scale=",
495+				    strlen("down-scale="))) {
496+			double down_scale =
497+				atof(config + strlen("down-scale="));
498+			if (down_scale == output->base.down_scale ||
499+			    down_scale < 0.125 || down_scale > 1)
500+				continue;
501+
502+			output->base.down_scale = down_scale;
503+			weston_output_damage(&output->base);
504+		} else if (!strncmp(config, "size=", strlen("size="))) {
505+			int w, h;
506+
507+			if (sscanf(config, "size=%dx%d", &w, &h) != 2)
508+				continue;
509+
510+			drm_output_set_size(output, w, h);
511+		} else if (!strncmp(config, "pos=", strlen("pos="))) {
512+			int x, y;
513+
514+			if (sscanf(config, "pos=%d,%d", &x, &y) != 2)
515+				continue;
516+
517+			weston_output_move(&output->base, x, y);
518+			output->base.fixed_position = true;
519+
520+			weston_compositor_reflow_outputs(b->compositor);
521+		} else if (!strncmp(config, "rect=", strlen("rect="))) {
522+			int x1, y1, x2, y2, ret;
523+
524+			ret = sscanf(config, "rect=<%d,%d,%d,%d>",
525+				     &x1, &y1, &x2, &y2);
526+			if (ret != 4)
527+				continue;
528+
529+			output->plane_bounds.x1 = x1;
530+			output->plane_bounds.y1 = y1;
531+			output->plane_bounds.x2 = x2;
532+			output->plane_bounds.y2 = y2;
533+			weston_output_schedule_repaint(&output->base);
534+		}
535+	}
536+}
537+
538+static int
539+config_timer_handler(void *data)
540+{
541+#define MAX_CONF_LEN 32
542+#define _STR(x) #x
543+#define STR(x) _STR(x)
544+
545+	struct drm_backend *b = data;
546+	struct stat st;
547+	char type[MAX_CONF_LEN], key[MAX_CONF_LEN], value[MAX_CONF_LEN];
548+	const char *config_file;
549+	FILE *conf_fp;
550+
551+	wl_event_source_timer_update(b->config_timer, DRM_CONFIG_UPDATE_MS);
552+
553+	config_file = getenv("WESTON_DRM_CONFIG");
554+	if (!config_file)
555+		config_file = WESTON_DRM_CONFIG_FILE;
556+
557+	if (stat(config_file, &st) < 0)
558+		return 0;
559+
560+	if (st.st_mtime && !memcmp(&st, &b->config_stat, sizeof(st)))
561+		return 0;
562+
563+	conf_fp = fopen(config_file, "r");
564+	if (!conf_fp)
565+		return 0;
566+
567+	/**
568+	 * Parse configs, formated with <type>:<key>:<value>
569+	 * For example: "output:all:rotate90"
570+	 */
571+	while (3 == fscanf(conf_fp,
572+			   "%" STR(MAX_CONF_LEN) "[^:]:"
573+			   "%" STR(MAX_CONF_LEN) "[^:]:"
574+			   "%" STR(MAX_CONF_LEN) "s ", type, key, value)) {
575+		if (!strcmp(type, "output"))
576+			config_handle_output(b, key, value);
577+	}
578+
579+	fclose(conf_fp);
580+
581+	stat(config_file, &st);
582+	b->config_stat = st;
583+	return 0;
584+}
585+
586 enum drm_head_mode {
587 	DRM_HEAD_MODE_DEFAULT,
588 	DRM_HEAD_MODE_PRIMARY,
589@@ -3830,6 +4078,10 @@ drm_backend_create(struct weston_compositor *compositor,
590 	b->hotplug_timer =
591 		wl_event_loop_add_timer(loop, hotplug_timer_handler, b);
592
593+	b->config_timer =
594+		wl_event_loop_add_timer(loop, config_timer_handler, b);
595+	config_timer_handler(b);
596+
597 	return b;
598
599 err_udev_monitor:
600diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c
601index e7bbfa5..ff7421d 100644
602--- a/libweston/backend-drm/modes.c
603+++ b/libweston/backend-drm/modes.c
604@@ -385,15 +385,19 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
605 	if (mode == NULL)
606 		return NULL;
607
608-	mode->base.flags = 0;
609-	mode->base.width = info->hdisplay;
610-	mode->base.height = info->vdisplay;
611-
612-	if (b->virtual_width && b->virtual_height) {
613+	if (output->base.fixed_size) {
614+		mode->base.width = output->base.width;
615+		mode->base.height = output->base.height;
616+	} else if (b->virtual_width && b->virtual_height) {
617 		mode->base.width = b->virtual_width;
618 		mode->base.height = b->virtual_height;
619+	} else {
620+		mode->base.width = info->hdisplay;
621+		mode->base.height = info->vdisplay;
622 	}
623
624+	mode->base.flags = 0;
625+
626 	mode->base.refresh = drm_refresh_rate_mHz(info);
627 	mode->mode_info = *info;
628 	mode->blob_id = 0;
629@@ -566,7 +570,7 @@ update_head_from_connector(struct drm_head *head)
630  * @param current_mode Mode currently being displayed on this output
631  * @returns A mode from the output's mode list, or NULL if none available
632  */
633-static struct drm_mode *
634+struct drm_mode *
635 drm_output_choose_initial_mode(struct drm_backend *backend,
636 			       struct drm_output *output,
637 			       enum weston_drm_backend_output_mode mode,
638@@ -619,8 +623,8 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
639 	}
640
641 	wl_list_for_each_reverse(drm_mode, &output->base.mode_list, base.link) {
642-		if (width == drm_mode->base.width &&
643-		    height == drm_mode->base.height &&
644+		if (width == drm_mode->mode_info.hdisplay &&
645+		    height == drm_mode->mode_info.vdisplay &&
646 		    (refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) {
647 			if (!backend->aspect_ratio_supported ||
648 			    aspect_ratio == drm_mode->base.aspect_ratio)
649diff --git a/libweston/compositor.c b/libweston/compositor.c
650index 7680445..0aa9d1a 100644
651--- a/libweston/compositor.c
652+++ b/libweston/compositor.c
653@@ -3009,6 +3009,11 @@ weston_output_repaint(struct weston_output *output, void *repaint_data)
654 static void
655 weston_output_schedule_repaint_reset(struct weston_output *output)
656 {
657+	if (output->idle_repaint_source) {
658+		wl_event_source_remove(output->idle_repaint_source);
659+		output->idle_repaint_source = NULL;
660+	}
661+
662 	output->repaint_status = REPAINT_NOT_SCHEDULED;
663 	TL_POINT(output->compositor, "core_repaint_exit_loop",
664 		 TLP_OUTPUT(output), TLP_END);
665@@ -3022,6 +3027,11 @@ weston_output_maybe_repaint(struct weston_output *output, struct timespec *now,
666 	int ret = 0;
667 	int64_t msec_to_repaint;
668
669+	/* If we're sleeping, drop the repaint machinery entirely; we will
670+	 * explicitly repaint it when we come back. */
671+	if (output->freezing)
672+		goto err;
673+
674 	/* We're not ready yet; come back to make a decision later. */
675 	if (output->repaint_status != REPAINT_SCHEDULED)
676 		return ret;
677@@ -3048,11 +3058,11 @@ weston_output_maybe_repaint(struct weston_output *output, struct timespec *now,
678 	 * output. */
679 	ret = weston_output_repaint(output, repaint_data);
680 	weston_compositor_read_presentation_clock(compositor, now);
681-	if (ret != 0)
682+	if (ret < 0)
683 		goto err;
684
685-	output->repainted = true;
686-	return ret;
687+	output->repainted = !ret;
688+	return 0;
689
690 err:
691 	weston_output_schedule_repaint_reset(output);
692@@ -3105,7 +3115,7 @@ output_repaint_timer_handler(void *data)
693 	struct weston_output *output;
694 	struct timespec now;
695 	void *repaint_data = NULL;
696-	int ret = 0;
697+	int ret = 0, repainted = 0;
698
699 	if (!access(getenv("WESTON_FREEZE_DISPLAY") ? : "", F_OK)) {
700 		usleep(DEFAULT_REPAINT_WINDOW * 1000);
701@@ -3122,9 +3132,11 @@ output_repaint_timer_handler(void *data)
702 		ret = weston_output_maybe_repaint(output, &now, repaint_data);
703 		if (ret)
704 			break;
705+
706+		repainted |= output->repainted;
707 	}
708
709-	if (ret == 0) {
710+	if (ret == 0 && repainted) {
711 		if (compositor->backend->repaint_flush)
712 			ret = compositor->backend->repaint_flush(compositor,
713 							 repaint_data);
714@@ -6099,7 +6111,7 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor)
715 		wl_list_for_each(head, &output->head_list, output_link)
716 			weston_head_update_global(head);
717
718-		if (!weston_output_valid(output))
719+		if (!weston_output_valid(output) || output->fixed_position)
720 			continue;
721
722 		x = next_x;
723@@ -6546,6 +6558,9 @@ weston_output_set_transform(struct weston_output *output,
724
725 	weston_compositor_reflow_outputs(output->compositor);
726
727+	wl_signal_emit(&output->compositor->output_resized_signal,
728+		       output);
729+
730 	/* Notify clients of the change for output transform. */
731 	wl_list_for_each(head, &output->head_list, output_link) {
732 		wl_resource_for_each(resource, &head->resource_list) {
733@@ -6678,6 +6693,8 @@ weston_output_init(struct weston_output *output,
734 	/* Can't use -1 on uint32_t and 0 is valid enum value */
735 	output->transform = UINT32_MAX;
736
737+	output->down_scale = 1.0f;
738+
739 	pixman_region32_init(&output->region);
740 	wl_list_init(&output->mode_list);
741 }
742@@ -6769,11 +6786,8 @@ weston_output_create_heads_string(struct weston_output *output)
743 WL_EXPORT int
744 weston_output_enable(struct weston_output *output)
745 {
746-	struct weston_compositor *c = output->compositor;
747-	struct weston_output *iterator;
748 	struct weston_head *head;
749 	char *head_names;
750-	int x = 0, y = 0;
751
752 	if (output->enabled) {
753 		weston_log("Error: attempt to enable an enabled output '%s'\n",
754@@ -6798,20 +6812,17 @@ weston_output_enable(struct weston_output *output)
755 		assert(head->model);
756 	}
757
758-	iterator = container_of(c->output_list.prev,
759-				struct weston_output, link);
760-
761-	if (!wl_list_empty(&c->output_list))
762-		x = iterator->x + iterator->width;
763-
764 	/* Make sure the scale is set up */
765 	assert(output->scale);
766
767 	/* Make sure we have a transform set */
768 	assert(output->transform != UINT32_MAX);
769
770-	output->x = x;
771-	output->y = y;
772+	if (!output->fixed_position) {
773+		output->x = 0;
774+		output->y = 0;
775+	}
776+
777 	output->original_scale = output->scale;
778
779 	wl_signal_init(&output->frame_signal);
780@@ -6820,8 +6831,7 @@ weston_output_enable(struct weston_output *output)
781 	weston_output_transform_scale_init(output, output->transform, output->scale);
782 	weston_output_init_zoom(output);
783
784-	weston_output_init_geometry(output, x, y);
785-	weston_output_damage(output);
786+	weston_output_init_geometry(output, output->x, output->y);
787
788 	wl_list_init(&output->animation_list);
789 	wl_list_init(&output->feedback_list);
790@@ -6848,6 +6858,8 @@ weston_output_enable(struct weston_output *output)
791 		   output->name, head_names);
792 	free(head_names);
793
794+	weston_compositor_reflow_outputs(output->compositor);
795+
796 	return 0;
797 }
798
799diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c
800index 754adce..d7182e7 100644
801--- a/libweston/pixman-renderer.c
802+++ b/libweston/pixman-renderer.c
803@@ -158,6 +158,9 @@ pixman_renderer_compute_transform(pixman_transform_t *transform_out,
804 	   specified buffer transform/scale */
805 	matrix = output->inverse_matrix;
806
807+	weston_matrix_scale(&matrix,
808+			    1 / output->down_scale, 1 / output->down_scale, 1);
809+
810 	if (ev->transform.enabled) {
811 		weston_matrix_multiply(&matrix, &ev->transform.inverse);
812 	} else {
813diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c
814index 6e04baa..f853149 100644
815--- a/libweston/renderer-gl/gl-renderer.c
816+++ b/libweston/renderer-gl/gl-renderer.c
817@@ -1720,6 +1720,10 @@ gl_renderer_repaint_output(struct weston_output *output,
818
819 	/* Calculate the global GL matrix */
820 	go->output_matrix = output->matrix;
821+
822+	weston_matrix_scale(&go->output_matrix,
823+			    output->down_scale, output->down_scale, 1);
824+
825 	weston_matrix_translate(&go->output_matrix,
826 				-(output->current_mode->width / 2.0),
827 				-(output->current_mode->height / 2.0), 0);
828--
8292.20.1
830
831