1From 267f9e075009cac9b4ac4dbd24d0d3cd2621f041 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/93] 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          | 265 ++++++++++++++++++++++++++-
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/pixman-renderer.c          |   3 +
42 libweston/renderer-gl/gl-renderer.c  |   4 +
43 11 files changed, 437 insertions(+), 26 deletions(-)
44
45diff --git a/compositor/main.c b/compositor/main.c
46index dcfe85b..d65f8aa 100644
47--- a/compositor/main.c
48+++ b/compositor/main.c
49@@ -2080,6 +2080,20 @@ drm_backend_output_configure(struct weston_output *output,
50 	}
51 	free(s);
52
53+	weston_config_section_get_string(section, "pos", &s, NULL);
54+	if (s) {
55+		if (sscanf(s, "%d,%d", &output->x, &output->y) == 2)
56+			output->fixed_position = true;
57+		free(s);
58+	}
59+
60+	weston_config_section_get_string(section, "size", &s, NULL);
61+	if (s) {
62+		if (sscanf(s, "%dx%d", &output->width, &output->height) == 2)
63+			output->fixed_size = true;
64+		free(s);
65+	}
66+
67 	if (api->set_mode(output, mode, modeline) < 0) {
68 		weston_log("Cannot configure an output using weston_drm_output_api.\n");
69 		free(modeline);
70diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
71index 7aa787c..be36b70 100644
72--- a/desktop-shell/shell.c
73+++ b/desktop-shell/shell.c
74@@ -3831,7 +3831,7 @@ weston_view_set_initial_position(struct weston_view *view,
75 	int ix = 0, iy = 0;
76 	int32_t range_x, range_y;
77 	int32_t x, y;
78-	struct weston_output *output, *target_output = NULL;
79+	struct weston_output *output, *target_output = NULL, *prefer_output = NULL;
80 	struct weston_seat *seat;
81 	pixman_rectangle32_t area;
82
83@@ -3856,16 +3856,20 @@ weston_view_set_initial_position(struct weston_view *view,
84 		}
85 	}
86
87-	wl_list_for_each(output, &compositor->output_list, link) {
88+	wl_list_for_each_reverse(output, &compositor->output_list, link) {
89 		if (output->unavailable)
90 			continue;
91
92-		if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) {
93+		if (output == compositor->prefer_output)
94+			prefer_output = output;
95+
96+		if (pixman_region32_contains_point(&output->region, ix, iy, NULL))
97 			target_output = output;
98-			break;
99-		}
100 	}
101
102+	if (prefer_output)
103+		target_output = prefer_output;
104+
105 	if (!target_output) {
106 		weston_view_set_position(view, 10 + random() % 400,
107 					 10 + random() % 400);
108@@ -4382,6 +4386,41 @@ shell_resize_surface_to_output(struct desktop_shell *shell,
109 					output->height);
110 }
111
112+static void
113+handle_output_resize_layer(struct desktop_shell *shell,
114+			   struct weston_layer *layer, void *data)
115+{
116+	struct weston_output *output = data;
117+	struct weston_view *view;
118+
119+	wl_list_for_each(view, &layer->view_list.link, layer_link.link) {
120+		struct weston_desktop_surface *desktop_surface;
121+		struct shell_surface *shsurf;
122+		bool dirty = false;
123+
124+		if (view->output != output)
125+			continue;
126+
127+		shsurf = get_shell_surface(view->surface);
128+		if (!shsurf)
129+			continue;
130+
131+		desktop_surface = shsurf->desktop_surface;
132+		if (weston_desktop_surface_get_fullscreen(desktop_surface)) {
133+			set_fullscreen(shsurf, true, output);
134+			dirty = true;
135+		}
136+		if (weston_desktop_surface_get_maximized(desktop_surface)) {
137+			set_maximized(shsurf, true);
138+			dirty = true;
139+		}
140+
141+		if (dirty) {
142+			weston_view_geometry_dirty(view);
143+			weston_surface_damage(view->surface);
144+		}
145+	}
146+}
147
148 static void
149 handle_output_resized(struct wl_listener *listener, void *data)
150@@ -4391,8 +4430,13 @@ handle_output_resized(struct wl_listener *listener, void *data)
151 	struct weston_output *output = (struct weston_output *)data;
152 	struct shell_output *sh_output = find_shell_output_from_weston_output(shell, output);
153
154+	if (shell->lock_surface)
155+		shell->lock_surface->committed(shell->lock_surface, 0, 0);
156+
157 	shell_resize_surface_to_output(shell, sh_output->background_surface, output);
158 	shell_resize_surface_to_output(shell, sh_output->panel_surface, output);
159+
160+	shell_for_each_layer(shell, handle_output_resize_layer, data);
161 }
162
163 static void
164@@ -4441,7 +4485,9 @@ handle_output_move_layer(struct desktop_shell *shell,
165
166 		x = view->geometry.x + output->move_x;
167 		y = view->geometry.y + output->move_y;
168-		weston_view_set_position(view, x, y);
169+
170+		if (pixman_region32_contains_point(&output->region, x, y, NULL))
171+			weston_view_set_position(view, x, y);
172 	}
173 }
174
175diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
176index a8ec105..84b16fe 100644
177--- a/include/libweston/libweston.h
178+++ b/include/libweston/libweston.h
179@@ -575,6 +575,12 @@ struct weston_output {
180 			    struct weston_head *head);
181
182 	bool unavailable;
183+	bool freezing;
184+
185+	bool fixed_position;
186+	bool fixed_size;
187+
188+	double down_scale;
189 };
190 #define weston_output_valid(o) \
191 	((o) && !(o)->destroying && !(o)->unavailable)
192@@ -1333,6 +1339,7 @@ struct weston_compositor {
193 	bool warned_about_unmapped_surface_or_view;
194
195 	enum weston_output_flow output_flow;
196+	struct weston_output *prefer_output;
197 };
198
199 struct weston_solid_buffer_values {
200diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h
201index 037c937..532593e 100644
202--- a/libweston/backend-drm/drm-internal.h
203+++ b/libweston/backend-drm/drm-internal.h
204@@ -115,6 +115,9 @@
205
206 #define DRM_RESIZE_FREEZE_MS    600
207
208+#define WESTON_DRM_CONFIG_FILE	"/tmp/.weston_drm.conf"
209+#define DRM_CONFIG_UPDATE_MS	100
210+
211 /**
212  * Represents the values of an enum-type KMS property
213  */
214@@ -364,6 +367,9 @@ struct drm_backend {
215 	int virtual_height;
216
217 	bool mirror_mode;
218+
219+	struct wl_event_source *config_timer;
220+	struct stat config_stat;
221 };
222
223 struct drm_mode {
224@@ -641,6 +647,9 @@ struct drm_output {
225 	bool is_mirror;
226
227 	pixman_box32_t plane_bounds;
228+
229+	uint32_t original_transform;
230+	int64_t last_resize_ms;
231 };
232
233 void
234@@ -739,6 +748,13 @@ drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list);
235 void
236 drm_output_print_modes(struct drm_output *output);
237
238+struct drm_mode *
239+drm_output_choose_initial_mode(struct drm_device *device,
240+			       struct drm_output *output,
241+			       enum weston_drm_backend_output_mode mode,
242+			       const char *modeline,
243+			       const drmModeModeInfo *current_mode);
244+
245 int
246 drm_output_set_mode(struct weston_output *base,
247 		    enum weston_drm_backend_output_mode mode,
248diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c
249index d366e2b..23dbe38 100644
250--- a/libweston/backend-drm/drm.c
251+++ b/libweston/backend-drm/drm.c
252@@ -41,6 +41,7 @@
253 #include <linux/vt.h>
254 #include <assert.h>
255 #include <sys/mman.h>
256+#include <sys/stat.h>
257 #include <time.h>
258
259 #include <xf86drm.h>
260@@ -75,6 +76,8 @@
261
262 static const char default_seat[] = "seat0";
263
264+static int config_timer_handler(void *data);
265+
266 static inline bool
267 drm_head_is_external(struct drm_head *head)
268 {
269@@ -508,6 +511,12 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
270 		return;
271 	}
272
273+	if (!sec && !usec) {
274+		weston_output_finish_frame(&output->base, NULL,
275+					   WP_PRESENTATION_FEEDBACK_INVALID);
276+		return;
277+	}
278+
279 	ts.tv_sec = sec;
280 	ts.tv_nsec = usec * 1000;
281
282@@ -687,8 +696,8 @@ out:
283 		return;
284 	}
285
286-	sw = fb->width;
287-	sh = fb->height;
288+	sw = fb->width * output->base.down_scale;
289+	sh = fb->height * output->base.down_scale;
290
291 	dx = output->plane_bounds.x1;
292 	dy = output->plane_bounds.y1;
293@@ -830,7 +839,8 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
294
295 	weston_compositor_read_presentation_clock(b->compositor, &now);
296 	now_ms = timespec_to_msec(&now);
297-	if (now_ms < b->last_resize_ms + b->resize_freeze_ms) {
298+	if (now_ms < b->last_resize_ms + b->resize_freeze_ms ||
299+	    now_ms < output->last_resize_ms + b->resize_freeze_ms) {
300 		/* Resize fullscreen/maxmium views(not always success) */
301 		if (now_ms < b->last_resize_ms + DRM_RESIZE_FREEZE_MS)
302 			wl_signal_emit(&b->compositor->output_resized_signal,
303@@ -839,7 +849,7 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
304 		weston_output_damage(output_base);
305 		weston_output_finish_frame(output_base, NULL,
306 					   WP_PRESENTATION_FEEDBACK_INVALID);
307-		return 0;
308+		goto not_repainted;
309 	}
310
311 	/* If planes have been disabled in the core, we might not have
312@@ -870,7 +880,8 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage)
313
314 err:
315 	drm_output_state_free(state);
316-	return 0;
317+not_repainted:
318+	return 1;
319 }
320
321 /* Determine the type of vblank synchronization to use for the output.
322@@ -2294,6 +2305,8 @@ drm_output_enable(struct weston_output *base)
323 	output->base.switch_mode = drm_output_switch_mode;
324 	output->base.set_gamma = drm_output_set_gamma;
325
326+	output->original_transform = output->base.transform;
327+
328 	output->state_invalid = true;
329
330 	weston_log("Output %s (crtc %d) video modes:\n",
331@@ -3186,6 +3199,7 @@ drm_destroy(struct weston_compositor *ec)
332
333 	udev_input_destroy(&b->input);
334
335+	wl_event_source_remove(b->config_timer);
336 	wl_event_source_remove(b->hotplug_timer);
337 	wl_event_source_remove(b->udev_drm_source);
338 	wl_event_source_remove(b->drm_source);
339@@ -3606,6 +3620,10 @@ output_create_notify(struct wl_listener *listener, void *data)
340 					     output_create_listener);
341
342 	drm_backend_update_outputs(b);
343+
344+	/* Force reload config */
345+	memset(&b->config_stat, 0, sizeof(b->config_stat));
346+	config_timer_handler(b);
347 }
348
349 static const struct weston_drm_output_api api = {
350@@ -3615,6 +3633,239 @@ static const struct weston_drm_output_api api = {
351 	drm_output_set_max_bpc,
352 };
353
354+static void
355+drm_output_rotate(struct drm_output *output, int rotate)
356+{
357+	struct drm_backend *b = to_drm_backend(output->base.compositor);
358+	uint32_t transform = output->original_transform;
359+	struct timespec now;
360+
361+	/* Hacky way to rotate transform */
362+	transform = (transform / 4) * 4 + (transform + rotate) % 4;
363+
364+	if (output->base.transform == transform)
365+		return;
366+
367+	/* Freeze output when rotating */
368+	weston_compositor_read_presentation_clock(b->compositor, &now);
369+	output->last_resize_ms = timespec_to_msec(&now);
370+
371+	weston_output_set_transform(&output->base, transform);
372+}
373+
374+static void
375+drm_output_modeset(struct drm_output *output, const char *modeline)
376+{
377+	struct drm_backend *b = to_drm_backend(output->base.compositor);
378+	struct drm_head *head =
379+		to_drm_head(weston_output_get_first_head(&output->base));
380+	struct drm_mode *mode;
381+	struct timespec now;
382+
383+	/* Unable to switch mode, let's retry later */
384+	if (output->page_flip_pending || output->atomic_complete_pending) {
385+		memset(&b->config_stat, 0, sizeof(b->config_stat));
386+		return;
387+	}
388+
389+	mode = drm_output_choose_initial_mode(b->drm, output,
390+					      WESTON_DRM_BACKEND_OUTPUT_PREFERRED,
391+					      modeline,
392+					      &head->inherited_mode);
393+
394+	weston_output_mode_set_native(&output->base, &mode->base,
395+				      output->base.current_scale);
396+	weston_output_damage(&output->base);
397+
398+	mode = to_drm_mode(output->base.current_mode);
399+
400+	weston_log("Output %s changed to %dx%d@%d for mode(%s)\n",
401+		   output->base.name,
402+		   mode->mode_info.hdisplay, mode->mode_info.vdisplay,
403+		   mode->mode_info.vrefresh,
404+		   modeline);
405+
406+	weston_compositor_read_presentation_clock(b->compositor, &now);
407+	b->last_update_ms = timespec_to_msec(&now);
408+}
409+
410+static void
411+drm_output_set_size(struct drm_output *output, const int w, const int h)
412+{
413+	struct drm_backend *b = to_drm_backend(output->base.compositor);
414+	struct weston_mode *mode;
415+	struct timespec now;
416+
417+	if (output->base.fixed_size &&
418+	    output->base.current_mode->width == w &&
419+	    output->base.current_mode->height == h)
420+		return;
421+
422+	wl_list_for_each(mode, &output->base.mode_list, link) {
423+		mode->width = w;
424+		mode->height = h;
425+	}
426+
427+	output->base.fixed_size = true;
428+
429+	/* Freeze output when resizing */
430+	weston_compositor_read_presentation_clock(b->compositor, &now);
431+	output->last_resize_ms = timespec_to_msec(&now);
432+
433+	weston_output_set_transform(&output->base, output->base.transform);
434+
435+	if (b->use_pixman) {
436+		drm_output_fini_pixman(output);
437+		if (drm_output_init_pixman(output, b) < 0)
438+			weston_log("failed to init output pixman state with "
439+				   "new mode\n");
440+	} else {
441+		drm_output_fini_egl(output);
442+		if (drm_output_init_egl(output, b) < 0)
443+			weston_log("failed to init output egl state with "
444+				   "new mode");
445+	}
446+
447+	drm_output_print_modes(output);
448+}
449+
450+static void
451+config_handle_output(struct drm_backend *b, const char *name,
452+		     const char *config)
453+{
454+	struct drm_output *output;
455+	bool is_all = !strcmp(name, "all");
456+
457+	wl_list_for_each(output, &b->compositor->output_list, base.link) {
458+		if (!is_all && strcmp(name, output->base.name))
459+			continue;
460+
461+		if (!strcmp(config, "primary")) {
462+			setenv("WESTON_DRM_PRIMARY", name, 1);
463+			hotplug_timer_handler(b->drm);
464+		} else if (!strcmp(config, "prefer")) {
465+			b->compositor->prefer_output = &output->base;
466+		} else if (!strncmp(config, "rotate", strlen("rotate"))) {
467+			int rotate = atoi(config + strlen("rotate")) / 90;
468+			drm_output_rotate(output, rotate);
469+		} else if (!strncmp(config, "mode=", strlen("mode="))) {
470+			drm_output_modeset(output, config + strlen("mode="));
471+		} else if (!strcmp(config, "freeze")) {
472+			output->base.freezing = true;
473+		} else if (!strcmp(config, "off")) {
474+			output->base.freezing = true;
475+			if (!output->virtual)
476+				drm_set_dpms(&output->base, WESTON_DPMS_OFF);
477+		} else if (!strcmp(config, "unfreeze") ||
478+			   !strcmp(config, "on")) {
479+			if (!output->base.freezing)
480+				continue;
481+
482+			output->base.freezing = false;
483+
484+			if (!output->virtual)
485+				drm_set_dpms(&output->base, WESTON_DPMS_ON);
486+
487+			drm_output_update_complete(output, 0, 0, 0);
488+			output->page_flip_pending = false;
489+			output->atomic_complete_pending = false;
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@@ -3958,6 +4209,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 07743ff..877e7a7 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/pixman-renderer.c b/libweston/pixman-renderer.c
817index 62c2cb1..22e7a42 100644
818--- a/libweston/pixman-renderer.c
819+++ b/libweston/pixman-renderer.c
820@@ -158,6 +158,9 @@ pixman_renderer_compute_transform(pixman_transform_t *transform_out,
821 	   specified buffer transform/scale */
822 	matrix = output->inverse_matrix;
823
824+	weston_matrix_scale(&matrix,
825+			    1 / output->down_scale, 1 / output->down_scale, 1);
826+
827 	if (ev->transform.enabled) {
828 		weston_matrix_multiply(&matrix, &ev->transform.inverse);
829 	} else {
830diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c
831index db4c7b1..3437bf2 100644
832--- a/libweston/renderer-gl/gl-renderer.c
833+++ b/libweston/renderer-gl/gl-renderer.c
834@@ -1685,6 +1685,10 @@ gl_renderer_repaint_output(struct weston_output *output,
835
836 	/* Calculate the global GL matrix */
837 	go->output_matrix = output->matrix;
838+
839+	weston_matrix_scale(&go->output_matrix,
840+			    output->down_scale, output->down_scale, 1);
841+
842 	weston_matrix_translate(&go->output_matrix,
843 				-(output->current_mode->width / 2.0),
844 				-(output->current_mode->height / 2.0), 0);
845--
8462.20.1
847
848