1From 979198f934cb6714a234452f962e690de3eeaff9 Mon Sep 17 00:00:00 2001 2From: Stefan Agner <stefan@agner.ch> 3Date: Sun, 22 Sep 2019 19:40:04 +0200 4Subject: [PATCH 84/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 c3af8ee..e30f27e 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@@ -3174,6 +3187,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@@ -3419,6 +3516,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 d40c9aa..6163825 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