1From 99e73c78fe530b89b110f0369458089591d192a6 Mon Sep 17 00:00:00 2001
2From: Stefan Agner <stefan@agner.ch>
3Date: Sun, 22 Sep 2019 19:40:04 +0200
4Subject: [PATCH 83/93] backend-vnc: add VNC support using Neat VNC library
5
6This adds basic VNC protocol support using the Neat VNC library
7(https://github.com/any1/neatvnc). Neat VNC depends on the AML main
8loop library. The backend makes use of AML's integrated epoll backend
9and connects AML via file descriptor with the Wayland event loop.
10
11This implementation does not support authentication and hardcodes the
12pixel format currently.
13
14Co-authored-by: Philipp Zabel <p.zabel@pengutronix.de>
15Co-authored-by: Rouven Czerwinski <r.czerwinski@pengutronix.de>
16Signed-off-by: Stefan Agner <stefan@agner.ch>
17[r.czerwinski@pengutronix.de:
18 - use new (as of 0.5.0) Neat VNC buffer API, with a buffer pool]
19Signed-off-by: Rouven Czerwinski <r.czerwinski@pengutronix.de>
20[p.zabel@pengutronix.de:
21 - transform repaint damage to output coordinates
22 - transform pointer coordinates into global space
23 - check that outputs and heads are in fact ours, see aab722bb1785..060ef82d9360
24 - track damage across multiple frame buffers
25 - choose pixel format by drm_fourcc, see 8b6c3fe0ad4b
26 - enable ctrl and alt modifiers
27 - fix frame timing to achieve a constant repaint rate
28 - pass initial size explicitly, see f4559b076008
29 - use resize_output with pixman-renderer, see 55d08f9634e8..84b5d0eb4bee
30 - allow configuring the refresh rate]
31Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
32(cherry picked from commit 12f766531002d0086c8cd2ea62a1605db934304a)
33Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
34---
35 compositor/main.c                 |   99 +++
36 include/libweston/backend-vnc.h   |   71 ++
37 include/libweston/libweston.h     |    1 +
38 include/libweston/meson.build     |    1 +
39 libweston/backend-vnc/meson.build |   32 +
40 libweston/backend-vnc/vnc.c       | 1032 +++++++++++++++++++++++++++++
41 libweston/compositor.c            |    1 +
42 libweston/meson.build             |    1 +
43 meson_options.txt                 |    6 +
44 9 files changed, 1244 insertions(+)
45 create mode 100644 include/libweston/backend-vnc.h
46 create mode 100644 libweston/backend-vnc/meson.build
47 create mode 100644 libweston/backend-vnc/vnc.c
48
49diff --git a/compositor/main.c b/compositor/main.c
50index a9ad103..aee3f24 100644
51--- a/compositor/main.c
52+++ b/compositor/main.c
53@@ -61,6 +61,7 @@
54 #include <libweston/backend-drm.h>
55 #include <libweston/backend-headless.h>
56 #include <libweston/backend-rdp.h>
57+#include <libweston/backend-vnc.h>
58 #include <libweston/backend-x11.h>
59 #include <libweston/backend-wayland.h>
60 #include <libweston/windowed-output-api.h>
61@@ -653,6 +654,9 @@ usage(int error_code)
62 #if defined(BUILD_RDP_COMPOSITOR)
63 			"\t\t\t\trdp-backend.so\n"
64 #endif
65+#if defined(BUILD_VNC_COMPOSITOR)
66+			"\t\t\t\tvnc-backend.so\n"
67+#endif
68 #if defined(BUILD_WAYLAND_COMPOSITOR)
69 			"\t\t\t\twayland-backend.so\n"
70 #endif
71@@ -720,6 +724,15 @@ usage(int error_code)
72 		"\n");
73 #endif
74
75+#if defined(BUILD_VNC_COMPOSITOR)
76+	fprintf(out,
77+		"Options for vnc-backend.so:\n\n"
78+		"  --width=WIDTH\t\tWidth of desktop\n"
79+		"  --height=HEIGHT\tHeight of desktop\n"
80+		"  --port=PORT\t\tThe port to listen on\n"
81+		"\n");
82+#endif
83+
84 #if defined(BUILD_WAYLAND_COMPOSITOR)
85 	fprintf(out,
86 		"Options for wayland-backend.so:\n\n"
87@@ -3177,6 +3190,90 @@ load_rdp_backend(struct weston_compositor *c,
88 	return ret;
89 }
90
91+static int
92+vnc_backend_output_configure(struct weston_output *output)
93+{
94+	struct wet_compositor *compositor = to_wet_compositor(output->compositor);
95+	struct wet_output_config *parsed_options = compositor->parsed_options;
96+	const struct weston_vnc_output_api *api = weston_vnc_output_get_api(output->compositor);
97+	int width = 640;
98+	int height = 480;
99+
100+	assert(parsed_options);
101+
102+	if (!api) {
103+		weston_log("Cannot use weston_vnc_output_api.\n");
104+		return -1;
105+	}
106+
107+	if (parsed_options->width)
108+		width = parsed_options->width;
109+
110+	if (parsed_options->height)
111+		height = parsed_options->height;
112+
113+	weston_output_set_scale(output, 1);
114+	weston_output_set_transform(output, WL_OUTPUT_TRANSFORM_NORMAL);
115+
116+	if (api->output_set_size(output, width, height) < 0) {
117+		weston_log("Cannot configure output \"%s\" using weston_vnc_output_api.\n",
118+			   output->name);
119+		return -1;
120+	}
121+	weston_log("vnc_backend_output_configure.. Done\n");
122+
123+	return 0;
124+}
125+
126+
127+static void
128+weston_vnc_backend_config_init(struct weston_vnc_backend_config *config)
129+{
130+	config->base.struct_version = WESTON_VNC_BACKEND_CONFIG_VERSION;
131+	config->base.struct_size = sizeof(struct weston_vnc_backend_config);
132+
133+	config->bind_address = NULL;
134+	config->port = 5900;
135+	config->refresh_rate = VNC_DEFAULT_FREQ;
136+}
137+
138+static int
139+load_vnc_backend(struct weston_compositor *c,
140+		int *argc, char *argv[], struct weston_config *wc)
141+{
142+	struct weston_vnc_backend_config config  = {{ 0, }};
143+	struct weston_config_section *section;
144+	int ret = 0;
145+
146+	struct wet_output_config *parsed_options = wet_init_parsed_options(c);
147+	if (!parsed_options)
148+		return -1;
149+
150+	weston_vnc_backend_config_init(&config);
151+
152+	const struct weston_option vnc_options[] = {
153+		{ WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width },
154+		{ WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height },
155+		{ WESTON_OPTION_STRING,  "address", 0, &config.bind_address },
156+		{ WESTON_OPTION_INTEGER, "port", 0, &config.port },
157+	};
158+
159+	parse_options(vnc_options, ARRAY_LENGTH(vnc_options), argc, argv);
160+
161+	wet_set_simple_head_configurator(c, vnc_backend_output_configure);
162+	section = weston_config_get_section(wc, "vnc", NULL, NULL);
163+	weston_config_section_get_int(section, "refresh-rate",
164+				      &config.refresh_rate,
165+				      VNC_DEFAULT_FREQ);
166+
167+	ret = weston_compositor_load_backend(c, WESTON_BACKEND_VNC,
168+					     &config.base);
169+
170+	free(config.bind_address);
171+
172+	return ret;
173+}
174+
175 static int
176 x11_backend_output_configure(struct weston_output *output)
177 {
178@@ -3422,6 +3519,8 @@ load_backend(struct weston_compositor *compositor, const char *backend,
179 		return load_headless_backend(compositor, argc, argv, config);
180 	else if (strstr(backend, "rdp-backend.so"))
181 		return load_rdp_backend(compositor, argc, argv, config);
182+	else if (strstr(backend, "vnc-backend.so"))
183+		return load_vnc_backend(compositor, argc, argv, config);
184 	else if (strstr(backend, "drm-backend.so"))
185 		return load_drm_backend(compositor, argc, argv, config);
186 	else if (strstr(backend, "x11-backend.so"))
187diff --git a/include/libweston/backend-vnc.h b/include/libweston/backend-vnc.h
188new file mode 100644
189index 0000000..0085df5
190--- /dev/null
191+++ b/include/libweston/backend-vnc.h
192@@ -0,0 +1,71 @@
193+/*
194+ * Copyright © 2019 Stefan Agner <stefan@agner.ch>
195+ *
196+ * Permission is hereby granted, free of charge, to any person obtaining
197+ * a copy of this software and associated documentation files (the
198+ * "Software"), to deal in the Software without restriction, including
199+ * without limitation the rights to use, copy, modify, merge, publish,
200+ * distribute, sublicense, and/or sell copies of the Software, and to
201+ * permit persons to whom the Software is furnished to do so, subject to
202+ * the following conditions:
203+ *
204+ * The above copyright notice and this permission notice (including the
205+ * next paragraph) shall be included in all copies or substantial
206+ * portions of the Software.
207+ *
208+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
209+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
210+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
211+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
212+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
213+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
214+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
215+ * SOFTWARE.
216+ */
217+
218+#ifndef WESTON_COMPOSITOR_VNC_H
219+#define WESTON_COMPOSITOR_VNC_H
220+
221+#ifdef  __cplusplus
222+extern "C" {
223+#endif
224+
225+#include <libweston/libweston.h>
226+#include <libweston/plugin-registry.h>
227+
228+#define WESTON_VNC_OUTPUT_API_NAME "weston_vnc_output_api_v1"
229+#define VNC_DEFAULT_FREQ 60
230+
231+struct weston_vnc_output_api {
232+	/** Initialize a VNC output with specified width and height.
233+	 *
234+	 * Returns 0 on success, -1 on failure.
235+	 */
236+	int (*output_set_size)(struct weston_output *output,
237+			       int width, int height);
238+};
239+
240+static inline const struct weston_vnc_output_api *
241+weston_vnc_output_get_api(struct weston_compositor *compositor)
242+{
243+	const void *api;
244+	api = weston_plugin_api_get(compositor, WESTON_VNC_OUTPUT_API_NAME,
245+				    sizeof(struct weston_vnc_output_api));
246+
247+	return (const struct weston_vnc_output_api *)api;
248+}
249+
250+#define WESTON_VNC_BACKEND_CONFIG_VERSION 1
251+
252+struct weston_vnc_backend_config {
253+	struct weston_backend_config base;
254+	char *bind_address;
255+	int port;
256+	int refresh_rate;
257+};
258+
259+#ifdef  __cplusplus
260+}
261+#endif
262+
263+#endif /* WESTON_COMPOSITOR_VNC_H */
264diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h
265index f6f4ecc..06a9ab9 100644
266--- a/include/libweston/libweston.h
267+++ b/include/libweston/libweston.h
268@@ -2090,6 +2090,7 @@ enum weston_compositor_backend {
269 	WESTON_BACKEND_DRM,
270 	WESTON_BACKEND_HEADLESS,
271 	WESTON_BACKEND_RDP,
272+	WESTON_BACKEND_VNC,
273 	WESTON_BACKEND_WAYLAND,
274 	WESTON_BACKEND_X11,
275 };
276diff --git a/include/libweston/meson.build b/include/libweston/meson.build
277index 8ae1001..f248dc5 100644
278--- a/include/libweston/meson.build
279+++ b/include/libweston/meson.build
280@@ -14,6 +14,7 @@ install_headers(
281 backend_drm_h =      files('backend-drm.h')
282 backend_headless_h = files('backend-headless.h')
283 backend_rdp_h =      files('backend-rdp.h')
284+backend_vnc_h =      files('backend-vnc.h')
285 backend_wayland_h =  files('backend-wayland.h')
286 backend_x11_h =      files('backend-x11.h')
287
288diff --git a/libweston/backend-vnc/meson.build b/libweston/backend-vnc/meson.build
289new file mode 100644
290index 0000000..4dfe022
291--- /dev/null
292+++ b/libweston/backend-vnc/meson.build
293@@ -0,0 +1,32 @@
294+if not get_option('backend-vnc')
295+	subdir_done()
296+endif
297+
298+config_h.set('BUILD_VNC_COMPOSITOR', '1')
299+dep_neatvnc = dependency('neatvnc', version: ['>= 0.5.0', '< 0.6.0'], required: false)
300+if not dep_neatvnc.found()
301+	error('VNC backend requires neatvnc which was not found. Or, you can use \'-Dbackend-vnc=false\'.')
302+endif
303+
304+dep_aml = dependency('aml', version: ['>= 0.1.0', '< 0.3.0'], required: false)
305+if not dep_aml.found()
306+	error('VNC backend requires libaml which was not found. Or, you can use \'-Dbackend-vnc=false\'.')
307+endif
308+
309+deps_vnc = [
310+	dep_libweston_private,
311+	dep_neatvnc,
312+	dep_aml,
313+	dep_libdrm_headers,
314+]
315+plugin_vnc = shared_library(
316+	'vnc-backend',
317+	[ 'vnc.c' ],
318+	include_directories: common_inc,
319+	dependencies: deps_vnc,
320+	name_prefix: '',
321+	install: true,
322+	install_dir: dir_module_libweston
323+)
324+env_modmap += 'vnc-backend.so=@0@;'.format(plugin_vnc.full_path())
325+install_headers(backend_vnc_h, subdir: dir_include_libweston_install)
326diff --git a/libweston/backend-vnc/vnc.c b/libweston/backend-vnc/vnc.c
327new file mode 100644
328index 0000000..8cfad2a
329--- /dev/null
330+++ b/libweston/backend-vnc/vnc.c
331@@ -0,0 +1,1032 @@
332+/*
333+ * Copyright © 2019-2020 Stefan Agner <stefan@agner.ch>
334+ * Copyright © 2021-2022 Pengutronix, Philipp Zabel <p.zabel@pengutronix.de>
335+ * Copyright © 2022 Pengutronix, Rouven Czerwinski <r.czerwinski@pengutronix.de>
336+ * based on backend-rdp:
337+ * Copyright © 2013 Hardening <rdp.effort@gmail.com>
338+ *
339+ * Permission is hereby granted, free of charge, to any person obtaining
340+ * a copy of this software and associated documentation files (the
341+ * "Software"), to deal in the Software without restriction, including
342+ * without limitation the rights to use, copy, modify, merge, publish,
343+ * distribute, sublicense, and/or sell copies of the Software, and to
344+ * permit persons to whom the Software is furnished to do so, subject to
345+ * the following conditions:
346+ *
347+ * The above copyright notice and this permission notice (including the
348+ * next paragraph) shall be included in all copies or substantial
349+ * portions of the Software.
350+ *
351+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
352+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
353+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
354+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
355+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
356+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
357+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
358+ * SOFTWARE.
359+ */
360+
361+#include "config.h"
362+
363+#include <assert.h>
364+#include <stdint.h>
365+#include <stdlib.h>
366+#include <string.h>
367+#include <errno.h>
368+#include <linux/input.h>
369+#include <netinet/in.h>
370+#include <sys/types.h>
371+#include <sys/socket.h>
372+#include <unistd.h>
373+#include <xkbcommon/xkbcommon-keysyms.h>
374+#include <xkbcommon/xkbcommon.h>
375+#include <aml.h>
376+#include <neatvnc.h>
377+#include <drm_fourcc.h>
378+
379+#include "shared/helpers.h"
380+#include "shared/xalloc.h"
381+#include "shared/timespec-util.h"
382+#include <libweston/libweston.h>
383+#include <libweston/backend-vnc.h>
384+#include "pixel-formats.h"
385+#include "pixman-renderer.h"
386+
387+#define DEFAULT_AXIS_STEP_DISTANCE 10
388+
389+struct vnc_output;
390+
391+struct vnc_backend {
392+	struct weston_backend base;
393+	struct weston_compositor *compositor;
394+	struct vnc_output *output;
395+
396+	struct xkb_rule_names xkb_rule_name;
397+	struct xkb_keymap *xkb_keymap;
398+
399+	struct aml *aml;
400+	struct wl_event_source *aml_event;
401+	struct nvnc *server;
402+	int vnc_monitor_refresh_rate;
403+};
404+
405+struct vnc_output {
406+	struct weston_output base;
407+	struct wl_event_source *finish_frame_timer;
408+	struct nvnc_display *display;
409+
410+	struct nvnc_fb_pool *fb_pool;
411+
412+	struct wl_list peers;
413+	struct wl_list fb_side_data_list;
414+};
415+
416+struct vnc_peer {
417+	struct vnc_backend *backend;
418+	struct weston_seat *seat;
419+	struct nvnc_client *client;
420+
421+	enum nvnc_button_mask last_button_mask;
422+	struct wl_list link;
423+};
424+
425+struct vnc_head {
426+	struct weston_head base;
427+};
428+
429+struct fb_side_data {
430+	pixman_image_t *pixman_image;
431+	pixman_region32_t damage;
432+	struct wl_list link;
433+};
434+
435+static inline struct vnc_backend *
436+to_vnc_backend(struct weston_compositor *base)
437+{
438+	return container_of(base->backend, struct vnc_backend, base);
439+}
440+
441+static void
442+vnc_output_destroy(struct weston_output *base);
443+
444+static inline struct vnc_output *
445+to_vnc_output(struct weston_output *base)
446+{
447+	if (base->destroy != vnc_output_destroy)
448+		return NULL;
449+	return container_of(base, struct vnc_output, base);
450+}
451+
452+static void
453+vnc_head_destroy(struct weston_head *base);
454+
455+static inline struct vnc_head *
456+to_vnc_head(struct weston_head *base)
457+{
458+	if (base->backend_id != vnc_head_destroy)
459+		return NULL;
460+	return container_of(base, struct vnc_head, base);
461+}
462+
463+struct vnc_keysym_to_keycode {
464+	const uint32_t keysym;
465+	const uint32_t code;
466+	const bool shift;
467+};
468+
469+static const
470+struct vnc_keysym_to_keycode key_translation[] = {
471+	{XKB_KEY_KP_Enter,	0x60,	false	},
472+	{XKB_KEY_Return,	0x1c,	false	},
473+	{XKB_KEY_space,		0x39,	false	},
474+	{XKB_KEY_BackSpace,	0xe,	false	},
475+	{XKB_KEY_Tab,		0xf,	false	},
476+	{XKB_KEY_Escape,	0x1,	false	},
477+	{XKB_KEY_Shift_L,	0x2a,	false	},
478+	{XKB_KEY_Shift_R,	0x36,	false	},
479+	{XKB_KEY_Control_L,	0x1d,	false	},
480+	{XKB_KEY_Control_R,	0x9d,	false	},
481+	{XKB_KEY_Alt_L,		0x38,	false	},
482+	{XKB_KEY_Alt_R,		0x64,	false	},
483+	{XKB_KEY_Meta_L,	0x38,	false	},
484+	{XKB_KEY_Meta_R,	0x64,	false	},
485+	{XKB_KEY_Super_L,	0x7d,	false	},
486+	{XKB_KEY_Print, 	0x63,	false	},
487+	{XKB_KEY_Pause, 	0x77,	false	},
488+	{XKB_KEY_Caps_Lock, 	0x3a,	false	},
489+	{XKB_KEY_Scroll_Lock, 	0x46,	false	},
490+	{XKB_KEY_A,		0x1e,	true	},
491+	{XKB_KEY_a,		0x1e,	false	},
492+	{XKB_KEY_B,		0x30,	true	},
493+	{XKB_KEY_b,		0x30,	false	},
494+	{XKB_KEY_C,		0x2e,	true	},
495+	{XKB_KEY_c,		0x2e,	false	},
496+	{XKB_KEY_D,		0x20,	true	},
497+	{XKB_KEY_d,		0x20,	false	},
498+	{XKB_KEY_E,		0x12,	true	},
499+	{XKB_KEY_e,		0x12,	false	},
500+	{XKB_KEY_F,		0x21,	true	},
501+	{XKB_KEY_f,		0x21,	false	},
502+	{XKB_KEY_G,		0x22,	true	},
503+	{XKB_KEY_g,		0x22,	false	},
504+	{XKB_KEY_H,		0x23,	true	},
505+	{XKB_KEY_h,		0x23,	false	},
506+	{XKB_KEY_I,		0x17,	true	},
507+	{XKB_KEY_i,		0x17,	false	},
508+	{XKB_KEY_J,		0x24,	true	},
509+	{XKB_KEY_j,		0x24,	false	},
510+	{XKB_KEY_K,		0x25,	true	},
511+	{XKB_KEY_k,		0x25,	false	},
512+	{XKB_KEY_L,		0x26,	true	},
513+	{XKB_KEY_l,		0x26,	false	},
514+	{XKB_KEY_M,		0x32,	true	},
515+	{XKB_KEY_m,		0x32,	false	},
516+	{XKB_KEY_N,		0x31,	true	},
517+	{XKB_KEY_n,		0x31,	false	},
518+	{XKB_KEY_O,		0x18,	true	},
519+	{XKB_KEY_o,		0x18,	false	},
520+	{XKB_KEY_P,		0x19,	true	},
521+	{XKB_KEY_p,		0x19,	false	},
522+	{XKB_KEY_Q,		0x10,	true	},
523+	{XKB_KEY_q,		0x10,	false	},
524+	{XKB_KEY_R,		0x13,	true	},
525+	{XKB_KEY_r,		0x13,	false	},
526+	{XKB_KEY_S,		0x1f,	true	},
527+	{XKB_KEY_s,		0x1f,	false	},
528+	{XKB_KEY_T,		0x14,	true	},
529+	{XKB_KEY_t,		0x14,	false	},
530+	{XKB_KEY_U,		0x16,	true	},
531+	{XKB_KEY_u,		0x16,	false	},
532+	{XKB_KEY_V,		0x2f,	true	},
533+	{XKB_KEY_v,		0x2f,	false	},
534+	{XKB_KEY_W,		0x11,	true	},
535+	{XKB_KEY_w,		0x11,	false	},
536+	{XKB_KEY_X,		0x2d,	true	},
537+	{XKB_KEY_x,		0x2d,	false	},
538+	{XKB_KEY_Y,		0x15,	true	},
539+	{XKB_KEY_y,		0x15,	false	},
540+	{XKB_KEY_Z,		0x2c,	true	},
541+	{XKB_KEY_z,		0x2c,	false	},
542+	{XKB_KEY_grave,		0x29,	false	},
543+	{XKB_KEY_asciitilde,	0x29,	true	},
544+	{XKB_KEY_1,		0x02,	false	},
545+	{XKB_KEY_exclam,	0x02,	true	},
546+	{XKB_KEY_2,		0x03,	false	},
547+	{XKB_KEY_at,		0x03,	true	},
548+	{XKB_KEY_3,		0x04,	false	},
549+	{XKB_KEY_numbersign,	0x04,	true	},
550+	{XKB_KEY_4,		0x05,	false	},
551+	{XKB_KEY_dollar,	0x05,	true	},
552+	{XKB_KEY_5,		0x06,	false	},
553+	{XKB_KEY_percent,	0x06,	true	},
554+	{XKB_KEY_6,		0x07,	false	},
555+	{XKB_KEY_asciicircum,	0x07,	true	},
556+	{XKB_KEY_7,		0x08,	false	},
557+	{XKB_KEY_ampersand,	0x08,	true	},
558+	{XKB_KEY_8,		0x09,	false	},
559+	{XKB_KEY_asterisk,	0x09,	true	},
560+	{XKB_KEY_9,		0x0a,	false	},
561+	{XKB_KEY_parenleft,	0x0a,	true	},
562+	{XKB_KEY_0,		0x0b,	false	},
563+	{XKB_KEY_parenright,	0x0b,	true	},
564+	{XKB_KEY_minus,		0x0c,	false,	},
565+	{XKB_KEY_underscore,	0x0c,	true	},
566+	{XKB_KEY_equal,		0x0d,	false	},
567+	{XKB_KEY_plus,		0x0d,	true	},
568+	{XKB_KEY_bracketleft,	0x1a,	false	},
569+	{XKB_KEY_braceleft,	0x1a,	true	},
570+	{XKB_KEY_bracketright,	0x1b,	false	},
571+	{XKB_KEY_braceright,	0x1b,	true	},
572+	{XKB_KEY_semicolon,	0x27,	false	},
573+	{XKB_KEY_colon,		0x27,	true	},
574+	{XKB_KEY_apostrophe,	0x28,	false	},
575+	{XKB_KEY_quotedbl,	0x28,	true	},
576+	{XKB_KEY_backslash,	0x2b,	false	},
577+	{XKB_KEY_bar,		0x2b,	true	},
578+	{XKB_KEY_comma,		0x33,	false	},
579+	{XKB_KEY_less,		0x33,	true	},
580+	{XKB_KEY_period,	0x34,	false	},
581+	{XKB_KEY_greater,	0x34,	true	},
582+	{XKB_KEY_slash,		0x35,	false	},
583+	{XKB_KEY_question,	0x35,	true	},
584+	{XKB_KEY_F1,		0x3b,	false	},
585+	{XKB_KEY_F2,		0x3c,   false	},
586+	{XKB_KEY_F3,		0x3d,   false	},
587+	{XKB_KEY_F4,		0x3e,   false	},
588+	{XKB_KEY_F5,		0x3f,   false	},
589+	{XKB_KEY_F6,		0x40,   false	},
590+	{XKB_KEY_F7,		0x41,   false	},
591+	{XKB_KEY_F8,		0x42,   false	},
592+	{XKB_KEY_F9,		0x43,   false	},
593+	{XKB_KEY_F10,		0x44,   false	},
594+	{XKB_KEY_F11,		0x57,   false	},
595+	{XKB_KEY_F12,		0x58,   false	},
596+	{XKB_KEY_Home,		0x66,   false	},
597+	{XKB_KEY_Up,		0x67,   false	},
598+	{XKB_KEY_Prior,		0x68,   false	},
599+	{XKB_KEY_Left,		0x69,   false	},
600+	{XKB_KEY_Right,		0x6a,   false	},
601+	{XKB_KEY_End,		0x6b,   false	},
602+	{XKB_KEY_Down,		0x6c,   false	},
603+	{XKB_KEY_Next,		0x6d,   false	},
604+	{ },
605+};
606+
607+static void
608+vnc_handle_key_event(struct nvnc_client *client, uint32_t keysym,
609+		     bool is_pressed)
610+{
611+	struct vnc_peer *peer = nvnc_get_userdata(client);
612+	uint32_t key = 0;
613+	bool needs_shift = false;
614+	enum weston_key_state_update state_update;
615+	enum wl_keyboard_key_state state;
616+	struct timespec time;
617+	int i;
618+
619+	weston_compositor_get_time(&time);
620+
621+	if (is_pressed)
622+		state = WL_KEYBOARD_KEY_STATE_PRESSED;
623+	else
624+		state = WL_KEYBOARD_KEY_STATE_RELEASED;
625+
626+	/* Generally ignore shift state as per RFC6143 Section 7.5.4 */
627+	if (keysym == XKB_KEY_Shift_L || keysym == XKB_KEY_Shift_R)
628+		return;
629+
630+	/* Allow selected modifiers */
631+	if (keysym == XKB_KEY_Control_L || keysym == XKB_KEY_Control_R ||
632+	    keysym == XKB_KEY_Alt_L || keysym == XKB_KEY_Alt_R)
633+		state_update = STATE_UPDATE_AUTOMATIC;
634+	else
635+		state_update = STATE_UPDATE_NONE;
636+
637+	for (i = 0; key_translation[i].keysym; i++) {
638+		if (key_translation[i].keysym == keysym) {
639+			key = key_translation[i].code;
640+			needs_shift = key_translation[i].shift;
641+			break;
642+		}
643+	}
644+
645+	if (!key) {
646+		weston_log("Key not found: keysym %08x, translated %08x\n",
647+			    keysym, key);
648+		return;
649+	}
650+
651+	/* emulate lshift press */
652+	if (needs_shift)
653+		notify_key(peer->seat, &time, KEY_LEFTSHIFT,
654+			   WL_KEYBOARD_KEY_STATE_PRESSED,
655+			   STATE_UPDATE_AUTOMATIC);
656+
657+	/* send detected key code */
658+	notify_key(peer->seat, &time, key, state, state_update);
659+
660+	/* emulate lshift release */
661+	if (needs_shift)
662+		notify_key(peer->seat, &time, KEY_LEFTSHIFT,
663+			   WL_KEYBOARD_KEY_STATE_RELEASED,
664+			   STATE_UPDATE_AUTOMATIC);
665+}
666+
667+static void
668+vnc_pointer_event(struct nvnc_client *client, uint16_t x, uint16_t y,
669+		  enum nvnc_button_mask button_mask)
670+{
671+	struct vnc_peer *peer = nvnc_get_userdata(client);
672+	struct vnc_output *output = peer->backend->output;
673+	struct timespec time;
674+	enum nvnc_button_mask changed_button_mask;
675+
676+	weston_compositor_get_time(&time);
677+
678+	if (x < output->base.width && y < output->base.height) {
679+		double global_x, global_y;
680+
681+		weston_output_transform_coordinate(&output->base, x, y,
682+						   &global_x, &global_y);
683+		notify_motion_absolute(peer->seat, &time, global_x, global_y);
684+	}
685+
686+	changed_button_mask = peer->last_button_mask ^ button_mask;
687+
688+	if (changed_button_mask & NVNC_BUTTON_LEFT)
689+		notify_button(peer->seat, &time, BTN_LEFT,
690+			      (button_mask & NVNC_BUTTON_LEFT) ?
691+			      WL_POINTER_BUTTON_STATE_PRESSED :
692+			      WL_POINTER_BUTTON_STATE_RELEASED);
693+
694+	if (changed_button_mask & NVNC_BUTTON_MIDDLE)
695+		notify_button(peer->seat, &time, BTN_MIDDLE,
696+			      (button_mask & NVNC_BUTTON_MIDDLE) ?
697+			      WL_POINTER_BUTTON_STATE_PRESSED :
698+			      WL_POINTER_BUTTON_STATE_RELEASED);
699+
700+	if (changed_button_mask & NVNC_BUTTON_RIGHT)
701+		notify_button(peer->seat, &time, BTN_RIGHT,
702+			      (button_mask & NVNC_BUTTON_RIGHT) ?
703+			      WL_POINTER_BUTTON_STATE_PRESSED :
704+			      WL_POINTER_BUTTON_STATE_RELEASED);
705+
706+	if ((button_mask & NVNC_SCROLL_UP) ||
707+	    (button_mask & NVNC_SCROLL_DOWN)) {
708+		struct weston_pointer_axis_event weston_event;
709+
710+		weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
711+
712+		/* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c */
713+		if (button_mask & NVNC_SCROLL_UP)
714+			weston_event.value = -DEFAULT_AXIS_STEP_DISTANCE;
715+		if (button_mask & NVNC_SCROLL_DOWN)
716+			weston_event.value = DEFAULT_AXIS_STEP_DISTANCE;
717+		weston_event.has_discrete = false;
718+
719+		notify_axis(peer->seat, &time, &weston_event);
720+	}
721+
722+	peer->last_button_mask = button_mask;
723+
724+	notify_pointer_frame(peer->seat);
725+}
726+
727+static void
728+vnc_client_cleanup(struct nvnc_client *client)
729+{
730+	struct vnc_peer *peer = nvnc_get_userdata(client);
731+
732+	wl_list_remove(&peer->link);
733+	weston_seat_release_keyboard(peer->seat);
734+	weston_seat_release_pointer(peer->seat);
735+	weston_seat_release(peer->seat);
736+	free(peer);
737+	weston_log("VNC Client disconnected\n");
738+}
739+
740+static void
741+fb_side_data_destroy(void *userdata)
742+{
743+	struct fb_side_data *fb_side_data = userdata;
744+
745+	wl_list_remove(&fb_side_data->link);
746+	pixman_region32_fini(&fb_side_data->damage);
747+	pixman_image_unref(fb_side_data->pixman_image);
748+	free(fb_side_data);
749+}
750+
751+
752+/*
753+ * Convert damage rectangles from 32-bit global coordinates to 16-bit local
754+ * coordinates. The output transformation has to be a pure translation.
755+ */
756+static void
757+vnc_convert_damage(struct pixman_region16 *dst, struct pixman_region32 *src,
758+		   int x, int y)
759+{
760+	struct pixman_box32 *src_rects;
761+	struct pixman_box16 *dest_rects;
762+	int n_rects = 0;
763+	int i;
764+
765+	src_rects = pixman_region32_rectangles(src, &n_rects);
766+	if (!n_rects)
767+		return;
768+
769+	dest_rects = xcalloc(n_rects, sizeof(*dest_rects));
770+
771+	for (i = 0; i < n_rects; i++) {
772+		dest_rects[i].x1 = src_rects[i].x1 - x;
773+		dest_rects[i].y1 = src_rects[i].y1 - y;
774+		dest_rects[i].x2 = src_rects[i].x2 - x;
775+		dest_rects[i].y2 = src_rects[i].y2 - y;
776+	}
777+
778+	pixman_region_init_rects(dst, dest_rects, n_rects);
779+	free(dest_rects);
780+}
781+
782+static void
783+vnc_update_buffer(struct nvnc_display *display, struct pixman_region32 *damage)
784+{
785+	struct nvnc *server = nvnc_display_get_server(display);
786+	struct vnc_backend *backend = nvnc_get_userdata(server);
787+	struct vnc_output *output = backend->output;
788+	struct weston_compositor *ec = output->base.compositor;
789+	struct fb_side_data *fb_side_data;
790+	pixman_region16_t local_damage;
791+	struct nvnc_fb *fb;
792+
793+	fb = nvnc_fb_pool_acquire(output->fb_pool);
794+	assert(fb);
795+
796+	fb_side_data = nvnc_get_userdata(fb);
797+	if (!fb_side_data) {
798+		const struct pixel_format_info *pfmt;
799+
800+		fb_side_data = xzalloc(sizeof(*fb_side_data));
801+
802+		pfmt = pixel_format_get_info(DRM_FORMAT_XRGB8888);
803+		fb_side_data->pixman_image =
804+			pixman_image_create_bits(pfmt->pixman_format,
805+						 output->base.width,
806+						 output->base.height,
807+						 nvnc_fb_get_addr(fb),
808+						 output->base.width * 4);
809+
810+		/* This is a new buffer, so the whole surface is damaged. */
811+		pixman_region32_copy(&fb_side_data->damage,
812+				     &output->base.region);
813+
814+		nvnc_set_userdata(fb, fb_side_data, fb_side_data_destroy);
815+		wl_list_insert(&output->fb_side_data_list, &fb_side_data->link);
816+	}
817+
818+	pixman_renderer_output_set_buffer(&output->base,
819+					  fb_side_data->pixman_image);
820+
821+	ec->renderer->repaint_output(&output->base, &fb_side_data->damage);
822+
823+	/* Convert to local coordinates before clearing accumulated damage */
824+	pixman_region_init(&local_damage);
825+	vnc_convert_damage(&local_damage, &fb_side_data->damage,
826+					  output->base.x, output->base.y);
827+
828+	/* Clear accumulated damage after repaint */
829+	pixman_region32_clear(&fb_side_data->damage);
830+
831+	nvnc_display_feed_buffer(output->display, fb, &local_damage);
832+	nvnc_fb_unref(fb);
833+	pixman_region_fini(&local_damage);
834+}
835+
836+static void
837+vnc_new_client(struct nvnc_client *client)
838+{
839+	struct nvnc *server = nvnc_client_get_server(client);
840+	struct vnc_backend *backend = nvnc_get_userdata(server);
841+	struct vnc_output *output = backend->output;
842+	struct vnc_peer *peer;
843+	const char *seat_name = "VNC Client";
844+
845+	weston_log("New VNC client connected\n");
846+
847+	peer = xzalloc(sizeof(*peer));
848+	peer->client = client;
849+	peer->backend = backend;
850+	peer->seat = zalloc(sizeof(*peer->seat));
851+
852+	if (!peer->seat) {
853+		weston_log("unable to create a weston_seat\n");
854+		return;
855+	}
856+	weston_seat_init(peer->seat, backend->compositor, seat_name);
857+	weston_seat_init_pointer(peer->seat);
858+	weston_seat_init_keyboard(peer->seat, backend->xkb_keymap);
859+
860+	wl_list_insert(&output->peers, &peer->link);
861+
862+	nvnc_set_userdata(client, peer, NULL);
863+	nvnc_set_client_cleanup_fn(client, vnc_client_cleanup);
864+
865+	/*
866+	 * Make up for repaints that were skipped when no clients were
867+	 * connected.
868+	 */
869+	weston_output_schedule_repaint(&output->base);
870+}
871+
872+
873+static int
874+finish_frame_handler(void *data)
875+{
876+	struct vnc_output *output = data;
877+	int refresh_nsec = millihz_to_nsec(output->base.current_mode->refresh);
878+	struct timespec now, ts;
879+	int delta;
880+
881+	/* The timer only has msec precision, but if we approximately hit our
882+	 * target, report an exact time stamp by adding to the previous frame
883+	 * time.
884+	 */
885+	timespec_add_nsec(&ts, &output->base.frame_time, refresh_nsec);
886+
887+	/* If we are more than 1.5 ms late, report the current time instead. */
888+	weston_compositor_read_presentation_clock(output->base.compositor, &now);
889+	delta = (int)timespec_sub_to_nsec(&now, &ts);
890+	if (delta > 1500000)
891+		ts = now;
892+
893+	weston_output_finish_frame(&output->base, &ts, 0);
894+
895+	return 1;
896+}
897+
898+static int
899+vnc_output_enable(struct weston_output *base)
900+{
901+	struct vnc_output *output = to_vnc_output(base);
902+	struct vnc_backend *backend;
903+	struct wl_event_loop *loop;
904+	const struct pixman_renderer_output_options options = {
905+		.use_shadow = true,
906+		.fb_size = {
907+			.width = output->base.width,
908+			.height = output->base.height,
909+		},
910+	};
911+
912+	assert(output);
913+
914+	backend = to_vnc_backend(base->compositor);
915+	backend->output = output;
916+
917+	if (pixman_renderer_output_create(&output->base, &options) < 0)
918+		return -1;
919+
920+	loop = wl_display_get_event_loop(backend->compositor->wl_display);
921+	output->finish_frame_timer = wl_event_loop_add_timer(loop,
922+							     finish_frame_handler,
923+							     output);
924+
925+	output->fb_pool = nvnc_fb_pool_new(output->base.width,
926+					   output->base.height,
927+					   DRM_FORMAT_XRGB8888,
928+					   output->base.width);
929+
930+	output->display = nvnc_display_new(0, 0);
931+
932+	wl_list_init(&output->fb_side_data_list);
933+
934+	nvnc_add_display(backend->server, output->display);
935+
936+	/*
937+	 * Neat VNC warns when a client connects before a display buffer has
938+	 * been set. Repaint once to create an initial buffer.
939+	 */
940+	vnc_update_buffer(output->display, &output->base.region);
941+
942+	return 0;
943+}
944+
945+static int
946+vnc_output_disable(struct weston_output *base)
947+{
948+	struct vnc_output *output = to_vnc_output(base);
949+	struct vnc_backend *backend;
950+
951+	assert(output);
952+
953+	backend = to_vnc_backend(base->compositor);
954+
955+	if (!output->base.enabled)
956+		return 0;
957+
958+	pixman_renderer_output_destroy(&output->base);
959+
960+	nvnc_display_unref(output->display);
961+	nvnc_fb_pool_unref(output->fb_pool);
962+
963+	wl_event_source_remove(output->finish_frame_timer);
964+	backend->output = NULL;
965+
966+	return 0;
967+}
968+
969+static void
970+vnc_output_destroy(struct weston_output *base)
971+{
972+	struct vnc_output *output = to_vnc_output(base);
973+
974+	/* Can only be called on outputs created by vnc_create_output() */
975+	assert(output);
976+
977+	vnc_output_disable(&output->base);
978+	weston_output_release(&output->base);
979+
980+	free(output);
981+}
982+
983+static struct weston_output *
984+vnc_create_output(struct weston_compositor *compositor, const char *name)
985+{
986+	struct vnc_output *output;
987+
988+	output = zalloc(sizeof *output);
989+	if (output == NULL)
990+		return NULL;
991+
992+	weston_output_init(&output->base, compositor, name);
993+
994+	output->base.destroy = vnc_output_destroy;
995+	output->base.disable = vnc_output_disable;
996+	output->base.enable = vnc_output_enable;
997+	output->base.attach_head = NULL;
998+
999+	weston_compositor_add_pending_output(&output->base, compositor);
1000+
1001+	return &output->base;
1002+}
1003+
1004+static void
1005+vnc_destroy(struct weston_compositor *ec)
1006+{
1007+	struct weston_head *base, *next;
1008+	struct vnc_backend *backend = to_vnc_backend(ec);
1009+
1010+	nvnc_close(backend->server);
1011+
1012+	weston_compositor_shutdown(ec);
1013+
1014+	wl_event_source_remove(backend->aml_event);
1015+
1016+	aml_unref(backend->aml);
1017+
1018+	wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
1019+		vnc_head_destroy(base);
1020+
1021+	xkb_keymap_unref(backend->xkb_keymap);
1022+
1023+	free(backend);
1024+}
1025+
1026+static int
1027+vnc_head_create(struct weston_compositor *compositor, const char *name)
1028+{
1029+	struct vnc_head *head;
1030+
1031+	head = zalloc(sizeof *head);
1032+	if (!head)
1033+		return -1;
1034+
1035+	weston_head_init(&head->base, name);
1036+
1037+	head->base.backend_id = vnc_head_destroy;
1038+
1039+	weston_head_set_connection_status(&head->base, true);
1040+	weston_compositor_add_head(compositor, &head->base);
1041+
1042+	return 0;
1043+}
1044+
1045+static void
1046+vnc_head_destroy(struct weston_head *base)
1047+{
1048+	struct vnc_head *head = to_vnc_head(base);
1049+
1050+	if (!head)
1051+		return;
1052+
1053+	weston_head_release(&head->base);
1054+	free(head);
1055+}
1056+
1057+static int
1058+vnc_output_start_repaint_loop(struct weston_output *output)
1059+{
1060+	struct timespec ts;
1061+
1062+	weston_compositor_read_presentation_clock(output->compositor, &ts);
1063+	weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
1064+
1065+	return 0;
1066+}
1067+
1068+static int
1069+vnc_output_repaint(struct weston_output *base, pixman_region32_t *damage)
1070+{
1071+	struct vnc_output *output = to_vnc_output(base);
1072+	struct weston_compositor *ec = output->base.compositor;
1073+	struct vnc_backend *backend = to_vnc_backend(ec);
1074+	struct timespec now, target;
1075+	int refresh_nsec = millihz_to_nsec(output->base.current_mode->refresh);
1076+	int refresh_msec = refresh_nsec / 1000000;
1077+	int next_frame_delta;
1078+
1079+	assert(output);
1080+
1081+	if (pixman_region32_not_empty(damage)) {
1082+		struct fb_side_data *fb_side_data;
1083+
1084+		/* Accumulate damage in all buffers */
1085+		wl_list_for_each(fb_side_data, &output->fb_side_data_list, link)
1086+			pixman_region32_union(&fb_side_data->damage,
1087+					      &fb_side_data->damage, damage);
1088+
1089+		/* Only repaint when a client is connected */
1090+		if (!wl_list_empty(&output->peers))
1091+			vnc_update_buffer(output->display, damage);
1092+
1093+		pixman_region32_subtract(&ec->primary_plane.damage,
1094+					 &ec->primary_plane.damage, damage);
1095+	}
1096+
1097+	/*
1098+	 * Make sure damage of this (or previous) damage is handled
1099+	 *
1100+	 * This will usually invoke the render callback where the (pixman)
1101+	 * renderer gets invoked
1102+	 */
1103+	aml_dispatch(backend->aml);
1104+
1105+	weston_compositor_read_presentation_clock(ec, &now);
1106+	timespec_add_nsec(&target, &output->base.frame_time, refresh_nsec);
1107+
1108+	next_frame_delta = (int)timespec_sub_to_msec(&target, &now);
1109+	if (next_frame_delta < 1)
1110+		next_frame_delta = 1;
1111+	if (next_frame_delta > refresh_msec)
1112+		next_frame_delta = refresh_msec;
1113+
1114+	wl_event_source_timer_update(output->finish_frame_timer,
1115+				     next_frame_delta);
1116+
1117+	return 0;
1118+}
1119+
1120+static struct weston_mode *
1121+vnc_insert_new_mode(struct weston_output *output, int width, int height,
1122+		    int rate)
1123+{
1124+	struct weston_mode *mode;
1125+
1126+	mode = zalloc(sizeof *mode);
1127+	if (!mode)
1128+		return NULL;
1129+	mode->width = width;
1130+	mode->height = height;
1131+	mode->refresh = rate;
1132+	wl_list_insert(&output->mode_list, &mode->link);
1133+
1134+	return mode;
1135+}
1136+
1137+static struct weston_mode *
1138+vnc_ensure_matching_mode(struct weston_output *output,
1139+			 struct weston_mode *target)
1140+{
1141+	struct vnc_backend *backend = to_vnc_backend(output->compositor);
1142+	struct weston_mode *local;
1143+
1144+	wl_list_for_each(local, &output->mode_list, link) {
1145+		if ((local->width == target->width) &&
1146+		    (local->height == target->height))
1147+			return local;
1148+	}
1149+
1150+	return vnc_insert_new_mode(output, target->width, target->height,
1151+				   backend->vnc_monitor_refresh_rate);
1152+}
1153+
1154+static int
1155+vnc_switch_mode(struct weston_output *base, struct weston_mode *target_mode)
1156+{
1157+	struct vnc_output *output = to_vnc_output(base);
1158+	struct weston_mode *local_mode;
1159+	struct weston_size fb_size;
1160+
1161+	assert(output);
1162+
1163+	local_mode = vnc_ensure_matching_mode(base, target_mode);
1164+	if (!local_mode) {
1165+		weston_log("mode %dx%d not available\n",
1166+			   target_mode->width, target_mode->height);
1167+		return -ENOENT;
1168+	}
1169+
1170+	if (local_mode == base->current_mode)
1171+		return 0;
1172+
1173+	base->current_mode->flags &= ~WL_OUTPUT_MODE_CURRENT;
1174+
1175+	base->current_mode = base->native_mode = local_mode;
1176+	base->current_mode->flags |= WL_OUTPUT_MODE_CURRENT;
1177+
1178+	fb_size.width = target_mode->width;
1179+	fb_size.height = target_mode->height;
1180+
1181+	weston_renderer_resize_output(base, &fb_size, NULL);
1182+
1183+	nvnc_fb_pool_unref(output->fb_pool);
1184+
1185+	output->fb_pool = nvnc_fb_pool_new(target_mode->width,
1186+					   target_mode->height,
1187+					   DRM_FORMAT_XRGB8888,
1188+					   target_mode->width * 4);
1189+
1190+	return 0;
1191+}
1192+
1193+static int
1194+vnc_output_set_size(struct weston_output *base, int width, int height)
1195+{
1196+	struct vnc_output *output = to_vnc_output(base);
1197+	struct vnc_backend *backend = to_vnc_backend(base->compositor);
1198+	struct weston_head *head;
1199+	struct weston_mode *current_mode;
1200+	struct weston_mode init_mode;
1201+
1202+	/* We can only be called once. */
1203+	assert(!output->base.current_mode);
1204+
1205+	wl_list_for_each(head, &output->base.head_list, output_link) {
1206+		weston_head_set_monitor_strings(head, "weston", "vnc", NULL);
1207+
1208+		weston_head_set_physical_size(head, 0, 0);
1209+	}
1210+
1211+	wl_list_init(&output->peers);
1212+
1213+	init_mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
1214+	init_mode.width = width;
1215+	init_mode.height = height;
1216+	init_mode.refresh = backend->vnc_monitor_refresh_rate;
1217+
1218+	current_mode = vnc_ensure_matching_mode(&output->base, &init_mode);
1219+	if (!current_mode)
1220+		return -1;
1221+
1222+	output->base.current_mode = output->base.native_mode = current_mode;
1223+
1224+	output->base.start_repaint_loop = vnc_output_start_repaint_loop;
1225+	output->base.repaint = vnc_output_repaint;
1226+	output->base.assign_planes = NULL;
1227+	output->base.set_backlight = NULL;
1228+	output->base.set_dpms = NULL;
1229+	output->base.switch_mode = vnc_switch_mode;
1230+
1231+	return 0;
1232+}
1233+
1234+static const struct weston_vnc_output_api api = {
1235+	vnc_output_set_size,
1236+};
1237+
1238+static int
1239+vnc_aml_dispatch(int fd, uint32_t mask, void *data)
1240+{
1241+	struct aml *aml = data;
1242+
1243+	aml_poll(aml, 0);
1244+	aml_dispatch(aml);
1245+
1246+	return 0;
1247+}
1248+
1249+static struct vnc_backend *
1250+vnc_backend_create(struct weston_compositor *compositor,
1251+		   struct weston_vnc_backend_config *config)
1252+{
1253+	struct vnc_backend *backend;
1254+	struct wl_event_loop *loop;
1255+	struct weston_head *base, *next;
1256+	int ret;
1257+	int fd;
1258+
1259+	backend = zalloc(sizeof *backend);
1260+	if (backend == NULL)
1261+		return NULL;
1262+
1263+	backend->compositor = compositor;
1264+	backend->base.destroy = vnc_destroy;
1265+	backend->base.create_output = vnc_create_output;
1266+	backend->vnc_monitor_refresh_rate = config->refresh_rate * 1000;
1267+
1268+	compositor->backend = &backend->base;
1269+
1270+	if (weston_compositor_set_presentation_clock_software(compositor) < 0)
1271+		goto err_compositor;
1272+
1273+	if (pixman_renderer_init(compositor) < 0)
1274+		goto err_compositor;
1275+
1276+	if (vnc_head_create(compositor, "vnc") < 0)
1277+		goto err_compositor;
1278+
1279+	compositor->capabilities |= WESTON_CAP_ARBITRARY_MODES;
1280+
1281+	backend->xkb_rule_name.rules = strdup("evdev");
1282+	backend->xkb_rule_name.model = strdup("pc105");
1283+	backend->xkb_rule_name.layout = strdup("us");
1284+
1285+	backend->xkb_keymap = xkb_keymap_new_from_names(
1286+					backend->compositor->xkb_context,
1287+					&backend->xkb_rule_name, 0);
1288+
1289+	loop = wl_display_get_event_loop(backend->compositor->wl_display);
1290+
1291+	backend->aml = aml_new();
1292+	if (!backend->aml)
1293+		goto err_output;
1294+	aml_set_default(backend->aml);
1295+
1296+	fd = aml_get_fd(backend->aml);
1297+
1298+	backend->aml_event = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
1299+						  vnc_aml_dispatch,
1300+						  backend->aml);
1301+
1302+	backend->server = nvnc_open(config->bind_address, config->port);
1303+	if (!backend->server)
1304+		goto err_output;
1305+
1306+	nvnc_set_new_client_fn(backend->server, vnc_new_client);
1307+	nvnc_set_pointer_fn(backend->server, vnc_pointer_event);
1308+	nvnc_set_key_fn(backend->server, vnc_handle_key_event);
1309+	nvnc_set_userdata(backend->server, backend, NULL);
1310+	nvnc_set_name(backend->server, "Weston VNC backend");
1311+
1312+	ret = weston_plugin_api_register(compositor, WESTON_VNC_OUTPUT_API_NAME,
1313+					 &api, sizeof(api));
1314+	if (ret < 0) {
1315+		weston_log("Failed to register output API.\n");
1316+		goto err_output;
1317+	}
1318+
1319+	return backend;
1320+
1321+err_output:
1322+	if (backend->output)
1323+		weston_output_release(&backend->output->base);
1324+	wl_list_for_each_safe(base, next, &compositor->head_list, compositor_link)
1325+		vnc_head_destroy(base);
1326+err_compositor:
1327+	weston_compositor_shutdown(compositor);
1328+	free(backend);
1329+	return NULL;
1330+}
1331+
1332+static void
1333+config_init_to_defaults(struct weston_vnc_backend_config *config)
1334+{
1335+	config->bind_address = NULL;
1336+	config->port = 5900;
1337+	config->refresh_rate = VNC_DEFAULT_FREQ;
1338+}
1339+
1340+WL_EXPORT int
1341+weston_backend_init(struct weston_compositor *compositor,
1342+		    struct weston_backend_config *config_base)
1343+{
1344+	struct vnc_backend *backend;
1345+	struct weston_vnc_backend_config config = {{ 0, }};
1346+
1347+	weston_log("Initializing VNC backend\n");
1348+
1349+	if (config_base == NULL ||
1350+	    config_base->struct_version != WESTON_VNC_BACKEND_CONFIG_VERSION ||
1351+	    config_base->struct_size > sizeof(struct weston_vnc_backend_config)) {
1352+		weston_log("VNC backend config structure is invalid\n");
1353+		return -1;
1354+	}
1355+
1356+	config_init_to_defaults(&config);
1357+	memcpy(&config, config_base, config_base->struct_size);
1358+
1359+	backend = vnc_backend_create(compositor, &config);
1360+	if (backend == NULL)
1361+		return -1;
1362+	return 0;
1363+}
1364diff --git a/libweston/compositor.c b/libweston/compositor.c
1365index cd5c48d..24f4f36 100644
1366--- a/libweston/compositor.c
1367+++ b/libweston/compositor.c
1368@@ -9101,6 +9101,7 @@ static const char * const backend_map[] = {
1369 	[WESTON_BACKEND_DRM] =		"drm-backend.so",
1370 	[WESTON_BACKEND_HEADLESS] =	"headless-backend.so",
1371 	[WESTON_BACKEND_RDP] =		"rdp-backend.so",
1372+	[WESTON_BACKEND_VNC] =		"vnc-backend.so",
1373 	[WESTON_BACKEND_WAYLAND] =	"wayland-backend.so",
1374 	[WESTON_BACKEND_X11] =		"x11-backend.so",
1375 };
1376diff --git a/libweston/meson.build b/libweston/meson.build
1377index 6a845cc..6906244 100644
1378--- a/libweston/meson.build
1379+++ b/libweston/meson.build
1380@@ -241,5 +241,6 @@ subdir('renderer-gl')
1381 subdir('backend-drm')
1382 subdir('backend-headless')
1383 subdir('backend-rdp')
1384+subdir('backend-vnc')
1385 subdir('backend-wayland')
1386 subdir('backend-x11')
1387diff --git a/meson_options.txt b/meson_options.txt
1388index 055457d..df5ba60 100644
1389--- a/meson_options.txt
1390+++ b/meson_options.txt
1391@@ -32,6 +32,12 @@ option(
1392 	value: true,
1393 	description: 'Compositor: RDP screen-sharing support'
1394 )
1395+option(
1396+	'backend-vnc',
1397+	type: 'boolean',
1398+	value: true,
1399+	description: 'Weston backend: VNC remote screensharing'
1400+)
1401 option(
1402 	'backend-wayland',
1403 	type: 'boolean',
1404--
14052.20.1
1406
1407