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