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